diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de4dc82d5..9042cda7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,19 +41,26 @@ jobs: - name: Build run: make all + - name: Make OpenAPI Spec + run: make openapi + - name: Uploading code coverage - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - run: bash <(curl -s https://codecov.io/bash) + uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: ./coverage.txt + flags: unittests + name: codecov-umbrella + fail_ci_if_error: true - name: Get branch name id: extract_branch shell: bash - run: echo "##[set-output name=branch;]$(ehco ${GITHUB_REF#refs/heads/})" + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - name: Build and push docker images env: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} if: github.event_name == 'push' - run: bash hack/docker_build.sh ${{ steps.extract_branch.outputs.branch }} \ No newline at end of file + run: bash hack/docker_build.sh ${{ steps.extract_branch.outputs.branch }} diff --git a/Makefile b/Makefile index f8231c70a..64a297d9f 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,6 @@ # Copyright 2018 The KubeSphere Authors. 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 -# described in the file LICENSE. -# -# 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. +# Use of this source code is governed by a Apache license +# that can be found in the LICENSE file. # Produce CRDs that work back to Kubernetes 1.11 (no version conversion) CRD_OPTIONS ?= "crd:trivialVersions=true" @@ -42,20 +34,12 @@ define ALL_HELP_INFO # debugging tools like delve. endef .PHONY: all -all: test hypersphere ks-apiserver ks-apigateway ks-iam controller-manager +all: test hypersphere ks-apiserver controller-manager # Build ks-apiserver binary ks-apiserver: fmt vet hack/gobuild.sh cmd/ks-apiserver -# Build ks-apigateway binary -ks-apigateway: fmt vet - hack/gobuild.sh cmd/ks-apigateway - -# Build ks-iam binary -ks-iam: fmt vet - hack/gobuild.sh cmd/ks-iam - # Build controller-manager binary controller-manager: fmt vet hack/gobuild.sh cmd/controller-manager @@ -74,7 +58,7 @@ vet: generate # Generate manifests e.g. CRD, RBAC etc. manifests: - go run ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go all + go run ./vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go object:headerFile=./hack/boilerplate.go.txt paths=./pkg/apis/... rbac:roleName=controller-perms ${CRD_OPTIONS} output:crd:artifacts:config=config/crd/bases deploy: manifests kubectl apply -f config/crds @@ -92,8 +76,10 @@ deepcopy: openapi: go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/tenant/v1alpha1 -p kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/servicemesh/v1alpha2 -p kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list - go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/api/networking/v1,./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/network/v1alpha1 -p kubesphere.io/kubesphere/pkg/apis/network/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list - go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/devops/v1alpha1 -p kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list + go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/api/networking/v1,./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./vendor/k8s.io/apimachinery/pkg/util/intstr,./pkg/apis/network/v1alpha1 -p kubesphere.io/kubesphere/pkg/apis/network/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list + go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/devops/v1alpha1,./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/api/core/v1 -p kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list + go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/cluster/v1alpha1,./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/api/core/v1 -p kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list + go run ./vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go -O openapi_generated -i ./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./pkg/apis/devops/v1alpha3,./vendor/k8s.io/apimachinery/pkg/runtime -p kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3 -h ./hack/boilerplate.go.txt --report-filename ./api/api-rules/violation_exceptions.list go run ./tools/cmd/crd-doc-gen/main.go # Build the docker image docker-build: all diff --git a/api/api-rules/violation_exceptions.list b/api/api-rules/violation_exceptions.list index 47d96cb5f..fd4c03d2b 100644 --- a/api/api-rules/violation_exceptions.list +++ b/api/api-rules/violation_exceptions.list @@ -24,7 +24,11 @@ API rule violation: list_type_missing,k8s.io/apimachinery/pkg/apis/meta/v1,Table API rule violation: list_type_missing,k8s.io/apimachinery/pkg/apis/meta/v1,TableRow,Cells API rule violation: list_type_missing,k8s.io/apimachinery/pkg/apis/meta/v1,TableRow,Conditions API rule violation: list_type_missing,k8s.io/apimachinery/pkg/apis/meta/v1,UpdateOptions,DryRun -API rule violation: list_type_missing,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1,S2iBinaryList,Items +API rule violation: list_type_missing,k8s.io/apimachinery/pkg/runtime,RawExtension,Raw +API rule violation: list_type_missing,k8s.io/apimachinery/pkg/runtime,Unknown,Raw +API rule violation: list_type_missing,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,DevOpsProjectList,Items +API rule violation: list_type_missing,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,NoScmPipeline,Parameters +API rule violation: list_type_missing,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,PipelineList,Items API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,APIResourceList,APIResources API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Duration,Duration API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEvent,Object @@ -32,4 +36,51 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,InternalEve API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,MicroTime,Time API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,StatusCause,Type API rule violation: names_match,k8s.io/apimachinery/pkg/apis/meta/v1,Time,Time -API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1,S2iBinarySpec,MD5 +API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,ContentEncoding +API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,ContentType +API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,Raw +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,ApiUri +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,CloneOption +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,CredentialId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,DiscoverBranches +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,DiscoverPRFromForks +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,DiscoverPRFromOrigin +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,RegexFilter +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,BitbucketServerSource,ScmId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,DiscarderProperty,DaysToKeep +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,DiscarderProperty,NumToKeep +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GitSource,CloneOption +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GitSource,CredentialId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GitSource,DiscoverBranches +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GitSource,RegexFilter +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GitSource,ScmId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,ApiUri +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,CloneOption +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,CredentialId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,DiscoverBranches +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,DiscoverPRFromForks +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,DiscoverPRFromOrigin +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,RegexFilter +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,GithubSource,ScmId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchJobTrigger,CreateActionJobsToTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchJobTrigger,DeleteActionJobsToTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,BitbucketServerSource +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,Description +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,GitHubSource +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,GitSource +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,MultiBranchJobTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,ScriptPath +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,SingleSvnSource +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,SourceType +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,SvnSource +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,MultiBranchPipeline,TimerTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,NoScmPipeline,Description +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,NoScmPipeline,DisableConcurrent +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,NoScmPipeline,RemoteTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,NoScmPipeline,TimerTrigger +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,Parameter,DefaultValue +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,PipelineSpec,MultiBranchPipeline +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,SingleSvnSource,CredentialId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,SingleSvnSource,ScmId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,SvnSource,CredentialId +API rule violation: names_match,kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3,SvnSource,ScmId diff --git a/api/ks-openapi-spec/swagger.json b/api/ks-openapi-spec/swagger.json index b89b2b6c2..78f7d8688 100644 --- a/api/ks-openapi-spec/swagger.json +++ b/api/ks-openapi-spec/swagger.json @@ -12,7 +12,7 @@ "name": "Apache", "url": "http://www.apache.org/licenses/" }, - "version": "2.0.2", + "version": "v0.0.0", "x-taggroups": [ { "name": "IAM", @@ -76,8 +76,7 @@ { "name": "Logging", "tags": [ - "Log Query", - "Fluent Bit Setting" + "Log Query" ] } ] @@ -112,88 +111,6 @@ } } }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project" - ], - "summary": "Get the specified DevOps Project", - "operationId": "GetDevOpsProjectHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project" - ], - "summary": "Update the specified DevOps Project", - "operationId": "UpdateProjectHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - } - } - }, "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/checkCron": { "post": { "consumes": [ @@ -248,7 +165,7 @@ } } }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/credentials": { + "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/credentials/{credential}/usage": { "get": { "consumes": [ "application/json" @@ -259,178 +176,8 @@ "tags": [ "DevOps Project Credential" ], - "summary": "Get all credentials of the specified DevOps project", - "operationId": "GetDevOpsProjectCredentialsHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - } - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Credential" - ], - "summary": "Create a credential in the specified DevOps project", - "operationId": "CreateDevOpsProjectCredentialHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/credentials/{credential}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Credential" - ], - "summary": "Get the specified credential of the DevOps project", - "operationId": "GetDevOpsProjectCredentialHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "credential's ID, e.g. dockerhub-id", - "name": "credential", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "\nGet extra credential content if this query parameter is set. \nSpecifically, there are three types of info in a credential. One is the basic info that must be returned for each query such as name, id, etc.\nThe second one is non-encrypted info such as the username of the username-password type of credential, which returns when the \"content\" parameter is set to non-empty.\nThe last one is encrypted info, such as the password of the username-password type of credential, which never returns.\n", - "name": "content", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Credential" - ], - "summary": "Update the specified credential of the DevOps project", - "operationId": "UpdateDevOpsProjectCredentialHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "credential's ID, e.g. dockerhub-id", - "name": "credential", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.JenkinsCredential" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Credential" - ], - "summary": "Delete the specified credential of the DevOps project", - "operationId": "DeleteDevOpsProjectCredentialHandler", + "summary": "Get the specified credential usage of the DevOps project", + "operationId": "GetProjectCredentialUsage", "parameters": [ { "type": "string", @@ -447,331 +194,17 @@ "required": true } ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/defaultroles": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Get the build-in roles info of the specified DevOps project", - "operationId": "GetDevOpsProjectDefaultRoles", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - } - ], "responses": { "200": { "description": "ok", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.Role" - } + "$ref": "#/definitions/devops.Credential" } }, "default": { "description": "ok", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.Role" - } - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/members": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Get the members of the specified DevOps project", - "operationId": "GetDevOpsProjectMembersHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "limit=%d,page=%d", - "default": "limit=10,page=1", - "description": "page", - "name": "paging", - "in": "query" - }, - { - "type": "string", - "format": "key=%s,key~%s", - "description": "query conditions, support using key-value pairs separated by comma to search, like 'conditions:somekey=somevalue,anotherkey=anothervalue'", - "name": "conditions", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Add a member to the specified DevOps project", - "operationId": "AddDevOpsProjectMemberHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/members/{member}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Get the specified member of the DevOps project", - "operationId": "GetDevOpsProjectMemberHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "member's username, e.g. admin", - "name": "member", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - } - }, - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Delete the specified member of the DevOps project", - "operationId": "DeleteDevOpsProjectMemberHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "member's username, e.g. admin", - "name": "member", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "patch": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Project Member" - ], - "summary": "Update the specified member of the DevOps project", - "operationId": "UpdateDevOpsProjectMemberHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "member's username, e.g. admin", - "name": "member", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.DevOpsProjectMembership" - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Create a DevOps project pipeline", - "operationId": "CreateDevOpsProjectPipelineHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" + "$ref": "#/definitions/devops.Credential" } } } @@ -820,82 +253,6 @@ } } } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Update the specified pipeline of the DevOps project", - "operationId": "UpdateDevOpsProjectPipelineHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of pipeline, e.g. sample-pipeline", - "name": "pipeline", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" - } - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Delete the specified pipeline of the DevOps project", - "operationId": "DeleteDevOpsProjectPipelineHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of pipeline, e.g. sample-pipeline", - "name": "pipeline", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - } } }, "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines/{pipeline}/branches": { @@ -910,7 +267,7 @@ "DevOps Pipeline" ], "summary": "(MultiBranchesPipeline) Get all branches in the specified pipeline.", - "operationId": "GetPipeBranch", + "operationId": "GetPipelineBranch", "parameters": [ { "type": "string", @@ -956,7 +313,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/devops.PipeBranch" + "$ref": "#/definitions/devops.PipelineBranch" } } }, @@ -965,7 +322,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/devops.PipeBranch" + "$ref": "#/definitions/devops.PipelineBranch" } } } @@ -983,7 +340,7 @@ "tags": [ "DevOps Pipeline" ], - "summary": "(MultiBranchesPipeline) Get all activities in the specified pipeline.", + "summary": "(MultiBranchesPipeline) Get the specified branch pipeline of the DevOps project", "operationId": "GetBranchPipeline", "parameters": [ { @@ -1072,13 +429,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.QueuedBlueRun" + "$ref": "#/definitions/devops.RunPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.QueuedBlueRun" + "$ref": "#/definitions/devops.RunPipeline" } } } @@ -1095,7 +452,7 @@ "tags": [ "DevOps Pipeline" ], - "summary": "(MultiBranchesPipeline) Get all runs in the specified branch", + "summary": "(MultiBranchesPipeline) Get details in the specified pipeline activity.", "operationId": "GetBranchPipelineRun", "parameters": [ { @@ -1131,13 +488,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.BranchPipelineRun" + "$ref": "#/definitions/devops.PipelineRun" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.BranchPipelineRun" + "$ref": "#/definitions/devops.PipelineRun" } } } @@ -1292,7 +649,7 @@ "DevOps Pipeline" ], "summary": "(MultiBranchesPipeline) Get run nodes.", - "operationId": "GetPipelineRunNodesbyBranch", + "operationId": "GetBranchPipelineRunNodes", "parameters": [ { "type": "string", @@ -1681,13 +1038,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.ReplayPipe" + "$ref": "#/definitions/devops.ReplayPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.ReplayPipe" + "$ref": "#/definitions/devops.ReplayPipeline" } } } @@ -1756,71 +1113,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.StopPipe" + "$ref": "#/definitions/devops.StopPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.StopPipe" - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines/{pipeline}/branches/{branch}/sonarstatus": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Get the sonar quality check information for the specified pipeline branch of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/", - "operationId": "GetMultiBranchesPipelineSonarStatusHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of pipeline, e.g. sample-pipeline", - "name": "pipeline", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "branch name, e.g. master", - "name": "branch", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.SonarStatus" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.SonarStatus" - } + "$ref": "#/definitions/devops.StopPipeline" } } } @@ -1881,51 +1180,6 @@ } } }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines/{pipeline}/config": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Get the configuration information of the specified pipeline of the DevOps Project", - "operationId": "GetDevOpsProjectPipelineHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of pipeline, e.g. sample-pipeline", - "name": "pipeline", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/devops.ProjectPipeline" - } - } - } - } - }, "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines/{pipeline}/consolelog": { "get": { "consumes": [ @@ -1974,7 +1228,7 @@ "DevOps Pipeline" ], "summary": "Get all runs of the specified pipeline", - "operationId": "SearchPipelineRuns", + "operationId": "ListPipelineRuns", "parameters": [ { "type": "string", @@ -2016,13 +1270,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/struct%20%7B%20Items%20%7C%7Cdevops.BranchPipelineRun%20%22json:%5C%22items%5C%22%22;%20Total%20int%20%22json:%5C%22total_count%5C%22%22%20%7D" + "$ref": "#/definitions/devops.PipelineRunList" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/struct%20%7B%20Items%20%7C%7Cdevops.BranchPipelineRun%20%22json:%5C%22items%5C%22%22;%20Total%20int%20%22json:%5C%22total_count%5C%22%22%20%7D" + "$ref": "#/definitions/devops.PipelineRunList" } } } @@ -2067,13 +1321,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.QueuedBlueRun" + "$ref": "#/definitions/devops.RunPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.QueuedBlueRun" + "$ref": "#/definitions/devops.RunPipeline" } } } @@ -2090,7 +1344,7 @@ "tags": [ "DevOps Pipeline" ], - "summary": "Get all activities in the specified pipeline.", + "summary": "Get details in the specified pipeline activity.", "operationId": "GetPipelineRun", "parameters": [ { @@ -2612,13 +1866,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.ReplayPipe" + "$ref": "#/definitions/devops.ReplayPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.ReplayPipe" + "$ref": "#/definitions/devops.ReplayPipeline" } } } @@ -2680,13 +1934,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.StopPipe" + "$ref": "#/definitions/devops.StopPipeline" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/devops.StopPipe" + "$ref": "#/definitions/devops.StopPipeline" } } } @@ -2735,154 +1989,6 @@ } } }, - "/kapis/devops.kubesphere.io/v1alpha2/devops/{devops}/pipelines/{pipeline}/sonarstatus": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "DevOps Pipeline" - ], - "summary": "Get the sonar quality information for the specified pipeline of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/", - "operationId": "GetPipelineSonarStatusHandler", - "parameters": [ - { - "type": "string", - "description": "DevOps project's ID, e.g. project-RRRRAzLBlLEm", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of pipeline, e.g. sample-pipeline", - "name": "pipeline", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.SonarStatus" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.SonarStatus" - } - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file": { - "put": { - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "summary": "Upload S2iBinary file", - "operationId": "UploadS2iBinary", - "parameters": [ - { - "type": "string", - "description": "the name of namespaces", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of s2ibinary", - "name": "s2ibinary", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "file to upload", - "name": "s2ibinary", - "in": "formData" - }, - { - "type": "string", - "description": "md5 of file", - "name": "md5", - "in": "formData" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha1.S2iBinary" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha1.S2iBinary" - } - } - } - } - }, - "/kapis/devops.kubesphere.io/v1alpha2/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file/{file}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/octet-stream" - ], - "summary": "Download S2iBinary file", - "operationId": "DownloadS2iBinary", - "parameters": [ - { - "type": "string", - "description": "the name of namespaces", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of s2ibinary", - "name": "s2ibinary", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of binary file", - "name": "file", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok" - }, - "default": { - "description": "ok" - } - } - } - }, "/kapis/devops.kubesphere.io/v1alpha2/scms/{scm}/organizations": { "get": { "consumes": [ @@ -3148,7 +2254,7 @@ "DevOps Pipeline" ], "summary": "Search DevOps resource. More info: https://github.com/jenkinsci/blueocean-plugin/tree/master/blueocean-rest#get-pipelines-across-organization", - "operationId": "SearchPipelines", + "operationId": "ListPipelines", "parameters": [ { "type": "string", @@ -3184,13 +2290,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/struct%20%7B%20Items%20%7C%7Cdevops.Pipeline%20%22json:%5C%22items%5C%22%22;%20Total%20int%20%22json:%5C%22total_count%5C%22%22%20%7D" + "$ref": "#/definitions/devops.PipelineList" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/struct%20%7B%20Items%20%7C%7Cdevops.Pipeline%20%22json:%5C%22items%5C%22%22;%20Total%20int%20%22json:%5C%22total_count%5C%22%22%20%7D" + "$ref": "#/definitions/devops.PipelineList" } } } @@ -3337,6 +2443,7 @@ "/kapis/devops.kubesphere.io/v1alpha2/webhook/github": { "post": { "consumes": [ + "application/x-www-form-urlencoded", "application/json" ], "produces": [ @@ -3354,45 +2461,6 @@ } } }, - "/kapis/iam.kubesphere.io/v1alpha2/authenticate": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver.", - "operationId": "TokenReviewHandler", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/iam.TokenReview" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/iam.TokenReview" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/iam.TokenReview" - } - } - } - } - }, "/kapis/iam.kubesphere.io/v1alpha2/clusterroles": { "get": { "consumes": [ @@ -3410,57 +2478,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.ClusterRoleList" + "$ref": "#/definitions/models.PageableResponse" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.ClusterRoleList" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/clusterroles/{clusterrole}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "List all policy rules of the specified cluster role.", - "operationId": "ListClusterRoleRules", - "parameters": [ - { - "type": "string", - "description": "cluster role name", - "name": "clusterrole", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } + "$ref": "#/definitions/models.PageableResponse" } } } @@ -3490,106 +2514,7 @@ ], "responses": { "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/devops/{devops}/roles/{role}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "List all policy rules of the specified role in the given devops project.", - "operationId": "ListDevopsRoleRules", - "parameters": [ - { - "type": "string", - "description": "devops project ID", - "name": "devops", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "devops role name", - "name": "role", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/login": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "KubeSphere APIs support token-based authentication via the Authtoken request header. The POST Login API is used to retrieve the authentication token. After the authentication token is obtained, it must be inserted into the Authtoken header for all requests.", - "operationId": "Login", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/iam.LoginRequest" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.Token" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.Token" - } + "description": "OK" } } } @@ -3620,115 +2545,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.RoleList" + "$ref": "#/definitions/models.PageableResponse" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.RoleList" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/namespaces/{namespace}/roles/{role}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "List all policy rules of the specified role in the given namespace.", - "operationId": "ListRoleRules", - "parameters": [ - { - "type": "string", - "description": "kubernetes namespace", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "role name", - "name": "role", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/namespaces/{namespace}/roles/{role}/users": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "Retrieve the users that are bound to the role in the specified namespace.", - "operationId": "ListRoleUsers", - "parameters": [ - { - "type": "string", - "description": "kubernetes namespace", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "role name", - "name": "role", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.NamespacedUser" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.NamespacedUser" - } + "$ref": "#/definitions/models.PageableResponse" } } } @@ -3758,158 +2581,7 @@ ], "responses": { "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.NamespacedUser" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.NamespacedUser" - } - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/rulesmapping/clusterroles": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "Get the mapping relationships between cluster roles and policy rules.", - "operationId": "ClusterRulesMapping", - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Rule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Rule" - } - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/rulesmapping/roles": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "Get the mapping relationships between namespaced roles and policy rules.", - "operationId": "RulesMapping", - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Rule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Rule" - } - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/users": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "List all users.", - "operationId": "ListUsers", - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "Create a user account.", - "operationId": "CreateUser", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1alpha2.CreateUserRequest" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } + "description": "OK" } } } @@ -3923,9 +2595,9 @@ "application/json" ], "tags": [ - "Identity Management" + "Access Management" ], - "summary": "Describe the specified user.", + "summary": "Retrieve user details.", "operationId": "DescribeUser", "parameters": [ { @@ -3940,137 +2612,19 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/models.User" + "$ref": "#/definitions/v1alpha2.UserDetail" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/models.User" - } - } - } - }, - "put": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "Update information about the specified user.", - "operationId": "UpdateUser", - "parameters": [ - { - "type": "string", - "description": "username", - "name": "user", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1alpha2.UserUpdateRequest" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - } - } - }, - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "Delete the specified user.", - "operationId": "DeleteUser", - "parameters": [ - { - "type": "string", - "description": "username", - "name": "user", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" + "$ref": "#/definitions/v1alpha2.UserDetail" } } } } }, - "/kapis/iam.kubesphere.io/v1alpha2/users/{user}/logs": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Identity Management" - ], - "summary": "Retrieve the \"login logs\" for the specified user.", - "operationId": "UserLoginLogs", - "parameters": [ - { - "type": "string", - "description": "username", - "name": "user", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.LoginLog" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.LoginLog" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/users/{user}/roles": { + "/kapis/iam.kubesphere.io/v1alpha2/users/{user}/clusterroles": { "get": { "consumes": [ "application/json" @@ -4081,8 +2635,8 @@ "tags": [ "Access Management" ], - "summary": "Retrieve all the roles that are assigned to the specified user.", - "operationId": "ListUserRoles", + "summary": "Retrieve user roles in clusters.", + "operationId": "ListRolesOfUser", "parameters": [ { "type": "string", @@ -4096,13 +2650,89 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/iam.RoleList" + "$ref": "#/definitions/v1alpha2.RoleList" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/iam.RoleList" + "$ref": "#/definitions/v1alpha2.RoleList" + } + } + } + } + }, + "/kapis/iam.kubesphere.io/v1alpha2/users/{user}/namespaceroles": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Access Management" + ], + "summary": "Retrieve user roles in namespaces.", + "operationId": "ListRolesOfUser", + "parameters": [ + { + "type": "string", + "description": "username", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.RoleList" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.RoleList" + } + } + } + } + }, + "/kapis/iam.kubesphere.io/v1alpha2/users/{user}/workspaceroles": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Access Management" + ], + "summary": "Retrieve user roles in workspaces.", + "operationId": "ListRolesOfUser", + "parameters": [ + { + "type": "string", + "description": "username", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.RoleList" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.RoleList" } } } @@ -4132,16 +2762,7 @@ ], "responses": { "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.UserList" - } + "description": "OK" } } }, @@ -4164,76 +2785,16 @@ "name": "workspace", "in": "path", "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1alpha2.InviteUserRequest" - } } ], "responses": { "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } + "description": "OK" } } } }, "/kapis/iam.kubesphere.io/v1alpha2/workspaces/{workspace}/members/{member}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "Describe the specified user in the given workspace.", - "operationId": "DescribeWorkspaceUser", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "username", - "name": "member", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DescribeWorkspaceUserResponse" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DescribeWorkspaceUserResponse" - } - } - } - }, "delete": { "consumes": [ "application/json" @@ -4302,112 +2863,7 @@ ], "responses": { "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.ClusterRoleList" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.ClusterRoleList" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/workspaces/{workspace}/roles/{role}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "Describe the workspace role.", - "operationId": "DescribeWorkspaceRole", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "workspace role name", - "name": "role", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1.ClusterRole" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1.ClusterRole" - } - } - } - } - }, - "/kapis/iam.kubesphere.io/v1alpha2/workspaces/{workspace}/roles/{role}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Access Management" - ], - "summary": "List all policy rules of the specified workspace role.", - "operationId": "ListWorkspaceRoleRules", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "workspace role name", - "name": "role", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/models.SimpleRule" - } - } + "description": "OK" } } } @@ -4424,7 +2880,7 @@ "Log Query" ], "summary": "Query logs against the cluster.", - "operationId": "LoggingQueryCluster", + "operationId": "handleClusterQuery", "parameters": [ { "type": "string", @@ -4544,405 +3000,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" + "$ref": "#/definitions/v1alpha2.APIResponse" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - } - } - } - }, - "/kapis/logging.kubesphere.io/v1alpha2/fluentbit/outputs": { - "get": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json", - "application/octet-stream" - ], - "tags": [ - "Fluent Bit Setting" - ], - "summary": "List all Fluent bit output plugins.", - "operationId": "LoggingQueryFluentbitOutputs", - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - } - } - }, - "post": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Fluent Bit Setting" - ], - "summary": "Add a new Fluent bit output plugin.", - "operationId": "LoggingInsertFluentbitOutput", - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/fluentbitclient.OutputPlugin" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - } - } - } - }, - "/kapis/logging.kubesphere.io/v1alpha2/fluentbit/outputs/{output}": { - "put": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Fluent Bit Setting" - ], - "summary": "Update the specific Fluent bit output plugin.", - "operationId": "LoggingUpdateFluentbitOutput", - "parameters": [ - { - "type": "string", - "description": "ID of the output.", - "name": "output", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/fluentbitclient.OutputPlugin" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - } - } - }, - "delete": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Fluent Bit Setting" - ], - "summary": "Delete the specific Fluent bit output plugin.", - "operationId": "LoggingDeleteFluentbitOutput", - "parameters": [ - { - "type": "string", - "description": "ID of the output.", - "name": "output", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/log.FluentbitOutputsResult" - } - } - } - } - }, - "/kapis/logging.kubesphere.io/v1alpha2/namespaces/{namespace}": { - "get": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Log Query" - ], - "summary": "Query logs against the specific namespace.", - "operationId": "LoggingQueryNamespace", - "parameters": [ - { - "type": "string", - "description": "The name of the namespace.", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "default": "query", - "description": "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.", - "name": "operation", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`", - "name": "workloads", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "workload_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`", - "name": "pods", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "pod_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`", - "name": "containers", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "container_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).", - "name": "log_query", - "in": "query" - }, - { - "type": "string", - "default": "15m", - "description": "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).", - "name": "interval", - "in": "query" - }, - { - "type": "string", - "description": "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "default": "desc", - "description": "Sort order. One of acs, desc. This field sorts logs by timestamp.", - "name": "sort", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).", - "name": "from", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - } - } - } - }, - "/kapis/logging.kubesphere.io/v1alpha2/namespaces/{namespace}/pods/{pod}": { - "get": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Log Query" - ], - "summary": "Query logs against the specific pod.", - "operationId": "LoggingQueryPod", - "parameters": [ - { - "type": "string", - "description": "The name of the namespace.", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Pod name.", - "name": "pod", - "in": "path", - "required": true - }, - { - "type": "string", - "default": "query", - "description": "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.", - "name": "operation", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`", - "name": "containers", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "container_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).", - "name": "log_query", - "in": "query" - }, - { - "type": "string", - "default": "15m", - "description": "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).", - "name": "interval", - "in": "query" - }, - { - "type": "string", - "description": "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "default": "desc", - "description": "Sort order. One of acs, desc. This field sorts logs by timestamp.", - "name": "sort", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).", - "name": "from", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" + "$ref": "#/definitions/v1alpha2.APIResponse" } } } @@ -4955,13 +3019,14 @@ "application/xml" ], "produces": [ - "application/json" + "application/json", + "text/plain" ], "tags": [ "Log Query" ], "summary": "Query logs against the specific container.", - "operationId": "LoggingQueryContainer", + "operationId": "handleContainerQuery", "parameters": [ { "type": "string", @@ -5042,283 +3107,19 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" + "$ref": "#/definitions/v1alpha2.APIResponse" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" + "$ref": "#/definitions/v1alpha2.APIResponse" } } } } }, - "/kapis/logging.kubesphere.io/v1alpha2/namespaces/{namespace}/workloads/{workload}": { - "get": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Log Query" - ], - "summary": "Query logs against the specific workload.", - "operationId": "LoggingQueryWorkload", - "parameters": [ - { - "type": "string", - "description": "The name of the namespace.", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "The name of the workload.", - "name": "workload", - "in": "path", - "required": true - }, - { - "type": "string", - "default": "query", - "description": "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.", - "name": "operation", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`", - "name": "pods", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "pod_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`", - "name": "containers", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "container_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).", - "name": "log_query", - "in": "query" - }, - { - "type": "string", - "default": "15m", - "description": "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).", - "name": "interval", - "in": "query" - }, - { - "type": "string", - "description": "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "default": "desc", - "description": "Sort order. One of acs, desc. This field sorts logs by timestamp.", - "name": "sort", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).", - "name": "from", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - } - } - } - }, - "/kapis/logging.kubesphere.io/v1alpha2/workspaces/{workspace}": { - "get": { - "consumes": [ - "application/json", - "application/xml" - ], - "produces": [ - "application/json", - "application/octet-stream" - ], - "tags": [ - "Log Query" - ], - "summary": "Query logs against the specific workspace.", - "operationId": "LoggingQueryWorkspace", - "parameters": [ - { - "type": "string", - "description": "The name of the workspace.", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "default": "query", - "description": "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.", - "name": "operation", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of namespaces. This field restricts the query to specified namespaces. For example, the following filter matches the namespace my-ns and demo-ns: `my-ns,demo-ns`", - "name": "namespaces", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **namespaces**, this field performs fuzzy matching on namespaces. For example, the following value limits the query to namespaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "namespace_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`", - "name": "workloads", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "workload_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`", - "name": "pods", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "pod_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`", - "name": "containers", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "container_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).", - "name": "log_query", - "in": "query" - }, - { - "type": "string", - "default": "15m", - "description": "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).", - "name": "interval", - "in": "query" - }, - { - "type": "string", - "description": "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "default": "desc", - "description": "Sort order. One of acs, desc. This field sorts logs by timestamp.", - "name": "sort", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).", - "name": "from", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.QueryResult" - } - } - } - } - }, - "/kapis/monitoring.kubesphere.io/v1alpha2/cluster": { + "/kapis/monitoring.kubesphere.io/v1alpha3/cluster": { "get": { "consumes": [ "application/json" @@ -5330,7 +3131,7 @@ "Cluster Metrics" ], "summary": "Get cluster-level metric data.", - "operationId": "MonitorCluster", + "operationId": "handleClusterMetricsQuery", "parameters": [ { "type": "string", @@ -5362,35 +3163,28 @@ "description": "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.", "name": "time", "in": "query" - }, - { - "type": "string", - "description": "Additional operations. Currently available types is statistics. It retrieves the total number of workspaces, devops projects, namespaces, accounts in the cluster at the moment.", - "name": "type", - "in": "query" } ], "responses": { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/components/{component}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/components/{component}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5399,7 +3193,7 @@ "Component Metrics" ], "summary": "Get component-level metric data of the specific system component.", - "operationId": "MonitorComponent", + "operationId": "handleComponentMetricsQuery", "parameters": [ { "type": "string", @@ -5444,23 +3238,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5469,7 +3262,7 @@ "Namespace Metrics" ], "summary": "Get namespace-level metric data of all namespaces.", - "operationId": "MonitorNamespace", + "operationId": "handleNamespaceMetricsQuery", "parameters": [ { "type": "string", @@ -5539,23 +3332,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5564,7 +3356,7 @@ "Namespace Metrics" ], "summary": "Get namespace-level metric data of the specific namespace.", - "operationId": "MonitorNamespace", + "operationId": "handleNamespaceMetricsQuery", "parameters": [ { "type": "string", @@ -5609,23 +3401,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/persistentvolumeclaims": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/persistentvolumeclaims": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5634,7 +3425,7 @@ "PVC Metrics" ], "summary": "Get PVC-level metric data of the specific namespace's PVCs.", - "operationId": "MonitorPVC", + "operationId": "handlePVCMetricsQuery", "parameters": [ { "type": "string", @@ -5711,23 +3502,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/persistentvolumeclaims/{pvc}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/persistentvolumeclaims/{pvc}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5736,7 +3526,7 @@ "PVC Metrics" ], "summary": "Get PVC-level metric data of a specific PVC. Navigate to the PVC by the PVC's namespace.", - "operationId": "MonitorPVC", + "operationId": "handlePVCMetricsQuery", "parameters": [ { "type": "string", @@ -5788,23 +3578,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/pods": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/pods": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5813,7 +3602,7 @@ "Pod Metrics" ], "summary": "Get pod-level metric data of the specific namespace's pods.", - "operationId": "MonitorPod", + "operationId": "handlePodMetricsQuery", "parameters": [ { "type": "string", @@ -5890,23 +3679,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/pods/{pod}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/pods/{pod}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5915,7 +3703,7 @@ "Pod Metrics" ], "summary": "Get pod-level metric data of a specific pod. Navigate to the pod by the pod's namespace.", - "operationId": "MonitorPod", + "operationId": "handlePodMetricsQuery", "parameters": [ { "type": "string", @@ -5967,23 +3755,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/pods/{pod}/containers": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/pods/{pod}/containers": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -5992,7 +3779,7 @@ "Container Metrics" ], "summary": "Get container-level metric data of a specific pod's containers. Navigate to the pod by the pod's namespace.", - "operationId": "MonitorContainer", + "operationId": "handleContainerMetricsQuery", "parameters": [ { "type": "string", @@ -6076,23 +3863,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/pods/{pod}/containers/{container}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/pods/{pod}/containers/{container}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6101,7 +3887,7 @@ "Container Metrics" ], "summary": "Get container-level metric data of a specific container. Navigate to the container by the pod name and the namespace.", - "operationId": "MonitorContainer", + "operationId": "handleContainerMetricsQuery", "parameters": [ { "type": "string", @@ -6160,23 +3946,130 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/workloads": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/targets/metadata": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Custom Metrics" + ], + "summary": "Get metadata of metrics for the specific namespace.", + "operationId": "handleMetadataQuery", + "parameters": [ + { + "type": "string", + "description": "The name of the namespace.", + "name": "namespace", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/monitoring.Metadata" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/monitoring.Metadata" + } + } + } + } + }, + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/targets/query": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Custom Metrics" + ], + "summary": "Make an ad-hoc query in the specific namespace.", + "operationId": "handleAdhocQuery", + "parameters": [ + { + "type": "string", + "description": "The name of the namespace.", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "The expression to be evaluated.", + "name": "expr", + "in": "query" + }, + { + "type": "string", + "description": "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ", + "name": "start", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ", + "name": "end", + "in": "query" + }, + { + "type": "string", + "default": "10m", + "description": "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).", + "name": "step", + "in": "query" + }, + { + "type": "string", + "description": "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.", + "name": "time", + "in": "query" + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/monitoring.Metric" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/monitoring.Metric" + } + } + } + } + }, + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/workloads": { + "get": { + "consumes": [ + "application/json" ], "produces": [ "application/json" @@ -6185,7 +4078,7 @@ "Workload Metrics" ], "summary": "Get workload-level metric data of a specific namespace's workloads.", - "operationId": "MonitorWorkload", + "operationId": "handleWorkloadMetricsQuery", "parameters": [ { "type": "string", @@ -6262,23 +4155,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/workloads/{kind}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/workloads/{kind}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6287,7 +4179,7 @@ "Workload Metrics" ], "summary": "Get workload-level metric data of all workloads which belongs to a specific kind.", - "operationId": "MonitorWorkload", + "operationId": "handleWorkloadMetricsQuery", "parameters": [ { "type": "string", @@ -6371,23 +4263,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/namespaces/{namespace}/workloads/{kind}/{workload}/pods": { + "/kapis/monitoring.kubesphere.io/v1alpha3/namespaces/{namespace}/workloads/{kind}/{workload}/pods": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6396,7 +4287,7 @@ "Pod Metrics" ], "summary": "Get pod-level metric data of a specific workload's pods. Navigate to the workload by the namespace.", - "operationId": "MonitorPod", + "operationId": "handlePodMetricsQuery", "parameters": [ { "type": "string", @@ -6487,23 +4378,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/nodes": { + "/kapis/monitoring.kubesphere.io/v1alpha3/nodes": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6512,7 +4402,7 @@ "Node Metrics" ], "summary": "Get node-level metric data of all nodes.", - "operationId": "MonitorNode", + "operationId": "handleNodeMetricsQuery", "parameters": [ { "type": "string", @@ -6582,23 +4472,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/nodes/{node}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/nodes/{node}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6607,7 +4496,7 @@ "Node Metrics" ], "summary": "Get node-level metric data of the specific node.", - "operationId": "MonitorNode", + "operationId": "handleNodeMetricsQuery", "parameters": [ { "type": "string", @@ -6652,23 +4541,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/nodes/{node}/pods": { + "/kapis/monitoring.kubesphere.io/v1alpha3/nodes/{node}/pods": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6677,7 +4565,7 @@ "Pod Metrics" ], "summary": "Get pod-level metric data of all pods on a specific node.", - "operationId": "MonitorPod", + "operationId": "handlePodMetricsQuery", "parameters": [ { "type": "string", @@ -6754,23 +4642,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/nodes/{node}/pods/{pod}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/nodes/{node}/pods/{pod}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6779,7 +4666,7 @@ "Pod Metrics" ], "summary": "Get pod-level metric data of a specific pod. Navigate to the pod by the node where it is scheduled.", - "operationId": "MonitorPod", + "operationId": "handlePodMetricsQuery", "parameters": [ { "type": "string", @@ -6831,23 +4718,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/storageclasses/{storageclass}/persistentvolumeclaims": { + "/kapis/monitoring.kubesphere.io/v1alpha3/storageclasses/{storageclass}/persistentvolumeclaims": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6856,7 +4742,7 @@ "PVC Metrics" ], "summary": "Get PVC-level metric data of the specific storageclass's PVCs.", - "operationId": "MonitorPVC", + "operationId": "handlePVCMetricsQuery", "parameters": [ { "type": "string", @@ -6933,23 +4819,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/workspaces": { + "/kapis/monitoring.kubesphere.io/v1alpha3/workspaces": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -6958,7 +4843,7 @@ "Workspace Metrics" ], "summary": "Get workspace-level metric data of all workspaces.", - "operationId": "MonitorWorkspace", + "operationId": "handleWorkspaceMetricsQuery", "parameters": [ { "type": "string", @@ -7028,23 +4913,22 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/workspaces/{workspace}": { + "/kapis/monitoring.kubesphere.io/v1alpha3/workspaces/{workspace}": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -7053,7 +4937,7 @@ "Workspace Metrics" ], "summary": "Get workspace-level metric data of a specific workspace.", - "operationId": "MonitorWorkspace", + "operationId": "handleWorkspaceMetricsQuery", "parameters": [ { "type": "string", @@ -7092,35 +4976,28 @@ "description": "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.", "name": "time", "in": "query" - }, - { - "type": "string", - "description": "Additional operations. Currently available types is statistics. It retrieves the total number of namespaces, devops projects, members and roles in this workspace at the moment.", - "name": "type", - "in": "query" } ], "responses": { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } } } } }, - "/kapis/monitoring.kubesphere.io/v1alpha2/workspaces/{workspace}/namespaces": { + "/kapis/monitoring.kubesphere.io/v1alpha3/workspaces/{workspace}/namespaces": { "get": { "consumes": [ - "application/json", - "application/xml" + "application/json" ], "produces": [ "application/json" @@ -7129,7 +5006,7 @@ "Namespace Metrics" ], "summary": "Get namespace-level metric data of a specific workspace.", - "operationId": "MonitorNamespace", + "operationId": "handleNamespaceMetricsQuery", "parameters": [ { "type": "string", @@ -7206,13 +5083,96 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/metrics.Response" + "$ref": "#/definitions/monitoring.Metrics" + } + } + } + } + }, + "/kapis/network.kubesphere.io/v1alpha2/namespaces/{namespace}/topology": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Network Topology" + ], + "summary": "Get the topology with specifying a namespace", + "operationId": "getNamespaceTopology", + "parameters": [ + { + "type": "string", + "description": "name of the namespace", + "name": "namespace", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.TopologyResponse" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.TopologyResponse" + } + } + } + } + }, + "/kapis/network.kubesphere.io/v1alpha2/namespaces/{namespace}/topology/{node_id}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Network Topology" + ], + "summary": "Get the topology with specifying a node id in the whole topology and specifying a namespace", + "operationId": "getNamespaceNodeTopology", + "parameters": [ + { + "type": "string", + "description": "name of the namespace", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "id of the node in the whole topology", + "name": "node_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.NodeResponse" + } + }, + "default": { + "description": "ok", + "schema": { + "$ref": "#/definitions/v1alpha2.NodeResponse" } } } @@ -8828,7 +6788,7 @@ "application/json" ], "summary": "Rerun job whether the job is complete or not", - "operationId": "RerunJob", + "operationId": "handleJobReRun", "deprecated": true, "parameters": [ { @@ -8850,41 +6810,12 @@ "description": "action must be \"rerun\"", "name": "action", "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - } - } - } - }, - "/kapis/operations.kubesphere.io/v1alpha2/nodes/{node}/drainage": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "summary": "remove a node from service, safely evict all of your pods from a node and you can power down the node. More info: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/", - "operationId": "DrainNode", - "deprecated": true, - "parameters": [ { "type": "string", - "description": "node name", - "name": "node", - "in": "path", + "description": "version of job, rerun when the version matches", + "name": "resourceVersion", + "in": "query", "required": true } ], @@ -8916,18 +6847,18 @@ "Cluster Resources" ], "summary": "get abnormal workloads' count of whole cluster", - "operationId": "GetClusterAbnormalWorkloads", + "operationId": "handleGetNamespacedAbnormalWorkloads", "responses": { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/status.WorkLoadStatus" + "$ref": "#/definitions/api.Workloads" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/status.WorkLoadStatus" + "$ref": "#/definitions/api.Workloads" } } } @@ -8945,18 +6876,18 @@ "Component Status" ], "summary": "Get the health status of system components.", - "operationId": "GetSystemHealthStatus", + "operationId": "handleGetSystemHealthStatus", "responses": { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/models.HealthStatus" + "$ref": "#/definitions/v1alpha2.HealthStatus" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/models.HealthStatus" + "$ref": "#/definitions/v1alpha2.HealthStatus" } } } @@ -8974,14 +6905,14 @@ "Component Status" ], "summary": "List the system components.", - "operationId": "GetComponents", + "operationId": "handleGetComponents", "responses": { "200": { "description": "ok", "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.ComponentStatus" + "$ref": "#/definitions/v1alpha2.ComponentStatus" } } }, @@ -8990,7 +6921,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/models.ComponentStatus" + "$ref": "#/definitions/v1alpha2.ComponentStatus" } } } @@ -9009,7 +6940,7 @@ "Component Status" ], "summary": "Describe the specified system component.", - "operationId": "GetComponentStatus", + "operationId": "handleGetComponentStatus", "parameters": [ { "type": "string", @@ -9023,13 +6954,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ComponentStatus" + "$ref": "#/definitions/v1alpha2.ComponentStatus" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ComponentStatus" + "$ref": "#/definitions/v1alpha2.ComponentStatus" } } } @@ -9047,7 +6978,7 @@ "Verification" ], "summary": "Verify if the kubernetes secret has read access to the git repository", - "operationId": "GitReadVerify", + "operationId": "handleVerifyGitCredential", "parameters": [ { "name": "body", @@ -9086,7 +7017,7 @@ "Namespace Resources" ], "summary": "get abnormal workloads' count of specified namespace", - "operationId": "GetNamespacedAbnormalWorkloads", + "operationId": "handleGetNamespacedAbnormalWorkloads", "parameters": [ { "type": "string", @@ -9100,13 +7031,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/status.WorkLoadStatus" + "$ref": "#/definitions/api.Workloads" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/status.WorkLoadStatus" + "$ref": "#/definitions/api.Workloads" } } } @@ -9124,7 +7055,7 @@ "Namespace Resources" ], "summary": "Get the specified daemonset revision", - "operationId": "GetDaemonSetRevision", + "operationId": "handleGetDaemonSetRevision", "parameters": [ { "type": "string", @@ -9176,7 +7107,7 @@ "Namespace Resources" ], "summary": "Get the specified deployment revision", - "operationId": "GetDeployRevision", + "operationId": "handleGetDeploymentRevision", "parameters": [ { "type": "string", @@ -9216,57 +7147,6 @@ } } }, - "/kapis/resources.kubesphere.io/v1alpha2/namespaces/{namespace}/jobs/{job}": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Namespace Resources" - ], - "summary": "Rerun job whether the job is complete or not", - "operationId": "RerunJob", - "parameters": [ - { - "type": "string", - "description": "job name", - "name": "job", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of the namespace where the job runs in", - "name": "namespace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "action must be \"rerun\"", - "name": "action", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - } - } - } - }, "/kapis/resources.kubesphere.io/v1alpha2/namespaces/{namespace}/quotas": { "get": { "consumes": [ @@ -9279,7 +7159,7 @@ "Namespace Resources" ], "summary": "get specified namespace's resource quota and usage", - "operationId": "GetNamespaceQuotas", + "operationId": "handleGetNamespaceQuotas", "parameters": [ { "type": "string", @@ -9293,13 +7173,13 @@ "200": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ResourceQuota" + "$ref": "#/definitions/api.ResourceQuota" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ResourceQuota" + "$ref": "#/definitions/api.ResourceQuota" } } } @@ -9317,7 +7197,7 @@ "Namespace Resources" ], "summary": "List router of a specified project", - "operationId": "GetRouter", + "operationId": "handleGetRouter", "parameters": [ { "type": "string", @@ -9353,7 +7233,7 @@ "Namespace Resources" ], "summary": "Update a router for a specified project", - "operationId": "UpdateRouter", + "operationId": "handleUpdateRouter", "parameters": [ { "type": "string", @@ -9389,7 +7269,7 @@ "Namespace Resources" ], "summary": "Create a router for a specified project", - "operationId": "CreateRouter", + "operationId": "handleCreateRouter", "parameters": [ { "type": "string", @@ -9425,7 +7305,7 @@ "Namespace Resources" ], "summary": "List router of a specified project", - "operationId": "DeleteRouter", + "operationId": "handleDeleteRouter", "parameters": [ { "type": "string", @@ -9463,7 +7343,7 @@ "Namespace Resources" ], "summary": "Get the specified statefulset revision", - "operationId": "GetStatefulSetRevision", + "operationId": "handleGetStatefulSetRevision", "parameters": [ { "type": "string", @@ -9515,7 +7395,8 @@ "Namespace Resources" ], "summary": "Namespace level resource query", - "operationId": "ListNamespacedResources", + "operationId": "handleListNamespaceResources", + "deprecated": true, "parameters": [ { "type": "string", @@ -9575,44 +7456,6 @@ } } }, - "/kapis/resources.kubesphere.io/v1alpha2/nodes/{node}/drainage": { - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Cluster Resources" - ], - "summary": "remove a node from service, safely evict all of your pods from a node and you can power down the node. More info: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/", - "operationId": "DrainNode", - "parameters": [ - { - "type": "string", - "description": "node name", - "name": "node", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - } - } - } - }, "/kapis/resources.kubesphere.io/v1alpha2/quotas": { "get": { "consumes": [ @@ -9625,18 +7468,18 @@ "Cluster Resources" ], "summary": "get whole cluster's resource usage", - "operationId": "GetClusterQuotas", + "operationId": "handleGetClusterQuotas", "responses": { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ResourceQuota" + "$ref": "#/definitions/api.ResourceQuota" } }, "default": { "description": "ok", "schema": { - "$ref": "#/definitions/models.ResourceQuota" + "$ref": "#/definitions/api.ResourceQuota" } } } @@ -9654,7 +7497,7 @@ "Docker Registry" ], "summary": "Retrieve the blob from the registry identified", - "operationId": "RegistryImageBlob", + "operationId": "handleGetRegistryEntry", "parameters": [ { "type": "string", @@ -9707,14 +7550,14 @@ "Verification" ], "summary": "verify if a user has access to the docker registry", - "operationId": "RegistryVerify", + "operationId": "handleVerifyRegistryCredential", "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/registries.AuthInfo" + "$ref": "#/definitions/api.RegistryCredential" } } ], @@ -9734,35 +7577,6 @@ } } }, - "/kapis/resources.kubesphere.io/v1alpha2/routers": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Cluster Resources" - ], - "summary": "List all routers of all projects", - "operationId": "GetAllRouters", - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1.ServiceList" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1.ServiceList" - } - } - } - } - }, "/kapis/resources.kubesphere.io/v1alpha2/users/{user}/kubeconfig": { "get": { "consumes": [ @@ -9814,7 +7628,7 @@ "User Resources" ], "summary": "get user's kubectl pod", - "operationId": "GetKubectl", + "operationId": "GetKubectlPod", "parameters": [ { "type": "string", @@ -9851,8 +7665,9 @@ "tags": [ "Cluster Resources" ], - "summary": "Cluster level resource query", - "operationId": "ListResources", + "summary": "Cluster level resources", + "operationId": "handleListNamespaceResources", + "deprecated": true, "parameters": [ { "type": "string", @@ -9905,6 +7720,257 @@ } } }, + "/kapis/resources.kubesphere.io/v1alpha3/componenthealth": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Component Status" + ], + "summary": "Get the health status of system components.", + "operationId": "handleGetSystemHealthStatus", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha2.HealthStatus" + } + }, + "default": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha2.HealthStatus" + } + } + } + } + }, + "/kapis/resources.kubesphere.io/v1alpha3/components": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Component Status" + ], + "summary": "List the system components.", + "operationId": "handleGetComponents", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.ComponentStatus" + } + } + }, + "default": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.ComponentStatus" + } + } + } + } + } + }, + "/kapis/resources.kubesphere.io/v1alpha3/components/{component}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Component Status" + ], + "summary": "Describe the specified system component.", + "operationId": "handleGetComponentStatus", + "parameters": [ + { + "type": "string", + "description": "component name", + "name": "component", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha2.ComponentStatus" + } + }, + "default": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1alpha2.ComponentStatus" + } + } + } + } + }, + "/kapis/resources.kubesphere.io/v1alpha3/namespaces/{namespace}/{resources}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Namespaced Resource" + ], + "summary": "Namespace level resource query", + "operationId": "handleListResources", + "parameters": [ + { + "type": "string", + "description": "the name of the project", + "name": "namespace", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "namespace level resource type, e.g. pods,jobs,configmaps,services.", + "name": "resources", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name used to do filtering", + "name": "name", + "in": "query" + }, + { + "type": "string", + "format": "page=%d", + "default": "page=1", + "description": "page", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "default": "ascending=false", + "description": "sort parameters, e.g. reverse=true", + "name": "ascending", + "in": "query" + }, + { + "type": "string", + "description": "sort parameters, e.g. orderBy=createTime", + "name": "sortBy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.ListResult" + } + }, + "default": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.ListResult" + } + } + } + } + }, + "/kapis/resources.kubesphere.io/v1alpha3/{resources}": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Namespaced Resource" + ], + "summary": "Cluster level resources", + "operationId": "handleListResources", + "parameters": [ + { + "type": "string", + "description": "cluster level resource type, e.g. pods,jobs,configmaps,services.", + "name": "resources", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name used to do filtering", + "name": "name", + "in": "query" + }, + { + "type": "string", + "format": "page=%d", + "default": "page=1", + "description": "page", + "name": "page", + "in": "query" + }, + { + "type": "string", + "description": "limit", + "name": "limit", + "in": "query" + }, + { + "type": "string", + "default": "ascending=false", + "description": "sort parameters, e.g. reverse=true", + "name": "ascending", + "in": "query" + }, + { + "type": "string", + "description": "sort parameters, e.g. orderBy=createTime", + "name": "sortBy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.ListResult" + } + }, + "default": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.ListResult" + } + } + } + } + }, "/kapis/servicemesh.kubesphere.io/v1alpha2/namespaces/graph": { "get": { "consumes": [ @@ -9917,7 +7983,7 @@ "ServiceMesh" ], "summary": "Get graph from all namespaces", - "operationId": "GetNamespacesGraph", + "operationId": "getNamespacesGraph", "parameters": [ { "type": "string", @@ -9994,7 +8060,7 @@ "ServiceMesh" ], "summary": "Get app health", - "operationId": "GetAppHealth", + "operationId": "getAppHealth", "parameters": [ { "type": "string", @@ -10053,7 +8119,7 @@ "ServiceMesh" ], "summary": "Get app metrics from a specific namespace", - "operationId": "GetAppMetrics", + "operationId": "getAppMetrics", "parameters": [ { "type": "string", @@ -10167,7 +8233,7 @@ "ServiceMesh" ], "summary": "Get service graph for a specific namespace", - "operationId": "GetNamespaceGraph", + "operationId": "getNamespaceGraph", "parameters": [ { "type": "string", @@ -10251,7 +8317,7 @@ "ServiceMesh" ], "summary": "Get app/service/workload health of a namespace", - "operationId": "GetNamespaceHealth", + "operationId": "getNamespaceHealth", "parameters": [ { "type": "string", @@ -10323,7 +8389,7 @@ "ServiceMesh" ], "summary": "Get metrics from a specific namespace", - "operationId": "GetNamespaceMetrics", + "operationId": "getNamespaceMetrics", "parameters": [ { "type": "string", @@ -10437,7 +8503,7 @@ "ServiceMesh" ], "summary": "Get service health", - "operationId": "GetServiceHealth", + "operationId": "getServiceHealth", "parameters": [ { "type": "string", @@ -10496,7 +8562,7 @@ "ServiceMesh" ], "summary": "Get service metrics from a specific namespace", - "operationId": "GetServiceMetrics", + "operationId": "getServiceMetrics", "parameters": [ { "type": "string", @@ -10610,7 +8676,7 @@ "ServiceMesh" ], "summary": "Get tracing of a service, should have servicemesh enabled first", - "operationId": "GetServiceTracing", + "operationId": "getServiceTracing", "parameters": [ { "type": "string", @@ -10683,7 +8749,7 @@ "ServiceMesh" ], "summary": "Get workload health", - "operationId": "GetWorkloadHealth", + "operationId": "getWorkloadHealth", "parameters": [ { "type": "string", @@ -10742,7 +8808,7 @@ "ServiceMesh" ], "summary": "Get workload metrics from a specific namespace", - "operationId": "GetWorkloadMetrics", + "operationId": "getWorkloadMetrics", "parameters": [ { "type": "string", @@ -10851,255 +8917,6 @@ } } }, - "/kapis/tenant.kubesphere.io/v1alpha2/devops/{devops}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List the rules of the specified DevOps project for the current user", - "operationId": "ListDevopsRules", - "parameters": [ - { - "type": "string", - "description": "devops project ID", - "name": "devops", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/devopscount": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Get the devops projects count for the member", - "operationId": "GetDevOpsProjectsCount", - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/struct%20%7B%20Count%20uint32%20%22json:%5C%22count%5C%22%22%20%7D" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/struct%20%7B%20Count%20uint32%20%22json:%5C%22count%5C%22%22%20%7D" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/logs": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Query cluster-level logs in a multi-tenants environment", - "operationId": "LogQuery", - "parameters": [ - { - "type": "string", - "default": "query", - "description": "Operation type. This can be one of four types: query (for querying logs), statistics (for retrieving statistical data), histogram (for displaying log count by time interval) and export (for exporting logs). Defaults to query.", - "name": "operation", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of workspaces. This field restricts the query to specified workspaces. For example, the following filter matches the workspace my-ws and demo-ws: `my-ws,demo-ws`", - "name": "workspaces", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **workspaces**, this field performs fuzzy matching on workspaces. For example, the following value limits the query to workspaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "workspace_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of namespaces. This field restricts the query to specified namespaces. For example, the following filter matches the namespace my-ns and demo-ns: `my-ns,demo-ns`", - "name": "namespaces", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **namespaces**, this field performs fuzzy matching on namespaces. For example, the following value limits the query to namespaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "namespace_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`", - "name": "workloads", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "workload_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`", - "name": "pods", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "pod_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`", - "name": "containers", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.", - "name": "container_query", - "in": "query" - }, - { - "type": "string", - "description": "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).", - "name": "log_query", - "in": "query" - }, - { - "type": "string", - "default": "15m", - "description": "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).", - "name": "interval", - "in": "query" - }, - { - "type": "string", - "description": "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "start_time", - "in": "query" - }, - { - "type": "string", - "description": "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.", - "name": "end_time", - "in": "query" - }, - { - "type": "string", - "default": "desc", - "description": "Sort order. One of acs, desc. This field sorts logs by timestamp.", - "name": "sort", - "in": "query" - }, - { - "type": "integer", - "default": 0, - "description": "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).", - "name": "from", - "in": "query" - }, - { - "type": "integer", - "default": 10, - "description": "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).", - "name": "size", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.Response" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.Response" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/namespaces/{namespace}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List the rules of the specified namespace for the current user", - "operationId": "ListNamespaceRules", - "parameters": [ - { - "type": "string", - "description": "the name of the namespace", - "name": "namespace", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - } - } - } - }, "/kapis/tenant.kubesphere.io/v1alpha2/workspaces": { "get": { "consumes": [ @@ -11129,288 +8946,6 @@ } } }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Describe the specified workspace", - "operationId": "DescribeWorkspace", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha1.Workspace" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha1.Workspace" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/devops": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List devops projects for the current user", - "operationId": "ListDevopsProjects", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "limit=%d,page=%d", - "default": "limit=10,page=1", - "description": "page", - "name": "paging", - "in": "query" - }, - { - "type": "string", - "format": "key=%s,key~%s", - "description": "query conditions", - "name": "conditions", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK" - } - } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Create a devops project in the specified workspace", - "operationId": "CreateDevopsProject", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/devops/{devops}": { - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Delete the specified devops project from the workspace", - "operationId": "DeleteDevopsProject", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "devops project ID", - "name": "devops", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/v1alpha2.DevOpsProject" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/members/{member}/devops": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List the devops projects for the workspace member", - "operationId": "ListDevopsProjectsByUsername", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "workspace member's username", - "name": "member", - "in": "path", - "required": true - }, - { - "type": "string", - "format": "limit=%d,page=%d", - "default": "limit=10,page=1", - "description": "page", - "name": "paging", - "in": "query" - }, - { - "type": "string", - "format": "key=%s,key~%s", - "description": "query conditions", - "name": "conditions", - "in": "query" - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.PageableResponse" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.PageableResponse" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/members/{member}/namespaces": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List the namespaces for the workspace member", - "operationId": "ListNamespacesByUsername", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "workspace member's username", - "name": "member", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1.Namespace" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1.Namespace" - } - } - } - } - } - }, "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/namespaces": { "get": { "consumes": [ @@ -11453,131 +8988,6 @@ } } } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Create a namespace in the specified workspace", - "operationId": "CreateNamespace", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1.Namespace" - } - } - }, - "default": { - "description": "ok", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/v1.Namespace" - } - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/namespaces/{namespace}": { - "delete": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "Delete the specified namespace from the workspace", - "operationId": "DeleteNamespace", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the name of the namespace", - "name": "namespace", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/errors.Error" - } - } - } - } - }, - "/kapis/tenant.kubesphere.io/v1alpha2/workspaces/{workspace}/rules": { - "get": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Tenant Resources" - ], - "summary": "List the rules of the specified workspace for the current user", - "operationId": "ListWorkspaceRules", - "parameters": [ - { - "type": "string", - "description": "workspace name", - "name": "workspace", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - }, - "default": { - "description": "ok", - "schema": { - "$ref": "#/definitions/models.SimpleRule" - } - } - } } }, "/kapis/terminal.kubesphere.io/v1alpha2/namespaces/{namespace}/pods/{pod}": { @@ -11592,7 +9002,7 @@ "Terminal" ], "summary": "create terminal session", - "operationId": "CreateTerminalSession", + "operationId": "handleTerminalSession", "responses": { "200": { "description": "OK" @@ -11793,7 +9203,7 @@ "type": "string" }, "replayable": { - "description": "replayable or not", + "description": "Replayable or not", "type": "boolean" }, "result": { @@ -11805,7 +9215,7 @@ "type": "string" }, "startTime": { - "description": "start run", + "description": "the time of start", "type": "string" }, "state": { @@ -11829,6 +9239,14 @@ "shortDescription": { "description": "short description", "type": "string" + }, + "userId": { + "description": "user id", + "type": "string" + }, + "userName": { + "description": "user name", + "type": "string" } } }, @@ -12018,6 +9436,85 @@ } } }, + "api.ListResult": { + "required": [ + "items", + "totalItems" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/api.ListResult.items" + } + }, + "totalItems": { + "type": "integer", + "format": "int32" + } + } + }, + "api.ListResult.items": {}, + "api.RegistryCredential": { + "required": [ + "username", + "password", + "serverhost" + ], + "properties": { + "password": { + "description": "password", + "type": "string" + }, + "serverhost": { + "description": "registry server host", + "type": "string" + }, + "username": { + "description": "username", + "type": "string" + } + } + }, + "api.ResourceQuota": { + "required": [ + "namespace", + "data" + ], + "properties": { + "data": { + "description": "resource quota status", + "$ref": "#/definitions/v1.ResourceQuotaStatus" + }, + "namespace": { + "description": "namespace", + "type": "string" + } + } + }, + "api.Workloads": { + "required": [ + "namespace", + "data" + ], + "properties": { + "data": { + "description": "the number of unhealthy workloads", + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "items": { + "description": "unhealthy workloads", + "type": "object" + }, + "namespace": { + "description": "the name of the namespace", + "type": "string" + } + } + }, "big.Int": { "required": [ "neg", @@ -12238,52 +9735,6 @@ } } }, - "devops.BitbucketServerSource": { - "properties": { - "api_uri": { - "description": "The api url can specify the location of the github apiserver.For private cloud configuration", - "type": "string" - }, - "credential_id": { - "description": "credential id to access github source", - "type": "string" - }, - "discover_branches": { - "description": "Discover branch configuration", - "type": "integer", - "format": "int32" - }, - "discover_pr_from_forks": { - "description": "Discover fork PR configuration", - "$ref": "#/definitions/devops.DiscoverPRFromForks" - }, - "discover_pr_from_origin": { - "description": "Discover origin PR configuration", - "type": "integer", - "format": "int32" - }, - "git_clone_option": { - "description": "advavced git clone options", - "$ref": "#/definitions/devops.GitCloneOption" - }, - "owner": { - "description": "owner of github repo", - "type": "string" - }, - "regex_filter": { - "description": "Regex used to match the name of the branch that needs to be run", - "type": "string" - }, - "repo": { - "description": "repo name of github repo", - "type": "string" - }, - "scm_id": { - "description": "uid of scm", - "type": "string" - } - } - }, "devops.BranchPipeline": { "properties": { "_class": { @@ -12376,151 +9827,6 @@ } } }, - "devops.BranchPipelineRun": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "_links": { - "description": "references the reachable path to this resource", - "$ref": "#/definitions/._links" - }, - "actions": { - "description": "the list of all actions", - "type": "array", - "items": { - "$ref": "#/definitions/devops.BranchPipelineRun.actions" - } - }, - "artifactsZipFile": { - "description": "the artifacts zip file", - "$ref": "#/definitions/devops.BranchPipelineRun.artifactsZipFile" - }, - "branch": { - "$ref": "#/definitions/.branch" - }, - "causeOfBlockage": { - "description": "the cause of blockage", - "$ref": "#/definitions/devops.BranchPipelineRun.causeOfBlockage" - }, - "causes": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.BranchPipelineRun.causes" - } - }, - "changeSet": { - "description": "changeset information", - "type": "array", - "items": { - "$ref": "#/definitions/devops.BranchPipelineRun.changeSet" - } - }, - "commitId": { - "description": "commit id", - "type": "string" - }, - "commitUrl": { - "description": "commit url ", - "$ref": "#/definitions/devops.BranchPipelineRun.commitUrl" - }, - "description": { - "description": "description of resource", - "$ref": "#/definitions/devops.BranchPipelineRun.description" - }, - "durationInMillis": { - "description": "duration time in millis", - "type": "integer", - "format": "int32" - }, - "enQueueTime": { - "description": "the time of enter the queue", - "type": "string" - }, - "endTime": { - "description": "the time of end", - "type": "string" - }, - "estimatedDurationInMillis": { - "description": "estimated duration time, unit is millis", - "type": "integer", - "format": "int32" - }, - "id": { - "description": "id", - "type": "string" - }, - "name": { - "description": "name", - "$ref": "#/definitions/devops.BranchPipelineRun.name" - }, - "organization": { - "description": "the name of organization", - "type": "string" - }, - "pipeline": { - "description": "pipeline name", - "type": "string" - }, - "pullRequest": { - "description": "pull request", - "$ref": "#/definitions/devops.BranchPipelineRun.pullRequest" - }, - "replayable": { - "description": "replayable or not", - "type": "boolean" - }, - "result": { - "description": "the result of pipeline run. e.g. SUCCESS", - "type": "string" - }, - "runSummary": { - "description": "pipeline run summary", - "type": "string" - }, - "startTime": { - "description": "the time of start", - "type": "string" - }, - "state": { - "description": "run state. e.g. RUNNING", - "type": "string" - }, - "type": { - "description": "source type, such as \"WorkflowRun\"", - "type": "string" - } - } - }, - "devops.BranchPipelineRun.actions": {}, - "devops.BranchPipelineRun.artifactsZipFile": {}, - "devops.BranchPipelineRun.causeOfBlockage": {}, - "devops.BranchPipelineRun.causes": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "shortDescription": { - "description": "short description", - "type": "string" - }, - "userId": { - "description": "user id", - "type": "string" - }, - "userName": { - "description": "user name", - "type": "string" - } - } - }, - "devops.BranchPipelineRun.changeSet": {}, - "devops.BranchPipelineRun.commitUrl": {}, - "devops.BranchPipelineRun.description": {}, - "devops.BranchPipelineRun.name": {}, - "devops.BranchPipelineRun.pullRequest": {}, "devops.BranchPipelineRunNodes": { "properties": { "_class": { @@ -12736,10 +10042,11 @@ }, "value": { "description": "value", - "type": "string" + "$ref": "#/definitions/devops.CheckPlayloadParameters.value" } } }, + "devops.CheckPlayloadParameters.value": {}, "devops.CheckScript": { "properties": { "column": { @@ -12774,6 +10081,69 @@ } } }, + "devops.Credential": { + "required": [ + "id", + "type" + ], + "properties": { + "description": { + "description": "Credential's description'", + "type": "string" + }, + "display_name": { + "description": "Credential's display name", + "type": "string" + }, + "domain": { + "description": "Credential's domain,In ks we only use the default domain, default '_''", + "type": "string" + }, + "fingerprint": { + "description": "usage of the Credential", + "$ref": "#/definitions/devops.Credential.fingerprint" + }, + "id": { + "description": "Id of Credential, e.g. dockerhub-id", + "type": "string" + }, + "type": { + "description": "Type of Credential, e.g. ssh/kubeconfig", + "type": "string" + } + } + }, + "devops.Credential.fingerprint": { + "properties": { + "file_name": { + "description": "Credential's display name and description", + "type": "string" + }, + "hash": { + "description": "Credential's hash", + "type": "string" + }, + "usage": { + "description": "all usage of Credential", + "type": "array", + "items": { + "$ref": "#/definitions/devops.Credential.fingerprint.usage" + } + } + } + }, + "devops.Credential.fingerprint.usage": { + "properties": { + "name": { + "description": "pipeline full name", + "type": "string" + }, + "ranges": { + "description": "The build number of all pipelines that use this credential", + "$ref": "#/definitions/.ranges" + } + } + }, "devops.CronData": { "required": [ "cron" @@ -12805,154 +10175,6 @@ } } }, - "devops.DevOpsProjectMembership": { - "required": [ - "username", - "project_id", - "role", - "status" - ], - "properties": { - "grand_by": { - "description": "Username of the user who assigned the role", - "type": "string" - }, - "project_id": { - "description": "the DevOps Projects which project membership belongs to", - "type": "string" - }, - "role": { - "description": "DevOps Project membership's role type. e.g. owner '", - "type": "string" - }, - "status": { - "description": "Deprecated, Status of project membership. e.g. active ", - "type": "string" - }, - "username": { - "description": "Member's username,username can uniquely identify a user", - "type": "string" - } - } - }, - "devops.DiscarderProperty": { - "properties": { - "days_to_keep": { - "description": "days to keep pipeline", - "type": "string" - }, - "num_to_keep": { - "description": "nums to keep pipeline", - "type": "string" - } - } - }, - "devops.DiscoverPRFromForks": { - "properties": { - "strategy": { - "description": "github discover startegy", - "type": "integer", - "format": "int32" - }, - "trust": { - "description": "trust user type", - "type": "integer", - "format": "int32" - } - } - }, - "devops.GitCloneOption": { - "properties": { - "depth": { - "description": "git clone depth", - "type": "integer", - "format": "int32" - }, - "shallow": { - "description": "Whether to use git shallow clone", - "type": "boolean" - }, - "timeout": { - "description": "git clone timeout mins", - "type": "integer", - "format": "int32" - } - } - }, - "devops.GitSource": { - "properties": { - "credential_id": { - "description": "credential id to access git source", - "type": "string" - }, - "discover_branches": { - "description": "Whether to discover a branch", - "type": "boolean" - }, - "git_clone_option": { - "description": "advavced git clone options", - "$ref": "#/definitions/devops.GitCloneOption" - }, - "regex_filter": { - "description": "Regex used to match the name of the branch that needs to be run", - "type": "string" - }, - "scm_id": { - "description": "uid of scm", - "type": "string" - }, - "url": { - "description": "url of git source", - "type": "string" - } - } - }, - "devops.GithubSource": { - "properties": { - "api_uri": { - "description": "The api url can specify the location of the github apiserver.For private cloud configuration", - "type": "string" - }, - "credential_id": { - "description": "credential id to access github source", - "type": "string" - }, - "discover_branches": { - "description": "Discover branch configuration", - "type": "integer", - "format": "int32" - }, - "discover_pr_from_forks": { - "description": "Discover fork PR configuration", - "$ref": "#/definitions/devops.DiscoverPRFromForks" - }, - "discover_pr_from_origin": { - "description": "Discover origin PR configuration", - "type": "integer", - "format": "int32" - }, - "git_clone_option": { - "description": "advavced git clone options", - "$ref": "#/definitions/devops.GitCloneOption" - }, - "owner": { - "description": "owner of github repo", - "type": "string" - }, - "regex_filter": { - "description": "Regex used to match the name of the branch that needs to be run", - "type": "string" - }, - "repo": { - "description": "repo name of github repo", - "type": "string" - }, - "scm_id": { - "description": "uid of scm", - "type": "string" - } - } - }, "devops.Input": { "properties": { "_class": { @@ -12961,7 +10183,7 @@ }, "_links": { "description": "references the reachable path to this resource", - "$ref": "#/definitions/._links" + "$ref": "#/definitions/devops.Input._links" }, "id": { "description": "the id of check action", @@ -12988,215 +10210,25 @@ } } }, + "devops.Input._links": { + "properties": { + "self": { + "$ref": "#/definitions/devops.Input._links.self" + } + } + }, + "devops.Input._links.self": { + "properties": { + "_class": { + "type": "string" + }, + "href": { + "type": "string" + } + } + }, "devops.Input.parameters": {}, "devops.Input.submitter": {}, - "devops.JenkinsCredential": { - "required": [ - "id", - "type" - ], - "properties": { - "create_time": { - "description": "Credential's create_time'", - "type": "string", - "format": "date-time" - }, - "creator": { - "description": "Creator's username", - "type": "string" - }, - "description": { - "description": "Credential's description'", - "type": "string" - }, - "display_name": { - "description": "Credential's display name", - "type": "string" - }, - "domain": { - "description": "Credential's domain,In ks we only use the default domain, default '_''", - "type": "string" - }, - "fingerprint": { - "description": "usage of the Credential", - "$ref": "#/definitions/devops.JenkinsCredential.fingerprint" - }, - "id": { - "description": "Id of Credential, e.g. dockerhub-id", - "type": "string" - }, - "kubeconfig": { - "description": "kubeconfig Credential struct", - "$ref": "#/definitions/devops.KubeconfigCredential" - }, - "secret_text": { - "description": "secret_text Credential struct", - "$ref": "#/definitions/devops.SecretTextCredential" - }, - "ssh": { - "description": "ssh Credential struct", - "$ref": "#/definitions/devops.SshCredential" - }, - "type": { - "description": "Type of Credential, e.g. ssh/kubeconfig", - "type": "string" - }, - "username_password": { - "description": "username password Credential struct", - "$ref": "#/definitions/devops.UsernamePasswordCredential" - } - } - }, - "devops.JenkinsCredential.fingerprint": { - "properties": { - "file_name": { - "description": "Credential's display name and description", - "type": "string" - }, - "hash": { - "description": "Credential's hash", - "type": "string" - }, - "usage": { - "description": "all usage of Credential", - "type": "array", - "items": { - "$ref": "#/definitions/devops.JenkinsCredential.fingerprint.usage" - } - } - } - }, - "devops.JenkinsCredential.fingerprint.usage": { - "properties": { - "name": { - "description": "Jenkins pipeline full name", - "type": "string" - }, - "ranges": { - "description": "The build number of all pipelines that use this credential", - "$ref": "#/definitions/.ranges" - } - } - }, - "devops.KubeconfigCredential": { - "properties": { - "content": { - "description": "content of kubeconfig", - "type": "string" - } - } - }, - "devops.MultiBranchJobTrigger": { - "properties": { - "create_action_job_to_trigger": { - "description": "pipeline name to trigger", - "type": "string" - }, - "delete_action_job_to_trigger": { - "description": "pipeline name to trigger", - "type": "string" - } - } - }, - "devops.MultiBranchPipeline": { - "required": [ - "name", - "source_type", - "script_path" - ], - "properties": { - "bitbucket_server_source": { - "description": "bitbucket server scm defile", - "$ref": "#/definitions/devops.BitbucketServerSource" - }, - "descriptio": { - "description": "description of pipeline", - "type": "string" - }, - "discarder": { - "description": "Discarder of pipeline, managing when to drop a pipeline", - "$ref": "#/definitions/devops.DiscarderProperty" - }, - "git_source": { - "description": "git scm define", - "$ref": "#/definitions/devops.GitSource" - }, - "github_source": { - "description": "github scm define", - "$ref": "#/definitions/devops.GithubSource" - }, - "multibranch_job_trigger": { - "description": "Pipeline tasks that need to be triggered when branch creation/deletion", - "$ref": "#/definitions/devops.MultiBranchJobTrigger" - }, - "name": { - "description": "name of pipeline", - "type": "string" - }, - "script_path": { - "description": "script path in scm", - "type": "string" - }, - "single_svn_source": { - "description": "single branch svn scm define", - "$ref": "#/definitions/devops.SingleSvnSource" - }, - "source_type": { - "description": "type of scm, such as github/git/svn", - "type": "string" - }, - "svn_source": { - "description": "multi branch svn scm define", - "$ref": "#/definitions/devops.SvnSource" - }, - "timer_trigger": { - "description": "Timer to trigger pipeline run", - "$ref": "#/definitions/devops.TimerTrigger" - } - } - }, - "devops.NoScmPipeline": { - "required": [ - "name" - ], - "properties": { - "descriptio": { - "description": "description of pipeline", - "type": "string" - }, - "disable_concurrent": { - "description": "Whether to prohibit the pipeline from running in parallel", - "type": "boolean" - }, - "discarder": { - "description": "Discarder of pipeline, managing when to drop a pipeline", - "$ref": "#/definitions/devops.DiscarderProperty" - }, - "jenkinsfile": { - "description": "Jenkinsfile's content'", - "type": "string" - }, - "name": { - "description": "name of pipeline", - "type": "string" - }, - "parameters": { - "description": "Parameters define of pipeline,user could pass param when run pipeline", - "type": "array", - "items": { - "$ref": "#/definitions/devops.Parameter" - } - }, - "remote_trigger": { - "description": "Remote api define to trigger pipeline run", - "$ref": "#/definitions/devops.RemoteTrigger" - }, - "timer_trigger": { - "description": "Timer to trigger pipeline run", - "$ref": "#/definitions/devops.TimerTrigger" - } - } - }, "devops.NodeSteps": { "properties": { "_class": { @@ -13298,11 +10330,12 @@ "type": "string" }, "durationInMillis": { - "description": "duration time in millis", + "description": "duration time in mullis", "type": "integer", "format": "int32" }, "edges": { + "description": "edges", "type": "array", "items": { "$ref": "#/definitions/devops.NodesDetail.edges" @@ -13333,7 +10366,7 @@ "type": "string" }, "state": { - "description": "run state. e.g. SKIPPED", + "description": "run state. e.g. FINISHED", "type": "string" }, "steps": { @@ -13352,22 +10385,7 @@ "devops.NodesDetail.actions": {}, "devops.NodesDetail.causeOfBlockage": {}, "devops.NodesDetail.displayDescription": {}, - "devops.NodesDetail.edges": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "id": { - "description": "id", - "type": "string" - }, - "type": { - "description": "type", - "type": "string" - } - } - }, + "devops.NodesDetail.edges": {}, "devops.NodesDetail.firstParent": {}, "devops.OrgRepo": { "properties": { @@ -13384,122 +10402,6 @@ } } }, - "devops.Parameter": { - "required": [ - "name", - "type" - ], - "properties": { - "default_value": { - "description": "default value of param", - "type": "string" - }, - "description": { - "description": "description of pipeline", - "type": "string" - }, - "name": { - "description": "name of param", - "type": "string" - }, - "type": { - "description": "type of param", - "type": "string" - } - } - }, - "devops.PipeBranch": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "_links": { - "description": "references the reachable path to this resource", - "$ref": "#/definitions/._links" - }, - "actions": { - "description": "the list of all actions.", - "type": "array", - "items": { - "$ref": "#/definitions/devops.PipeBranch.actions" - } - }, - "branch": { - "$ref": "#/definitions/.branch" - }, - "disabled": { - "description": "disable or not, if disabled, can not do any action", - "type": "boolean" - }, - "displayName": { - "description": "display name", - "type": "string" - }, - "estimatedDurationInMillis": { - "description": "estimated duration time, unit is millis", - "type": "integer", - "format": "int32" - }, - "fullDisplayName": { - "description": "full display name", - "type": "string" - }, - "fullName": { - "description": "full name", - "type": "string" - }, - "latestRun": { - "$ref": "#/definitions/.latestRun" - }, - "name": { - "description": "name", - "type": "string" - }, - "organization": { - "description": "the name of organization", - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.PipeBranch.parameters" - } - }, - "permissions": { - "$ref": "#/definitions/.permissions" - }, - "weatherScore": { - "description": "the score to description the result of pipeline", - "type": "integer", - "format": "int32" - } - } - }, - "devops.PipeBranch.actions": {}, - "devops.PipeBranch.parameters": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "defaultParameterValue": { - "$ref": "#/definitions/.defaultParameterValue" - }, - "description": { - "description": "description", - "type": "string" - }, - "name": { - "description": "name", - "type": "string" - }, - "type": { - "description": "type", - "type": "string" - } - } - }, "devops.Pipeline": { "properties": { "_class": { @@ -13622,6 +10524,116 @@ "devops.Pipeline.disabled": {}, "devops.Pipeline.parameters": {}, "devops.Pipeline.pipelineFolderNames": {}, + "devops.PipelineBranch": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "_links": { + "description": "references the reachable path to this resource", + "$ref": "#/definitions/._links" + }, + "actions": { + "description": "the list of all actions.", + "type": "array", + "items": { + "$ref": "#/definitions/devops.PipelineBranch.actions" + } + }, + "branch": { + "$ref": "#/definitions/.branch" + }, + "disabled": { + "description": "disable or not, if disabled, can not do any action", + "type": "boolean" + }, + "displayName": { + "description": "display name", + "type": "string" + }, + "estimatedDurationInMillis": { + "description": "estimated duration time, unit is millis", + "type": "integer", + "format": "int32" + }, + "fullDisplayName": { + "description": "full display name", + "type": "string" + }, + "fullName": { + "description": "full name", + "type": "string" + }, + "latestRun": { + "$ref": "#/definitions/.latestRun" + }, + "name": { + "description": "name", + "type": "string" + }, + "organization": { + "description": "the name of organization", + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/devops.PipelineBranch.parameters" + } + }, + "permissions": { + "$ref": "#/definitions/.permissions" + }, + "weatherScore": { + "description": "the score to description the result of pipeline", + "type": "integer", + "format": "int32" + } + } + }, + "devops.PipelineBranch.actions": {}, + "devops.PipelineBranch.parameters": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "defaultParameterValue": { + "$ref": "#/definitions/.defaultParameterValue" + }, + "description": { + "description": "description", + "type": "string" + }, + "name": { + "description": "name", + "type": "string" + }, + "type": { + "description": "type", + "type": "string" + } + } + }, + "devops.PipelineList": { + "required": [ + "items", + "total_count" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/devops.Pipeline" + } + }, + "total_count": { + "type": "integer", + "format": "int32" + } + } + }, "devops.PipelineRun": { "properties": { "_class": { @@ -13770,6 +10782,151 @@ "devops.PipelineRun.description": {}, "devops.PipelineRun.name": {}, "devops.PipelineRun.pullRequest": {}, + "devops.PipelineRunList": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "_links": { + "description": "references the reachable path to this resource", + "$ref": "#/definitions/._links" + }, + "actions": { + "description": "the list of all actions", + "type": "array", + "items": { + "$ref": "#/definitions/devops.PipelineRunList.actions" + } + }, + "artifactsZipFile": { + "description": "the artifacts zip file", + "$ref": "#/definitions/devops.PipelineRunList.artifactsZipFile" + }, + "branch": { + "$ref": "#/definitions/.branch" + }, + "causeOfBlockage": { + "description": "the cause of blockage", + "$ref": "#/definitions/devops.PipelineRunList.causeOfBlockage" + }, + "causes": { + "type": "array", + "items": { + "$ref": "#/definitions/devops.PipelineRunList.causes" + } + }, + "changeSet": { + "description": "changeset information", + "type": "array", + "items": { + "$ref": "#/definitions/devops.PipelineRunList.changeSet" + } + }, + "commitId": { + "description": "commit id", + "type": "string" + }, + "commitUrl": { + "description": "commit url ", + "$ref": "#/definitions/devops.PipelineRunList.commitUrl" + }, + "description": { + "description": "description of resource", + "$ref": "#/definitions/devops.PipelineRunList.description" + }, + "durationInMillis": { + "description": "duration time in millis", + "type": "integer", + "format": "int32" + }, + "enQueueTime": { + "description": "the time of enter the queue", + "type": "string" + }, + "endTime": { + "description": "the time of end", + "type": "string" + }, + "estimatedDurationInMillis": { + "description": "estimated duration time, unit is millis", + "type": "integer", + "format": "int32" + }, + "id": { + "description": "id", + "type": "string" + }, + "name": { + "description": "name", + "$ref": "#/definitions/devops.PipelineRunList.name" + }, + "organization": { + "description": "the name of organization", + "type": "string" + }, + "pipeline": { + "description": "pipeline name", + "type": "string" + }, + "pullRequest": { + "description": "pull request", + "$ref": "#/definitions/devops.PipelineRunList.pullRequest" + }, + "replayable": { + "description": "replayable or not", + "type": "boolean" + }, + "result": { + "description": "the result of pipeline run. e.g. SUCCESS", + "type": "string" + }, + "runSummary": { + "description": "pipeline run summary", + "type": "string" + }, + "startTime": { + "description": "the time of start", + "type": "string" + }, + "state": { + "description": "run state. e.g. RUNNING", + "type": "string" + }, + "type": { + "description": "source type, such as \"WorkflowRun\"", + "type": "string" + } + } + }, + "devops.PipelineRunList.actions": {}, + "devops.PipelineRunList.artifactsZipFile": {}, + "devops.PipelineRunList.causeOfBlockage": {}, + "devops.PipelineRunList.causes": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "shortDescription": { + "description": "short description", + "type": "string" + }, + "userId": { + "description": "user id", + "type": "string" + }, + "userName": { + "description": "user name", + "type": "string" + } + } + }, + "devops.PipelineRunList.changeSet": {}, + "devops.PipelineRunList.commitUrl": {}, + "devops.PipelineRunList.description": {}, + "devops.PipelineRunList.name": {}, + "devops.PipelineRunList.pullRequest": {}, "devops.PipelineRunNodes": { "properties": { "_class": { @@ -13850,169 +11007,7 @@ "devops.PipelineRunNodes.displayDescription": {}, "devops.PipelineRunNodes.edges": {}, "devops.PipelineRunNodes.firstParent": {}, - "devops.ProjectPipeline": { - "required": [ - "type" - ], - "properties": { - "multi_branch_pipeline": { - "description": "in scm pipeline structs", - "$ref": "#/definitions/devops.MultiBranchPipeline" - }, - "pipeline": { - "description": "no scm pipeline structs", - "$ref": "#/definitions/devops.NoScmPipeline" - }, - "type": { - "description": "type of devops pipeline, in scm or no scm", - "type": "string" - } - } - }, - "devops.QueuedBlueRun": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "_links": { - "description": "references the reachable path to this resource", - "$ref": "#/definitions/._links" - }, - "actions": { - "description": "the list of all actions", - "type": "array", - "items": { - "$ref": "#/definitions/devops.QueuedBlueRun.actions" - } - }, - "artifactsZipFile": { - "description": "the artifacts zip file", - "$ref": "#/definitions/devops.QueuedBlueRun.artifactsZipFile" - }, - "causeOfBlockage": { - "description": "the cause of blockage", - "type": "string" - }, - "causes": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.QueuedBlueRun.causes" - } - }, - "changeSet": { - "description": "changeset information", - "type": "array", - "items": { - "$ref": "#/definitions/devops.QueuedBlueRun.changeSet" - } - }, - "description": { - "description": "description", - "$ref": "#/definitions/devops.QueuedBlueRun.description" - }, - "durationInMillis": { - "description": "duration time in millis", - "$ref": "#/definitions/devops.QueuedBlueRun.durationInMillis" - }, - "enQueueTime": { - "description": "the time of enter the queue", - "$ref": "#/definitions/devops.QueuedBlueRun.enQueueTime" - }, - "endTime": { - "description": "the time of end", - "$ref": "#/definitions/devops.QueuedBlueRun.endTime" - }, - "estimatedDurationInMillis": { - "description": "estimated duration time in millis", - "$ref": "#/definitions/devops.QueuedBlueRun.estimatedDurationInMillis" - }, - "id": { - "description": "id", - "type": "string" - }, - "name": { - "description": "name", - "$ref": "#/definitions/devops.QueuedBlueRun.name" - }, - "organization": { - "description": "the name of organization", - "type": "string" - }, - "pipeline": { - "description": "pipeline", - "type": "string" - }, - "queueId": { - "description": "queue id", - "type": "string" - }, - "replayable": { - "description": "replayable or not", - "type": "boolean" - }, - "result": { - "description": "the result of pipeline run. e.g. SUCCESS", - "type": "string" - }, - "runSummary": { - "description": "pipeline run summary", - "$ref": "#/definitions/devops.QueuedBlueRun.runSummary" - }, - "startTime": { - "description": "the time of start", - "$ref": "#/definitions/devops.QueuedBlueRun.startTime" - }, - "state": { - "description": "run state. e.g. RUNNING", - "type": "string" - }, - "type": { - "description": "type", - "type": "string" - } - } - }, - "devops.QueuedBlueRun.actions": {}, - "devops.QueuedBlueRun.artifactsZipFile": {}, - "devops.QueuedBlueRun.causes": { - "properties": { - "_class": { - "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", - "type": "string" - }, - "shortDescription": { - "description": "short description", - "type": "string" - }, - "userId": { - "description": "user id", - "type": "string" - }, - "userName": { - "description": "user name", - "type": "string" - } - } - }, - "devops.QueuedBlueRun.changeSet": {}, - "devops.QueuedBlueRun.description": {}, - "devops.QueuedBlueRun.durationInMillis": {}, - "devops.QueuedBlueRun.enQueueTime": {}, - "devops.QueuedBlueRun.endTime": {}, - "devops.QueuedBlueRun.estimatedDurationInMillis": {}, - "devops.QueuedBlueRun.name": {}, - "devops.QueuedBlueRun.runSummary": {}, - "devops.QueuedBlueRun.startTime": {}, - "devops.RemoteTrigger": { - "properties": { - "token": { - "description": "remote trigger token", - "type": "string" - } - } - }, - "devops.ReplayPipe": { + "devops.ReplayPipeline": { "properties": { "_class": { "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", @@ -14026,12 +11021,12 @@ "description": "the list of all actions.", "type": "array", "items": { - "$ref": "#/definitions/devops.ReplayPipe.actions" + "$ref": "#/definitions/devops.ReplayPipeline.actions" } }, "artifactsZipFile": { "description": "the artifacts zip file", - "$ref": "#/definitions/devops.ReplayPipe.artifactsZipFile" + "$ref": "#/definitions/devops.ReplayPipeline.artifactsZipFile" }, "causeOfBlockage": { "description": "the cause of blockage", @@ -14040,35 +11035,35 @@ "causes": { "type": "array", "items": { - "$ref": "#/definitions/devops.ReplayPipe.causes" + "$ref": "#/definitions/devops.ReplayPipeline.causes" } }, "changeSet": { "description": "changeset information", "type": "array", "items": { - "$ref": "#/definitions/devops.ReplayPipe.changeSet" + "$ref": "#/definitions/devops.ReplayPipeline.changeSet" } }, "description": { "description": "description", - "$ref": "#/definitions/devops.ReplayPipe.description" + "$ref": "#/definitions/devops.ReplayPipeline.description" }, "durationInMillis": { "description": "duration time in millis", - "$ref": "#/definitions/devops.ReplayPipe.durationInMillis" + "$ref": "#/definitions/devops.ReplayPipeline.durationInMillis" }, "enQueueTime": { "description": "the time of enter the queue", - "$ref": "#/definitions/devops.ReplayPipe.enQueueTime" + "$ref": "#/definitions/devops.ReplayPipeline.enQueueTime" }, "endTime": { "description": "the time of end", - "$ref": "#/definitions/devops.ReplayPipe.endTime" + "$ref": "#/definitions/devops.ReplayPipeline.endTime" }, "estimatedDurationInMillis": { "description": "estimated duration time, unit is millis", - "$ref": "#/definitions/devops.ReplayPipe.estimatedDurationInMillis" + "$ref": "#/definitions/devops.ReplayPipeline.estimatedDurationInMillis" }, "id": { "description": "id", @@ -14076,7 +11071,7 @@ }, "name": { "description": "name", - "$ref": "#/definitions/devops.ReplayPipe.name" + "$ref": "#/definitions/devops.ReplayPipeline.name" }, "organization": { "description": "the name of organization", @@ -14100,11 +11095,11 @@ }, "runSummary": { "description": "pipeline run summary", - "$ref": "#/definitions/devops.ReplayPipe.runSummary" + "$ref": "#/definitions/devops.ReplayPipeline.runSummary" }, "startTime": { "description": "the time of start", - "$ref": "#/definitions/devops.ReplayPipe.startTime" + "$ref": "#/definitions/devops.ReplayPipeline.startTime" }, "state": { "description": "run state. e.g. RUNNING", @@ -14116,9 +11111,9 @@ } } }, - "devops.ReplayPipe.actions": {}, - "devops.ReplayPipe.artifactsZipFile": {}, - "devops.ReplayPipe.causes": { + "devops.ReplayPipeline.actions": {}, + "devops.ReplayPipeline.artifactsZipFile": {}, + "devops.ReplayPipeline.causes": { "properties": { "_class": { "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", @@ -14138,15 +11133,15 @@ } } }, - "devops.ReplayPipe.changeSet": {}, - "devops.ReplayPipe.description": {}, - "devops.ReplayPipe.durationInMillis": {}, - "devops.ReplayPipe.enQueueTime": {}, - "devops.ReplayPipe.endTime": {}, - "devops.ReplayPipe.estimatedDurationInMillis": {}, - "devops.ReplayPipe.name": {}, - "devops.ReplayPipe.runSummary": {}, - "devops.ReplayPipe.startTime": {}, + "devops.ReplayPipeline.changeSet": {}, + "devops.ReplayPipeline.description": {}, + "devops.ReplayPipeline.durationInMillis": {}, + "devops.ReplayPipeline.enQueueTime": {}, + "devops.ReplayPipeline.endTime": {}, + "devops.ReplayPipeline.estimatedDurationInMillis": {}, + "devops.ReplayPipeline.name": {}, + "devops.ReplayPipeline.runSummary": {}, + "devops.ReplayPipeline.startTime": {}, "devops.ReqJenkinsfile": { "properties": { "jenkinsfile": { @@ -14193,22 +11188,6 @@ } } }, - "devops.Role": { - "required": [ - "name", - "description" - ], - "properties": { - "description": { - "description": "role 's description'", - "type": "string" - }, - "name": { - "description": "role's name e.g. owner'", - "type": "string" - } - } - }, "devops.RunPayload": { "properties": { "parameters": { @@ -14231,6 +11210,141 @@ } } }, + "devops.RunPipeline": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "_links": { + "description": "references the reachable path to this resource", + "$ref": "#/definitions/._links" + }, + "actions": { + "description": "the list of all actions", + "type": "array", + "items": { + "$ref": "#/definitions/devops.RunPipeline.actions" + } + }, + "artifactsZipFile": { + "description": "the artifacts zip file", + "$ref": "#/definitions/devops.RunPipeline.artifactsZipFile" + }, + "causeOfBlockage": { + "description": "the cause of blockage", + "type": "string" + }, + "causes": { + "type": "array", + "items": { + "$ref": "#/definitions/devops.RunPipeline.causes" + } + }, + "changeSet": { + "description": "changeset information", + "type": "array", + "items": { + "$ref": "#/definitions/devops.RunPipeline.changeSet" + } + }, + "description": { + "description": "description", + "$ref": "#/definitions/devops.RunPipeline.description" + }, + "durationInMillis": { + "description": "duration time in millis", + "$ref": "#/definitions/devops.RunPipeline.durationInMillis" + }, + "enQueueTime": { + "description": "the time of enter the queue", + "$ref": "#/definitions/devops.RunPipeline.enQueueTime" + }, + "endTime": { + "description": "the time of end", + "$ref": "#/definitions/devops.RunPipeline.endTime" + }, + "estimatedDurationInMillis": { + "description": "estimated duration time in millis", + "$ref": "#/definitions/devops.RunPipeline.estimatedDurationInMillis" + }, + "id": { + "description": "id", + "type": "string" + }, + "name": { + "description": "name", + "$ref": "#/definitions/devops.RunPipeline.name" + }, + "organization": { + "description": "the name of organization", + "type": "string" + }, + "pipeline": { + "description": "pipeline", + "type": "string" + }, + "queueId": { + "description": "queue id", + "type": "string" + }, + "replayable": { + "description": "replayable or not", + "type": "boolean" + }, + "result": { + "description": "the result of pipeline run. e.g. SUCCESS", + "type": "string" + }, + "runSummary": { + "description": "pipeline run summary", + "$ref": "#/definitions/devops.RunPipeline.runSummary" + }, + "startTime": { + "description": "the time of start", + "$ref": "#/definitions/devops.RunPipeline.startTime" + }, + "state": { + "description": "run state. e.g. RUNNING", + "type": "string" + }, + "type": { + "description": "type", + "type": "string" + } + } + }, + "devops.RunPipeline.actions": {}, + "devops.RunPipeline.artifactsZipFile": {}, + "devops.RunPipeline.causes": { + "properties": { + "_class": { + "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", + "type": "string" + }, + "shortDescription": { + "description": "short description", + "type": "string" + }, + "userId": { + "description": "user id", + "type": "string" + }, + "userName": { + "description": "user name", + "type": "string" + } + } + }, + "devops.RunPipeline.changeSet": {}, + "devops.RunPipeline.description": {}, + "devops.RunPipeline.durationInMillis": {}, + "devops.RunPipeline.enQueueTime": {}, + "devops.RunPipeline.endTime": {}, + "devops.RunPipeline.estimatedDurationInMillis": {}, + "devops.RunPipeline.name": {}, + "devops.RunPipeline.runSummary": {}, + "devops.RunPipeline.startTime": {}, "devops.SCMOrg": { "properties": { "_class": { @@ -14279,63 +11393,7 @@ } } }, - "devops.SecretTextCredential": { - "properties": { - "secret": { - "description": "secret content of credential", - "type": "string" - } - } - }, - "devops.SingleSvnSource": { - "properties": { - "credential_id": { - "description": "credential id to access svn source", - "type": "string" - }, - "remote": { - "description": "remote address url", - "type": "string" - }, - "scm_id": { - "description": "uid of scm", - "type": "string" - } - } - }, - "devops.SonarStatus": { - "properties": { - "issues": { - "$ref": "#/definitions/sonargo.IssuesSearchObject" - }, - "jenkinsAction": { - "$ref": "#/definitions/gojenkins.GeneralObj" - }, - "measures": { - "$ref": "#/definitions/sonargo.MeasuresComponentObject" - }, - "task": { - "$ref": "#/definitions/sonargo.CeTaskObject" - } - } - }, - "devops.SshCredential": { - "properties": { - "passphrase": { - "description": "passphrase of ssh credential, password of ssh credential", - "type": "string" - }, - "private_key": { - "description": "private key of ssh credential", - "type": "string" - }, - "username": { - "description": "username of ssh credential", - "type": "string" - } - } - }, - "devops.StopPipe": { + "devops.StopPipeline": { "properties": { "_class": { "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", @@ -14349,31 +11407,31 @@ "description": "the list of all actions.", "type": "array", "items": { - "$ref": "#/definitions/devops.StopPipe.actions" + "$ref": "#/definitions/devops.StopPipeline.actions" } }, "artifactsZipFile": { "description": "the artifacts zip file", - "$ref": "#/definitions/devops.StopPipe.artifactsZipFile" + "$ref": "#/definitions/devops.StopPipeline.artifactsZipFile" }, "branch": { "$ref": "#/definitions/.branch" }, "causeOfBlockage": { "description": "the cause of blockage", - "$ref": "#/definitions/devops.StopPipe.causeOfBlockage" + "$ref": "#/definitions/devops.StopPipeline.causeOfBlockage" }, "causes": { "type": "array", "items": { - "$ref": "#/definitions/devops.StopPipe.causes" + "$ref": "#/definitions/devops.StopPipeline.causes" } }, "changeSet": { "description": "changeset information", "type": "array", "items": { - "$ref": "#/definitions/devops.StopPipe.changeSet" + "$ref": "#/definitions/devops.StopPipeline.changeSet" } }, "commitId": { @@ -14382,11 +11440,11 @@ }, "commitUrl": { "description": "commit url", - "$ref": "#/definitions/devops.StopPipe.commitUrl" + "$ref": "#/definitions/devops.StopPipeline.commitUrl" }, "description": { "description": "description", - "$ref": "#/definitions/devops.StopPipe.description" + "$ref": "#/definitions/devops.StopPipeline.description" }, "durationInMillis": { "description": "duration time in millis", @@ -14412,7 +11470,7 @@ }, "name": { "description": "name", - "$ref": "#/definitions/devops.StopPipe.name" + "$ref": "#/definitions/devops.StopPipeline.name" }, "organization": { "description": "the name of organization", @@ -14424,7 +11482,7 @@ }, "pullRequest": { "description": "pull request", - "$ref": "#/definitions/devops.StopPipe.pullRequest" + "$ref": "#/definitions/devops.StopPipeline.pullRequest" }, "replayable": { "description": "replayable or not", @@ -14452,10 +11510,10 @@ } } }, - "devops.StopPipe.actions": {}, - "devops.StopPipe.artifactsZipFile": {}, - "devops.StopPipe.causeOfBlockage": {}, - "devops.StopPipe.causes": { + "devops.StopPipeline.actions": {}, + "devops.StopPipeline.artifactsZipFile": {}, + "devops.StopPipeline.causeOfBlockage": {}, + "devops.StopPipeline.causes": { "properties": { "_class": { "description": "It’s a fully qualified name and is an identifier of the producer of this resource's capability.", @@ -14467,59 +11525,11 @@ } } }, - "devops.StopPipe.changeSet": {}, - "devops.StopPipe.commitUrl": {}, - "devops.StopPipe.description": {}, - "devops.StopPipe.name": {}, - "devops.StopPipe.pullRequest": {}, - "devops.SvnSource": { - "properties": { - "credential_id": { - "description": "credential id to access svn source", - "type": "string" - }, - "excludes": { - "description": "branches do not run pipeline", - "type": "string" - }, - "includes": { - "description": "branches to run pipeline", - "type": "string" - }, - "remote": { - "description": "remote address url", - "type": "string" - }, - "scm_id": { - "description": "uid of scm", - "type": "string" - } - } - }, - "devops.TimerTrigger": { - "properties": { - "cron": { - "description": "jenkins cron script", - "type": "string" - }, - "interval": { - "description": "interval ms", - "type": "string" - } - } - }, - "devops.UsernamePasswordCredential": { - "properties": { - "password": { - "description": "password of username_password credential", - "type": "string" - }, - "username": { - "description": "username of username_password credential", - "type": "string" - } - } - }, + "devops.StopPipeline.changeSet": {}, + "devops.StopPipeline.commitUrl": {}, + "devops.StopPipeline.description": {}, + "devops.StopPipeline.name": {}, + "devops.StopPipeline.pullRequest": {}, "devops.Validates": { "properties": { "credentialId": { @@ -14539,91 +11549,6 @@ } } }, - "fluentbitclient.KubernetesSecret": { - "required": [ - "name", - "key", - "namespace" - ], - "properties": { - "key": { - "type": "string" - }, - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - } - } - }, - "fluentbitclient.OutputPlugin": { - "required": [ - "parameters", - "type", - "name", - "enable" - ], - "properties": { - "enable": { - "description": "active status, one of true, false", - "type": "boolean" - }, - "id": { - "description": "output uuid", - "type": "string" - }, - "name": { - "description": "output plugin name, eg. fluentbit-output-es", - "type": "string" - }, - "parameters": { - "description": "output plugin configuration parameters", - "type": "array", - "items": { - "$ref": "#/definitions/fluentbitclient.Parameter" - } - }, - "type": { - "description": "output plugin type, eg. fluentbit-output-es", - "type": "string" - }, - "updatetime": { - "description": "last updatetime", - "type": "string", - "format": "date-time" - } - } - }, - "fluentbitclient.Parameter": { - "required": [ - "name", - "value" - ], - "properties": { - "name": { - "description": "configuration parameter key, eg. Name. refer to Fluent bit's Output Plugins Section for more configuration parameters.", - "type": "string" - }, - "value": { - "description": "configuration parameter value, eg. es. refer to Fluent bit's Output Plugins Section for more configuration parameters.", - "type": "string" - }, - "valueFrom": { - "$ref": "#/definitions/fluentbitclient.ValueFrom" - } - } - }, - "fluentbitclient.ValueFrom": { - "required": [ - "secretKeyRef" - ], - "properties": { - "secretKeyRef": { - "$ref": "#/definitions/fluentbitclient.KubernetesSecret" - } - } - }, "git.AuthInfo": { "required": [ "remoteUrl" @@ -14639,218 +11564,6 @@ } } }, - "gojenkins.BuildRevision": { - "properties": { - "SHA1": { - "type": "string" - }, - "branch": { - "type": "array", - "items": { - "$ref": "#/definitions/gojenkins.branch" - } - } - } - }, - "gojenkins.Builds": { - "required": [ - "buildNumber", - "buildResult", - "marked", - "revision" - ], - "properties": { - "buildNumber": { - "type": "integer", - "format": "int64" - }, - "buildResult": { - "$ref": "#/definitions/gojenkins.Builds.buildResult" - }, - "marked": { - "$ref": "#/definitions/gojenkins.BuildRevision" - }, - "revision": { - "$ref": "#/definitions/gojenkins.BuildRevision" - } - } - }, - "gojenkins.Builds.buildResult": {}, - "gojenkins.GeneralObj": { - "properties": { - "TotalCount": { - "type": "integer", - "format": "int64" - }, - "UrlName": { - "type": "string" - }, - "_class": { - "type": "string" - }, - "buildsByBranchName": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/gojenkins.Builds" - } - }, - "causes": { - "type": "array", - "items": { - "$ref": "#/definitions/gojenkins.GeneralObj.causes" - } - }, - "ceTaskId": { - "type": "string" - }, - "lastBuiltRevision": { - "$ref": "#/definitions/gojenkins.BuildRevision" - }, - "mercurialNodeName": { - "type": "string" - }, - "mercurialRevisionNumber": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/definitions/gojenkins.parameter" - } - }, - "remoteUrls": { - "type": "array", - "items": { - "type": "string" - } - }, - "scmName": { - "type": "string" - }, - "serverUrl": { - "type": "string" - }, - "sonarqubeDashboardUrl": { - "type": "string" - }, - "subdir": { - "$ref": "#/definitions/gojenkins.GeneralObj.subdir" - } - } - }, - "gojenkins.GeneralObj.causes": {}, - "gojenkins.GeneralObj.subdir": {}, - "gojenkins.branch": { - "properties": { - "Name": { - "type": "string" - }, - "SHA1": { - "type": "string" - } - } - }, - "gojenkins.parameter": { - "required": [ - "Name", - "Value" - ], - "properties": { - "Name": { - "type": "string" - }, - "Value": { - "type": "string" - } - } - }, - "iam.LoginRequest": { - "required": [ - "username", - "password" - ], - "properties": { - "password": { - "description": "password", - "type": "string" - }, - "username": { - "description": "username", - "type": "string" - } - } - }, - "iam.RoleList": { - "required": [ - "clusterRole", - "roles" - ], - "properties": { - "clusterRole": { - "description": "cluster role list", - "type": "array", - "items": { - "$ref": "#/definitions/v1.ClusterRole" - } - }, - "roles": { - "description": "role list", - "type": "array", - "items": { - "$ref": "#/definitions/v1.Role" - } - } - } - }, - "iam.Spec": { - "required": [ - "token" - ], - "properties": { - "token": { - "description": "access token", - "type": "string" - } - } - }, - "iam.Status": { - "required": [ - "authenticated" - ], - "properties": { - "authenticated": { - "description": "is authenticated", - "type": "boolean" - }, - "user": { - "description": "user info", - "type": "object" - } - } - }, - "iam.TokenReview": { - "required": [ - "apiVersion", - "kind" - ], - "properties": { - "apiVersion": { - "description": "Kubernetes API version", - "type": "string" - }, - "kind": { - "description": "kind of the API object", - "type": "string" - }, - "spec": { - "$ref": "#/definitions/iam.Spec" - }, - "status": { - "description": "token review status", - "$ref": "#/definitions/iam.Status" - } - } - }, "inf.Dec": { "required": [ "unscaled", @@ -14865,92 +11578,102 @@ } } }, - "log.FluentbitOutputsResult": { + "logging.Bucket": { "required": [ - "status" + "time", + "count" ], "properties": { - "error": { - "description": "debug information", - "type": "string" - }, - "outputs": { - "description": "array of fluent bit output plugins", - "type": "array", - "items": { - "$ref": "#/definitions/fluentbitclient.OutputPlugin" - } - }, - "status": { - "description": "response status", + "count": { + "description": "total number of logs at intervals", "type": "integer", - "format": "int32" + "format": "int64" + }, + "time": { + "description": "timestamp", + "type": "integer", + "format": "int64" } } }, - "metrics.APIResponse": { + "logging.Histogram": { "required": [ - "status", - "data" + "total", + "histograms" ], "properties": { - "data": { - "description": "actual metric result", - "$ref": "#/definitions/v1alpha2.QueryResult" - }, - "error": { - "type": "string" - }, - "errorType": { - "type": "string" - }, - "metric_name": { - "description": "metric name, eg. scheduler_up_sum", - "type": "string" - }, - "status": { - "description": "result status, one of error, success", - "type": "string" - }, - "warnings": { + "histograms": { + "description": "actual array of histogram results", "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/logging.Bucket" } + }, + "total": { + "description": "total number of logs", + "type": "integer", + "format": "int64" } } }, - "metrics.Response": { + "logging.Logs": { "required": [ - "metrics_level", - "results" + "total" ], "properties": { - "metrics_level": { - "description": "metric level, eg. cluster", - "type": "string" - }, - "page": { - "description": "current page returned", - "type": "integer", - "format": "int32" - }, - "results": { + "records": { "description": "actual array of results", "type": "array", "items": { - "$ref": "#/definitions/metrics.APIResponse" + "$ref": "#/definitions/logging.Record" } }, - "total_item": { - "description": "page size", + "total": { + "description": "total number of matched results", "type": "integer", - "format": "int32" + "format": "int64" + } + } + }, + "logging.Record": { + "properties": { + "container": { + "description": "container name", + "type": "string" }, - "total_page": { - "description": "total number of pages", + "log": { + "description": "log message", + "type": "string" + }, + "namespace": { + "description": "namespace", + "type": "string" + }, + "pod": { + "description": "pod name", + "type": "string" + }, + "time": { + "description": "log timestamp", + "type": "string" + } + } + }, + "logging.Statistics": { + "required": [ + "containers", + "logs" + ], + "properties": { + "containers": { + "description": "total number of containers", "type": "integer", - "format": "int32" + "format": "int64" + }, + "logs": { + "description": "total number of logs", + "type": "integer", + "format": "int64" } } }, @@ -14989,23 +11712,6 @@ } } }, - "models.Action": { - "required": [ - "name", - "rules" - ], - "properties": { - "name": { - "type": "string" - }, - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/v1.PolicyRule" - } - } - } - }, "models.AppHealth": { "required": [ "workloadStatuses", @@ -15023,96 +11729,6 @@ } } }, - "models.ComponentStatus": { - "required": [ - "name", - "namespace", - "selfLink", - "label", - "startedAt", - "totalBackends", - "healthyBackends" - ], - "properties": { - "healthyBackends": { - "description": "the number of healthy backend components", - "type": "integer", - "format": "int32" - }, - "label": { - "description": "labels", - "$ref": "#/definitions/models.ComponentStatus.label" - }, - "name": { - "description": "component name", - "type": "string" - }, - "namespace": { - "description": "the name of the namespace", - "type": "string" - }, - "selfLink": { - "description": "self link", - "type": "string" - }, - "startedAt": { - "description": "started time", - "type": "string", - "format": "date-time" - }, - "totalBackends": { - "description": "the total replicas of each backend system component", - "type": "integer", - "format": "int32" - } - } - }, - "models.ComponentStatus.label": {}, - "models.HealthStatus": { - "required": [ - "kubesphereStatus", - "kubernetesStatus", - "nodeStatus" - ], - "properties": { - "kubernetesStatus": { - "description": "kubernetes components status", - "type": "array", - "items": { - "$ref": "#/definitions/v1.ComponentStatus" - } - }, - "kubesphereStatus": { - "description": "kubesphere components status", - "type": "array", - "items": { - "$ref": "#/definitions/models.ComponentStatus" - } - }, - "nodeStatus": { - "description": "nodes status", - "$ref": "#/definitions/models.NodeStatus" - } - } - }, - "models.NodeStatus": { - "required": [ - "totalNodes", - "healthyNodes" - ], - "properties": { - "healthyNodes": { - "description": "the number of healthy nodes", - "type": "integer", - "format": "int32" - }, - "totalNodes": { - "description": "total number of nodes", - "type": "integer", - "format": "int32" - } - } - }, "models.PageableResponse": { "required": [ "items", @@ -15196,143 +11812,6 @@ } } }, - "models.ResourceQuota": { - "required": [ - "namespace", - "data" - ], - "properties": { - "data": { - "description": "resource quota status", - "$ref": "#/definitions/v1.ResourceQuotaStatus" - }, - "namespace": { - "description": "namespace", - "type": "string" - } - } - }, - "models.Rule": { - "required": [ - "name", - "actions" - ], - "properties": { - "actions": { - "type": "array", - "items": { - "$ref": "#/definitions/models.Action" - } - }, - "name": { - "type": "string" - } - } - }, - "models.SimpleRule": { - "required": [ - "name", - "actions" - ], - "properties": { - "actions": { - "description": "actions", - "type": "array", - "items": { - "type": "string" - } - }, - "name": { - "description": "rule name", - "type": "string" - } - } - }, - "models.Token": { - "required": [ - "access_token" - ], - "properties": { - "access_token": { - "description": "access token", - "type": "string" - } - } - }, - "models.User": { - "required": [ - "username", - "email", - "description", - "create_time", - "avatar_url", - "last_login_time", - "status", - "cluster_role" - ], - "properties": { - "avatar_url": { - "type": "string" - }, - "cluster_role": { - "type": "string" - }, - "create_time": { - "type": "string", - "format": "date-time" - }, - "current_password": { - "type": "string" - }, - "description": { - "type": "string" - }, - "email": { - "type": "string" - }, - "groups": { - "type": "array", - "items": { - "type": "string" - } - }, - "lang": { - "type": "string" - }, - "last_login_time": { - "type": "string" - }, - "password": { - "type": "string" - }, - "role": { - "type": "string" - }, - "role_bind_time": { - "type": "string", - "format": "date-time" - }, - "role_binding": { - "type": "string" - }, - "roles": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "status": { - "type": "integer", - "format": "int32" - }, - "username": { - "type": "string" - }, - "workspace_role": { - "type": "string" - } - } - }, "models.WorkloadStatus": { "required": [ "name", @@ -15353,6 +11832,105 @@ } } }, + "monitoring.Metadata": { + "required": [ + "data" + ], + "properties": { + "data": { + "description": "actual array of results", + "type": "array", + "items": { + "$ref": "#/definitions/monitoring.Metadata" + } + } + } + }, + "monitoring.Metric": { + "properties": { + "data": { + "description": "actual metric result", + "$ref": "#/definitions/monitoring.MetricData" + }, + "error": { + "type": "string" + }, + "metric_name": { + "description": "metric name, eg. scheduler_up_sum", + "type": "string" + } + } + }, + "monitoring.MetricData": { + "properties": { + "result": { + "description": "metric data including labels, time series and values", + "type": "array", + "items": { + "$ref": "#/definitions/monitoring.MetricValue" + } + }, + "resultType": { + "description": "result type, one of matrix, vector", + "type": "string" + } + } + }, + "monitoring.MetricValue": { + "properties": { + "metric": { + "description": "time series labels", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "value": { + "description": "time series, values of vector type", + "type": "array", + "items": { + "type": "number" + } + }, + "values": { + "description": "time series, values of matrix type", + "type": "array", + "items": { + "$ref": "#/definitions/monitoring.Point" + } + } + } + }, + "monitoring.Metrics": { + "required": [ + "results" + ], + "properties": { + "page": { + "description": "current page returned", + "type": "integer", + "format": "int32" + }, + "results": { + "description": "actual array of results", + "type": "array", + "items": { + "$ref": "#/definitions/monitoring.Metric" + } + }, + "total_item": { + "description": "page size", + "type": "integer", + "format": "int32" + }, + "total_page": { + "description": "total number of pages", + "type": "integer", + "format": "int32" + } + } + }, + "monitoring.Point": {}, "openpitrix.App": { "required": [ "category_set" @@ -16233,27 +12811,6 @@ } }, "prometheus.Metrics.histograms": {}, - "registries.AuthInfo": { - "required": [ - "username", - "password", - "serverhost" - ], - "properties": { - "password": { - "description": "password", - "type": "string" - }, - "serverhost": { - "description": "registry server host", - "type": "string" - }, - "username": { - "description": "username", - "type": "string" - } - } - }, "registries.Config": { "properties": { "ArgsEscaped": { @@ -16662,734 +13219,6 @@ } } }, - "sonargo.CeTaskObject": { - "properties": { - "task": { - "$ref": "#/definitions/sonargo.Task" - } - } - }, - "sonargo.Comment": { - "properties": { - "createdAt": { - "type": "string" - }, - "htmlText": { - "type": "string" - }, - "key": { - "type": "string" - }, - "login": { - "type": "string" - }, - "markdown": { - "type": "string" - }, - "updatable": { - "type": "boolean" - } - } - }, - "sonargo.Component": { - "properties": { - "analysisDate": { - "type": "string" - }, - "description": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "key": { - "type": "string" - }, - "language": { - "type": "string" - }, - "lastAnalysisDate": { - "type": "string" - }, - "leakPeriodDate": { - "type": "string" - }, - "longName": { - "type": "string" - }, - "measures": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.SonarMeasure" - } - }, - "name": { - "type": "string" - }, - "organization": { - "type": "string" - }, - "path": { - "type": "string" - }, - "project": { - "type": "string" - }, - "qualifier": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "uuid": { - "type": "string" - }, - "version": { - "type": "string" - }, - "visibility": { - "type": "string" - } - } - }, - "sonargo.Facet": { - "properties": { - "name": { - "type": "string" - }, - "property": { - "type": "string" - }, - "values": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.FacetValue" - } - } - } - }, - "sonargo.FacetValue": { - "properties": { - "count": { - "type": "integer", - "format": "int64" - }, - "val": { - "type": "string" - } - } - }, - "sonargo.History": { - "properties": { - "date": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "sonargo.Issue": { - "properties": { - "actions": { - "type": "array", - "items": { - "type": "string" - } - }, - "assignee": { - "type": "string" - }, - "author": { - "type": "string" - }, - "closeDate": { - "type": "string" - }, - "comments": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Comment" - } - }, - "component": { - "type": "string" - }, - "creationDate": { - "type": "string" - }, - "debt": { - "type": "string" - }, - "effort": { - "type": "string" - }, - "flows": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Issue.flows" - } - }, - "fromHotspot": { - "type": "boolean" - }, - "hash": { - "type": "string" - }, - "key": { - "type": "string" - }, - "line": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "organization": { - "type": "string" - }, - "project": { - "type": "string" - }, - "resolution": { - "type": "string" - }, - "rule": { - "type": "string" - }, - "severity": { - "type": "string" - }, - "status": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "textRange": { - "$ref": "#/definitions/sonargo.TextRange" - }, - "transitions": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "string" - }, - "updateDate": { - "type": "string" - } - } - }, - "sonargo.Issue.flows": {}, - "sonargo.IssuesSearchObject": { - "properties": { - "components": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Component" - } - }, - "debtTotal": { - "type": "integer", - "format": "int32" - }, - "effortTotal": { - "type": "integer", - "format": "int32" - }, - "facets": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Facet" - } - }, - "issues": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Issue" - } - }, - "p": { - "type": "integer", - "format": "int32" - }, - "paging": { - "$ref": "#/definitions/sonargo.Paging" - }, - "ps": { - "type": "integer", - "format": "int32" - }, - "rules": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Rule" - } - }, - "total": { - "type": "integer", - "format": "int32" - }, - "users": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.User" - } - } - } - }, - "sonargo.MeasuresComponentObject": { - "properties": { - "component": { - "$ref": "#/definitions/sonargo.Component" - }, - "metrics": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Metric" - } - }, - "periods": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Period" - } - } - } - }, - "sonargo.Metric": { - "required": [ - "Description", - "direction", - "Domain", - "Key", - "Name", - "Type" - ], - "properties": { - "Description": { - "type": "string" - }, - "Domain": { - "type": "string" - }, - "Key": { - "type": "string" - }, - "Name": { - "type": "string" - }, - "Type": { - "type": "string" - }, - "custom": { - "type": "boolean" - }, - "decimalScale": { - "type": "integer", - "format": "int32" - }, - "direction": { - "type": "integer", - "format": "int32" - }, - "hidden": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "qualitative": { - "type": "boolean" - } - } - }, - "sonargo.Paging": { - "properties": { - "pageIndex": { - "type": "integer", - "format": "int32" - }, - "pageSize": { - "type": "integer", - "format": "int32" - }, - "total": { - "type": "integer", - "format": "int32" - } - } - }, - "sonargo.Period": { - "properties": { - "bestValue": { - "type": "boolean" - }, - "date": { - "type": "string" - }, - "index": { - "type": "integer", - "format": "int64" - }, - "mode": { - "type": "string" - }, - "parameter": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "sonargo.Rule": { - "properties": { - "createdAt": { - "type": "string" - }, - "debtOverloaded": { - "type": "boolean" - }, - "htmlDesc": { - "type": "string" - }, - "isExternal": { - "type": "boolean" - }, - "isTemplate": { - "type": "boolean" - }, - "key": { - "type": "string" - }, - "lang": { - "type": "string" - }, - "langName": { - "type": "string" - }, - "mdDesc": { - "type": "string" - }, - "name": { - "type": "string" - }, - "params": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.RuleParam" - } - }, - "remFnOverloaded": { - "type": "boolean" - }, - "repo": { - "type": "string" - }, - "scope": { - "type": "string" - }, - "severity": { - "type": "string" - }, - "status": { - "type": "string" - }, - "sysTags": { - "type": "array", - "items": { - "type": "string" - } - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "templateKey": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "sonargo.RuleParam": { - "properties": { - "defaultValue": { - "type": "string" - }, - "htmlDesc": { - "type": "string" - }, - "key": { - "type": "string" - }, - "type": { - "type": "string" - } - } - }, - "sonargo.SonarMeasure": { - "properties": { - "bestValue": { - "type": "boolean" - }, - "history": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.History" - } - }, - "metric": { - "type": "string" - }, - "periods": { - "type": "array", - "items": { - "$ref": "#/definitions/sonargo.Period" - } - }, - "value": { - "type": "string" - } - } - }, - "sonargo.Task": { - "properties": { - "analysisId": { - "type": "string" - }, - "componentId": { - "type": "string" - }, - "componentKey": { - "type": "string" - }, - "componentName": { - "type": "string" - }, - "componentQualifier": { - "type": "string" - }, - "errorMessage": { - "type": "string" - }, - "errorStacktrace": { - "type": "string" - }, - "errorType": { - "type": "string" - }, - "executedAt": { - "type": "string" - }, - "executionTimeMs": { - "type": "integer", - "format": "int64" - }, - "finishedAt": { - "type": "string" - }, - "hasErrorStacktrace": { - "type": "boolean" - }, - "hasScannerContext": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "logs": { - "type": "boolean" - }, - "organization": { - "type": "string" - }, - "scannerContext": { - "type": "string" - }, - "startedAt": { - "type": "string" - }, - "status": { - "type": "string" - }, - "submittedAt": { - "type": "string" - }, - "submitterLogin": { - "type": "string" - }, - "taskType": { - "type": "string" - }, - "type": { - "type": "string" - }, - "warningCount": { - "type": "integer", - "format": "int32" - }, - "warnings": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "sonargo.TextRange": { - "properties": { - "endLine": { - "type": "integer", - "format": "int32" - }, - "endOffset": { - "type": "integer", - "format": "int32" - }, - "startLine": { - "type": "integer", - "format": "int32" - }, - "startOffset": { - "type": "integer", - "format": "int32" - } - } - }, - "sonargo.User": { - "properties": { - "active": { - "type": "boolean" - }, - "avatar": { - "type": "string" - }, - "email": { - "type": "string" - }, - "externalIdentity": { - "type": "string" - }, - "externalProvider": { - "type": "string" - }, - "groups": { - "type": "array", - "items": { - "type": "string" - } - }, - "local": { - "type": "boolean" - }, - "login": { - "type": "string" - }, - "name": { - "type": "string" - }, - "scmAccounts": { - "type": "array", - "items": { - "type": "string" - } - }, - "selected": { - "type": "boolean" - }, - "tokensCount": { - "type": "integer", - "format": "int32" - } - } - }, - "status.WorkLoadStatus": { - "required": [ - "namespace", - "data" - ], - "properties": { - "data": { - "description": "the number of unhealthy workloads", - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "items": { - "description": "unhealthy workloads", - "type": "object" - }, - "namespace": { - "description": "the name of the namespace", - "type": "string" - } - } - }, - "struct { Count uint32 \"json:\\\"count\\\"\" }": { - "required": [ - "count" - ], - "properties": { - "count": { - "type": "integer" - } - } - }, - "struct { Items ||devops.BranchPipelineRun \"json:\\\"items\\\"\"; Total int \"json:\\\"total_count\\\"\" }": { - "required": [ - "items", - "total_count" - ], - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.BranchPipelineRun" - } - }, - "total_count": { - "type": "integer", - "format": "int32" - } - } - }, - "struct { Items ||devops.Pipeline \"json:\\\"items\\\"\"; Total int \"json:\\\"total_count\\\"\" }": { - "required": [ - "items", - "total_count" - ], - "properties": { - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/devops.Pipeline" - } - }, - "total_count": { - "type": "integer", - "format": "int32" - } - } - }, "v1.AWSElasticBlockStoreVolumeSource": { "description": "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", "required": [ @@ -17432,18 +13261,6 @@ } } }, - "v1.AggregationRule": { - "description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole", - "properties": { - "clusterRoleSelectors": { - "description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added", - "type": "array", - "items": { - "$ref": "#/definitions/v1.LabelSelector" - } - } - } - }, "v1.AzureDataDiskCachingMode": {}, "v1.AzureDataDiskKind": {}, "v1.AzureDiskVolumeSource": { @@ -17500,6 +13317,37 @@ } } }, + "v1.CSIVolumeSource": { + "description": "Represents a source location of a volume to mount, managed by an external CSI driver", + "required": [ + "driver" + ], + "properties": { + "driver": { + "description": "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", + "type": "string" + }, + "fsType": { + "description": "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", + "type": "string" + }, + "nodePublishSecretRef": { + "description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.", + "$ref": "#/definitions/v1.LocalObjectReference" + }, + "readOnly": { + "description": "Specifies a read-only configuration for the volume. Defaults to false (read/write).", + "type": "boolean" + }, + "volumeAttributes": { + "description": "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, "v1.Capabilities": { "description": "Adds and removes POSIX capabilities from running containers.", "properties": { @@ -17527,7 +13375,7 @@ ], "properties": { "monitors": { - "description": "Required: Monitors is a collection of Ceph monitors More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "description": "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", "type": "array", "items": { "type": "string" @@ -17538,19 +13386,19 @@ "type": "string" }, "readOnly": { - "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", "type": "boolean" }, "secretFile": { - "description": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "description": "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", "type": "string" }, "secretRef": { - "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "description": "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", "$ref": "#/definitions/v1.LocalObjectReference" }, "user": { - "description": "Optional: User is the rados user name, default is admin More info: https://releases.k8s.io/HEAD/examples/volumes/cephfs/README.md#how-to-use-it", + "description": "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", "type": "string" } } @@ -17562,11 +13410,11 @@ ], "properties": { "fsType": { - "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "description": "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", "type": "string" }, "readOnly": { - "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "description": "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", "type": "boolean" }, "secretRef": { @@ -17574,7 +13422,7 @@ "$ref": "#/definitions/v1.LocalObjectReference" }, "volumeID": { - "description": "volume id used to identify the volume in cinder More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "description": "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", "type": "string" } } @@ -17589,86 +13437,6 @@ } } }, - "v1.ClusterRole": { - "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", - "required": [ - "rules" - ], - "properties": { - "aggregationRule": { - "description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.", - "$ref": "#/definitions/v1.AggregationRule" - }, - "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": { - "description": "Standard object's metadata.", - "$ref": "#/definitions/v1.ObjectMeta" - }, - "rules": { - "description": "Rules holds all the PolicyRules for this ClusterRole", - "type": "array", - "items": { - "$ref": "#/definitions/v1.PolicyRule" - } - } - } - }, - "v1.ComponentCondition": { - "description": "Information about the condition of a component.", - "required": [ - "type", - "status" - ], - "properties": { - "error": { - "description": "Condition error code for a component. For example, a health check error code.", - "type": "string" - }, - "message": { - "description": "Message about the condition for a component. For example, information about a health check.", - "type": "string" - }, - "status": { - "description": "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", - "type": "string" - }, - "type": { - "description": "Type of condition for a component. Valid value: \"Healthy\"", - "type": "string" - } - } - }, - "v1.ComponentStatus": { - "description": "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", - "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" - }, - "conditions": { - "description": "List of component conditions observed", - "type": "array", - "items": { - "$ref": "#/definitions/v1.ComponentCondition" - } - }, - "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": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", - "$ref": "#/definitions/v1.ObjectMeta" - } - } - }, "v1.ConfigMapEnvSource": { "description": "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", "properties": { @@ -17697,7 +13465,7 @@ "type": "string" }, "optional": { - "description": "Specify whether the ConfigMap or it's key must be defined", + "description": "Specify whether the ConfigMap or its key must be defined", "type": "boolean" } } @@ -17717,7 +13485,7 @@ "type": "string" }, "optional": { - "description": "Specify whether the ConfigMap or it's keys must be defined", + "description": "Specify whether the ConfigMap or its keys must be defined", "type": "boolean" } } @@ -17742,7 +13510,7 @@ "type": "string" }, "optional": { - "description": "Specify whether the ConfigMap or it's keys must be defined", + "description": "Specify whether the ConfigMap or its keys must be defined", "type": "boolean" } } @@ -17820,6 +13588,10 @@ "description": "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", "$ref": "#/definitions/v1.SecurityContext" }, + "startupProbe": { + "description": "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is an alpha feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + "$ref": "#/definitions/v1.Probe" + }, "stdin": { "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", "type": "boolean" @@ -17894,23 +13666,23 @@ "description": "DaemonSet represents the configuration of a daemon set.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "The desired behavior of this daemon set. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.DaemonSetSpec" }, "status": { - "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "The current status of this daemon set. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.DaemonSetStatus" } } @@ -18055,11 +13827,11 @@ "description": "Deployment enables declarative updates for Pods and ReplicaSets.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { @@ -18339,6 +14111,127 @@ } } }, + "v1.EphemeralContainer": { + "description": "An EphemeralContainer is a container that may be added temporarily to an existing pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a pod is removed or restarted. If an ephemeral container causes a pod to exceed its resource allocation, the pod may be evicted. Ephemeral containers may not be added by directly updating the pod spec. They must be added via the pod's ephemeralcontainers subresource, and they will appear in the pod spec once added. This is an alpha feature enabled by the EphemeralContainers feature flag.", + "required": [ + "name" + ], + "properties": { + "args": { + "description": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string" + } + }, + "command": { + "description": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "description": "List of environment variables to set in the container. Cannot be updated.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.EnvVar" + } + }, + "envFrom": { + "description": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.EnvFromSource" + } + }, + "image": { + "description": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", + "type": "string" + }, + "imagePullPolicy": { + "description": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + "type": "string" + }, + "lifecycle": { + "description": "Lifecycle is not allowed for ephemeral containers.", + "$ref": "#/definitions/v1.Lifecycle" + }, + "livenessProbe": { + "description": "Probes are not allowed for ephemeral containers.", + "$ref": "#/definitions/v1.Probe" + }, + "name": { + "description": "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + "type": "string" + }, + "ports": { + "description": "Ports are not allowed for ephemeral containers.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.ContainerPort" + } + }, + "readinessProbe": { + "description": "Probes are not allowed for ephemeral containers.", + "$ref": "#/definitions/v1.Probe" + }, + "resources": { + "description": "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "securityContext": { + "description": "SecurityContext is not allowed for ephemeral containers.", + "$ref": "#/definitions/v1.SecurityContext" + }, + "startupProbe": { + "description": "Probes are not allowed for ephemeral containers.", + "$ref": "#/definitions/v1.Probe" + }, + "stdin": { + "description": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + "type": "boolean" + }, + "stdinOnce": { + "description": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + "type": "boolean" + }, + "targetContainerName": { + "description": "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container is run in whatever namespaces are shared for the pod. Note that the container runtime must support this feature.", + "type": "string" + }, + "terminationMessagePath": { + "description": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + "type": "string" + }, + "terminationMessagePolicy": { + "description": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + "type": "string" + }, + "tty": { + "description": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + "type": "boolean" + }, + "volumeDevices": { + "description": "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.VolumeDevice" + } + }, + "volumeMounts": { + "description": "Pod volumes to mount into the container's filesystem. Cannot be updated.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.VolumeMount" + } + }, + "workingDir": { + "description": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + "type": "string" + } + } + }, "v1.ExecAction": { "description": "ExecAction describes a \"run in container\" action.", "properties": { @@ -18481,15 +14374,15 @@ ], "properties": { "endpoints": { - "description": "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "description": "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", "type": "string" }, "path": { - "description": "Path is the Glusterfs volume path. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "description": "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", "type": "string" }, "readOnly": { - "description": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md#create-a-pod", + "description": "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", "type": "boolean" } } @@ -18592,6 +14485,7 @@ } } }, + "v1.IPFamily": {}, "v1.ISCSIVolumeSource": { "description": "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", "required": [ @@ -18650,37 +14544,6 @@ } } }, - "v1.Initializer": { - "description": "Initializer is information about an initializer that has not yet completed.", - "required": [ - "name" - ], - "properties": { - "name": { - "description": "name of the process that is responsible for initializing this object.", - "type": "string" - } - } - }, - "v1.Initializers": { - "description": "Initializers tracks the progress of initialization.", - "required": [ - "pending" - ], - "properties": { - "pending": { - "description": "Pending is a list of initializers that must execute in order before this object is visible. When the last pending initializer is removed, and no failing result is set, the initializers struct will be set to nil and the object is considered as initialized and visible to all clients.", - "type": "array", - "items": { - "$ref": "#/definitions/v1.Initializer" - } - }, - "result": { - "description": "If result is set with the Failure field, the object will be persisted to storage and then deleted, ensuring that other clients can observe the deletion.", - "$ref": "#/definitions/v1.Status" - } - } - }, "v1.KeyToPath": { "description": "Maps a string key to a path within a volume.", "required": [ @@ -18754,7 +14617,7 @@ "$ref": "#/definitions/v1.Handler" }, "preStop": { - "description": "PreStop is called immediately before a container is terminated. The container is terminated after the handler completes. The reason for termination is passed to the handler. Regardless of the outcome of the handler, the container is eventually terminated. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", + "description": "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", "$ref": "#/definitions/v1.Handler" } } @@ -18766,12 +14629,17 @@ "description": "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", "type": "string" }, + "remainingItemCount": { + "description": "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + "type": "integer", + "format": "int64" + }, "resourceVersion": { - "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency", + "description": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", "type": "string" }, "selfLink": { - "description": "selfLink is a URL representing this object. Populated by the system. Read-only.", + "description": "selfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", "type": "string" } } @@ -18810,6 +14678,35 @@ } } }, + "v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "properties": { + "apiVersion": { + "description": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "type": "string" + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "time": { + "description": "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", + "type": "string" + } + } + }, "v1.MountPropagationMode": {}, "v1.NFSVolumeSource": { "description": "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", @@ -18836,27 +14733,53 @@ "description": "Namespace provides a scope for Names. Use of multiple namespaces is optional.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.NamespaceSpec" }, "status": { - "description": "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.NamespaceStatus" } } }, + "v1.NamespaceCondition": { + "description": "NamespaceCondition contains details about state of namespace.", + "required": [ + "type", + "status" + ], + "properties": { + "lastTransitionTime": { + "type": "string" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of namespace controller condition.", + "type": "string" + } + } + }, "v1.NamespaceSpec": { "description": "NamespaceSpec describes the attributes on a Namespace.", "properties": { @@ -18872,6 +14795,13 @@ "v1.NamespaceStatus": { "description": "NamespaceStatus is information about the current status of a Namespace.", "properties": { + "conditions": { + "description": "Represents the latest available observations of a namespace's current state.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.NamespaceCondition" + } + }, "phase": { "description": "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", "type": "string" @@ -18983,7 +14913,7 @@ "type": "string" }, "creationTimestamp": { - "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "type": "string" }, "deletionGracePeriodSeconds": { @@ -18992,7 +14922,7 @@ "format": "int64" }, "deletionTimestamp": { - "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "type": "string" }, "finalizers": { @@ -19003,7 +14933,7 @@ } }, "generateName": { - "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#idempotency", + "description": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", "type": "string" }, "generation": { @@ -19011,10 +14941,6 @@ "type": "integer", "format": "int64" }, - "initializers": { - "description": "An initializer is a controller which enforces some system invariant at object creation time. This field is a list of initializers that have not yet acted on this object. If nil or empty, this object has been completely initialized. Otherwise, the object is considered uninitialized and is hidden (in list/watch and get calls) from clients that haven't explicitly asked to observe uninitialized objects.\n\nWhen an object is created, the system will populate this list with the current set of initializers. Only privileged users may set or modify this list. Once it is empty, it may not be modified further by any user.", - "$ref": "#/definitions/v1.Initializers" - }, "labels": { "description": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", "type": "object", @@ -19022,6 +14948,13 @@ "type": "string" } }, + "managedFields": { + "description": "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.ManagedFieldsEntry" + } + }, "name": { "description": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", "type": "string" @@ -19038,11 +14971,11 @@ } }, "resourceVersion": { - "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#concurrency-control-and-consistency", + "description": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", "type": "string" }, "selfLink": { - "description": "SelfLink is a URL representing this object. Populated by the system. Read-only.", + "description": "SelfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", "type": "string" }, "uid": { @@ -19073,7 +15006,7 @@ "type": "boolean" }, "kind": { - "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "name": { @@ -19091,15 +15024,15 @@ "description": "PersistentVolumeClaim is a user's request for and claim to a persistent volume", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { @@ -19145,9 +15078,6 @@ }, "v1.PersistentVolumeClaimSpec": { "description": "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", - "required": [ - "dataSource" - ], "properties": { "accessModes": { "description": "AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", @@ -19395,6 +15325,10 @@ "items": { "$ref": "#/definitions/v1.Sysctl" } + }, + "windowsOptions": { + "description": "The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "$ref": "#/definitions/v1.WindowsSecurityContextOptions" } } }, @@ -19433,9 +15367,16 @@ "type": "string" }, "enableServiceLinks": { - "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links.", + "description": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", "type": "boolean" }, + "ephemeralContainers": { + "description": "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is alpha-level and is only honored by servers that enable the EphemeralContainers feature.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.EphemeralContainer" + } + }, "hostAliases": { "description": "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", "type": "array", @@ -19467,7 +15408,7 @@ } }, "initContainers": { - "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, or Liveness probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + "description": "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", "type": "array", "items": { "$ref": "#/definitions/v1.Container" @@ -19484,6 +15425,17 @@ "type": "string" } }, + "overhead": { + "description": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/resource.Quantity" + } + }, + "preemptionPolicy": { + "description": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is alpha-level and is only honored by servers that enable the NonPreemptingPriority feature.", + "$ref": "#/definitions/v1.PreemptionPolicy" + }, "priority": { "description": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", "type": "integer", @@ -19494,7 +15446,7 @@ "type": "string" }, "readinessGates": { - "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://github.com/kubernetes/community/blob/master/keps/sig-network/0007-pod-ready%2B%2B.md", + "description": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", "type": "array", "items": { "$ref": "#/definitions/v1.PodReadinessGate" @@ -19505,7 +15457,7 @@ "type": "string" }, "runtimeClassName": { - "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://github.com/kubernetes/community/blob/master/keps/sig-node/0014-runtime-class.md This is an alpha feature and may change in the future.", + "description": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", "type": "string" }, "schedulerName": { @@ -19544,6 +15496,13 @@ "$ref": "#/definitions/v1.Toleration" } }, + "topologySpreadConstraints": { + "description": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. This field is alpha-level and is only honored by clusters that enables the EvenPodsSpread feature. All topologySpreadConstraints are ANDed.", + "type": "array", + "items": { + "$ref": "#/definitions/v1.TopologySpreadConstraint" + } + }, "volumes": { "description": "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", "type": "array", @@ -19557,58 +15516,15 @@ "description": "PodTemplateSpec describes the data a pod should have when created from a template", "properties": { "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.PodSpec" } } }, - "v1.PolicyRule": { - "description": "PolicyRule holds information that describes a policy rule, but does not contain information about who the rule applies to or which namespace the rule applies to.", - "required": [ - "verbs" - ], - "properties": { - "apiGroups": { - "description": "APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of the enumerated resources in any API group will be allowed.", - "type": "array", - "items": { - "type": "string" - } - }, - "nonResourceURLs": { - "description": "NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. Rules can either apply to API resources (such as \"pods\" or \"secrets\") or non-resource URL paths (such as \"/api\"), but not both.", - "type": "array", - "items": { - "type": "string" - } - }, - "resourceNames": { - "description": "ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.", - "type": "array", - "items": { - "type": "string" - } - }, - "resources": { - "description": "Resources is a list of resources this rule applies to. ResourceAll represents all resources.", - "type": "array", - "items": { - "type": "string" - } - }, - "verbs": { - "description": "Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds.", - "type": "array", - "items": { - "type": "string" - } - } - } - }, "v1.PortworxVolumeSource": { "description": "PortworxVolumeSource represents a Portworx volume resource.", "required": [ @@ -19629,6 +15545,7 @@ } } }, + "v1.PreemptionPolicy": {}, "v1.PreferredSchedulingTerm": { "description": "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", "required": [ @@ -19674,7 +15591,7 @@ "format": "int32" }, "successThreshold": { - "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness. Minimum value is 1.", + "description": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", "type": "integer", "format": "int32" }, @@ -19729,6 +15646,10 @@ "description": "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", "type": "string" }, + "tenant": { + "description": "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", + "type": "string" + }, "user": { "description": "User to map volume access to Defaults to serivceaccount user", "type": "string" @@ -19751,34 +15672,34 @@ "type": "string" }, "image": { - "description": "The rados image name. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "string" }, "keyring": { - "description": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "string" }, "monitors": { - "description": "A collection of Ceph monitors. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "array", "items": { "type": "string" } }, "pool": { - "description": "The rados pool name. Default is rbd. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "string" }, "readOnly": { - "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "boolean" }, "secretRef": { - "description": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "$ref": "#/definitions/v1.LocalObjectReference" }, "user": { - "description": "The rados user name. Default is admin. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md#how-to-use-it", + "description": "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", "type": "string" } } @@ -19787,23 +15708,23 @@ "description": "ReplicaSet ensures that a specified number of pod replicas are running at any given time.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "If the Labels of a ReplicaSet are empty, they are defaulted to be the same as the Pod(s) that the ReplicaSet manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Spec defines the specification of the desired behavior of the ReplicaSet. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.ReplicaSetSpec" }, "status": { - "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Status is the most recently observed status of the ReplicaSet. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.ReplicaSetStatus" } } @@ -19961,33 +15882,6 @@ } } }, - "v1.Role": { - "description": "Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.", - "required": [ - "rules" - ], - "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": { - "description": "Standard object's metadata.", - "$ref": "#/definitions/v1.ObjectMeta" - }, - "rules": { - "description": "Rules holds all the PolicyRules for this Role", - "type": "array", - "items": { - "$ref": "#/definitions/v1.PolicyRule" - } - } - } - }, "v1.RollingUpdateDaemonSet": { "description": "Spec to control the desired behavior of daemon set rolling update.", "properties": { @@ -20119,7 +16013,7 @@ "type": "string" }, "optional": { - "description": "Specify whether the Secret or it's key must be defined", + "description": "Specify whether the Secret or its key must be defined", "type": "boolean" } } @@ -20173,7 +16067,7 @@ } }, "optional": { - "description": "Specify whether the Secret or it's keys must be defined", + "description": "Specify whether the Secret or its keys must be defined", "type": "boolean" }, "secretName": { @@ -20222,6 +16116,10 @@ "seLinuxOptions": { "description": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", "$ref": "#/definitions/v1.SELinuxOptions" + }, + "windowsOptions": { + "description": "The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "$ref": "#/definitions/v1.WindowsSecurityContextOptions" } } }, @@ -20229,23 +16127,23 @@ "description": "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.ServiceSpec" }, "status": { - "description": "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1.ServiceStatus" } } @@ -20271,33 +16169,6 @@ } } }, - "v1.ServiceList": { - "description": "ServiceList holds a list of services.", - "required": [ - "items" - ], - "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" - }, - "items": { - "description": "List of services", - "type": "array", - "items": { - "$ref": "#/definitions/v1.Service" - } - }, - "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": { - "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - "$ref": "#/definitions/v1.ListMeta" - } - } - }, "v1.ServicePort": { "description": "ServicePort contains information on service's port.", "required": [ @@ -20305,7 +16176,7 @@ ], "properties": { "name": { - "description": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service.", + "description": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", "type": "string" }, "nodePort": { @@ -20355,6 +16226,10 @@ "type": "integer", "format": "int32" }, + "ipFamily": { + "description": "ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.", + "$ref": "#/definitions/v1.IPFamily" + }, "loadBalancerIP": { "description": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", "type": "string" @@ -20393,7 +16268,7 @@ "$ref": "#/definitions/v1.SessionAffinityConfig" }, "type": { - "description": "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services ", + "description": "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", "type": "string" } } @@ -20420,11 +16295,11 @@ "description": "StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { @@ -20582,94 +16457,6 @@ } } }, - "v1.Status": { - "description": "Status is a return value for calls that don't return other objects.", - "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" - }, - "code": { - "description": "Suggested HTTP return code for this status, 0 if not set.", - "type": "integer", - "format": "int32" - }, - "details": { - "description": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - "$ref": "#/definitions/v1.StatusDetails" - }, - "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" - }, - "message": { - "description": "A human-readable description of the status of this operation.", - "type": "string" - }, - "metadata": { - "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - "$ref": "#/definitions/v1.ListMeta" - }, - "reason": { - "description": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", - "type": "string" - }, - "status": { - "description": "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", - "type": "string" - } - } - }, - "v1.StatusCause": { - "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", - "properties": { - "field": { - "description": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", - "type": "string" - }, - "message": { - "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", - "type": "string" - }, - "reason": { - "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", - "type": "string" - } - } - }, - "v1.StatusDetails": { - "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", - "properties": { - "causes": { - "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", - "type": "array", - "items": { - "$ref": "#/definitions/v1.StatusCause" - } - }, - "group": { - "description": "The group attribute of the resource associated with the status StatusReason.", - "type": "string" - }, - "kind": { - "description": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", - "type": "string" - }, - "name": { - "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", - "type": "string" - }, - "retryAfterSeconds": { - "description": "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", - "type": "integer", - "format": "int32" - }, - "uid": { - "description": "UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids", - "type": "string" - } - } - }, "v1.StorageOSVolumeSource": { "description": "Represents a StorageOS persistent volume resource.", "properties": { @@ -20754,6 +16541,33 @@ } } }, + "v1.TopologySpreadConstraint": { + "description": "TopologySpreadConstraint specifies how to spread matching pods among the given topology.", + "required": [ + "maxSkew", + "topologyKey", + "whenUnsatisfiable" + ], + "properties": { + "labelSelector": { + "description": "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.", + "$ref": "#/definitions/v1.LabelSelector" + }, + "maxSkew": { + "description": "MaxSkew describes the degree to which pods may be unevenly distributed. It's the maximum permitted difference between the number of matching pods in any two topology domains of a given topology type. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: ", + "type": "integer", + "format": "int32" + }, + "topologyKey": { + "description": "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each \u003ckey, value\u003e as a \"bucket\", and try to put balanced number of pods into each bucket. It's a required field.", + "type": "string" + }, + "whenUnsatisfiable": { + "description": "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it - ScheduleAnyway tells the scheduler to still schedule it It's considered as \"Unsatisfiable\" if and only if placing incoming pod on any topology violates \"MaxSkew\". For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: ", + "type": "string" + } + } + }, "v1.TypedLocalObjectReference": { "description": "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", "required": [ @@ -20799,13 +16613,17 @@ "$ref": "#/definitions/v1.CephFSVolumeSource" }, "cinder": { - "description": "Cinder represents a cinder volume attached and mounted on kubelets host machine More info: https://releases.k8s.io/HEAD/examples/mysql-cinder-pd/README.md", + "description": "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", "$ref": "#/definitions/v1.CinderVolumeSource" }, "configMap": { "description": "ConfigMap represents a configMap that should populate this volume", "$ref": "#/definitions/v1.ConfigMapVolumeSource" }, + "csi": { + "description": "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", + "$ref": "#/definitions/v1.CSIVolumeSource" + }, "downwardAPI": { "description": "DownwardAPI represents downward API about the pod that should populate this volume", "$ref": "#/definitions/v1.DownwardAPIVolumeSource" @@ -20835,7 +16653,7 @@ "$ref": "#/definitions/v1.GitRepoVolumeSource" }, "glusterfs": { - "description": "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://releases.k8s.io/HEAD/examples/volumes/glusterfs/README.md", + "description": "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", "$ref": "#/definitions/v1.GlusterfsVolumeSource" }, "hostPath": { @@ -20843,7 +16661,7 @@ "$ref": "#/definitions/v1.HostPathVolumeSource" }, "iscsi": { - "description": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://releases.k8s.io/HEAD/examples/volumes/iscsi/README.md", + "description": "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", "$ref": "#/definitions/v1.ISCSIVolumeSource" }, "name": { @@ -20875,7 +16693,7 @@ "$ref": "#/definitions/v1.QuobyteVolumeSource" }, "rbd": { - "description": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://releases.k8s.io/HEAD/examples/volumes/rbd/README.md", + "description": "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", "$ref": "#/definitions/v1.RBDVolumeSource" }, "scaleIO": { @@ -20939,6 +16757,10 @@ "subPath": { "description": "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", "type": "string" + }, + "subPathExpr": { + "description": "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15.", + "type": "string" } } }, @@ -21005,82 +16827,39 @@ } } }, - "v1alpha1.S2iBinary": { + "v1.WindowsSecurityContextOptions": { + "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.", "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", + "gmsaCredentialSpec": { + "description": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", "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", + "gmsaCredentialSpecName": { + "description": "GMSACredentialSpecName is the name of the GMSA credential spec to use. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", "type": "string" }, - "metadata": { - "$ref": "#/definitions/v1.ObjectMeta" - }, - "spec": { - "$ref": "#/definitions/v1alpha1.S2iBinarySpec" - }, - "status": { - "$ref": "#/definitions/v1alpha1.S2iBinaryStatus" - } - } - }, - "v1alpha1.S2iBinarySpec": { - "properties": { - "downloadURL": { - "type": "string" - }, - "fileName": { - "type": "string" - }, - "md5": { - "type": "string" - }, - "size": { - "type": "string" - }, - "uploadTimeStamp": { + "runAsUserName": { + "description": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. This field is alpha-level and it is only honored by servers that enable the WindowsRunAsUserName feature flag.", "type": "string" } } }, - "v1alpha1.S2iBinaryStatus": { + "v1alpha2.APIResponse": { "properties": { - "phase": { - "type": "string" + "histogram": { + "description": "histogram results", + "$ref": "#/definitions/logging.Histogram" + }, + "query": { + "description": "query results", + "$ref": "#/definitions/logging.Logs" + }, + "statistics": { + "description": "statistics results", + "$ref": "#/definitions/logging.Statistics" } } }, - "v1alpha1.Workspace": { - "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": { - "$ref": "#/definitions/v1.ObjectMeta" - }, - "spec": { - "$ref": "#/definitions/v1alpha1.WorkspaceSpec" - }, - "status": { - "$ref": "#/definitions/v1alpha1.WorkspaceStatus" - } - } - }, - "v1alpha1.WorkspaceSpec": { - "properties": { - "manager": { - "type": "string" - } - } - }, - "v1alpha1.WorkspaceStatus": {}, "v1alpha2.BadRequestError": { "required": [ "status", @@ -21096,164 +16875,179 @@ } } }, - "v1alpha2.ClusterRoleList": { + "v1alpha2.Column": { "required": [ - "items", - "total_count" + "id", + "label", + "dataType" ], "properties": { - "items": { - "description": "paging data", - "type": "array", - "items": { - "$ref": "#/definitions/v1.ClusterRole" - } + "dataType": { + "type": "string" }, - "total_count": { - "description": "total count", + "id": { + "type": "string" + }, + "label": { + "type": "string" + } + } + }, + "v1alpha2.ComponentStatus": { + "required": [ + "name", + "namespace", + "selfLink", + "label", + "startedAt", + "totalBackends", + "healthyBackends" + ], + "properties": { + "healthyBackends": { + "description": "the number of healthy backend components", + "type": "integer", + "format": "int32" + }, + "label": { + "description": "labels", + "$ref": "#/definitions/v1alpha2.ComponentStatus.label" + }, + "name": { + "description": "component name", + "type": "string" + }, + "namespace": { + "description": "the name of the namespace", + "type": "string" + }, + "selfLink": { + "description": "self link", + "type": "string" + }, + "startedAt": { + "description": "started time", + "type": "string", + "format": "date-time" + }, + "totalBackends": { + "description": "the total replicas of each backend system component", "type": "integer", "format": "int32" } } }, - "v1alpha2.CreateUserRequest": { + "v1alpha2.ComponentStatus.label": {}, + "v1alpha2.Connection": { "required": [ - "username", - "email", - "description", - "password", - "cluster_role" + "id", + "nodeId", + "label" ], "properties": { - "cluster_role": { - "description": "user's cluster role", + "id": { "type": "string" }, - "description": { - "description": "user's description", + "label": { "type": "string" }, - "email": { - "description": "email address", + "labelMinor": { "type": "string" }, - "lang": { - "description": "user's language setting, default is zh-CN", - "type": "string" + "metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.MetadataRow" + } }, - "password": { - "description": "password'", - "type": "string" - }, - "username": { - "description": "username", + "nodeId": { "type": "string" } } }, - "v1alpha2.DescribeWorkspaceUserResponse": { + "v1alpha2.ConnectionsSummary": { "required": [ - "username", - "email", - "lang", - "description", - "cluster_role", - "workspace_role", - "create_time", - "last_login_time" + "id", + "topologyId", + "label", + "columns", + "connections" ], "properties": { - "cluster_role": { - "description": "user's cluster role", + "columns": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Column" + } + }, + "connections": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Connection" + } + }, + "id": { "type": "string" }, - "create_time": { - "description": "user creation time", - "type": "string", - "format": "date-time" - }, - "description": { - "description": "user's description", + "label": { "type": "string" }, - "email": { - "description": "email address", - "type": "string" - }, - "lang": { - "description": "user's language setting, default is zh-CN", - "type": "string" - }, - "last_login_time": { - "description": "last login time", - "type": "string", - "format": "date-time" - }, - "username": { - "description": "username", - "type": "string" - }, - "workspace_role": { - "description": "user's workspace role", + "topologyId": { "type": "string" } } }, - "v1alpha2.DevOpsProject": { + "v1alpha2.Control": { "required": [ - "project_id", - "name", - "creator", - "create_time", - "status", - "workspace" + "id", + "human", + "icon", + "rank" ], "properties": { - "create_time": { - "description": "DevOps Project's Creation time", - "type": "string", - "format": "date-time" - }, - "creator": { - "description": "Creator's username", + "confirmation": { "type": "string" }, - "description": { - "description": "DevOps Projects's Description, used to describe the DevOps Project", + "human": { "type": "string" }, - "extra": { - "description": "Internal Use", + "icon": { "type": "string" }, - "name": { - "description": "DevOps Projects's Name", + "id": { "type": "string" }, - "project_id": { - "description": "ProjectId must be unique within a workspace, it is generated by kubesphere.", + "rank": { + "type": "integer", + "format": "int32" + } + } + }, + "v1alpha2.ControlInstance": { + "required": [ + "ProbeID", + "NodeID", + "Control" + ], + "properties": { + "Control": { + "$ref": "#/definitions/v1alpha2.Control" + }, + "NodeID": { "type": "string" }, - "status": { - "description": "DevOps project's status. e.g. active", - "type": "string" - }, - "visibility": { - "description": "Deprecated Field", - "type": "string" - }, - "workspace": { - "description": "The workspace to which the devops project belongs", + "ProbeID": { "type": "string" } } }, + "v1alpha2.FinalizerName": {}, "v1alpha2.GraphResponse": { "required": [ - "timestamp", "duration", "graphType", - "elements" + "elements", + "timestamp" ], "properties": { "duration": { @@ -21272,198 +17066,117 @@ } } }, - "v1alpha2.HighLight": { + "v1alpha2.HealthStatus": { + "required": [ + "kubesphereStatus", + "nodeStatus" + ], "properties": { - "kubernetes.container_name.keyword": { - "description": "containers to highlight", + "kubesphereStatus": { + "description": "kubesphere components status", "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/v1alpha2.ComponentStatus" } }, - "kubernetes.namespace_name.keyword": { - "description": "namespaces to highlight", - "type": "array", - "items": { - "type": "string" - } - }, - "kubernetes.pod_name.keyword": { - "description": "pods to highlight", - "type": "array", - "items": { - "type": "string" - } - }, - "log": { - "description": "log messages to highlight", - "type": "array", - "items": { - "type": "string" - } + "nodeStatus": { + "description": "nodes status", + "$ref": "#/definitions/v1alpha2.NodeStatus" } } }, - "v1alpha2.HistogramRecord": { + "v1alpha2.MetadataRow": { "required": [ - "time", - "count" + "id", + "label", + "value" ], "properties": { - "count": { - "description": "total number of logs at intervals", + "dataType": { + "type": "string" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "priority": { + "type": "number", + "format": "double" + }, + "truncate": { "type": "integer", - "format": "int64" + "format": "int32" }, - "time": { - "description": "timestamp", - "type": "integer", - "format": "int64" + "value": { + "type": "string" } } }, - "v1alpha2.HistogramResult": { + "v1alpha2.Metric": { "required": [ - "total", - "histograms" + "min", + "max" ], "properties": { - "histograms": { - "description": "actual array of histogram results", + "max": { + "type": "number", + "format": "double" + }, + "min": { + "type": "number", + "format": "double" + }, + "samples": { "type": "array", "items": { - "$ref": "#/definitions/v1alpha2.HistogramRecord" - } - }, - "total": { - "description": "total number of logs", - "type": "integer", - "format": "int64" - } - } - }, - "v1alpha2.Hit": { - "required": [ - "_source", - "highlight", - "sort" - ], - "properties": { - "_source": { - "$ref": "#/definitions/v1alpha2.Source" - }, - "highlight": { - "$ref": "#/definitions/v1alpha2.HighLight" - }, - "sort": { - "type": "array", - "items": { - "type": "integer" + "$ref": "#/definitions/v1alpha2.Sample" } } } }, - "v1alpha2.Hits": { + "v1alpha2.MetricRow": { "required": [ - "total", - "hits" + "ID", + "Label", + "Format", + "Group", + "Value", + "ValueEmpty", + "Priority", + "URL", + "Metric" ], "properties": { - "hits": { - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.Hit" - } - }, - "total": { - "$ref": "#/definitions/v1alpha2.Hits.total" - } - } - }, - "v1alpha2.Hits.total": {}, - "v1alpha2.InviteUserRequest": { - "required": [ - "username", - "workspace_role" - ], - "properties": { - "username": { - "description": "username", + "Format": { "type": "string" }, - "workspace_role": { - "description": "user's workspace role'", - "type": "string" - } - } - }, - "v1alpha2.Kubernetes": { - "required": [ - "namespace_name", - "pod_name", - "container_name", - "host" - ], - "properties": { - "container_name": { + "Group": { "type": "string" }, - "host": { + "ID": { "type": "string" }, - "namespace_name": { + "Label": { "type": "string" }, - "pod_name": { - "type": "string" - } - } - }, - "v1alpha2.LogRecord": { - "properties": { - "container": { - "description": "container name", + "Metric": { + "$ref": "#/definitions/v1alpha2.Metric" + }, + "Priority": { + "type": "number", + "format": "double" + }, + "URL": { "type": "string" }, - "highlight": { - "description": "highlighted log fragment", - "$ref": "#/definitions/v1alpha2.HighLight" + "Value": { + "type": "number", + "format": "double" }, - "host": { - "description": "node id", - "type": "string" - }, - "log": { - "description": "log message", - "type": "string" - }, - "namespace": { - "description": "namespace", - "type": "string" - }, - "pod": { - "description": "pod name", - "type": "string" - }, - "time": { - "description": "log timestamp", - "type": "string" - } - } - }, - "v1alpha2.LoginLog": { - "required": [ - "login_time", - "login_ip" - ], - "properties": { - "login_ip": { - "description": "last login ip", - "type": "string" - }, - "login_time": { - "description": "last login time", - "type": "string" + "ValueEmpty": { + "type": "boolean" } } }, @@ -21487,54 +17200,209 @@ } } }, - "v1alpha2.NamespacedUser": { + "v1alpha2.Node": { "required": [ - "username", - "email", - "description", - "role", - "role_binding", - "role_bind_time", - "create_time", - "last_login_time" + "labelMinor", + "id", + "label", + "rank", + "controls" ], "properties": { - "create_time": { - "description": "user creation time", - "type": "string", - "format": "date-time" + "adjacency": { + "type": "array", + "items": { + "type": "string" + } }, - "description": { - "description": "user's description", + "children": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.NodeSummaryGroup" + } + }, + "connections": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.ConnectionsSummary" + } + }, + "controls": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.ControlInstance" + } + }, + "id": { "type": "string" }, - "email": { - "description": "email address", + "label": { "type": "string" }, - "lang": { - "description": "user's language setting, default is zh-CN", + "labelMinor": { "type": "string" }, - "last_login_time": { - "description": "last login time", - "type": "string", - "format": "date-time" + "metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.MetadataRow" + } }, - "role": { - "description": "user's role in the specified namespace", + "metrics": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.MetricRow" + } + }, + "parents": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Parent" + } + }, + "pseudo": { + "type": "boolean" + }, + "rank": { "type": "string" }, - "role_bind_time": { - "description": "user's role binding time", + "shape": { "type": "string" }, - "role_binding": { - "description": "user's role binding name in the specified namespace", + "stack": { + "type": "boolean" + }, + "tables": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Table" + } + }, + "tag": { + "type": "string" + } + } + }, + "v1alpha2.NodeResponse": { + "required": [ + "node" + ], + "properties": { + "node": { + "$ref": "#/definitions/v1alpha2.Node" + } + } + }, + "v1alpha2.NodeStatus": { + "required": [ + "totalNodes", + "healthyNodes" + ], + "properties": { + "healthyNodes": { + "description": "the number of healthy nodes", + "type": "integer", + "format": "int32" + }, + "totalNodes": { + "description": "total number of nodes", + "type": "integer", + "format": "int32" + } + } + }, + "v1alpha2.NodeSummary": { + "required": [ + "id", + "label", + "labelMinor", + "rank" + ], + "properties": { + "adjacency": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { "type": "string" }, - "username": { - "description": "username", + "label": { + "type": "string" + }, + "labelMinor": { + "type": "string" + }, + "metadata": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.MetadataRow" + } + }, + "metrics": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.MetricRow" + } + }, + "parents": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Parent" + } + }, + "pseudo": { + "type": "boolean" + }, + "rank": { + "type": "string" + }, + "shape": { + "type": "string" + }, + "stack": { + "type": "boolean" + }, + "tables": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Table" + } + }, + "tag": { + "type": "string" + } + } + }, + "v1alpha2.NodeSummaryGroup": { + "required": [ + "id", + "label", + "nodes", + "topologyId", + "columns" + ], + "properties": { + "columns": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Column" + } + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "nodes": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.NodeSummary" + } + }, + "topologyId": { "type": "string" } } @@ -21554,275 +17422,295 @@ } } }, - "v1alpha2.QueryResult": { + "v1alpha2.Parent": { "required": [ - "resultType", - "result" + "id", + "label", + "topologyId" ], "properties": { - "result": { - "description": "metric data including labels, time series and values", - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.QueryValue" - } + "id": { + "type": "string" }, - "resultType": { - "description": "result type, one of matrix, vector", + "label": { + "type": "string" + }, + "topologyId": { "type": "string" } } }, - "v1alpha2.QueryValue": { - "properties": { - "metric": { - "description": "time series labels", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "value": { - "description": "time series, values of vector type", - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.QueryValue.value" - } - }, - "values": { - "description": "time series, values of matrix type", - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.QueryValue.values" - } - } - } - }, - "v1alpha2.QueryValue.value": {}, - "v1alpha2.QueryValue.values": {}, - "v1alpha2.ReadResult": { + "v1alpha2.Role": { "required": [ - "total" + "target", + "rules" ], "properties": { - "_scroll_id": { + "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/sig-architecture/api-conventions.md#resources", "type": "string" }, - "records": { - "description": "actual array of results", + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/v1.ObjectMeta" + }, + "rules": { "type": "array", "items": { - "$ref": "#/definitions/v1alpha2.LogRecord" + "$ref": "#/definitions/v1alpha2.RuleRef" } }, - "total": { - "description": "total number of matched results", - "type": "integer", - "format": "int64" - } - } - }, - "v1alpha2.Response": { - "required": [ - "_scroll_id", - "_shards", - "hits", - "aggregations" - ], - "properties": { - "_scroll_id": { - "type": "string" - }, - "_shards": { - "$ref": "#/definitions/v1alpha2.Shards" - }, - "aggregations": { - "type": "string" - }, - "hits": { - "$ref": "#/definitions/v1alpha2.Hits" + "target": { + "$ref": "#/definitions/v1alpha2.Target" } } }, "v1alpha2.RoleList": { "required": [ - "items", - "total_count" + "items" ], "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, "items": { - "description": "paging data", "type": "array", "items": { - "$ref": "#/definitions/v1.Role" + "$ref": "#/definitions/v1alpha2.Role" } }, - "total_count": { - "description": "total count", - "type": "integer", - "format": "int32" - } - } - }, - "v1alpha2.Shards": { - "required": [ - "total", - "successful", - "skipped", - "failed" - ], - "properties": { - "failed": { - "type": "integer", - "format": "int64" - }, - "skipped": { - "type": "integer", - "format": "int64" - }, - "successful": { - "type": "integer", - "format": "int64" - }, - "total": { - "type": "integer", - "format": "int64" - } - } - }, - "v1alpha2.Source": { - "required": [ - "log", - "time", - "kubernetes" - ], - "properties": { - "kubernetes": { - "$ref": "#/definitions/v1alpha2.Kubernetes" - }, - "log": { + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, - "time": { - "type": "string" + "metadata": { + "$ref": "#/definitions/v1.ListMeta" } } }, - "v1alpha2.StatisticsResult": { + "v1alpha2.Row": { "required": [ - "containers", - "logs" + "id", + "entries" ], "properties": { - "containers": { - "description": "total number of containers", - "type": "integer", - "format": "int64" - }, - "logs": { - "description": "total number of logs", - "type": "integer", - "format": "int64" - } - } - }, - "v1alpha2.UserList": { - "required": [ - "items", - "total_count" - ], - "properties": { - "items": { - "description": "paging data", - "type": "array", - "items": { - "$ref": "#/definitions/v1alpha2.UserList.items" + "entries": { + "type": "object", + "additionalProperties": { + "type": "string" } }, - "total_count": { - "description": "total count", - "type": "integer", - "format": "int32" + "id": { + "type": "string" } } }, - "v1alpha2.UserList.items": { + "v1alpha2.RuleRef": { "required": [ - "username", - "email", - "description", - "cluster_role", - "create_time", - "last_login_time" + "apiGroup", + "kind", + "name" ], "properties": { - "cluster_role": { - "description": "user's cluster role", + "apiGroup": { "type": "string" }, - "create_time": { - "description": "user creation time", + "kind": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "v1alpha2.Sample": { + "required": [ + "date", + "value" + ], + "properties": { + "date": { "type": "string", "format": "date-time" }, - "description": { - "description": "user's description", + "value": { + "type": "number", + "format": "double" + } + } + }, + "v1alpha2.Table": { + "required": [ + "id", + "label", + "type", + "columns", + "rows" + ], + "properties": { + "columns": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Column" + } + }, + "id": { "type": "string" }, - "email": { - "description": "email address", + "label": { "type": "string" }, - "lang": { - "description": "user's language setting, default is zh-CN", - "type": "string" + "rows": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.Row" + } }, - "last_login_time": { - "description": "last login time", - "type": "string", - "format": "date-time" + "truncationCount": { + "type": "integer", + "format": "int32" }, - "username": { - "description": "username", + "type": { "type": "string" } } }, - "v1alpha2.UserUpdateRequest": { + "v1alpha2.Target": { "required": [ - "username", - "email", - "lang", - "description", - "cluster_role" + "scope", + "name" ], "properties": { - "cluster_role": { - "description": "user's cluster role", + "name": { "type": "string" }, - "current_password": { - "description": "this is necessary if you need to change your password", + "scope": { + "type": "string" + } + } + }, + "v1alpha2.TopologyResponse": { + "required": [ + "nodes" + ], + "properties": { + "nodes": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v1alpha2.NodeSummary" + } + } + } + }, + "v1alpha2.User": { + "required": [ + "spec" + ], + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/v1alpha2.UserSpec" + }, + "status": { + "$ref": "#/definitions/v1alpha2.UserStatus" + } + } + }, + "v1alpha2.UserCondition": { + "required": [ + "type", + "status" + ], + "properties": { + "lastTransitionTime": { + "type": "string" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "status": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "v1alpha2.UserDetail": { + "required": [ + "User", + "globalRole" + ], + "properties": { + "User": { + "$ref": "#/definitions/v1alpha2.User" + }, + "globalRole": { + "$ref": "#/definitions/v1alpha2.Role" + } + } + }, + "v1alpha2.UserSpec": { + "required": [ + "email", + "password" + ], + "properties": { "description": { - "description": "user's description", + "type": "string" + }, + "displayName": { "type": "string" }, "email": { - "description": "email address", "type": "string" }, + "finalizers": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.FinalizerName" + } + }, + "groups": { + "type": "array", + "items": { + "type": "string" + } + }, "lang": { - "description": "user's language setting, default is zh-CN", "type": "string" }, "password": { - "description": "this is necessary if you need to change your password", "type": "string" + } + } + }, + "v1alpha2.UserStatus": { + "properties": { + "conditions": { + "type": "array", + "items": { + "$ref": "#/definitions/v1alpha2.UserCondition" + } }, - "username": { - "description": "username", + "state": { "type": "string" } } @@ -21913,26 +17801,26 @@ } }, "v1beta1.Ingress": { - "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.", + "description": "Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc. DEPRECATED - This group version of Ingress is deprecated by networking.k8s.io/v1beta1 Ingress. See the release notes for more information.", "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", + "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/sig-architecture/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", + "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/sig-architecture/api-conventions.md#types-kinds", "type": "string" }, "metadata": { - "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "$ref": "#/definitions/v1.ObjectMeta" }, "spec": { - "description": "Spec is the desired state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Spec is the desired state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1beta1.IngressSpec" }, "status": { - "description": "Status is the current state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status", + "description": "Status is the current state of the Ingress. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", "$ref": "#/definitions/v1beta1.IngressStatus" } } diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index ca45b83c5..74d0fde9b 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -44,6 +44,1005 @@ } } }, + "/apis/cluster.kubesphere.io/": { + "get": { + "description": "get information of a group", + "consumes": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo" + ], + "operationId": "getClusterKubesphereIoAPIGroup", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" + } + } + } + } + }, + "/apis/cluster.kubesphere.io/v1alpha1/": { + "get": { + "description": "get available resources", + "consumes": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "getClusterKubesphereIoV1alpha1APIResources", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + } + } + }, + "/apis/cluster.kubesphere.io/v1alpha1/clusters": { + "get": { + "description": "list or watch objects of kind Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "listClusterKubesphereIoV1alpha1Cluster", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterList" + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "post": { + "description": "create a Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "createClusterKubesphereIoV1alpha1Cluster", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "delete": { + "description": "delete collection of Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "deleteClusterKubesphereIoV1alpha1CollectionCluster", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/cluster.kubesphere.io/v1alpha1/clusters/{name}": { + "get": { + "description": "read the specified Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "readClusterKubesphereIoV1alpha1Cluster", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "put": { + "description": "replace the specified Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "replaceClusterKubesphereIoV1alpha1Cluster", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "delete": { + "description": "delete a Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "deleteClusterKubesphereIoV1alpha1Cluster", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "patch": { + "description": "partially update the specified Cluster", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "patchClusterKubesphereIoV1alpha1Cluster", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Cluster", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/cluster.kubesphere.io/v1alpha1/clusters/{name}/status": { + "get": { + "description": "read status of the specified Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "readClusterKubesphereIoV1alpha1ClusterStatus", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "put": { + "description": "replace status of the specified Cluster", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "replaceClusterKubesphereIoV1alpha1ClusterStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "patch": { + "description": "partially update status of the specified Cluster", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "patchClusterKubesphereIoV1alpha1ClusterStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Cluster", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/cluster.kubesphere.io/v1alpha1/watch/clusters": { + "get": { + "description": "watch individual changes to a list of Cluster. deprecated: use the 'watch' parameter with a list operation instead.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "watchClusterKubesphereIoV1alpha1ClusterList", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/cluster.kubesphere.io/v1alpha1/watch/clusters/{name}": { + "get": { + "description": "watch changes to an object of kind Cluster. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "clusterKubesphereIo_v1alpha1" + ], + "operationId": "watchClusterKubesphereIoV1alpha1Cluster", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "cluster.kubesphere.io", + "version": "v1alpha1", + "kind": "Cluster" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "name of the Cluster", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, "/apis/devops.kubesphere.io/": { "get": { "description": "get information of a group", @@ -104,7 +1103,7 @@ } } }, - "/apis/devops.kubesphere.io/v1alpha1/s2ibinary": { + "/apis/devops.kubesphere.io/v1alpha1/s2ibinaries": { "get": { "description": "list or watch objects of kind S2iBinary", "consumes": [ @@ -400,7 +1399,7 @@ } ] }, - "/apis/devops.kubesphere.io/v1alpha1/s2ibinary/{name}": { + "/apis/devops.kubesphere.io/v1alpha1/s2ibinaries/{name}": { "get": { "description": "read the specified S2iBinary", "consumes": [ @@ -654,7 +1653,7 @@ } ] }, - "/apis/devops.kubesphere.io/v1alpha1/s2ibinary/{name}/status": { + "/apis/devops.kubesphere.io/v1alpha1/s2ibinaries/{name}/status": { "get": { "description": "read status of the specified S2iBinary", "consumes": [ @@ -915,7 +1914,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderList" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderList" } } }, @@ -949,7 +1948,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, { @@ -971,19 +1970,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1151,7 +2150,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1185,7 +2184,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, { @@ -1207,13 +2206,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1354,7 +2353,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1405,7 +2404,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1439,7 +2438,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, { @@ -1461,13 +2460,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1533,7 +2532,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" } } }, @@ -1644,7 +2643,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateList" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateList" } } }, @@ -1678,7 +2677,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, { @@ -1700,19 +2699,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -1880,7 +2879,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -1914,7 +2913,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, { @@ -1936,13 +2935,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -2083,7 +3082,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -2134,7 +3133,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -2168,7 +3167,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, { @@ -2190,13 +3189,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -2262,7 +3261,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" } } }, @@ -2373,7 +3372,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunList" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunList" } } }, @@ -2407,7 +3406,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, { @@ -2429,19 +3428,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2609,7 +3608,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2643,7 +3642,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, { @@ -2665,13 +3664,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2812,7 +3811,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2863,7 +3862,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2897,7 +3896,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, { @@ -2919,13 +3918,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -2991,7 +3990,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" } } }, @@ -3020,7 +4019,7 @@ } ] }, - "/apis/devops.kubesphere.io/v1alpha1/watch/s2ibinary": { + "/apis/devops.kubesphere.io/v1alpha1/watch/s2ibinaries": { "get": { "description": "watch individual changes to a list of S2iBinary. deprecated: use the 'watch' parameter with a list operation instead.", "consumes": [ @@ -3121,7 +4120,7 @@ } ] }, - "/apis/devops.kubesphere.io/v1alpha1/watch/s2ibinary/{name}": { + "/apis/devops.kubesphere.io/v1alpha1/watch/s2ibinaries/{name}": { "get": { "description": "watch changes to an object of kind S2iBinary. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", "consumes": [ @@ -3860,37 +4859,7 @@ } ] }, - "/apis/network.kubesphere.io/": { - "get": { - "description": "get information of a group", - "consumes": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf" - ], - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf" - ], - "schemes": [ - "https" - ], - "tags": [ - "networkKubesphereIo" - ], - "operationId": "getNetworkKubesphereIoAPIGroup", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" - } - } - } - } - }, - "/apis/network.kubesphere.io/v1alpha1/": { + "/apis/devops.kubesphere.io/v1alpha3/": { "get": { "description": "get available resources", "consumes": [ @@ -3907,9 +4876,9 @@ "https" ], "tags": [ - "networkKubesphereIo_v1alpha1" + "devopsKubesphereIo_v1alpha3" ], - "operationId": "getNetworkKubesphereIoV1alpha1APIResources", + "operationId": "getDevopsKubesphereIoV1alpha3APIResources", "responses": { "200": { "description": "OK", @@ -3920,9 +4889,9 @@ } } }, - "/apis/network.kubesphere.io/v1alpha1/watch/workspacenetworkpolicies": { + "/apis/devops.kubesphere.io/v1alpha3/devopsprojects": { "get": { - "description": "watch individual changes to a list of WorkspaceNetworkPolicy. deprecated: use the 'watch' parameter with a list operation instead.", + "description": "list or watch objects of kind DevOpsProject", "consumes": [ "*/*" ], @@ -3937,219 +4906,9 @@ "https" ], "tags": [ - "networkKubesphereIo_v1alpha1" + "devopsKubesphereIo_v1alpha3" ], - "operationId": "watchNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicyList", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "x-kubernetes-action": "watchlist", - "x-kubernetes-group-version-kind": { - "group": "network.kubesphere.io", - "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" - } - }, - "parameters": [ - { - "uniqueItems": true, - "type": "boolean", - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", - "name": "allowWatchBookmarks", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "name": "continue", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "name": "fieldSelector", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "name": "labelSelector", - "in": "query" - }, - { - "uniqueItems": true, - "type": "integer", - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "name": "limit", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "If 'true', then the output is pretty printed.", - "name": "pretty", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", - "name": "resourceVersion", - "in": "query" - }, - { - "uniqueItems": true, - "type": "integer", - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "name": "timeoutSeconds", - "in": "query" - }, - { - "uniqueItems": true, - "type": "boolean", - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "name": "watch", - "in": "query" - } - ] - }, - "/apis/network.kubesphere.io/v1alpha1/watch/workspacenetworkpolicies/{name}": { - "get": { - "description": "watch changes to an object of kind WorkspaceNetworkPolicy. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", - "consumes": [ - "*/*" - ], - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch" - ], - "schemes": [ - "https" - ], - "tags": [ - "networkKubesphereIo_v1alpha1" - ], - "operationId": "watchNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" - } - } - }, - "x-kubernetes-action": "watch", - "x-kubernetes-group-version-kind": { - "group": "network.kubesphere.io", - "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" - } - }, - "parameters": [ - { - "uniqueItems": true, - "type": "boolean", - "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", - "name": "allowWatchBookmarks", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - "name": "continue", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - "name": "fieldSelector", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - "name": "labelSelector", - "in": "query" - }, - { - "uniqueItems": true, - "type": "integer", - "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - "name": "limit", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "name of the WorkspaceNetworkPolicy", - "name": "name", - "in": "path", - "required": true - }, - { - "uniqueItems": true, - "type": "string", - "description": "If 'true', then the output is pretty printed.", - "name": "pretty", - "in": "query" - }, - { - "uniqueItems": true, - "type": "string", - "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", - "name": "resourceVersion", - "in": "query" - }, - { - "uniqueItems": true, - "type": "integer", - "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - "name": "timeoutSeconds", - "in": "query" - }, - { - "uniqueItems": true, - "type": "boolean", - "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "name": "watch", - "in": "query" - } - ] - }, - "/apis/network.kubesphere.io/v1alpha1/workspacenetworkpolicies": { - "get": { - "description": "list or watch objects of kind WorkspaceNetworkPolicy", - "consumes": [ - "*/*" - ], - "produces": [ - "application/json", - "application/yaml", - "application/vnd.kubernetes.protobuf", - "application/json;stream=watch", - "application/vnd.kubernetes.protobuf;stream=watch" - ], - "schemes": [ - "https" - ], - "tags": [ - "networkKubesphereIo_v1alpha1" - ], - "operationId": "listNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "listDevopsKubesphereIoV1alpha3DevOpsProject", "parameters": [ { "uniqueItems": true, @@ -4212,19 +4971,19 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyList" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectList" } } }, "x-kubernetes-action": "list", "x-kubernetes-group-version-kind": { - "group": "network.kubesphere.io", - "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" } }, "post": { - "description": "create a WorkspaceNetworkPolicy", + "description": "create a DevOpsProject", "consumes": [ "*/*" ], @@ -4237,16 +4996,16 @@ "https" ], "tags": [ - "networkKubesphereIo_v1alpha1" + "devopsKubesphereIo_v1alpha3" ], - "operationId": "createNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "createDevopsKubesphereIoV1alpha3DevOpsProject", "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" } }, { @@ -4268,31 +5027,1901 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" } }, "202": { "description": "Accepted", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" } } }, "x-kubernetes-action": "post", "x-kubernetes-group-version-kind": { - "group": "network.kubesphere.io", - "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" } }, "delete": { - "description": "delete collection of WorkspaceNetworkPolicy", + "description": "delete collection of DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "deleteDevopsKubesphereIoV1alpha3CollectionDevOpsProject", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/devopsprojects/{name}": { + "get": { + "description": "read the specified DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "readDevopsKubesphereIoV1alpha3DevOpsProject", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "put": { + "description": "replace the specified DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "replaceDevopsKubesphereIoV1alpha3DevOpsProject", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "delete": { + "description": "delete a DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "deleteDevopsKubesphereIoV1alpha3DevOpsProject", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "patch": { + "description": "partially update the specified DevOpsProject", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "patchDevopsKubesphereIoV1alpha3DevOpsProject", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the DevOpsProject", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/devopsprojects/{name}/status": { + "get": { + "description": "read status of the specified DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "readDevopsKubesphereIoV1alpha3DevOpsProjectStatus", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "put": { + "description": "replace status of the specified DevOpsProject", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "replaceDevopsKubesphereIoV1alpha3DevOpsProjectStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "patch": { + "description": "partially update status of the specified DevOpsProject", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "patchDevopsKubesphereIoV1alpha3DevOpsProjectStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the DevOpsProject", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/pipelines": { + "get": { + "description": "list or watch objects of kind Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "listDevopsKubesphereIoV1alpha3Pipeline", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineList" + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "post": { + "description": "create a Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "createDevopsKubesphereIoV1alpha3Pipeline", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "delete": { + "description": "delete collection of Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "deleteDevopsKubesphereIoV1alpha3CollectionPipeline", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/pipelines/{name}": { + "get": { + "description": "read the specified Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "readDevopsKubesphereIoV1alpha3Pipeline", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "put": { + "description": "replace the specified Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "replaceDevopsKubesphereIoV1alpha3Pipeline", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "delete": { + "description": "delete a Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "deleteDevopsKubesphereIoV1alpha3Pipeline", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + "name": "gracePeriodSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + "name": "orphanDependents", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + "name": "propagationPolicy", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "patch": { + "description": "partially update the specified Pipeline", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "patchDevopsKubesphereIoV1alpha3Pipeline", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Pipeline", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/pipelines/{name}/status": { + "get": { + "description": "read status of the specified Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "readDevopsKubesphereIoV1alpha3PipelineStatus", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "put": { + "description": "replace status of the specified Pipeline", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "replaceDevopsKubesphereIoV1alpha3PipelineStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "patch": { + "description": "partially update status of the specified Pipeline", + "consumes": [ + "application/json-patch+json", + "application/merge-patch+json", + "application/strategic-merge-patch+json", + "application/apply-patch+yaml" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "patchDevopsKubesphereIoV1alpha3PipelineStatus", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + "name": "fieldManager", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + "name": "force", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "description": "name of the Pipeline", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/watch/devopsprojects": { + "get": { + "description": "watch individual changes to a list of DevOpsProject. deprecated: use the 'watch' parameter with a list operation instead.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "watchDevopsKubesphereIoV1alpha3DevOpsProjectList", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/watch/devopsprojects/{name}": { + "get": { + "description": "watch changes to an object of kind DevOpsProject. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "watchDevopsKubesphereIoV1alpha3DevOpsProject", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "DevOpsProject" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "name of the DevOpsProject", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/watch/pipelines": { + "get": { + "description": "watch individual changes to a list of Pipeline. deprecated: use the 'watch' parameter with a list operation instead.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "watchDevopsKubesphereIoV1alpha3PipelineList", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/devops.kubesphere.io/v1alpha3/watch/pipelines/{name}": { + "get": { + "description": "watch changes to an object of kind Pipeline. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "devopsKubesphereIo_v1alpha3" + ], + "operationId": "watchDevopsKubesphereIoV1alpha3Pipeline", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "devops.kubesphere.io", + "version": "v1alpha3", + "kind": "Pipeline" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "name of the Pipeline", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/network.kubesphere.io/": { + "get": { + "description": "get information of a group", + "consumes": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo" + ], + "operationId": "getNetworkKubesphereIoAPIGroup", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIGroup" + } + } + } + } + }, + "/apis/network.kubesphere.io/v1alpha1/": { + "get": { + "description": "get available resources", + "consumes": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo_v1alpha1" + ], + "operationId": "getNetworkKubesphereIoV1alpha1APIResources", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + } + } + }, + "/apis/network.kubesphere.io/v1alpha1/namespacenetworkpolicies": { + "get": { + "description": "list or watch objects of kind NamespaceNetworkPolicy", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo_v1alpha1" + ], + "operationId": "listNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicyList" + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "network.kubesphere.io", + "version": "v1alpha1", + "kind": "NamespaceNetworkPolicy" + } + }, + "post": { + "description": "create a NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4307,7 +6936,75 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "deleteNetworkKubesphereIoV1alpha1CollectionWorkspaceNetworkPolicy", + "operationId": "createNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" + } + }, + { + "uniqueItems": true, + "type": "string", + "description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + "name": "dryRun", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + "name": "fieldManager", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" + } + }, + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" + } + }, + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "network.kubesphere.io", + "version": "v1alpha1", + "kind": "NamespaceNetworkPolicy" + } + }, + "delete": { + "description": "delete collection of NamespaceNetworkPolicy", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo_v1alpha1" + ], + "operationId": "deleteNetworkKubesphereIoV1alpha1CollectionNamespaceNetworkPolicy", "parameters": [ { "uniqueItems": true, @@ -4413,7 +7110,7 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "parameters": [ @@ -4426,9 +7123,9 @@ } ] }, - "/apis/network.kubesphere.io/v1alpha1/workspacenetworkpolicies/{name}": { + "/apis/network.kubesphere.io/v1alpha1/namespacenetworkpolicies/{name}": { "get": { - "description": "read the specified WorkspaceNetworkPolicy", + "description": "read the specified NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4443,12 +7140,12 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "readNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "readNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4456,11 +7153,11 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "put": { - "description": "replace the specified WorkspaceNetworkPolicy", + "description": "replace the specified NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4475,14 +7172,14 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "replaceNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "replaceNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } }, { @@ -4504,13 +7201,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4518,11 +7215,11 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "delete": { - "description": "delete a WorkspaceNetworkPolicy", + "description": "delete a NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4537,7 +7234,7 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "deleteNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "deleteNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", "parameters": [ { "name": "body", @@ -4593,11 +7290,11 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "patch": { - "description": "partially update the specified WorkspaceNetworkPolicy", + "description": "partially update the specified NamespaceNetworkPolicy", "consumes": [ "application/json-patch+json", "application/merge-patch+json", @@ -4615,7 +7312,7 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "patchNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicy", + "operationId": "patchNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", "parameters": [ { "name": "body", @@ -4651,7 +7348,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4659,14 +7356,14 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "parameters": [ { "uniqueItems": true, "type": "string", - "description": "name of the WorkspaceNetworkPolicy", + "description": "name of the NamespaceNetworkPolicy", "name": "name", "in": "path", "required": true @@ -4680,9 +7377,9 @@ } ] }, - "/apis/network.kubesphere.io/v1alpha1/workspacenetworkpolicies/{name}/status": { + "/apis/network.kubesphere.io/v1alpha1/namespacenetworkpolicies/{name}/status": { "get": { - "description": "read status of the specified WorkspaceNetworkPolicy", + "description": "read status of the specified NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4697,12 +7394,12 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "readNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicyStatus", + "operationId": "readNetworkKubesphereIoV1alpha1NamespaceNetworkPolicyStatus", "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4710,11 +7407,11 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "put": { - "description": "replace status of the specified WorkspaceNetworkPolicy", + "description": "replace status of the specified NamespaceNetworkPolicy", "consumes": [ "*/*" ], @@ -4729,14 +7426,14 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "replaceNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicyStatus", + "operationId": "replaceNetworkKubesphereIoV1alpha1NamespaceNetworkPolicyStatus", "parameters": [ { "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } }, { @@ -4758,13 +7455,13 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } }, "201": { "description": "Created", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4772,11 +7469,11 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "patch": { - "description": "partially update status of the specified WorkspaceNetworkPolicy", + "description": "partially update status of the specified NamespaceNetworkPolicy", "consumes": [ "application/json-patch+json", "application/merge-patch+json", @@ -4794,7 +7491,7 @@ "tags": [ "networkKubesphereIo_v1alpha1" ], - "operationId": "patchNetworkKubesphereIoV1alpha1WorkspaceNetworkPolicyStatus", + "operationId": "patchNetworkKubesphereIoV1alpha1NamespaceNetworkPolicyStatus", "parameters": [ { "name": "body", @@ -4830,7 +7527,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } } }, @@ -4838,14 +7535,14 @@ "x-kubernetes-group-version-kind": { "group": "network.kubesphere.io", "version": "v1alpha1", - "kind": "WorkspaceNetworkPolicy" + "kind": "NamespaceNetworkPolicy" } }, "parameters": [ { "uniqueItems": true, "type": "string", - "description": "name of the WorkspaceNetworkPolicy", + "description": "name of the NamespaceNetworkPolicy", "name": "name", "in": "path", "required": true @@ -4859,6 +7556,216 @@ } ] }, + "/apis/network.kubesphere.io/v1alpha1/watch/namespacenetworkpolicies": { + "get": { + "description": "watch individual changes to a list of NamespaceNetworkPolicy. deprecated: use the 'watch' parameter with a list operation instead.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo_v1alpha1" + ], + "operationId": "watchNetworkKubesphereIoV1alpha1NamespaceNetworkPolicyList", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watchlist", + "x-kubernetes-group-version-kind": { + "group": "network.kubesphere.io", + "version": "v1alpha1", + "kind": "NamespaceNetworkPolicy" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, + "/apis/network.kubesphere.io/v1alpha1/watch/namespacenetworkpolicies/{name}": { + "get": { + "description": "watch changes to an object of kind NamespaceNetworkPolicy. deprecated: use the 'watch' parameter with a list operation instead, filtered to a single item with the 'fieldSelector' parameter.", + "consumes": [ + "*/*" + ], + "produces": [ + "application/json", + "application/yaml", + "application/vnd.kubernetes.protobuf", + "application/json;stream=watch", + "application/vnd.kubernetes.protobuf;stream=watch" + ], + "schemes": [ + "https" + ], + "tags": [ + "networkKubesphereIo_v1alpha1" + ], + "operationId": "watchNetworkKubesphereIoV1alpha1NamespaceNetworkPolicy", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.WatchEvent" + } + } + }, + "x-kubernetes-action": "watch", + "x-kubernetes-group-version-kind": { + "group": "network.kubesphere.io", + "version": "v1alpha1", + "kind": "NamespaceNetworkPolicy" + } + }, + "parameters": [ + { + "uniqueItems": true, + "type": "boolean", + "description": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + "name": "allowWatchBookmarks", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + "name": "continue", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "name": "fieldSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "name": "labelSelector", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + "name": "limit", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "name of the NamespaceNetworkPolicy", + "name": "name", + "in": "path", + "required": true + }, + { + "uniqueItems": true, + "type": "string", + "description": "If 'true', then the output is pretty printed.", + "name": "pretty", + "in": "query" + }, + { + "uniqueItems": true, + "type": "string", + "description": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + "name": "resourceVersion", + "in": "query" + }, + { + "uniqueItems": true, + "type": "integer", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "name": "timeoutSeconds", + "in": "query" + }, + { + "uniqueItems": true, + "type": "boolean", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "name": "watch", + "in": "query" + } + ] + }, "/apis/tenant.kubesphere.io/": { "get": { "description": "get information of a group", @@ -5860,911 +8767,6 @@ } }, "definitions": { - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.AuthConfig": { - "description": "AuthConfig is our abstraction of the Registry authorization information for whatever docker client we happen to be based on", - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "password": { - "type": "string" - }, - "secretRef": { - "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" - }, - "serverAddress": { - "type": "string" - }, - "username": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.CGroupLimits": { - "description": "CGroupLimits holds limits used to constrain container resources.", - "type": "object", - "required": [ - "memoryLimitBytes", - "cpuShares", - "cpuPeriod", - "cpuQuota", - "memorySwap", - "parent" - ], - "properties": { - "cpuPeriod": { - "type": "integer", - "format": "int64" - }, - "cpuQuota": { - "type": "integer", - "format": "int64" - }, - "cpuShares": { - "type": "integer", - "format": "int64" - }, - "memoryLimitBytes": { - "type": "integer", - "format": "int64" - }, - "memorySwap": { - "type": "integer", - "format": "int64" - }, - "parent": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.ContainerInfo": { - "type": "object", - "properties": { - "buildVolumes": { - "description": "BuildVolumes specifies a list of volumes to mount to container running the build.", - "type": "array", - "items": { - "type": "string" - } - }, - "builderImage": { - "description": "BaseImage are the images this template will use.", - "type": "string" - }, - "runtimeArtifacts": { - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.VolumeSpec" - } - }, - "runtimeImage": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.DockerConfig": { - "description": "DockerConfig contains the configuration for a Docker connection.", - "type": "object", - "required": [ - "endPoint", - "certFile", - "keyFile", - "caFile", - "useTLS", - "tlsVerify" - ], - "properties": { - "caFile": { - "description": "CAFile is the certificate authority file path for a TLS connection", - "type": "string" - }, - "certFile": { - "description": "CertFile is the certificate file path for a TLS connection", - "type": "string" - }, - "endPoint": { - "description": "Endpoint is the docker network endpoint or socket", - "type": "string" - }, - "keyFile": { - "description": "KeyFile is the key file path for a TLS connection", - "type": "string" - }, - "tlsVerify": { - "description": "TLSVerify indicates if TLS peer must be verified", - "type": "boolean" - }, - "useTLS": { - "description": "UseTLS indicates if TLS must be used", - "type": "boolean" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.EnvironmentSpec": { - "description": "EnvironmentSpec specifies a single environment variable.", - "type": "object", - "required": [ - "name", - "value" - ], - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.Parameter": { - "type": "object", - "properties": { - "defaultValue": { - "type": "string" - }, - "description": { - "type": "string" - }, - "key": { - "type": "string" - }, - "optValues": { - "type": "array", - "items": { - "type": "string" - } - }, - "required": { - "type": "boolean" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.ProxyConfig": { - "description": "ProxyConfig holds proxy configuration.", - "type": "object", - "properties": { - "httpProxy": { - "type": "string" - }, - "httpsProxy": { - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuildResult": { - "type": "object", - "properties": { - "commandPull": { - "description": "Command for pull image.", - "type": "string" - }, - "imageCreated": { - "description": "Image created time.", - "type": "string" - }, - "imageID": { - "description": "Image ID.", - "type": "string" - }, - "imageName": { - "description": "ImageName is the name of artifact", - "type": "string" - }, - "imageRepoTags": { - "description": "image tags.", - "type": "array", - "items": { - "type": "string" - } - }, - "imageSize": { - "description": "The size in bytes of the image", - "type": "integer", - "format": "int64" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuildSource": { - "type": "object", - "properties": { - "binaryName": { - "description": "Binary file Name", - "type": "string" - }, - "binarySize": { - "description": "Binary file Size", - "type": "integer", - "format": "int64" - }, - "builderImage": { - "description": "// BuilderImage describes which image is used for building the result images.", - "type": "string" - }, - "commitID": { - "description": "CommitID represents an arbitrary extended object reference in Git as SHA-1", - "type": "string" - }, - "committerEmail": { - "description": "CommitterEmail contains the e-mail of the committer", - "type": "string" - }, - "committerName": { - "description": "CommitterName contains the name of the committer", - "type": "string" - }, - "description": { - "description": "Description is a result image description label. The default is no description.", - "type": "string" - }, - "revisionId": { - "description": "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", - "type": "string" - }, - "sourceUrl": { - "description": "SourceURL is url of the codes such as https://github.com/a/b.git", - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder": { - "description": "S2iBuilder is the Schema for the s2ibuilders API", - "type": "object", - "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - }, - "spec": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderSpec" - }, - "status": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderStatus" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iBuilder", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderList": { - "description": "S2iBuilderList contains a list of S2iBuilder", - "type": "object", - "required": [ - "items" - ], - "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/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilder" - } - }, - "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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iBuilderList", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderSpec": { - "description": "S2iBuilderSpec defines the desired state of S2iBuilder", - "type": "object", - "properties": { - "config": { - "description": "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iConfig" - }, - "fromTemplate": { - "description": "FromTemplate define some inputs from user", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.UserDefineTemplate" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderStatus": { - "description": "S2iBuilderStatus defines the observed state of S2iBuilder", - "type": "object", - "required": [ - "runCount" - ], - "properties": { - "lastRunName": { - "description": "LastRunState return the name of the newest run of this builder", - "type": "string" - }, - "lastRunStartTime": { - "description": "LastRunStartTime return the startTime of the newest run of this builder", - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - }, - "lastRunState": { - "description": "LastRunState return the state of the newest run of this builder", - "type": "string" - }, - "runCount": { - "description": "RunCount represent the sum of s2irun of this builder", - "type": "integer", - "format": "int32" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate": { - "description": "S2iBuilderTemplate is the Schema for the s2ibuildertemplates API", - "type": "object", - "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - }, - "spec": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateSpec" - }, - "status": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateStatus" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iBuilderTemplate", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateList": { - "description": "S2iBuilderTemplateList contains a list of S2iBuilderTemplate", - "type": "object", - "required": [ - "items" - ], - "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/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" - } - }, - "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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iBuilderTemplateList", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateSpec": { - "description": "S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate", - "type": "object", - "properties": { - "codeFramework": { - "description": "CodeFramework means which language this template is designed for and which framework is using if has framework. Like Java, NodeJS etc", - "type": "string" - }, - "containerInfo": { - "description": "Images are the images this template will use.", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.ContainerInfo" - } - }, - "defaultBaseImage": { - "description": "DefaultBaseImage is the image that will be used by default", - "type": "string" - }, - "description": { - "description": "Description illustrate the purpose of this template", - "type": "string" - }, - "environment": { - "description": "Parameters is a set of environment variables to be passed to the image.", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.Parameter" - } - }, - "iconPath": { - "description": "IconPath is used for frontend display", - "type": "string" - }, - "version": { - "description": "Version of template", - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuilderTemplateStatus": { - "description": "S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate", - "type": "object" - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iConfig": { - "type": "object", - "required": [ - "imageName", - "sourceUrl" - ], - "properties": { - "addHost": { - "description": "AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host", - "type": "array", - "items": { - "type": "string" - } - }, - "asDockerfile": { - "description": "AsDockerfile indicates the path where the Dockerfile should be written instead of building a new image.", - "type": "string" - }, - "assembleUser": { - "description": "AssembleUser specifies the user to run the assemble script in container", - "type": "string" - }, - "blockOnBuild": { - "description": "BlockOnBuild prevents s2i from performing a docker build operation if one is necessary to execute ONBUILD commands, or to layer source code into the container for images that don't have a tar binary available, if the image contains ONBUILD commands that would be executed.", - "type": "boolean" - }, - "buildVolumes": { - "description": "BuildVolumes specifies a list of volumes to mount to container running the build.", - "type": "array", - "items": { - "type": "string" - } - }, - "builderBaseImageVersion": { - "description": "BuilderBaseImageVersion provides optional version information about the builder base image.", - "type": "string" - }, - "builderImage": { - "description": "BuilderImage describes which image is used for building the result images.", - "type": "string" - }, - "builderImageVersion": { - "description": "BuilderImageVersion provides optional version information about the builder image.", - "type": "string" - }, - "builderPullPolicy": { - "description": "BuilderPullPolicy specifies when to pull the builder image", - "type": "string" - }, - "callbackUrl": { - "description": "CallbackURL is a URL which is called upon successful build to inform about that fact.", - "type": "string" - }, - "cgroupLimits": { - "description": "CGroupLimits describes the cgroups limits that will be applied to any containers run by s2i.", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.CGroupLimits" - }, - "contextDir": { - "description": "Specify a relative directory inside the application repository that should be used as a root directory for the application.", - "type": "string" - }, - "description": { - "description": "Description is a result image description label. The default is no description.", - "type": "string" - }, - "destination": { - "description": "Destination specifies a location where the untar operation will place its artifacts.", - "type": "string" - }, - "displayName": { - "description": "DisplayName is a result image display-name label. This defaults to the output image name.", - "type": "string" - }, - "dockerConfig": { - "description": "DockerConfig describes how to access host docker daemon.", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.DockerConfig" - }, - "dockerNetworkMode": { - "description": "DockerNetworkMode is used to set the docker network setting to --net=container:\u003cid\u003e when the builder is invoked from a container.", - "type": "string" - }, - "dropCapabilities": { - "description": "DropCapabilities contains a list of capabilities to drop when executing containers", - "type": "array", - "items": { - "type": "string" - } - }, - "environment": { - "description": "Environment is a map of environment variables to be passed to the image.", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.EnvironmentSpec" - } - }, - "excludeRegExp": { - "description": "ExcludeRegExp contains a string representation of the regular expression desired for deciding which files to exclude from the tar stream", - "type": "string" - }, - "export": { - "description": "Export Push the result image to specify image registry in tag", - "type": "boolean" - }, - "gitSecretRef": { - "description": "GitSecretRef is the BasicAuth Secret of Git Clone", - "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" - }, - "hasOnBuild": { - "description": "HasOnBuild will be set to true if the builder image contains ONBUILD instructions", - "type": "boolean" - }, - "imageName": { - "description": "ImageName Contains the registry address and reponame, tag should set by field tag alone", - "type": "string" - }, - "imageScriptsUrl": { - "description": "ImageScriptsURL is the default location to find the assemble/run scripts for a builder image. This url can be a reference within the builder image if the scheme is specified as image://", - "type": "string" - }, - "imageWorkDir": { - "description": "ImageWorkDir is the default working directory for the builder image.", - "type": "string" - }, - "incremental": { - "description": "Incremental describes whether to try to perform incremental build.", - "type": "boolean" - }, - "incrementalAuthentication": { - "description": "IncrementalAuthentication holds the authentication information for pulling the previous image from private repositories", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.AuthConfig" - }, - "incrementalFromTag": { - "description": "IncrementalFromTag sets an alternative image tag to look for existing artifacts. Tag is used by default if this is not set.", - "type": "string" - }, - "injections": { - "description": "Injections specifies a list source/destination folders that are injected to the container that runs assemble. All files we inject will be truncated after the assemble script finishes.", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.VolumeSpec" - } - }, - "isBinaryURL": { - "description": "IsBinaryURL explain the type of SourceURL. If it is IsBinaryURL, it will download the file directly without using git.", - "type": "boolean" - }, - "keepSymlinks": { - "description": "KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow symlinks and copy files by content.", - "type": "boolean" - }, - "labelNamespace": { - "description": "LabelNamespace provides the namespace under which the labels will be generated.", - "type": "string" - }, - "labels": { - "description": "Labels specify labels and their values to be applied to the resulting image. Label keys must have non-zero length. The labels defined here override generated labels in case they have the same name.", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "layeredBuild": { - "description": "LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage.", - "type": "boolean" - }, - "nodeAffinityKey": { - "description": "The key of Node Affinity.", - "type": "string" - }, - "nodeAffinityValues": { - "description": "The values of Node Affinity.", - "type": "array", - "items": { - "type": "string" - } - }, - "outputBuildResult": { - "description": "Whether output build result to status.", - "type": "boolean" - }, - "outputImageName": { - "description": "OutputImageName is a result image name without tag, default is latest. tag will append to ImageName in the end", - "type": "string" - }, - "preserveWorkingDir": { - "description": "PreserveWorkingDir describes if working directory should be left after processing.", - "type": "boolean" - }, - "previousImagePullPolicy": { - "description": "PreviousImagePullPolicy specifies when to pull the previously build image when doing incremental build", - "type": "string" - }, - "pullAuthentication": { - "description": "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.AuthConfig" - }, - "pushAuthentication": { - "description": "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.AuthConfig" - }, - "removePreviousImage": { - "description": "RemovePreviousImage describes if previous image should be removed after successful build. This applies only to incremental builds.", - "type": "boolean" - }, - "revisionId": { - "description": "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", - "type": "string" - }, - "runImage": { - "description": "RunImage will trigger a \"docker run ...\" invocation of the produced image so the user can see if it operates as he would expect", - "type": "boolean" - }, - "runtimeArtifacts": { - "description": "RuntimeArtifacts specifies a list of source/destination pairs that will be copied from builder to a runtime image. Source can be a file or directory. Destination must be a directory. Regardless whether it is an absolute or relative path, it will be placed into image's WORKDIR. Destination also can be empty or equals to \".\", in this case it just refers to a root of WORKDIR. In case it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files label on a RuntimeImage.", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.VolumeSpec" - } - }, - "runtimeAuthentication": { - "description": "RuntimeAuthentication holds the authentication information for pulling the runtime Docker images from private repositories.", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.AuthConfig" - }, - "runtimeImage": { - "description": "RuntimeImage specifies the image that will be a base for resulting image and will be used for running an application. By default, BuilderImage is used for building and running, but the latter may be overridden.", - "type": "string" - }, - "runtimeImagePullPolicy": { - "description": "RuntimeImagePullPolicy specifies when to pull a runtime image.", - "type": "string" - }, - "scriptDownloadProxyConfig": { - "description": "ScriptDownloadProxyConfig optionally specifies the http and https proxy to use when downloading scripts", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.ProxyConfig" - }, - "scriptsUrl": { - "description": "ScriptsURL is a URL describing where to fetch the S2I scripts from during build process. This url can be a reference within the builder image if the scheme is specified as image://", - "type": "string" - }, - "securityOpt": { - "description": "SecurityOpt are passed as options to the docker containers launched by s2i.", - "type": "array", - "items": { - "type": "string" - } - }, - "sourceUrl": { - "description": "SourceURL is url of the codes such as https://github.com/a/b.git", - "type": "string" - }, - "tag": { - "description": "Tag is a result image tag name.", - "type": "string" - }, - "taintKey": { - "description": "The name of taint.", - "type": "string" - }, - "usage": { - "description": "Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked", - "type": "boolean" - }, - "workingDir": { - "description": "WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.", - "type": "string" - }, - "workingSourceDir": { - "description": "WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download that is later used as the root for ignore processing", - "type": "string" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun": { - "description": "S2iRun is the Schema for the s2iruns API", - "type": "object", - "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - }, - "spec": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunSpec" - }, - "status": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunStatus" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iRun", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunList": { - "description": "S2iRunList contains a list of S2iRun", - "type": "object", - "required": [ - "items" - ], - "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/sig-architecture/api-conventions.md#resources", - "type": "string" - }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRun" - } - }, - "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/sig-architecture/api-conventions.md#types-kinds", - "type": "string" - }, - "metadata": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - } - }, - "x-kubernetes-group-version-kind": [ - { - "group": "devops.kubesphere.io", - "kind": "S2iRunList", - "version": "v1alpha1" - } - ] - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunSpec": { - "description": "S2iRunSpec defines the desired state of S2iRun", - "type": "object", - "required": [ - "builderName" - ], - "properties": { - "backoffLimit": { - "description": "BackoffLimit limits the restart count of each s2irun. Default is 0", - "type": "integer", - "format": "int32" - }, - "builderName": { - "description": "BuilderName specify the name of s2ibuilder, required", - "type": "string" - }, - "newRevisionId": { - "description": "NewRevisionId override the default NewRevisionId in its s2ibuilder.", - "type": "string" - }, - "newSourceURL": { - "description": "NewSourceURL is used to download new binary artifacts", - "type": "string" - }, - "newTag": { - "description": "NewTag override the default tag in its s2ibuilder, image name cannot be changed.", - "type": "string" - }, - "secondsAfterFinished": { - "description": "SecondsAfterFinished if is set and greater than zero, and the job created by s2irun become successful or failed , the job will be auto deleted after SecondsAfterFinished", - "type": "integer", - "format": "int32" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iRunStatus": { - "description": "S2iRunStatus defines the observed state of S2iRun", - "type": "object", - "properties": { - "completionTime": { - "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - }, - "kubernetesJobName": { - "description": "KubernetesJobName is the job name in k8s", - "type": "string" - }, - "logURL": { - "description": "LogURL is uesd for external log handler to let user know where is log located in", - "type": "string" - }, - "runState": { - "description": "RunState indicates whether this job is done or failed", - "type": "string" - }, - "s2iBuildResult": { - "description": "S2i build result info.", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuildResult" - }, - "s2iBuildSource": { - "description": "S2i build source info.", - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.S2iBuildSource" - }, - "startTime": { - "description": "StartTime represent when this run began", - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.UserDefineTemplate": { - "type": "object", - "properties": { - "builderImage": { - "description": "BaseImage specify which version of this template to use", - "type": "string" - }, - "name": { - "description": "Name specify a template to use, so many fields in Config can left empty", - "type": "string" - }, - "parameters": { - "description": "Parameters must use with `template`, fill some parameters which template will use", - "type": "array", - "items": { - "$ref": "#/definitions/com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.Parameter" - } - } - } - }, - "com.github.kubesphere.s2ioperator.pkg.apis.devops.v1alpha1.VolumeSpec": { - "description": "VolumeSpec represents a single volume mount point.", - "type": "object", - "properties": { - "destination": { - "description": "Destination is the path to mount the volume to - absolute or relative.", - "type": "string" - }, - "keep": { - "description": "Keep indicates if the mounted data should be kept in the final image.", - "type": "boolean" - }, - "source": { - "description": "Source is a reference to the volume source.", - "type": "string" - } - } - }, "io.k8s.api.core.v1.LocalObjectReference": { "description": "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", "type": "object", @@ -7028,11 +9030,21 @@ "kind": "DeleteOptions", "version": "v1" }, + { + "group": "cluster.kubesphere.io", + "kind": "DeleteOptions", + "version": "v1alpha1" + }, { "group": "devops.kubesphere.io", "kind": "DeleteOptions", "version": "v1alpha1" }, + { + "group": "devops.kubesphere.io", + "kind": "DeleteOptions", + "version": "v1alpha3" + }, { "group": "network.kubesphere.io", "kind": "DeleteOptions", @@ -7072,53 +9084,6 @@ } } }, - "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector": { - "description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", - "type": "object", - "properties": { - "matchExpressions": { - "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", - "type": "array", - "items": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement" - } - }, - "matchLabels": { - "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - }, - "io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelectorRequirement": { - "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", - "type": "object", - "required": [ - "key", - "operator" - ], - "properties": { - "key": { - "description": "key is the label key that the selector applies to.", - "type": "string", - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge" - }, - "operator": { - "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", - "type": "string" - }, - "values": { - "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", - "type": "array", - "items": { - "type": "string" - } - } - } - }, "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", "type": "object", @@ -7459,11 +9424,21 @@ "kind": "WatchEvent", "version": "v1" }, + { + "group": "cluster.kubesphere.io", + "kind": "WatchEvent", + "version": "v1alpha1" + }, { "group": "devops.kubesphere.io", "kind": "WatchEvent", "version": "v1alpha1" }, + { + "group": "devops.kubesphere.io", + "kind": "WatchEvent", + "version": "v1alpha3" + }, { "group": "network.kubesphere.io", "kind": "WatchEvent", @@ -7490,6 +9465,368 @@ "type": "string", "format": "int-or-string" }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster": { + "description": "Cluster is the schema for the clusters API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterSpec" + }, + "status": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "cluster.kubesphere.io", + "kind": "Cluster", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterCondition": { + "type": "object", + "required": [ + "type", + "status" + ], + "properties": { + "lastTransitionTime": { + "description": "Last time the condition transitioned from one status to another.", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "lastUpdateTime": { + "description": "The last time this condition was updated.", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "message": { + "description": "A human readable message indicating details about the transition.", + "type": "string" + }, + "reason": { + "description": "The reason for the condition's last transition.", + "type": "string" + }, + "status": { + "description": "Status of the condition, one of True, False, Unknown.", + "type": "string" + }, + "type": { + "description": "Type of the condition", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterList": { + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Cluster" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "cluster.kubesphere.io", + "kind": "ClusterList", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterSpec": { + "type": "object", + "properties": { + "connection": { + "description": "Connection holds info to connect to the member cluster", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Connection" + }, + "enable": { + "description": "Desired state of the cluster", + "type": "boolean" + }, + "joinFederation": { + "description": "Join cluster as a kubefed cluster", + "type": "boolean" + }, + "provider": { + "description": "Provider of the cluster, this field is just for description", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterStatus": { + "type": "object", + "properties": { + "conditions": { + "description": "Represents the latest available observations of a cluster's current state.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.ClusterCondition" + } + }, + "kubernetesVersion": { + "description": "GitVersion of the kubernetes cluster, this field is populated by cluster controller", + "type": "string" + }, + "nodeCount": { + "description": "Count of the kubernetes cluster nodes This field may not reflect the instant status of the cluster.", + "type": "integer", + "format": "int32" + }, + "region": { + "description": "Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'.", + "type": "string" + }, + "zones": { + "description": "Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'.", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.cluster.v1alpha1.Connection": { + "type": "object", + "properties": { + "kubeconfig": { + "description": "KubeConfig content used to connect to cluster api server Should provide this field explicitly if connection type is direct. Will be populated by ks-proxy if connection type is proxy.", + "type": "string", + "format": "byte" + }, + "kubernetesAPIEndpoint": { + "description": "Kubernetes API Server endpoint. Example: https://10.10.0.1:6443 Should provide this field explicitly if connection type is direct. Will be populated by ks-apiserver if connection type is proxy.", + "type": "string" + }, + "kubernetesAPIServerPort": { + "description": "KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic Only applicable when connection type is proxy.", + "type": "integer", + "format": "int32" + }, + "kubesphereAPIEndpoint": { + "description": "KubeSphere API Server endpoint. Example: http://10.10.0.11:8080 Should provide this field explicitly if connection type is direct. Will be populated by ks-apiserver if connection type is proxy.", + "type": "string" + }, + "kubesphereAPIServerPort": { + "description": "KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic Only applicable when connection type is proxy.", + "type": "integer", + "format": "int32" + }, + "token": { + "description": "Token used by agents of member cluster to connect to host cluster proxy. This field is populated by apiserver only if connection type is proxy.", + "type": "string" + }, + "type": { + "description": "type defines how host cluster will connect to host cluster ConnectionTypeDirect means direct connection, this requires\n kubeconfig and kubesphere apiserver endpoint provided\nConnectionTypeProxy means using kubesphere proxy, no kubeconfig\n or kubesphere apiserver endpoint required", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.AuthConfig": { + "description": "AuthConfig is our abstraction of the Registry authorization information for whatever docker client we happen to be based on", + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "secretRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "serverAddress": { + "type": "string" + }, + "username": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.CGroupLimits": { + "description": "CGroupLimits holds limits used to constrain container resources.", + "type": "object", + "required": [ + "memoryLimitBytes", + "cpuShares", + "cpuPeriod", + "cpuQuota", + "memorySwap", + "parent" + ], + "properties": { + "cpuPeriod": { + "type": "integer", + "format": "int64" + }, + "cpuQuota": { + "type": "integer", + "format": "int64" + }, + "cpuShares": { + "type": "integer", + "format": "int64" + }, + "memoryLimitBytes": { + "type": "integer", + "format": "int64" + }, + "memorySwap": { + "type": "integer", + "format": "int64" + }, + "parent": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.ContainerInfo": { + "type": "object", + "properties": { + "buildVolumes": { + "description": "BuildVolumes specifies a list of volumes to mount to container running the build.", + "type": "array", + "items": { + "type": "string" + } + }, + "builderImage": { + "description": "BaseImage are the images this template will use.", + "type": "string" + }, + "runtimeArtifacts": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.VolumeSpec" + } + }, + "runtimeImage": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.DockerConfig": { + "description": "DockerConfig contains the configuration for a Docker connection.", + "type": "object", + "required": [ + "endPoint", + "certFile", + "keyFile", + "caFile", + "useTLS", + "tlsVerify" + ], + "properties": { + "caFile": { + "description": "CAFile is the certificate authority file path for a TLS connection", + "type": "string" + }, + "certFile": { + "description": "CertFile is the certificate file path for a TLS connection", + "type": "string" + }, + "endPoint": { + "description": "Endpoint is the docker network endpoint or socket", + "type": "string" + }, + "keyFile": { + "description": "KeyFile is the key file path for a TLS connection", + "type": "string" + }, + "tlsVerify": { + "description": "TLSVerify indicates if TLS peer must be verified", + "type": "boolean" + }, + "useTLS": { + "description": "UseTLS indicates if TLS must be used", + "type": "boolean" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.EnvironmentSpec": { + "description": "EnvironmentSpec specifies a single environment variable.", + "type": "object", + "required": [ + "name", + "value" + ], + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.Parameter": { + "type": "object", + "properties": { + "defaultValue": { + "type": "string" + }, + "description": { + "type": "string" + }, + "key": { + "type": "string" + }, + "optValues": { + "type": "array", + "items": { + "type": "string" + } + }, + "required": { + "type": "boolean" + }, + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.ProxyConfig": { + "description": "ProxyConfig holds proxy configuration.", + "type": "object", + "properties": { + "httpProxy": { + "type": "string" + }, + "httpsProxy": { + "type": "string" + } + } + }, "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBinary": { "description": "S2iBinary is the Schema for the s2ibinaries API", "type": "object", @@ -7589,8 +9926,83 @@ } } }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy": { - "description": "WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace", + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuildResult": { + "type": "object", + "properties": { + "commandPull": { + "description": "Command for pull image.", + "type": "string" + }, + "imageCreated": { + "description": "Image created time.", + "type": "string" + }, + "imageID": { + "description": "Image ID.", + "type": "string" + }, + "imageName": { + "description": "ImageName is the name of artifact", + "type": "string" + }, + "imageRepoTags": { + "description": "image tags.", + "type": "array", + "items": { + "type": "string" + } + }, + "imageSize": { + "description": "The size in bytes of the image", + "type": "integer", + "format": "int64" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuildSource": { + "type": "object", + "properties": { + "binaryName": { + "description": "Binary file Name", + "type": "string" + }, + "binarySize": { + "description": "Binary file Size", + "type": "integer", + "format": "int64" + }, + "builderImage": { + "description": "// BuilderImage describes which image is used for building the result images.", + "type": "string" + }, + "commitID": { + "description": "CommitID represents an arbitrary extended object reference in Git as SHA-1", + "type": "string" + }, + "committerEmail": { + "description": "CommitterEmail contains the e-mail of the committer", + "type": "string" + }, + "committerName": { + "description": "CommitterName contains the name of the committer", + "type": "string" + }, + "description": { + "description": "Description is a result image description label. The default is no description.", + "type": "string" + }, + "revisionId": { + "description": "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", + "type": "string" + }, + "sourceUrl": { + "description": "SourceURL is url of the codes such as https://github.com/a/b.git", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder": { + "description": "S2iBuilder is the Schema for the s2ibuilders API", "type": "object", "properties": { "apiVersion": { @@ -7605,62 +10017,22 @@ "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" }, "spec": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicySpec" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderSpec" }, "status": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyStatus" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderStatus" } }, "x-kubernetes-group-version-kind": [ { - "group": "network.kubesphere.io", - "kind": "WorkspaceNetworkPolicy", + "group": "devops.kubesphere.io", + "kind": "S2iBuilder", "version": "v1alpha1" } ] }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyEgressRule": { - "description": "WorkspaceNetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and to.", - "type": "object", - "properties": { - "from": { - "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", - "type": "array", - "items": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyPeer" - } - }, - "ports": { - "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", - "type": "array", - "items": { - "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" - } - } - } - }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyIngressRule": { - "description": "WorkspaceNetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and from.", - "type": "object", - "properties": { - "from": { - "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", - "type": "array", - "items": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyPeer" - } - }, - "ports": { - "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", - "type": "array", - "items": { - "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" - } - } - } - }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyList": { - "description": "WorkspaceNetworkPolicyList contains a list of WorkspaceNetworkPolicy", + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderList": { + "description": "S2iBuilderList contains a list of S2iBuilder", "type": "object", "required": [ "items" @@ -7673,7 +10045,1132 @@ "items": { "type": "array", "items": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicy" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilder" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "S2iBuilderList", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderSpec": { + "description": "S2iBuilderSpec defines the desired state of S2iBuilder", + "type": "object", + "properties": { + "config": { + "description": "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iConfig" + }, + "fromTemplate": { + "description": "FromTemplate define some inputs from user", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.UserDefineTemplate" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderStatus": { + "description": "S2iBuilderStatus defines the observed state of S2iBuilder", + "type": "object", + "required": [ + "runCount" + ], + "properties": { + "lastRunName": { + "description": "LastRunState return the name of the newest run of this builder", + "type": "string" + }, + "lastRunStartTime": { + "description": "LastRunStartTime return the startTime of the newest run of this builder", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "lastRunState": { + "description": "LastRunState return the state of the newest run of this builder", + "type": "string" + }, + "runCount": { + "description": "RunCount represent the sum of s2irun of this builder", + "type": "integer", + "format": "int32" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate": { + "description": "S2iBuilderTemplate is the Schema for the s2ibuildertemplates API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateSpec" + }, + "status": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "S2iBuilderTemplate", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateList": { + "description": "S2iBuilderTemplateList contains a list of S2iBuilderTemplate", + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplate" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "S2iBuilderTemplateList", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateSpec": { + "description": "S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate", + "type": "object", + "properties": { + "codeFramework": { + "description": "CodeFramework means which language this template is designed for and which framework is using if has framework. Like Java, NodeJS etc", + "type": "string" + }, + "containerInfo": { + "description": "Images are the images this template will use.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.ContainerInfo" + } + }, + "defaultBaseImage": { + "description": "DefaultBaseImage is the image that will be used by default", + "type": "string" + }, + "description": { + "description": "Description illustrate the purpose of this template", + "type": "string" + }, + "environment": { + "description": "Parameters is a set of environment variables to be passed to the image.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.Parameter" + } + }, + "iconPath": { + "description": "IconPath is used for frontend display", + "type": "string" + }, + "version": { + "description": "Version of template", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuilderTemplateStatus": { + "description": "S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate", + "type": "object" + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iConfig": { + "type": "object", + "required": [ + "imageName", + "sourceUrl" + ], + "properties": { + "addHost": { + "description": "AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host", + "type": "array", + "items": { + "type": "string" + } + }, + "asDockerfile": { + "description": "AsDockerfile indicates the path where the Dockerfile should be written instead of building a new image.", + "type": "string" + }, + "assembleUser": { + "description": "AssembleUser specifies the user to run the assemble script in container", + "type": "string" + }, + "blockOnBuild": { + "description": "BlockOnBuild prevents s2i from performing a docker build operation if one is necessary to execute ONBUILD commands, or to layer source code into the container for images that don't have a tar binary available, if the image contains ONBUILD commands that would be executed.", + "type": "boolean" + }, + "branchExpression": { + "description": "Regular expressions, ignoring names that do not match the provided regular expression", + "type": "string" + }, + "buildVolumes": { + "description": "BuildVolumes specifies a list of volumes to mount to container running the build.", + "type": "array", + "items": { + "type": "string" + } + }, + "builderBaseImageVersion": { + "description": "BuilderBaseImageVersion provides optional version information about the builder base image.", + "type": "string" + }, + "builderImage": { + "description": "BuilderImage describes which image is used for building the result images.", + "type": "string" + }, + "builderImageVersion": { + "description": "BuilderImageVersion provides optional version information about the builder image.", + "type": "string" + }, + "builderPullPolicy": { + "description": "BuilderPullPolicy specifies when to pull the builder image", + "type": "string" + }, + "callbackUrl": { + "description": "CallbackURL is a URL which is called upon successful build to inform about that fact.", + "type": "string" + }, + "cgroupLimits": { + "description": "CGroupLimits describes the cgroups limits that will be applied to any containers run by s2i.", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.CGroupLimits" + }, + "contextDir": { + "description": "Specify a relative directory inside the application repository that should be used as a root directory for the application.", + "type": "string" + }, + "description": { + "description": "Description is a result image description label. The default is no description.", + "type": "string" + }, + "destination": { + "description": "Destination specifies a location where the untar operation will place its artifacts.", + "type": "string" + }, + "displayName": { + "description": "DisplayName is a result image display-name label. This defaults to the output image name.", + "type": "string" + }, + "dockerConfig": { + "description": "DockerConfig describes how to access host docker daemon.", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.DockerConfig" + }, + "dockerNetworkMode": { + "description": "DockerNetworkMode is used to set the docker network setting to --net=container:\u003cid\u003e when the builder is invoked from a container.", + "type": "string" + }, + "dropCapabilities": { + "description": "DropCapabilities contains a list of capabilities to drop when executing containers", + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "description": "Environment is a map of environment variables to be passed to the image.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.EnvironmentSpec" + } + }, + "excludeRegExp": { + "description": "ExcludeRegExp contains a string representation of the regular expression desired for deciding which files to exclude from the tar stream", + "type": "string" + }, + "export": { + "description": "Export Push the result image to specify image registry in tag", + "type": "boolean" + }, + "gitSecretRef": { + "description": "GitSecretRef is the BasicAuth Secret of Git Clone", + "$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference" + }, + "hasOnBuild": { + "description": "HasOnBuild will be set to true if the builder image contains ONBUILD instructions", + "type": "boolean" + }, + "imageName": { + "description": "ImageName Contains the registry address and reponame, tag should set by field tag alone", + "type": "string" + }, + "imageScriptsUrl": { + "description": "ImageScriptsURL is the default location to find the assemble/run scripts for a builder image. This url can be a reference within the builder image if the scheme is specified as image://", + "type": "string" + }, + "imageWorkDir": { + "description": "ImageWorkDir is the default working directory for the builder image.", + "type": "string" + }, + "incremental": { + "description": "Incremental describes whether to try to perform incremental build.", + "type": "boolean" + }, + "incrementalAuthentication": { + "description": "IncrementalAuthentication holds the authentication information for pulling the previous image from private repositories", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.AuthConfig" + }, + "incrementalFromTag": { + "description": "IncrementalFromTag sets an alternative image tag to look for existing artifacts. Tag is used by default if this is not set.", + "type": "string" + }, + "injections": { + "description": "Injections specifies a list source/destination folders that are injected to the container that runs assemble. All files we inject will be truncated after the assemble script finishes.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.VolumeSpec" + } + }, + "isBinaryURL": { + "description": "IsBinaryURL explain the type of SourceURL. If it is IsBinaryURL, it will download the file directly without using git.", + "type": "boolean" + }, + "keepSymlinks": { + "description": "KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow symlinks and copy files by content.", + "type": "boolean" + }, + "labelNamespace": { + "description": "LabelNamespace provides the namespace under which the labels will be generated.", + "type": "string" + }, + "labels": { + "description": "Labels specify labels and their values to be applied to the resulting image. Label keys must have non-zero length. The labels defined here override generated labels in case they have the same name.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "layeredBuild": { + "description": "LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage.", + "type": "boolean" + }, + "nodeAffinityKey": { + "description": "The key of Node Affinity.", + "type": "string" + }, + "nodeAffinityValues": { + "description": "The values of Node Affinity.", + "type": "array", + "items": { + "type": "string" + } + }, + "outputBuildResult": { + "description": "Whether output build result to status.", + "type": "boolean" + }, + "outputImageName": { + "description": "OutputImageName is a result image name without tag, default is latest. tag will append to ImageName in the end", + "type": "string" + }, + "preserveWorkingDir": { + "description": "PreserveWorkingDir describes if working directory should be left after processing.", + "type": "boolean" + }, + "previousImagePullPolicy": { + "description": "PreviousImagePullPolicy specifies when to pull the previously build image when doing incremental build", + "type": "string" + }, + "pullAuthentication": { + "description": "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.AuthConfig" + }, + "pushAuthentication": { + "description": "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.AuthConfig" + }, + "removePreviousImage": { + "description": "RemovePreviousImage describes if previous image should be removed after successful build. This applies only to incremental builds.", + "type": "boolean" + }, + "revisionId": { + "description": "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", + "type": "string" + }, + "runImage": { + "description": "RunImage will trigger a \"docker run ...\" invocation of the produced image so the user can see if it operates as he would expect", + "type": "boolean" + }, + "runtimeArtifacts": { + "description": "RuntimeArtifacts specifies a list of source/destination pairs that will be copied from builder to a runtime image. Source can be a file or directory. Destination must be a directory. Regardless whether it is an absolute or relative path, it will be placed into image's WORKDIR. Destination also can be empty or equals to \".\", in this case it just refers to a root of WORKDIR. In case it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files label on a RuntimeImage.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.VolumeSpec" + } + }, + "runtimeAuthentication": { + "description": "RuntimeAuthentication holds the authentication information for pulling the runtime Docker images from private repositories.", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.AuthConfig" + }, + "runtimeImage": { + "description": "RuntimeImage specifies the image that will be a base for resulting image and will be used for running an application. By default, BuilderImage is used for building and running, but the latter may be overridden.", + "type": "string" + }, + "runtimeImagePullPolicy": { + "description": "RuntimeImagePullPolicy specifies when to pull a runtime image.", + "type": "string" + }, + "scriptDownloadProxyConfig": { + "description": "ScriptDownloadProxyConfig optionally specifies the http and https proxy to use when downloading scripts", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.ProxyConfig" + }, + "scriptsUrl": { + "description": "ScriptsURL is a URL describing where to fetch the S2I scripts from during build process. This url can be a reference within the builder image if the scheme is specified as image://", + "type": "string" + }, + "secretCode": { + "description": "SecretCode", + "type": "string" + }, + "securityOpt": { + "description": "SecurityOpt are passed as options to the docker containers launched by s2i.", + "type": "array", + "items": { + "type": "string" + } + }, + "sourceUrl": { + "description": "SourceURL is url of the codes such as https://github.com/a/b.git", + "type": "string" + }, + "tag": { + "description": "Tag is a result image tag name.", + "type": "string" + }, + "taintKey": { + "description": "The name of taint.", + "type": "string" + }, + "usage": { + "description": "Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked", + "type": "boolean" + }, + "workingDir": { + "description": "WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.", + "type": "string" + }, + "workingSourceDir": { + "description": "WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download that is later used as the root for ignore processing", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun": { + "description": "S2iRun is the Schema for the s2iruns API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunSpec" + }, + "status": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "S2iRun", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunList": { + "description": "S2iRunList contains a list of S2iRun", + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRun" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "S2iRunList", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunSpec": { + "description": "S2iRunSpec defines the desired state of S2iRun", + "type": "object", + "required": [ + "builderName" + ], + "properties": { + "backoffLimit": { + "description": "BackoffLimit limits the restart count of each s2irun. Default is 0", + "type": "integer", + "format": "int32" + }, + "builderName": { + "description": "BuilderName specify the name of s2ibuilder, required", + "type": "string" + }, + "newRevisionId": { + "description": "NewRevisionId override the default NewRevisionId in its s2ibuilder.", + "type": "string" + }, + "newSourceURL": { + "description": "NewSourceURL is used to download new binary artifacts", + "type": "string" + }, + "newTag": { + "description": "NewTag override the default tag in its s2ibuilder, image name cannot be changed.", + "type": "string" + }, + "secondsAfterFinished": { + "description": "SecondsAfterFinished if is set and greater than zero, and the job created by s2irun become successful or failed , the job will be auto deleted after SecondsAfterFinished", + "type": "integer", + "format": "int32" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iRunStatus": { + "description": "S2iRunStatus defines the observed state of S2iRun", + "type": "object", + "properties": { + "completionTime": { + "description": "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + }, + "kubernetesJobName": { + "description": "KubernetesJobName is the job name in k8s", + "type": "string" + }, + "logURL": { + "description": "LogURL is uesd for external log handler to let user know where is log located in", + "type": "string" + }, + "runState": { + "description": "RunState indicates whether this job is done or failed", + "type": "string" + }, + "s2iBuildResult": { + "description": "S2i build result info.", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuildResult" + }, + "s2iBuildSource": { + "description": "S2i build source info.", + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.S2iBuildSource" + }, + "startTime": { + "description": "StartTime represent when this run began", + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.UserDefineTemplate": { + "type": "object", + "properties": { + "builderImage": { + "description": "BaseImage specify which version of this template to use", + "type": "string" + }, + "name": { + "description": "Name specify a template to use, so many fields in Config can left empty", + "type": "string" + }, + "parameters": { + "description": "Parameters must use with `template`, fill some parameters which template will use", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.Parameter" + } + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha1.VolumeSpec": { + "description": "VolumeSpec represents a single volume mount point.", + "type": "object", + "properties": { + "destination": { + "description": "Destination is the path to mount the volume to - absolute or relative.", + "type": "string" + }, + "keep": { + "description": "Keep indicates if the mounted data should be kept in the final image.", + "type": "boolean" + }, + "source": { + "description": "Source is a reference to the volume source.", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.BitbucketServerSource": { + "type": "object", + "properties": { + "api_uri": { + "type": "string" + }, + "credential_id": { + "type": "string" + }, + "discover_branches": { + "type": "integer", + "format": "int32" + }, + "discover_pr_from_forks": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscoverPRFromForks" + }, + "discover_pr_from_origin": { + "type": "integer", + "format": "int32" + }, + "git_clone_option": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitCloneOption" + }, + "owner": { + "type": "string" + }, + "regex_filter": { + "type": "string" + }, + "repo": { + "type": "string" + }, + "scm_id": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject": { + "description": "DevOpsProject is the Schema for the devopsprojects API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectSpec" + }, + "status": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "DevOpsProject", + "version": "v1alpha3" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectList": { + "description": "DevOpsProjectList contains a list of DevOpsProject", + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProject" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "DevOpsProjectList", + "version": "v1alpha3" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectSpec": { + "description": "DevOpsProjectSpec defines the desired state of DevOpsProject", + "type": "object" + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DevOpsProjectStatus": { + "description": "DevOpsProjectStatus defines the observed state of DevOpsProject", + "type": "object", + "properties": { + "adminNamespace": { + "description": "INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run \"make\" to regenerate code after modifying this file", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscarderProperty": { + "type": "object", + "properties": { + "days_to_keep": { + "type": "string" + }, + "num_to_keep": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscoverPRFromForks": { + "type": "object", + "properties": { + "strategy": { + "type": "integer", + "format": "int32" + }, + "trust": { + "type": "integer", + "format": "int32" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitCloneOption": { + "type": "object", + "properties": { + "depth": { + "type": "integer", + "format": "int32" + }, + "shallow": { + "type": "boolean" + }, + "timeout": { + "type": "integer", + "format": "int32" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitSource": { + "type": "object", + "properties": { + "credential_id": { + "type": "string" + }, + "discover_branches": { + "type": "boolean" + }, + "git_clone_option": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitCloneOption" + }, + "regex_filter": { + "type": "string" + }, + "scm_id": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GithubSource": { + "type": "object", + "properties": { + "api_uri": { + "type": "string" + }, + "credential_id": { + "type": "string" + }, + "discover_branches": { + "type": "integer", + "format": "int32" + }, + "discover_pr_from_forks": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscoverPRFromForks" + }, + "discover_pr_from_origin": { + "type": "integer", + "format": "int32" + }, + "git_clone_option": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitCloneOption" + }, + "owner": { + "type": "string" + }, + "regex_filter": { + "type": "string" + }, + "repo": { + "type": "string" + }, + "scm_id": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.MultiBranchJobTrigger": { + "type": "object", + "properties": { + "create_action_job_to_trigger": { + "type": "string" + }, + "delete_action_job_to_trigger": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.MultiBranchPipeline": { + "type": "object", + "required": [ + "name", + "source_type", + "script_path" + ], + "properties": { + "bitbucket_server_source": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.BitbucketServerSource" + }, + "descriptio": { + "type": "string" + }, + "discarder": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscarderProperty" + }, + "git_source": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GitSource" + }, + "github_source": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.GithubSource" + }, + "multibranch_job_trigger": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.MultiBranchJobTrigger" + }, + "name": { + "type": "string" + }, + "script_path": { + "type": "string" + }, + "single_svn_source": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.SingleSvnSource" + }, + "source_type": { + "type": "string" + }, + "svn_source": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.SvnSource" + }, + "timer_trigger": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.TimerTrigger" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.NoScmPipeline": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "descriptio": { + "type": "string" + }, + "disable_concurrent": { + "type": "boolean" + }, + "discarder": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.DiscarderProperty" + }, + "jenkinsfile": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Parameter" + } + }, + "remote_trigger": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.RemoteTrigger" + }, + "timer_trigger": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.TimerTrigger" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Parameter": { + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "default_value": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline": { + "description": "Pipeline is the Schema for the pipelines API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineSpec" + }, + "status": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "Pipeline", + "version": "v1alpha3" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineList": { + "description": "PipelineList contains a list of Pipeline", + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.Pipeline" + } + }, + "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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "devops.kubesphere.io", + "kind": "PipelineList", + "version": "v1alpha3" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineSpec": { + "description": "PipelineSpec defines the desired state of Pipeline", + "type": "object", + "required": [ + "type" + ], + "properties": { + "multi_branch_pipeline": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.MultiBranchPipeline" + }, + "pipeline": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.NoScmPipeline" + }, + "type": { + "description": "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.PipelineStatus": { + "description": "PipelineStatus defines the observed state of Pipeline", + "type": "object" + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.RemoteTrigger": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.SingleSvnSource": { + "type": "object", + "properties": { + "credential_id": { + "type": "string" + }, + "remote": { + "type": "string" + }, + "scm_id": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.SvnSource": { + "type": "object", + "properties": { + "credential_id": { + "type": "string" + }, + "excludes": { + "type": "string" + }, + "includes": { + "type": "string" + }, + "remote": { + "type": "string" + }, + "scm_id": { + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.devops.v1alpha3.TimerTrigger": { + "type": "object", + "properties": { + "cron": { + "description": "user in no scm job", + "type": "string" + }, + "interval": { + "description": "use in multi-branch job", + "type": "string" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy": { + "description": "NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies API", + "type": "object", + "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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicySpec" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "network.kubesphere.io", + "kind": "NamespaceNetworkPolicy", + "version": "v1alpha1" + } + ] + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicyList": { + "description": "NamespaceNetworkPolicyList contains a list of NamespaceNetworkPolicy", + "type": "object", + "required": [ + "items" + ], + "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/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicy" } }, "kind": { @@ -7687,66 +11184,119 @@ "x-kubernetes-group-version-kind": [ { "group": "network.kubesphere.io", - "kind": "WorkspaceNetworkPolicyList", + "kind": "NamespaceNetworkPolicyList", "version": "v1alpha1" } ] }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyPeer": { - "description": "WorkspaceNetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed. It is same as 'NetworkPolicyPeer' in k8s but with an additional field 'WorkspaceSelector'", - "type": "object", - "properties": { - "ipBlock": { - "description": "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.", - "$ref": "#/definitions/io.k8s.api.networking.v1.IPBlock" - }, - "namespaceSelector": { - "description": "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector.", - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" - }, - "podSelector": { - "description": "This is a label selector which selects Pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the Pods matching PodSelector in the policy's own Namespace.", - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" - }, - "workspaceSelector": { - "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" - } - } - }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicySpec": { - "description": "WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy", + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceNetworkPolicySpec": { + "description": "NamespaceNetworkPolicySpec provides the specification of a NamespaceNetworkPolicy", "type": "object", "properties": { "egress": { "description": "List of egress rules to be applied to the selected pods. Outgoing traffic is allowed if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic matches at least one egress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy limits all outgoing traffic (and serves solely to ensure that the pods it selects are isolated by default). This field is beta-level in 1.8", "type": "array", "items": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyEgressRule" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyEgressRule" } }, "ingress": { "description": "List of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default)", "type": "array", "items": { - "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyIngressRule" + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyIngressRule" } }, "policyTypes": { - "description": "List of rule types that the WorkspaceNetworkPolicy relates to. Valid options are Ingress, Egress, or Ingress,Egress. If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]).", + "description": "List of rule types that the NetworkPolicy relates to. Valid options are \"Ingress\", \"Egress\", or \"Ingress,Egress\". If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8", "type": "array", "items": { "type": "string" } - }, - "workspace": { - "description": "Workspace specify the name of ws to apply this workspace network policy", + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceSelector": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { "type": "string" } } }, - "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.WorkspaceNetworkPolicyStatus": { - "description": "WorkspaceNetworkPolicyStatus defines the observed state of WorkspaceNetworkPolicy", - "type": "object" + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyEgressRule": { + "description": "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", + "type": "object", + "properties": { + "ports": { + "description": "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "type": "array", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + } + }, + "to": { + "description": "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyPeer" + } + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyIngressRule": { + "description": "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.", + "type": "object", + "properties": { + "from": { + "description": "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the from list.", + "type": "array", + "items": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyPeer" + } + }, + "ports": { + "description": "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + "type": "array", + "items": { + "$ref": "#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort" + } + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NetworkPolicyPeer": { + "description": "NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed", + "type": "object", + "properties": { + "ipBlock": { + "description": "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.", + "$ref": "#/definitions/io.k8s.api.networking.v1.IPBlock" + }, + "namespace": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.NamespaceSelector" + }, + "service": { + "$ref": "#/definitions/io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.ServiceSelector" + } + } + }, + "io.kubesphere.kubesphere.pkg.apis.network.v1alpha1.ServiceSelector": { + "type": "object", + "required": [ + "name", + "namespace" + ], + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + } + } }, "io.kubesphere.kubesphere.pkg.apis.tenant.v1alpha1.Workspace": { "description": "Workspace is the Schema for the workspaces API", @@ -7817,6 +11367,9 @@ "properties": { "manager": { "type": "string" + }, + "networkIsolation": { + "type": "boolean" } } }, diff --git a/build/ks-apigateway/Dockerfile b/build/ks-apigateway/Dockerfile deleted file mode 100644 index 61700244d..000000000 --- a/build/ks-apigateway/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2018 The KubeSphere Authors. All rights reserved. -# Use of this source code is governed by a Apache license -# that can be found in the LICENSE file. - -# Copyright 2018 The KubeSphere Authors. All rights reserved. -# Use of this source code is governed by a Apache license -# that can be found in the LICENSE file. - -FROM golang:1.12 as ks-apigateway-builder - -COPY / /go/src/kubesphere.io/kubesphere -WORKDIR /go/src/kubesphere.io/kubesphere -RUN CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -i -ldflags '-w -s' -o ks-apigateway cmd/ks-apigateway/apiserver.go && \ - go run tools/cmd/doc-gen/main.go --output=install/swagger-ui/api.json - -FROM alpine:3.9 -RUN apk add --update ca-certificates && update-ca-certificates -COPY --from=ks-apigateway-builder /go/src/kubesphere.io/kubesphere/ks-apigateway /usr/local/bin/ -COPY --from=ks-apigateway-builder /go/src/kubesphere.io/kubesphere/install/swagger-ui /var/static/swagger-ui -CMD ["sh"] diff --git a/build/ks-iam/Dockerfile b/build/ks-iam/Dockerfile deleted file mode 100644 index 3e65a47e8..000000000 --- a/build/ks-iam/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2018 The KubeSphere Authors. All rights reserved. -# Use of this source code is governed by a Apache license -# that can be found in the LICENSE file. - -# Copyright 2018 The KubeSphere Authors. All rights reserved. -# Use of this source code is governed by a Apache license -# that can be found in the LICENSE file. -FROM golang:1.12 as ks-iam-builder - -COPY / /go/src/kubesphere.io/kubesphere - -WORKDIR /go/src/kubesphere.io/kubesphere -RUN CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64 GOFLAGS=-mod=vendor go build -i -ldflags '-w -s' -o ks-iam cmd/ks-iam/apiserver.go - -FROM alpine:3.9 -RUN apk add --update ca-certificates && update-ca-certificates -COPY --from=ks-iam-builder /go/src/kubesphere.io/kubesphere/ks-iam /usr/local/bin/ -CMD ["sh"] diff --git a/build/ks-network/Dockerfile b/build/ks-network/Dockerfile deleted file mode 100644 index fb5223cf3..000000000 --- a/build/ks-network/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM gcr.io/distroless/static:latest -WORKDIR / -COPY ks-network . -ENTRYPOINT ["/ks-network"] diff --git a/cmd/controller-manager/app/controllers.go b/cmd/controller-manager/app/controllers.go index b07246b39..6aaa453cb 100644 --- a/cmd/controller-manager/app/controllers.go +++ b/cmd/controller-manager/app/controllers.go @@ -18,137 +18,146 @@ package app import ( - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" + "k8s.io/klog" "kubesphere.io/kubesphere/pkg/controller/application" + "kubesphere.io/kubesphere/pkg/controller/cluster" "kubesphere.io/kubesphere/pkg/controller/destinationrule" + "kubesphere.io/kubesphere/pkg/controller/devopscredential" + "kubesphere.io/kubesphere/pkg/controller/devopsproject" "kubesphere.io/kubesphere/pkg/controller/job" + "kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy" + "kubesphere.io/kubesphere/pkg/controller/network/provider" + "kubesphere.io/kubesphere/pkg/controller/pipeline" "kubesphere.io/kubesphere/pkg/controller/s2ibinary" "kubesphere.io/kubesphere/pkg/controller/s2irun" "kubesphere.io/kubesphere/pkg/controller/storage/expansion" - - //"kubesphere.io/kubesphere/pkg/controller/job" + "kubesphere.io/kubesphere/pkg/controller/user" "kubesphere.io/kubesphere/pkg/controller/virtualservice" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/s3" "sigs.k8s.io/controller-runtime/pkg/manager" - "time" - - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - - applicationclientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned" - applicationinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" - s2iclientset "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - s2iinformers "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions" - istioclientset "istio.io/client-go/pkg/clientset/versioned" - istioinformers "istio.io/client-go/pkg/informers/externalversions" - kubesphereclientset "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - kubesphereinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" ) -const defaultResync = 600 * time.Second +func AddControllers( + mgr manager.Manager, + client k8s.Client, + informerFactory informers.InformerFactory, + devopsClient devops.Interface, + s3Client s3.Interface, + stopCh <-chan struct{}) error { -var log = logf.Log.WithName("controller-manager") + kubernetesInformer := informerFactory.KubernetesSharedInformerFactory() + istioInformer := informerFactory.IstioSharedInformerFactory() + kubesphereInformer := informerFactory.KubeSphereSharedInformerFactory() + applicationInformer := informerFactory.ApplicationSharedInformerFactory() -func AddControllers(mgr manager.Manager, cfg *rest.Config, stopCh <-chan struct{}) error { - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - log.Error(err, "building kubernetes client failed") - } - - istioclient, err := istioclientset.NewForConfig(cfg) - if err != nil { - log.Error(err, "create istio client failed") - return err - } - - applicationClient, err := applicationclientset.NewForConfig(cfg) - if err != nil { - log.Error(err, "create application client failed") - return err - } - s2iclient, err := s2iclientset.NewForConfig(cfg) - if err != nil { - log.Error(err, "create s2i client failed") - return err - } - kubesphereclient, err := kubesphereclientset.NewForConfig(cfg) - if err != nil { - log.Error(err, "create kubesphere client failed") - return err - } - - informerFactory := informers.NewSharedInformerFactory(kubeClient, defaultResync) - istioInformer := istioinformers.NewSharedInformerFactory(istioclient, defaultResync) - applicationInformer := applicationinformers.NewSharedInformerFactory(applicationClient, defaultResync) - s2iInformer := s2iinformers.NewSharedInformerFactory(s2iclient, defaultResync) - - kubesphereInformer := kubesphereinformers.NewSharedInformerFactory(kubesphereclient, defaultResync) - - vsController := virtualservice.NewVirtualServiceController(informerFactory.Core().V1().Services(), + vsController := virtualservice.NewVirtualServiceController(kubernetesInformer.Core().V1().Services(), istioInformer.Networking().V1alpha3().VirtualServices(), istioInformer.Networking().V1alpha3().DestinationRules(), kubesphereInformer.Servicemesh().V1alpha2().Strategies(), - kubeClient, - istioclient, - kubesphereclient) + client.Kubernetes(), + client.Istio(), + client.KubeSphere()) - drController := destinationrule.NewDestinationRuleController(informerFactory.Apps().V1().Deployments(), + drController := destinationrule.NewDestinationRuleController(kubernetesInformer.Apps().V1().Deployments(), istioInformer.Networking().V1alpha3().DestinationRules(), - informerFactory.Core().V1().Services(), + kubernetesInformer.Core().V1().Services(), kubesphereInformer.Servicemesh().V1alpha2().ServicePolicies(), - kubeClient, - istioclient, - kubesphereclient) + client.Kubernetes(), + client.Istio(), + client.KubeSphere()) - apController := application.NewApplicationController(informerFactory.Core().V1().Services(), - informerFactory.Apps().V1().Deployments(), - informerFactory.Apps().V1().StatefulSets(), + apController := application.NewApplicationController(kubernetesInformer.Core().V1().Services(), + kubernetesInformer.Apps().V1().Deployments(), + kubernetesInformer.Apps().V1().StatefulSets(), kubesphereInformer.Servicemesh().V1alpha2().Strategies(), kubesphereInformer.Servicemesh().V1alpha2().ServicePolicies(), applicationInformer.App().V1beta1().Applications(), - kubeClient, - applicationClient) + client.Kubernetes(), + client.Application()) - jobController := job.NewJobController(informerFactory.Batch().V1().Jobs(), kubeClient) + jobController := job.NewJobController(kubernetesInformer.Batch().V1().Jobs(), client.Kubernetes()) - s2iBinaryController := s2ibinary.NewController(kubesphereclient, - kubeClient, - kubesphereInformer.Devops().V1alpha1().S2iBinaries()) - - s2iRunController := s2irun.NewController(kubesphereclient, s2iclient, kubeClient, + s2iBinaryController := s2ibinary.NewController(client.Kubernetes(), + client.KubeSphere(), kubesphereInformer.Devops().V1alpha1().S2iBinaries(), - s2iInformer.Devops().V1alpha1().S2iRuns()) + s3Client, + ) + + s2iRunController := s2irun.NewS2iRunController(client.Kubernetes(), + client.KubeSphere(), + kubesphereInformer.Devops().V1alpha1().S2iBinaries(), + kubesphereInformer.Devops().V1alpha1().S2iRuns()) + + devopsProjectController := devopsproject.NewController(client.Kubernetes(), + client.KubeSphere(), devopsClient, + informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(), + informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().DevOpsProjects(), + ) + + devopsPipelineController := pipeline.NewController(client.Kubernetes(), + client.KubeSphere(), + devopsClient, + informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(), + informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().Pipelines()) + + devopsCredentialController := devopscredential.NewController(client.Kubernetes(), + devopsClient, + informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(), + informerFactory.KubernetesSharedInformerFactory().Core().V1().Secrets()) volumeExpansionController := expansion.NewVolumeExpansionController( - kubeClient, - informerFactory.Core().V1().PersistentVolumeClaims(), - informerFactory.Storage().V1().StorageClasses(), - informerFactory.Core().V1().Pods(), - informerFactory.Apps().V1().Deployments(), - informerFactory.Apps().V1().ReplicaSets(), - informerFactory.Apps().V1().StatefulSets()) + client.Kubernetes(), + kubernetesInformer.Core().V1().PersistentVolumeClaims(), + kubernetesInformer.Storage().V1().StorageClasses(), + kubernetesInformer.Core().V1().Pods(), + kubernetesInformer.Apps().V1().Deployments(), + kubernetesInformer.Apps().V1().ReplicaSets(), + kubernetesInformer.Apps().V1().StatefulSets()) - kubesphereInformer.Start(stopCh) - istioInformer.Start(stopCh) - informerFactory.Start(stopCh) - applicationInformer.Start(stopCh) - s2iInformer.Start(stopCh) + userController := user.NewController( + client.Kubernetes(), + client.KubeSphere(), + kubesphereInformer.Iam().V1alpha2().Users()) + + clusterController := cluster.NewClusterController( + client.Kubernetes(), + client.Config(), + kubesphereInformer.Cluster().V1alpha1().Clusters(), + client.KubeSphere().ClusterV1alpha1().Clusters()) + + nsnpProvider, err := provider.NewNsNetworkPolicyProvider(client.Kubernetes(), + kubernetesInformer.Networking().V1().NetworkPolicies()) + if err != nil { + return err + } + nsnpController := nsnetworkpolicy.NewNSNetworkPolicyController(client.Kubernetes(), + client.KubeSphere().NetworkV1alpha1(), kubesphereInformer.Network().V1alpha1().NamespaceNetworkPolicies(), + kubernetesInformer.Core().V1().Services(), kubernetesInformer.Core().V1().Nodes(), + kubesphereInformer.Tenant().V1alpha1().Workspaces(), + kubernetesInformer.Core().V1().Namespaces(), nsnpProvider) controllers := map[string]manager.Runnable{ - "virtualservice-controller": vsController, - "destinationrule-controller": drController, - "application-controller": apController, - "job-controller": jobController, - "s2ibinary-controller": s2iBinaryController, - "s2irun-controller": s2iRunController, - "volumeexpansion-controller": volumeExpansionController, + "virtualservice-controller": vsController, + "destinationrule-controller": drController, + "application-controller": apController, + "job-controller": jobController, + "s2ibinary-controller": s2iBinaryController, + "s2irun-controller": s2iRunController, + "volumeexpansion-controller": volumeExpansionController, + "devopsprojects-controller": devopsProjectController, + "pipeline-controller": devopsPipelineController, + "devopscredential-controller": devopsCredentialController, + "user-controller": userController, + "cluster-controller": clusterController, + "nsnp-controller": nsnpController, } for name, ctrl := range controllers { - err = mgr.Add(ctrl) - if err != nil { - log.Error(err, "add controller to manager failed", "name", name) + if err := mgr.Add(ctrl); err != nil { + klog.Error(err, "add controller to manager failed", "name", name) return err } } diff --git a/cmd/controller-manager/app/options/options.go b/cmd/controller-manager/app/options/options.go index 9a2bbb46a..143aa72c5 100644 --- a/cmd/controller-manager/app/options/options.go +++ b/cmd/controller-manager/app/options/options.go @@ -6,35 +6,39 @@ import ( "k8s.io/client-go/tools/leaderelection" cliflag "k8s.io/component-base/cli/flag" "k8s.io/klog" - kubesphereconfig "kubesphere.io/kubesphere/pkg/server/config" - "kubesphere.io/kubesphere/pkg/simple/client/devops" + kubesphereconfig "kubesphere.io/kubesphere/pkg/apiserver/config" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/multicluster" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/simple/client/s2is3" + "kubesphere.io/kubesphere/pkg/simple/client/s3" "strings" "time" ) type KubeSphereControllerManagerOptions struct { - KubernetesOptions *k8s.KubernetesOptions - DevopsOptions *devops.DevopsOptions - S3Options *s2is3.S3Options - OpenPitrixOptions *openpitrix.OpenPitrixOptions - - LeaderElection *leaderelection.LeaderElectionConfig + KubernetesOptions *k8s.KubernetesOptions + DevopsOptions *jenkins.Options + S3Options *s3.Options + OpenPitrixOptions *openpitrix.Options + MultiClusterOptions *multicluster.Options + LeaderElect bool + LeaderElection *leaderelection.LeaderElectionConfig } func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions { s := &KubeSphereControllerManagerOptions{ - KubernetesOptions: k8s.NewKubernetesOptions(), - DevopsOptions: devops.NewDevopsOptions(), - S3Options: s2is3.NewS3Options(), - OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), + KubernetesOptions: k8s.NewKubernetesOptions(), + DevopsOptions: jenkins.NewDevopsOptions(), + S3Options: s3.NewS3Options(), + OpenPitrixOptions: openpitrix.NewOptions(), + MultiClusterOptions: multicluster.NewOptions(), LeaderElection: &leaderelection.LeaderElectionConfig{ LeaseDuration: 30 * time.Second, RenewDeadline: 15 * time.Second, RetryPeriod: 5 * time.Second, }, + LeaderElect: false, } return s @@ -50,14 +54,19 @@ func (s *KubeSphereControllerManagerOptions) ApplyTo(conf *kubesphereconfig.Conf func (s *KubeSphereControllerManagerOptions) Flags() cliflag.NamedFlagSets { fss := cliflag.NamedFlagSets{} - s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes")) - s.DevopsOptions.AddFlags(fss.FlagSet("devops")) - s.S3Options.AddFlags(fss.FlagSet("s3")) - s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix")) + s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions) + s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions) + s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options) + s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions) + s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions) fs := fss.FlagSet("leaderelection") s.bindLeaderElectionFlags(s.LeaderElection, fs) + fs.BoolVar(&s.LeaderElect, "leader-elect", s.LeaderElect, ""+ + "Whether to enable leader election. This field should be enabled when controller manager"+ + "deployed with multiple replicas.") + kfs := fss.FlagSet("klog") local := flag.NewFlagSet("klog", flag.ExitOnError) klog.InitFlags(local) diff --git a/cmd/controller-manager/app/server.go b/cmd/controller-manager/app/server.go index 0e113bab0..8fb527258 100644 --- a/cmd/controller-manager/app/server.go +++ b/cmd/controller-manager/app/server.go @@ -32,10 +32,19 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/cmd/controller-manager/app/options" "kubesphere.io/kubesphere/pkg/apis" + controllerconfig "kubesphere.io/kubesphere/pkg/apiserver/config" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" - "kubesphere.io/kubesphere/pkg/controller" - controllerconfig "kubesphere.io/kubesphere/pkg/server/config" - "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/controller/namespace" + "kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy" + "kubesphere.io/kubesphere/pkg/controller/user" + "kubesphere.io/kubesphere/pkg/controller/workspace" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/utils/term" "os" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -44,20 +53,24 @@ import ( func NewControllerManagerCommand() *cobra.Command { s := options.NewKubeSphereControllerManagerOptions() + conf, err := controllerconfig.TryLoadFromDisk() + if err == nil { + // make sure LeaderElection is not nil + s = &options.KubeSphereControllerManagerOptions{ + KubernetesOptions: conf.KubernetesOptions, + DevopsOptions: conf.DevopsOptions, + S3Options: conf.S3Options, + OpenPitrixOptions: conf.OpenPitrixOptions, + MultiClusterOptions: conf.MultiClusterOptions, + LeaderElection: s.LeaderElection, + LeaderElect: s.LeaderElect, + } + } cmd := &cobra.Command{ Use: "controller-manager", Long: `KubeSphere controller manager is a daemon that`, Run: func(cmd *cobra.Command, args []string) { - - err := controllerconfig.Load() - if err != nil { - klog.Fatal(err) - os.Exit(1) - } - - s = Complete(s) - if errs := s.Validate(); len(errs) != 0 { klog.Error(utilerrors.NewAggregate(errs)) os.Exit(1) @@ -85,52 +98,36 @@ func NewControllerManagerCommand() *cobra.Command { return cmd } -func Complete(s *options.KubeSphereControllerManagerOptions) *options.KubeSphereControllerManagerOptions { - conf := controllerconfig.Get() - - conf.Apply(&controllerconfig.Config{ - DevopsOptions: s.DevopsOptions, - KubernetesOptions: s.KubernetesOptions, - S3Options: s.S3Options, - OpenPitrixOptions: s.OpenPitrixOptions, - }) - - out := &options.KubeSphereControllerManagerOptions{ - KubernetesOptions: conf.KubernetesOptions, - DevopsOptions: conf.DevopsOptions, - S3Options: conf.S3Options, - OpenPitrixOptions: conf.OpenPitrixOptions, - LeaderElection: s.LeaderElection, - } - - return out -} - -func CreateClientSet(conf *controllerconfig.Config, stopCh <-chan struct{}) error { - csop := &client.ClientSetOptions{} - - csop.SetKubernetesOptions(conf.KubernetesOptions). - SetDevopsOptions(conf.DevopsOptions). - SetS3Options(conf.S3Options). - SetOpenPitrixOptions(conf.OpenPitrixOptions). - SetKubeSphereOptions(conf.KubeSphereOptions) - client.NewClientSetFactory(csop, stopCh) - - return nil -} - func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{}) error { - err := CreateClientSet(controllerconfig.Get(), stopCh) + kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions) if err != nil { - klog.Error(err) + klog.Errorf("Failed to create kubernetes clientset %v", err) return err } - config := client.ClientSets().K8s().Config() + openpitrixClient, err := openpitrix.NewClient(s.OpenPitrixOptions) + if err != nil { + klog.Errorf("Failed to create openpitrix client %v", err) + return err + } + + devopsClient, err := jenkins.NewDevopsClient(s.DevopsOptions) + if err != nil { + klog.Errorf("Failed to create devops client %v", err) + return err + } + + s3Client, err := s3.NewS3Client(s.S3Options) + if err != nil { + klog.Errorf("Failed to create s3 client %v", err) + return err + } + + informerFactory := informers.NewInformerFactories(kubernetesClient.Kubernetes(), kubernetesClient.KubeSphere(), kubernetesClient.Istio(), kubernetesClient.Application()) run := func(ctx context.Context) { klog.V(0).Info("setting up manager") - mgr, err := manager.New(config, manager.Options{}) + mgr, err := manager.New(kubernetesClient.Config(), manager.Options{}) if err != nil { klog.Fatalf("unable to set up overall controller manager: %v", err) } @@ -141,16 +138,34 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{}) } klog.V(0).Info("Setting up controllers") - if err := controller.AddToManager(mgr); err != nil { + err = workspace.Add(mgr) + if err != nil { + klog.Fatal("Unable to create workspace controller") + } + + err = namespace.Add(mgr, openpitrixClient) + if err != nil { + klog.Fatal("Unable to create namespace controller") + } + + if err := AddControllers(mgr, kubernetesClient, informerFactory, devopsClient, s3Client, stopCh); err != nil { klog.Fatalf("unable to register controllers to the manager: %v", err) } - if err := AddControllers(mgr, config, stopCh); err != nil { - klog.Fatalf("unable to register controllers to the manager: %v", err) - } + // Start cache data after all informer is registered + informerFactory.Start(stopCh) - klog.V(0).Info("Starting the Cmd.") - if err := mgr.Start(stopCh); err != nil { + // Setup webhooks + klog.Info("setting up webhook server") + hookServer := mgr.GetWebhookServer() + + klog.Info("registering webhooks to the webhook server") + hookServer.Register("/mutating-encrypt-password-iam-kubesphere-io-v1alpha2-user", &webhook.Admission{Handler: &user.PasswordCipher{Client: mgr.GetClient()}}) + hookServer.Register("/validate-email-iam-kubesphere-io-v1alpha2-user", &webhook.Admission{Handler: &user.EmailValidator{Client: mgr.GetClient()}}) + hookServer.Register("/validate-service-nsnp-kubesphere-io-v1alpha1-network", &webhook.Admission{Handler: &nsnetworkpolicy.ServiceValidator{}}) + + klog.V(0).Info("Starting the controllers.") + if err = mgr.Start(stopCh); err != nil { klog.Fatalf("unable to run the manager: %v", err) } @@ -165,6 +180,11 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{}) cancel() }() + if !s.LeaderElect { + run(ctx) + return nil + } + id, err := os.Hostname() if err != nil { return err @@ -179,8 +199,8 @@ func Run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{}) lock, err := resourcelock.New(resourcelock.LeasesResourceLock, "kubesphere-system", "ks-controller-manager", - client.ClientSets().K8s().Kubernetes().CoreV1(), - client.ClientSets().K8s().Kubernetes().CoordinationV1(), + kubernetesClient.Kubernetes().CoreV1(), + kubernetesClient.Kubernetes().CoordinationV1(), resourcelock.ResourceLockConfig{ Identity: id, EventRecorder: record.NewBroadcaster().NewRecorder(scheme.Scheme, v1.EventSource{ diff --git a/cmd/hypersphere/hypersphere.go b/cmd/hypersphere/hypersphere.go index 08d901b5b..45db4ca8a 100644 --- a/cmd/hypersphere/hypersphere.go +++ b/cmd/hypersphere/hypersphere.go @@ -8,9 +8,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" controllermanager "kubesphere.io/kubesphere/cmd/controller-manager/app" - ksapigateway "kubesphere.io/kubesphere/cmd/ks-apigateway/app" ksapiserver "kubesphere.io/kubesphere/cmd/ks-apiserver/app" - ksaiam "kubesphere.io/kubesphere/cmd/ks-iam/app" "os" ) @@ -46,14 +44,10 @@ func commandFor(basename string, defaultCommand *cobra.Command, commands []func( func NewHyperSphereCommand() (*cobra.Command, []func() *cobra.Command) { apiserver := func() *cobra.Command { return ksapiserver.NewAPIServerCommand() } controllermanager := func() *cobra.Command { return controllermanager.NewControllerManagerCommand() } - iam := func() *cobra.Command { return ksaiam.NewAPIServerCommand() } - apigateway := func() *cobra.Command { return ksapigateway.NewAPIGatewayCommand() } commandFns := []func() *cobra.Command{ apiserver, controllermanager, - iam, - apigateway, } cmd := &cobra.Command{ diff --git a/cmd/ks-apigateway/apiserver.go b/cmd/ks-apigateway/apiserver.go deleted file mode 100644 index 05446247e..000000000 --- a/cmd/ks-apigateway/apiserver.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 ( - "kubesphere.io/kubesphere/cmd/ks-apigateway/app" - "os" -) - -func main() { - - cmd := app.NewAPIGatewayCommand() - - if err := cmd.Execute(); err != nil { - os.Exit(1) - } -} diff --git a/cmd/ks-apigateway/app/server.go b/cmd/ks-apigateway/app/server.go deleted file mode 100644 index 359b849e4..000000000 --- a/cmd/ks-apigateway/app/server.go +++ /dev/null @@ -1,53 +0,0 @@ -package app - -import ( - "flag" - "github.com/mholt/caddy/caddy/caddymain" - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/spf13/cobra" - apiserverconfig "kubesphere.io/kubesphere/pkg/server/config" - "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/signals" - - "kubesphere.io/kubesphere/pkg/apigateway" -) - -func NewAPIGatewayCommand() *cobra.Command { - - cmd := &cobra.Command{ - Use: "ks-apigateway", - Long: `The KubeSphere API Gateway, which is responsible -for proxy request to the right backend. API Gateway also proxy -Kubernetes API Server for KubeSphere authorization purpose. -`, - RunE: func(cmd *cobra.Command, args []string) error { - - err := apiserverconfig.Load() - if err != nil { - return err - } - - apigateway.RegisterPlugins() - - return Run(signals.SetupSignalHandler()) - }, - } - - cmd.Flags().AddGoFlagSet(flag.CommandLine) - - return cmd -} - -func Run(stopCh <-chan struct{}) error { - - csop := &client.ClientSetOptions{} - csop.SetKubernetesOptions(apiserverconfig.Get().KubernetesOptions) - client.NewClientSetFactory(csop, stopCh) - - httpserver.RegisterDevDirective("authenticate", "jwt") - httpserver.RegisterDevDirective("authentication", "jwt") - httpserver.RegisterDevDirective("swagger", "jwt") - caddymain.Run() - - return nil -} diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index e8de49030..0712abd23 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -1,68 +1,85 @@ package options import ( + "crypto/tls" "flag" + "fmt" cliflag "k8s.io/component-base/cli/flag" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" + "kubesphere.io/kubesphere/pkg/informers" genericoptions "kubesphere.io/kubesphere/pkg/server/options" - "kubesphere.io/kubesphere/pkg/simple/client/devops" - esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" "kubesphere.io/kubesphere/pkg/simple/client/k8s" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" + "kubesphere.io/kubesphere/pkg/simple/client/ldap" + esclient "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/multicluster" + "kubesphere.io/kubesphere/pkg/simple/client/network" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/simple/client/prometheus" - "kubesphere.io/kubesphere/pkg/simple/client/s2is3" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + fakes3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake" "kubesphere.io/kubesphere/pkg/simple/client/servicemesh" "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + "net/http" "strings" ) type ServerRunOptions struct { + ConfigFile string GenericServerRunOptions *genericoptions.ServerRunOptions + *apiserverconfig.Config - KubernetesOptions *k8s.KubernetesOptions - DevopsOptions *devops.DevopsOptions - SonarQubeOptions *sonarqube.SonarQubeOptions - ServiceMeshOptions *servicemesh.ServiceMeshOptions - MySQLOptions *mysql.MySQLOptions - MonitoringOptions *prometheus.PrometheusOptions - S3Options *s2is3.S3Options - OpenPitrixOptions *openpitrix.OpenPitrixOptions - LoggingOptions *esclient.ElasticSearchOptions + // + DebugMode bool } func NewServerRunOptions() *ServerRunOptions { - - s := ServerRunOptions{ + s := &ServerRunOptions{ GenericServerRunOptions: genericoptions.NewServerRunOptions(), - KubernetesOptions: k8s.NewKubernetesOptions(), - DevopsOptions: devops.NewDevopsOptions(), - SonarQubeOptions: sonarqube.NewSonarQubeOptions(), - ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), - MySQLOptions: mysql.NewMySQLOptions(), - MonitoringOptions: prometheus.NewPrometheusOptions(), - S3Options: s2is3.NewS3Options(), - OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), - LoggingOptions: esclient.NewElasticSearchOptions(), + Config: &apiserverconfig.Config{ + KubernetesOptions: k8s.NewKubernetesOptions(), + DevopsOptions: jenkins.NewDevopsOptions(), + SonarQubeOptions: sonarqube.NewSonarQubeOptions(), + ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), + NetworkOptions: network.NewNetworkOptions(), + MonitoringOptions: prometheus.NewPrometheusOptions(), + S3Options: s3.NewS3Options(), + OpenPitrixOptions: openpitrix.NewOptions(), + LoggingOptions: esclient.NewElasticSearchOptions(), + LdapOptions: ldap.NewOptions(), + RedisOptions: cache.NewRedisOptions(), + AuthenticationOptions: authoptions.NewAuthenticateOptions(), + MultiClusterOptions: multicluster.NewOptions(), + }, } - return &s + return s } func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { + fs := fss.FlagSet("generic") + fs.BoolVar(&s.DebugMode, "debug", false, "Don't enable this if you don't know what it means.") + s.GenericServerRunOptions.AddFlags(fs, s.GenericServerRunOptions) + s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes"), s.KubernetesOptions) + s.AuthenticationOptions.AddFlags(fss.FlagSet("authentication"), s.AuthenticationOptions) + s.AuthorizationOptions.AddFlags(fss.FlagSet("authorization"), s.AuthorizationOptions) + s.DevopsOptions.AddFlags(fss.FlagSet("devops"), s.DevopsOptions) + s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube"), s.SonarQubeOptions) + s.LdapOptions.AddFlags(fss.FlagSet("ldap"), s.LdapOptions) + s.RedisOptions.AddFlags(fss.FlagSet("redis"), s.RedisOptions) + s.S3Options.AddFlags(fss.FlagSet("s3"), s.S3Options) + s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix"), s.OpenPitrixOptions) + s.NetworkOptions.AddFlags(fss.FlagSet("network"), s.NetworkOptions) + s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh"), s.ServiceMeshOptions) + s.MonitoringOptions.AddFlags(fss.FlagSet("monitoring"), s.MonitoringOptions) + s.LoggingOptions.AddFlags(fss.FlagSet("logging"), s.LoggingOptions) + s.MultiClusterOptions.AddFlags(fss.FlagSet("multicluster"), s.MultiClusterOptions) - s.GenericServerRunOptions.AddFlags(fss.FlagSet("generic")) - s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes")) - s.MySQLOptions.AddFlags(fss.FlagSet("mysql")) - s.DevopsOptions.AddFlags(fss.FlagSet("devops")) - s.SonarQubeOptions.AddFlags(fss.FlagSet("sonarqube")) - s.S3Options.AddFlags(fss.FlagSet("s3")) - s.OpenPitrixOptions.AddFlags(fss.FlagSet("openpitrix")) - s.ServiceMeshOptions.AddFlags(fss.FlagSet("servicemesh")) - s.MonitoringOptions.AddFlags(fss.FlagSet("monitoring")) - s.LoggingOptions.AddFlags(fss.FlagSet("logging")) - - fs := fss.FlagSet("klog") + fs = fss.FlagSet("klog") local := flag.NewFlagSet("klog", flag.ExitOnError) klog.InitFlags(local) local.VisitAll(func(fl *flag.Flag) { @@ -72,3 +89,106 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { return fss } + +const fakeInterface string = "FAKE" + +// NewAPIServer creates an APIServer instance using given options +func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIServer, error) { + apiServer := &apiserver.APIServer{ + Config: s.Config, + } + + kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions) + if err != nil { + return nil, err + } + apiServer.KubernetesClient = kubernetesClient + + informerFactory := informers.NewInformerFactories(kubernetesClient.Kubernetes(), kubernetesClient.KubeSphere(), kubernetesClient.Istio(), kubernetesClient.Application()) + apiServer.InformerFactory = informerFactory + + if s.MonitoringOptions.Endpoint != "" { + monitoringClient, err := prometheus.NewPrometheus(s.MonitoringOptions) + if err != nil { + return nil, err + } + apiServer.MonitoringClient = monitoringClient + } + + if s.LoggingOptions.Host != "" { + loggingClient, err := esclient.NewElasticsearch(s.LoggingOptions) + if err != nil { + return nil, err + } + apiServer.LoggingClient = loggingClient + } + + if s.S3Options.Endpoint != "" { + if s.S3Options.Endpoint == fakeInterface && s.DebugMode { + apiServer.S3Client = fakes3.NewFakeS3() + } else { + s3Client, err := s3.NewS3Client(s.S3Options) + if err != nil { + return nil, err + } + apiServer.S3Client = s3Client + } + } + + if s.DevopsOptions.Host != "" { + devopsClient, err := jenkins.NewDevopsClient(s.DevopsOptions) + if err != nil { + return nil, err + } + apiServer.DevopsClient = devopsClient + } + + if s.SonarQubeOptions.Host != "" { + sonarClient, err := sonarqube.NewSonarQubeClient(s.SonarQubeOptions) + if err != nil { + return nil, err + } + apiServer.SonarClient = sonarqube.NewSonar(sonarClient.SonarQube()) + } + + if s.LdapOptions.Host != "" { + if s.LdapOptions.Host == fakeInterface && s.DebugMode { + apiServer.LdapClient = ldap.NewSimpleLdap() + } else { + ldapClient, err := ldap.NewLdapClient(s.LdapOptions, stopCh) + if err != nil { + return nil, err + } + apiServer.LdapClient = ldapClient + } + } + + var cacheClient cache.Interface + if s.RedisOptions.Host != "" { + if s.RedisOptions.Host == fakeInterface && s.DebugMode { + apiServer.CacheClient = cache.NewSimpleCache() + } else { + cacheClient, err = cache.NewRedisClient(s.RedisOptions, stopCh) + if err != nil { + return nil, err + } + apiServer.CacheClient = cacheClient + } + } + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", s.GenericServerRunOptions.InsecurePort), + } + + if s.GenericServerRunOptions.SecurePort != 0 { + certificate, err := tls.LoadX509KeyPair(s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey) + if err != nil { + return nil, err + } + server.TLSConfig.Certificates = []tls.Certificate{certificate} + } + + apiServer.Server = server + + return apiServer, nil +} diff --git a/cmd/ks-apiserver/app/options/validation.go b/cmd/ks-apiserver/app/options/validation.go index 4075d4edd..b4627f1a8 100644 --- a/cmd/ks-apiserver/app/options/validation.go +++ b/cmd/ks-apiserver/app/options/validation.go @@ -5,15 +5,17 @@ package options func (s *ServerRunOptions) Validate() []error { var errors []error + errors = append(errors, s.GenericServerRunOptions.Validate()...) errors = append(errors, s.DevopsOptions.Validate()...) errors = append(errors, s.KubernetesOptions.Validate()...) - errors = append(errors, s.MySQLOptions.Validate()...) errors = append(errors, s.ServiceMeshOptions.Validate()...) errors = append(errors, s.MonitoringOptions.Validate()...) errors = append(errors, s.SonarQubeOptions.Validate()...) errors = append(errors, s.S3Options.Validate()...) errors = append(errors, s.OpenPitrixOptions.Validate()...) + errors = append(errors, s.NetworkOptions.Validate()...) errors = append(errors, s.LoggingOptions.Validate()...) + errors = append(errors, s.AuthorizationOptions.Validate()...) return errors } diff --git a/cmd/ks-apiserver/app/server.go b/cmd/ks-apiserver/app/server.go index 5a7f789f6..2f6d68f2b 100644 --- a/cmd/ks-apiserver/app/server.go +++ b/cmd/ks-apiserver/app/server.go @@ -21,43 +21,32 @@ import ( "fmt" kconfig "github.com/kiali/kiali/config" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/runtime/schema" utilerrors "k8s.io/apimachinery/pkg/util/errors" cliflag "k8s.io/component-base/cli/flag" - "k8s.io/klog" "kubesphere.io/kubesphere/cmd/ks-apiserver/app/options" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/servicemesh/tracing" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/kapis" - "kubesphere.io/kubesphere/pkg/server" - apiserverconfig "kubesphere.io/kubesphere/pkg/server/config" - "kubesphere.io/kubesphere/pkg/server/filter" - "kubesphere.io/kubesphere/pkg/simple/client" + apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" "kubesphere.io/kubesphere/pkg/utils/signals" "kubesphere.io/kubesphere/pkg/utils/term" - "net/http" ) func NewAPIServerCommand() *cobra.Command { s := options.NewServerRunOptions() + // Load configuration from file + conf, err := apiserverconfig.TryLoadFromDisk() + if err == nil { + s = &options.ServerRunOptions{ + GenericServerRunOptions: s.GenericServerRunOptions, + Config: conf, + } + } + cmd := &cobra.Command{ Use: "ks-apiserver", Long: `The KubeSphere API server validates and configures data for the api objects. The API Server services REST operations and provides the frontend to the cluster's shared state through which all other components interact.`, RunE: func(cmd *cobra.Command, args []string) error { - err := apiserverconfig.Load() - if err != nil { - return err - } - - err = Complete(s) - if err != nil { - return err - } - if errs := s.Validate(); len(errs) != 0 { return utilerrors.NewAggregate(errs) } @@ -68,7 +57,6 @@ cluster's shared state through which all other components interact.`, fs := cmd.Flags() namedFlagSets := s.Flags() - for _, f := range namedFlagSets.FlagSets { fs.AddFlagSet(f) } @@ -84,31 +72,26 @@ cluster's shared state through which all other components interact.`, func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error { - err := CreateClientSet(apiserverconfig.Get(), stopCh) - if err != nil { - return err - } - - err = WaitForResourceSync(stopCh) - if err != nil { - return err - } - initializeServicemeshConfig(s) - err = CreateAPIServer(s) + apiserver, err := s.NewAPIServer(stopCh) if err != nil { return err } - return nil + err = apiserver.PrepareRun() + if err != nil { + return nil + } + + return apiserver.Run(stopCh) } func initializeServicemeshConfig(s *options.ServerRunOptions) { // Initialize kiali config config := kconfig.NewConfig() - tracing.JaegerQueryUrl = s.ServiceMeshOptions.JaegerQueryHost + //tracing.JaegerQueryUrl = s.ServiceMeshOptions.JaegerQueryHost // Exclude system namespaces config.API.Namespaces.Exclude = []string{"istio-system", "kubesphere*", "kube*"} @@ -123,230 +106,3 @@ func initializeServicemeshConfig(s *options.ServerRunOptions) { kconfig.Set(config) } - -// -func CreateAPIServer(s *options.ServerRunOptions) error { - var err error - - container := runtime.Container - container.DoNotRecover(false) - container.Filter(filter.Logging) - container.RecoverHandler(server.LogStackOnRecover) - - kapis.InstallAPIs(container) - - // install config api - apiserverconfig.InstallAPI(container) - - if s.GenericServerRunOptions.InsecurePort != 0 { - err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container) - if err == nil { - klog.V(0).Infof("Server listening on insecure port %d.", s.GenericServerRunOptions.InsecurePort) - } - } - - if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 { - err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container) - if err == nil { - klog.V(0).Infof("Server listening on secure port %d.", s.GenericServerRunOptions.SecurePort) - } - } - - return err -} - -func CreateClientSet(conf *apiserverconfig.Config, stopCh <-chan struct{}) error { - csop := &client.ClientSetOptions{} - - csop.SetDevopsOptions(conf.DevopsOptions). - SetSonarQubeOptions(conf.SonarQubeOptions). - SetKubernetesOptions(conf.KubernetesOptions). - SetMySQLOptions(conf.MySQLOptions). - SetLdapOptions(conf.LdapOptions). - SetS3Options(conf.S3Options). - SetOpenPitrixOptions(conf.OpenPitrixOptions). - SetPrometheusOptions(conf.MonitoringOptions). - SetKubeSphereOptions(conf.KubeSphereOptions). - SetElasticSearchOptions(conf.LoggingOptions) - - client.NewClientSetFactory(csop, stopCh) - - return nil -} - -func WaitForResourceSync(stopCh <-chan struct{}) error { - klog.V(0).Info("Start cache objects") - - discoveryClient := client.ClientSets().K8s().Discovery() - apiResourcesList, err := discoveryClient.ServerResources() - if err != nil { - return err - } - - isResourceExists := func(resource schema.GroupVersionResource) bool { - for _, apiResource := range apiResourcesList { - if apiResource.GroupVersion == resource.GroupVersion().String() { - for _, rsc := range apiResource.APIResources { - if rsc.Name == resource.Resource { - return true - } - } - } - } - return false - } - - informerFactory := informers.SharedInformerFactory() - - // resources we have to create informer first - k8sGVRs := []schema.GroupVersionResource{ - {Group: "", Version: "v1", Resource: "namespaces"}, - {Group: "", Version: "v1", Resource: "nodes"}, - {Group: "", Version: "v1", Resource: "resourcequotas"}, - {Group: "", Version: "v1", Resource: "pods"}, - {Group: "", Version: "v1", Resource: "services"}, - {Group: "", Version: "v1", Resource: "persistentvolumeclaims"}, - {Group: "", Version: "v1", Resource: "secrets"}, - {Group: "", Version: "v1", Resource: "configmaps"}, - - {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}, - {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, - - {Group: "apps", Version: "v1", Resource: "deployments"}, - {Group: "apps", Version: "v1", Resource: "daemonsets"}, - {Group: "apps", Version: "v1", Resource: "replicasets"}, - {Group: "apps", Version: "v1", Resource: "statefulsets"}, - {Group: "apps", Version: "v1", Resource: "controllerrevisions"}, - - {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, - - {Group: "batch", Version: "v1", Resource: "jobs"}, - {Group: "batch", Version: "v1beta1", Resource: "cronjobs"}, - - {Group: "extensions", Version: "v1beta1", Resource: "ingresses"}, - - {Group: "autoscaling", Version: "v2beta2", Resource: "horizontalpodautoscalers"}, - } - - for _, gvr := range k8sGVRs { - if !isResourceExists(gvr) { - klog.Warningf("resource %s not exists in the cluster", gvr) - } else { - _, err := informerFactory.ForResource(gvr) - if err != nil { - klog.Errorf("cannot create informer for %s", gvr) - return err - } - } - } - - informerFactory.Start(stopCh) - informerFactory.WaitForCacheSync(stopCh) - - s2iInformerFactory := informers.S2iSharedInformerFactory() - - s2iGVRs := []schema.GroupVersionResource{ - {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuildertemplates"}, - {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2iruns"}, - {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"}, - } - - for _, gvr := range s2iGVRs { - if !isResourceExists(gvr) { - klog.Warningf("resource %s not exists in the cluster", gvr) - } else { - _, err := s2iInformerFactory.ForResource(gvr) - if err != nil { - return err - } - } - } - - s2iInformerFactory.Start(stopCh) - s2iInformerFactory.WaitForCacheSync(stopCh) - - ksInformerFactory := informers.KsSharedInformerFactory() - - ksGVRs := []schema.GroupVersionResource{ - {Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}, - {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibinaries"}, - - {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "strategies"}, - {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "servicepolicies"}, - } - - for _, gvr := range ksGVRs { - if !isResourceExists(gvr) { - klog.Warningf("resource %s not exists in the cluster", gvr) - } else { - _, err := ksInformerFactory.ForResource(gvr) - if err != nil { - return err - } - } - } - - ksInformerFactory.Start(stopCh) - ksInformerFactory.WaitForCacheSync(stopCh) - - appInformerFactory := informers.AppSharedInformerFactory() - - appGVRs := []schema.GroupVersionResource{ - {Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}, - } - - for _, gvr := range appGVRs { - if !isResourceExists(gvr) { - klog.Warningf("resource %s not exists in the cluster", gvr) - } else { - _, err := appInformerFactory.ForResource(gvr) - if err != nil { - return err - } - } - } - - appInformerFactory.Start(stopCh) - appInformerFactory.WaitForCacheSync(stopCh) - - klog.V(0).Info("Finished caching objects") - - return nil - -} - -// apply server run options to configuration -func Complete(s *options.ServerRunOptions) error { - - // loading configuration file - conf := apiserverconfig.Get() - - conf.Apply(&apiserverconfig.Config{ - MySQLOptions: s.MySQLOptions, - DevopsOptions: s.DevopsOptions, - SonarQubeOptions: s.SonarQubeOptions, - KubernetesOptions: s.KubernetesOptions, - ServiceMeshOptions: s.ServiceMeshOptions, - MonitoringOptions: s.MonitoringOptions, - S3Options: s.S3Options, - OpenPitrixOptions: s.OpenPitrixOptions, - LoggingOptions: s.LoggingOptions, - }) - - *s = options.ServerRunOptions{ - GenericServerRunOptions: s.GenericServerRunOptions, - KubernetesOptions: conf.KubernetesOptions, - DevopsOptions: conf.DevopsOptions, - SonarQubeOptions: conf.SonarQubeOptions, - ServiceMeshOptions: conf.ServiceMeshOptions, - MySQLOptions: conf.MySQLOptions, - MonitoringOptions: conf.MonitoringOptions, - S3Options: conf.S3Options, - OpenPitrixOptions: conf.OpenPitrixOptions, - LoggingOptions: conf.LoggingOptions, - } - - return nil -} diff --git a/cmd/ks-iam/apiserver.go b/cmd/ks-iam/apiserver.go deleted file mode 100644 index 6ca2051f7..000000000 --- a/cmd/ks-iam/apiserver.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 ( - "kubesphere.io/kubesphere/cmd/ks-iam/app" - "log" -) - -func main() { - - cmd := app.NewAPIServerCommand() - - if err := cmd.Execute(); err != nil { - log.Fatalln(err) - } -} diff --git a/cmd/ks-iam/app/options/options.go b/cmd/ks-iam/app/options/options.go deleted file mode 100644 index 07a7529dd..000000000 --- a/cmd/ks-iam/app/options/options.go +++ /dev/null @@ -1,86 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 options - -import ( - "flag" - cliflag "k8s.io/component-base/cli/flag" - "k8s.io/klog" - genericoptions "kubesphere.io/kubesphere/pkg/server/options" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" - "kubesphere.io/kubesphere/pkg/simple/client/ldap" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "strings" - "time" -) - -type ServerRunOptions struct { - GenericServerRunOptions *genericoptions.ServerRunOptions - KubernetesOptions *k8s.KubernetesOptions - LdapOptions *ldap.LdapOptions - RedisOptions *redis.RedisOptions - MySQLOptions *mysql.MySQLOptions - AdminEmail string - AdminPassword string - TokenIdleTimeout time.Duration - JWTSecret string - AuthRateLimit string - EnableMultiLogin bool - GenerateKubeConfig bool -} - -func NewServerRunOptions() *ServerRunOptions { - s := &ServerRunOptions{ - GenericServerRunOptions: genericoptions.NewServerRunOptions(), - KubernetesOptions: k8s.NewKubernetesOptions(), - LdapOptions: ldap.NewLdapOptions(), - MySQLOptions: mysql.NewMySQLOptions(), - RedisOptions: redis.NewRedisOptions(), - } - return s -} - -func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) { - - fs := fss.FlagSet("generic") - - s.GenericServerRunOptions.AddFlags(fs) - fs.StringVar(&s.AdminEmail, "admin-email", "admin@kubesphere.io", "default administrator's email") - fs.StringVar(&s.AdminPassword, "admin-password", "passw0rd", "default administrator's password") - fs.DurationVar(&s.TokenIdleTimeout, "token-idle-timeout", 30*time.Minute, "tokens that are idle beyond that time will expire,0s means the token has no expiration time. valid time units are \"ns\",\"us\",\"ms\",\"s\",\"m\",\"h\"") - fs.StringVar(&s.JWTSecret, "jwt-secret", "", "jwt secret") - fs.StringVar(&s.AuthRateLimit, "auth-rate-limit", "5/30m", "specifies the maximum number of authentication attempts permitted and time interval,valid time units are \"s\",\"m\",\"h\"") - fs.BoolVar(&s.EnableMultiLogin, "enable-multi-login", false, "allow one account to have multiple sessions") - fs.BoolVar(&s.GenerateKubeConfig, "generate-kubeconfig", true, "generate kubeconfig for new users, kubeconfig is required in devops pipeline, set to false if you don't need devops.") - - s.KubernetesOptions.AddFlags(fss.FlagSet("kubernetes")) - s.LdapOptions.AddFlags(fss.FlagSet("ldap")) - s.RedisOptions.AddFlags(fss.FlagSet("redis")) - s.MySQLOptions.AddFlags(fss.FlagSet("mysql")) - - kfs := fss.FlagSet("klog") - local := flag.NewFlagSet("klog", flag.ExitOnError) - klog.InitFlags(local) - local.VisitAll(func(fl *flag.Flag) { - fl.Name = strings.Replace(fl.Name, "_", "-", -1) - kfs.AddGoFlag(fl) - }) - - return fss -} diff --git a/cmd/ks-iam/app/options/validation.go b/cmd/ks-iam/app/options/validation.go deleted file mode 100644 index 6f5e7e89d..000000000 --- a/cmd/ks-iam/app/options/validation.go +++ /dev/null @@ -1,11 +0,0 @@ -package options - -func (s *ServerRunOptions) Validate() []error { - errs := []error{} - - errs = append(errs, s.KubernetesOptions.Validate()...) - errs = append(errs, s.GenericServerRunOptions.Validate()...) - errs = append(errs, s.LdapOptions.Validate()...) - - return errs -} diff --git a/cmd/ks-iam/app/server.go b/cmd/ks-iam/app/server.go deleted file mode 100644 index 89db7d97b..000000000 --- a/cmd/ks-iam/app/server.go +++ /dev/null @@ -1,161 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 app - -import ( - "fmt" - "github.com/spf13/cobra" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - cliflag "k8s.io/component-base/cli/flag" - "k8s.io/klog" - "kubesphere.io/kubesphere/cmd/ks-iam/app/options" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/kapis" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/server" - apiserverconfig "kubesphere.io/kubesphere/pkg/server/config" - "kubesphere.io/kubesphere/pkg/server/filter" - "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/jwtutil" - "kubesphere.io/kubesphere/pkg/utils/signals" - "kubesphere.io/kubesphere/pkg/utils/term" - "net/http" -) - -func NewAPIServerCommand() *cobra.Command { - s := options.NewServerRunOptions() - - cmd := &cobra.Command{ - Use: "ks-iam", - Long: `The KubeSphere account server validates and configures data -for the api objects. The API Server services REST operations and provides the frontend to the -cluster's shared state through which all other components interact.`, - RunE: func(cmd *cobra.Command, args []string) error { - - err := apiserverconfig.Load() - if err != nil { - return err - } - - err = Complete(s) - if err != nil { - return err - } - - if errs := s.Validate(); len(errs) != 0 { - return utilerrors.NewAggregate(errs) - } - - return Run(s, signals.SetupSignalHandler()) - }, - } - - fs := cmd.Flags() - namedFlagSets := s.Flags() - - for _, f := range namedFlagSets.FlagSets { - fs.AddFlagSet(f) - } - - usageFmt := "Usage:\n %s\n" - cols, _, _ := term.TerminalSize(cmd.OutOrStdout()) - cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { - fmt.Fprintf(cmd.OutOrStdout(), "%s\n\n"+usageFmt, cmd.Long, cmd.UseLine()) - cliflag.PrintSections(cmd.OutOrStdout(), namedFlagSets, cols) - }) - - return cmd -} - -func Run(s *options.ServerRunOptions, stopChan <-chan struct{}) error { - csop := client.NewClientSetOptions() - csop.SetKubernetesOptions(s.KubernetesOptions). - SetLdapOptions(s.LdapOptions). - SetRedisOptions(s.RedisOptions). - SetMySQLOptions(s.MySQLOptions) - - client.NewClientSetFactory(csop, stopChan) - - waitForResourceSync(stopChan) - - err := iam.Init(s.AdminEmail, s.AdminPassword, s.AuthRateLimit, s.TokenIdleTimeout, s.EnableMultiLogin, s.GenerateKubeConfig) - - jwtutil.Setup(s.JWTSecret) - - if err != nil { - return err - } - - container := runtime.Container - container.Filter(filter.Logging) - container.DoNotRecover(false) - container.RecoverHandler(server.LogStackOnRecover) - - kapis.InstallAuthorizationAPIs(container) - - if s.GenericServerRunOptions.InsecurePort != 0 { - klog.Infof("Server listening on %s:%d ", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort) - err = http.ListenAndServe(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.InsecurePort), container) - } - - if s.GenericServerRunOptions.SecurePort != 0 && len(s.GenericServerRunOptions.TlsCertFile) > 0 && len(s.GenericServerRunOptions.TlsPrivateKey) > 0 { - klog.Infof("Server listening on %s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort) - err = http.ListenAndServeTLS(fmt.Sprintf("%s:%d", s.GenericServerRunOptions.BindAddress, s.GenericServerRunOptions.SecurePort), s.GenericServerRunOptions.TlsCertFile, s.GenericServerRunOptions.TlsPrivateKey, container) - } - - return err -} - -func Complete(s *options.ServerRunOptions) error { - conf := apiserverconfig.Get() - - conf.Apply(&apiserverconfig.Config{ - KubernetesOptions: s.KubernetesOptions, - LdapOptions: s.LdapOptions, - RedisOptions: s.RedisOptions, - MySQLOptions: s.MySQLOptions, - }) - - s.KubernetesOptions = conf.KubernetesOptions - s.LdapOptions = conf.LdapOptions - s.RedisOptions = conf.RedisOptions - s.MySQLOptions = conf.MySQLOptions - - return nil -} - -func waitForResourceSync(stopCh <-chan struct{}) { - - informerFactory := informers.SharedInformerFactory() - informerFactory.Rbac().V1().Roles().Lister() - informerFactory.Rbac().V1().RoleBindings().Lister() - informerFactory.Rbac().V1().ClusterRoles().Lister() - informerFactory.Rbac().V1().ClusterRoleBindings().Lister() - - informerFactory.Core().V1().Namespaces().Lister() - - informerFactory.Start(stopCh) - informerFactory.WaitForCacheSync(stopCh) - - ksInformerFactory := informers.KsSharedInformerFactory() - ksInformerFactory.Tenant().V1alpha1().Workspaces().Lister() - - ksInformerFactory.Start(stopCh) - ksInformerFactory.WaitForCacheSync(stopCh) -} diff --git a/cmd/ks-network/main.go b/cmd/ks-network/main.go deleted file mode 100644 index 7b3b6683d..000000000 --- a/cmd/ks-network/main.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "flag" - - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/controller/network/runoption" -) - -var opt runoption.RunOption - -func init() { - flag.StringVar(&opt.ProviderName, "np-provider", "calico", "specify the network policy provider, k8s or calico") - flag.BoolVar(&opt.AllowInsecureEtcd, "allow-insecure-etcd", false, "specify allow connect to etcd using insecure http") - flag.StringVar(&opt.DataStoreType, "datastore-type", "k8s", "specify the datastore type of calico") - //TODO add more flags -} - -func main() { - klog.InitFlags(nil) - flag.Set("logtostderr", "true") - flag.Parse() - klog.V(1).Info("Preparing kubernetes client") - klog.Fatal(opt.Run()) -} diff --git a/config/crd/bases/cluster.kubesphere.io_agents.yaml b/config/crd/bases/cluster.kubesphere.io_agents.yaml new file mode 100644 index 000000000..abdd6c024 --- /dev/null +++ b/config/crd/bases/cluster.kubesphere.io_agents.yaml @@ -0,0 +1,114 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: agents.cluster.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.Paused + name: Paused + type: bool + group: cluster.kubesphere.io + names: + kind: Agent + listKind: AgentList + plural: agents + singular: agent + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: Agent is the Schema for the agents API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AgentSpec defines the desired state of Agent + properties: + kubernetesAPIServerPort: + description: KubeAPIServerPort is the port which listens for forwarding + kube-apiserver traffic + type: integer + kubesphereAPIServerPort: + description: KubeSphereAPIServerPort is the port which listens for forwarding + kubesphere apigateway traffic + type: integer + paused: + description: Indicates that the agent is paused. + type: boolean + proxy: + description: Proxy address + type: string + token: + description: Token used by agents to connect to proxy. + type: string + type: object + status: + description: AgentStatus defines the observed state of Agent + properties: + conditions: + description: Represents the latest available observations of a agent's + current state. + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of AgentCondition + type: string + required: + - status + type: object + type: array + kubeconfig: + description: Issued new kubeconfig by proxy server + format: byte + type: string + ping: + description: Represents the connection quality, in ms + format: int64 + type: integer + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/cluster.kubesphere.io_clusters.yaml b/config/crd/bases/cluster.kubesphere.io_clusters.yaml new file mode 100644 index 000000000..b1c718e24 --- /dev/null +++ b/config/crd/bases/cluster.kubesphere.io_clusters.yaml @@ -0,0 +1,168 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: clusters.cluster.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.joinFederation + name: Federated + type: boolean + - JSONPath: .spec.provider + name: Provider + type: string + - JSONPath: .spec.enable + name: Active + type: boolean + - JSONPath: .status.kubernetesVersion + name: Version + type: string + group: cluster.kubesphere.io + names: + kind: Cluster + listKind: ClusterList + plural: clusters + singular: cluster + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: Cluster is the schema for the clusters API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + connection: + description: Connection holds info to connect to the member cluster + properties: + kubeconfig: + description: KubeConfig content used to connect to cluster api server + Should provide this field explicitly if connection type is direct. + Will be populated by ks-proxy if connection type is proxy. + format: byte + type: string + kubernetesAPIEndpoint: + description: Kubernetes API Server endpoint. This can be a hostname, + hostname:port, IP or IP:port. Should provide this field explicitly + if connection type is direct. Will be populated by ks-apiserver + if connection type is proxy. + type: string + kubernetesAPIServerPort: + description: KubeAPIServerPort is the port which listens for forwarding + kube-apiserver traffic Only applicable when connection type is + proxy. + type: integer + kubesphereAPIEndpoint: + description: KubeSphere API Server endpoint. This can be a hostname, + hostname:port, IP or IP:port. Should provide this field explicitly + if connection type is direct. Will be populated by ks-apiserver + if connection type is proxy. + type: string + kubesphereAPIServerPort: + description: KubeSphereAPIServerPort is the port which listens for + forwarding kubesphere apigateway traffic Only applicable when + connection type is proxy. + type: integer + token: + description: Token used by agents of member cluster to connect to + host cluster proxy. This field is populated by apiserver only + if connection type is proxy. + type: string + type: + description: type defines how host cluster will connect to host + cluster ConnectionTypeDirect means direct connection, this requires kubeconfig + and kubesphere apiserver endpoint provided ConnectionTypeProxy + means using kubesphere proxy, no kubeconfig or kubesphere apiserver + endpoint required + type: string + type: object + enable: + description: Desired state of the cluster + type: boolean + joinFederation: + description: Join cluster as a kubefed cluster + type: boolean + provider: + description: Provider of the cluster, this field is just for description + type: string + type: object + status: + properties: + conditions: + description: Represents the latest available observations of a cluster's + current state. + items: + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: The last time this condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of the condition + type: string + required: + - status + - type + type: object + type: array + kubernetesVersion: + description: GitVersion of the kubernetes cluster, this field is populated + by cluster controller + type: string + nodeCount: + description: Count of the kubernetes cluster nodes This field may not + reflect the instant status of the cluster. + type: integer + region: + description: Region is the name of the region in which all of the nodes + in the cluster exist. e.g. 'us-east1'. + type: string + zones: + description: Zones are the names of availability zones in which the + nodes of the cluster exist, e.g. 'us-east1-a'. + items: + type: string + type: array + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_devopsprojects.yaml b/config/crd/bases/devops.kubesphere.io_devopsprojects.yaml new file mode 100644 index 000000000..e0e6e0cd2 --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_devopsprojects.yaml @@ -0,0 +1,59 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: devopsprojects.devops.kubesphere.io +spec: + group: devops.kubesphere.io + names: + categories: + - devops + kind: DevOpsProject + listKind: DevOpsProjectList + plural: devopsprojects + singular: devopsproject + scope: Cluster + validation: + openAPIV3Schema: + description: DevOpsProject is the Schema for the devopsprojects API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DevOpsProjectSpec defines the desired state of DevOpsProject + type: object + status: + description: DevOpsProjectStatus defines the observed state of DevOpsProject + properties: + adminNamespace: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + version: v1alpha3 + versions: + - name: v1alpha3 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_pipelines.yaml b/config/crd/bases/devops.kubesphere.io_pipelines.yaml new file mode 100644 index 000000000..1d3df2fa6 --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_pipelines.yaml @@ -0,0 +1,260 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: pipelines.devops.kubesphere.io +spec: + group: devops.kubesphere.io + names: + kind: Pipeline + listKind: PipelineList + plural: pipelines + singular: pipeline + scope: Namespaced + validation: + openAPIV3Schema: + description: Pipeline is the Schema for the pipelines API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PipelineSpec defines the desired state of Pipeline + properties: + multi_branch_pipeline: + properties: + bitbucket_server_source: + properties: + api_uri: + type: string + credential_id: + type: string + discover_branches: + type: integer + discover_pr_from_forks: + properties: + strategy: + type: integer + trust: + type: integer + type: object + discover_pr_from_origin: + type: integer + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + owner: + type: string + regex_filter: + type: string + repo: + type: string + scm_id: + type: string + type: object + descriptio: + type: string + discarder: + properties: + days_to_keep: + type: string + num_to_keep: + type: string + type: object + git_source: + properties: + credential_id: + type: string + discover_branches: + type: boolean + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + regex_filter: + type: string + scm_id: + type: string + url: + type: string + type: object + github_source: + properties: + api_uri: + type: string + credential_id: + type: string + discover_branches: + type: integer + discover_pr_from_forks: + properties: + strategy: + type: integer + trust: + type: integer + type: object + discover_pr_from_origin: + type: integer + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + owner: + type: string + regex_filter: + type: string + repo: + type: string + scm_id: + type: string + type: object + multibranch_job_trigger: + properties: + create_action_job_to_trigger: + type: string + delete_action_job_to_trigger: + type: string + type: object + name: + type: string + script_path: + type: string + single_svn_source: + properties: + credential_id: + type: string + remote: + type: string + scm_id: + type: string + type: object + source_type: + type: string + svn_source: + properties: + credential_id: + type: string + excludes: + type: string + includes: + type: string + remote: + type: string + scm_id: + type: string + type: object + timer_trigger: + properties: + cron: + description: user in no scm job + type: string + interval: + description: use in multi-branch job + type: string + type: object + required: + - name + - script_path + - source_type + type: object + pipeline: + properties: + descriptio: + type: string + disable_concurrent: + type: boolean + discarder: + properties: + days_to_keep: + type: string + num_to_keep: + type: string + type: object + jenkinsfile: + type: string + name: + type: string + parameters: + items: + properties: + default_value: + type: string + description: + type: string + name: + type: string + type: + type: string + required: + - name + - type + type: object + type: array + remote_trigger: + properties: + token: + type: string + type: object + timer_trigger: + properties: + cron: + description: user in no scm job + type: string + interval: + description: use in multi-branch job + type: string + type: object + required: + - name + type: object + type: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + type: string + required: + - type + type: object + status: + description: PipelineStatus defines the observed state of Pipeline + type: object + type: object + version: v1alpha3 + versions: + - name: v1alpha3 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_s2ibinaries.yaml b/config/crd/bases/devops.kubesphere.io_s2ibinaries.yaml new file mode 100644 index 000000000..01658d5f6 --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_s2ibinaries.yaml @@ -0,0 +1,86 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: s2ibinaries.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.fileName + name: FileName + type: string + - JSONPath: .spec.md5 + name: MD5 + type: string + - JSONPath: .spec.size + name: Size + type: string + - JSONPath: .status.phase + name: Phase + type: string + group: devops.kubesphere.io + names: + kind: S2iBinary + listKind: S2iBinaryList + plural: s2ibinaries + singular: s2ibinary + scope: Namespaced + subresources: {} + validation: + openAPIV3Schema: + description: S2iBinary is the Schema for the s2ibinaries API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBinarySpec defines the desired state of S2iBinary + properties: + downloadURL: + description: DownloadURL in KubeSphere + type: string + fileName: + description: FileName is filename of binary + type: string + md5: + description: MD5 is Binary's MD5 Hash + type: string + size: + description: Size is the file size of file + type: string + uploadTimeStamp: + description: UploadTime is last upload time + format: date-time + type: string + type: object + status: + description: S2iBinaryStatus defines the observed state of S2iBinary + properties: + phase: + description: Phase is status of S2iBinary . Possible value is "Ready","UnableToDownload" + type: string + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_s2ibuilders.yaml b/config/crd/bases/devops.kubesphere.io_s2ibuilders.yaml new file mode 100644 index 000000000..54df7a1cb --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_s2ibuilders.yaml @@ -0,0 +1,578 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: s2ibuilders.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .status.runCount + name: RunCount + type: integer + - JSONPath: .status.lastRunState + name: LastRunState + type: string + - JSONPath: .status.lastRunName + name: LastRunName + type: string + - JSONPath: .status.lastRunStartTime + name: LastRunStartTime + type: date + group: devops.kubesphere.io + names: + kind: S2iBuilder + listKind: S2iBuilderList + plural: s2ibuilders + shortNames: + - s2ib + singular: s2ibuilder + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: S2iBuilder is the Schema for the s2ibuilders API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBuilderSpec defines the desired state of S2iBuilder + properties: + config: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + properties: + addHost: + description: AddHost Add a line to /etc/hosts for test purpose or + private use in LAN. Its format is host:IP,muliple hosts can be + added by using multiple --add-host + items: + type: string + type: array + asDockerfile: + description: AsDockerfile indicates the path where the Dockerfile + should be written instead of building a new image. + type: string + assembleUser: + description: AssembleUser specifies the user to run the assemble + script in container + type: string + blockOnBuild: + description: BlockOnBuild prevents s2i from performing a docker + build operation if one is necessary to execute ONBUILD commands, + or to layer source code into the container for images that don't + have a tar binary available, if the image contains ONBUILD commands + that would be executed. + type: boolean + branchExpression: + description: Regular expressions, ignoring names that do not match + the provided regular expression + type: string + buildVolumes: + description: BuildVolumes specifies a list of volumes to mount to + container running the build. + items: + type: string + type: array + builderBaseImageVersion: + description: BuilderBaseImageVersion provides optional version information + about the builder base image. + type: string + builderImage: + description: BuilderImage describes which image is used for building + the result images. + type: string + builderImageVersion: + description: BuilderImageVersion provides optional version information + about the builder image. + type: string + builderPullPolicy: + description: BuilderPullPolicy specifies when to pull the builder + image + type: string + callbackUrl: + description: CallbackURL is a URL which is called upon successful + build to inform about that fact. + type: string + cgroupLimits: + description: CGroupLimits describes the cgroups limits that will + be applied to any containers run by s2i. + properties: + cpuPeriod: + format: int64 + type: integer + cpuQuota: + format: int64 + type: integer + cpuShares: + format: int64 + type: integer + memoryLimitBytes: + format: int64 + type: integer + memorySwap: + format: int64 + type: integer + parent: + type: string + required: + - cpuPeriod + - cpuQuota + - cpuShares + - memoryLimitBytes + - memorySwap + - parent + type: object + contextDir: + description: Specify a relative directory inside the application + repository that should be used as a root directory for the application. + type: string + description: + description: Description is a result image description label. The + default is no description. + type: string + destination: + description: Destination specifies a location where the untar operation + will place its artifacts. + type: string + displayName: + description: DisplayName is a result image display-name label. This + defaults to the output image name. + type: string + dockerConfig: + description: DockerConfig describes how to access host docker daemon. + properties: + caFile: + description: CAFile is the certificate authority file path for + a TLS connection + type: string + certFile: + description: CertFile is the certificate file path for a TLS + connection + type: string + endPoint: + description: Endpoint is the docker network endpoint or socket + type: string + keyFile: + description: KeyFile is the key file path for a TLS connection + type: string + tlsVerify: + description: TLSVerify indicates if TLS peer must be verified + type: boolean + useTLS: + description: UseTLS indicates if TLS must be used + type: boolean + required: + - caFile + - certFile + - endPoint + - keyFile + - tlsVerify + - useTLS + type: object + dockerNetworkMode: + description: DockerNetworkMode is used to set the docker network + setting to --net=container: when the builder is invoked from + a container. + type: string + dropCapabilities: + description: DropCapabilities contains a list of capabilities to + drop when executing containers + items: + type: string + type: array + environment: + description: Environment is a map of environment variables to be + passed to the image. + items: + description: EnvironmentSpec specifies a single environment variable. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + excludeRegExp: + description: ExcludeRegExp contains a string representation of the + regular expression desired for deciding which files to exclude + from the tar stream + type: string + export: + description: Export Push the result image to specify image registry + in tag + type: boolean + gitSecretRef: + description: GitSecretRef is the BasicAuth Secret of Git Clone + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + hasOnBuild: + description: HasOnBuild will be set to true if the builder image + contains ONBUILD instructions + type: boolean + imageName: + description: ImageName Contains the registry address and reponame, + tag should set by field tag alone + type: string + imageScriptsUrl: + description: ImageScriptsURL is the default location to find the + assemble/run scripts for a builder image. This url can be a reference + within the builder image if the scheme is specified as image:// + type: string + imageWorkDir: + description: ImageWorkDir is the default working directory for the + builder image. + type: string + incremental: + description: Incremental describes whether to try to perform incremental + build. + type: boolean + incrementalAuthentication: + description: IncrementalAuthentication holds the authentication + information for pulling the previous image from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + incrementalFromTag: + description: IncrementalFromTag sets an alternative image tag to + look for existing artifacts. Tag is used by default if this is + not set. + type: string + injections: + description: Injections specifies a list source/destination folders + that are injected to the container that runs assemble. All files + we inject will be truncated after the assemble script finishes. + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume to + - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + isBinaryURL: + description: IsBinaryURL explain the type of SourceURL. If it is + IsBinaryURL, it will download the file directly without using + git. + type: boolean + keepSymlinks: + description: KeepSymlinks indicates to copy symlinks as symlinks. + Default behavior is to follow symlinks and copy files by content. + type: boolean + labelNamespace: + description: LabelNamespace provides the namespace under which the + labels will be generated. + type: string + labels: + additionalProperties: + type: string + description: Labels specify labels and their values to be applied + to the resulting image. Label keys must have non-zero length. + The labels defined here override generated labels in case they + have the same name. + type: object + layeredBuild: + description: LayeredBuild describes if this is build which layered + scripts and sources on top of BuilderImage. + type: boolean + nodeAffinityKey: + description: The key of Node Affinity. + type: string + nodeAffinityValues: + description: The values of Node Affinity. + items: + type: string + type: array + outputBuildResult: + description: Whether output build result to status. + type: boolean + outputImageName: + description: OutputImageName is a result image name without tag, + default is latest. tag will append to ImageName in the end + type: string + preserveWorkingDir: + description: PreserveWorkingDir describes if working directory should + be left after processing. + type: boolean + previousImagePullPolicy: + description: PreviousImagePullPolicy specifies when to pull the + previously build image when doing incremental build + type: string + pullAuthentication: + description: PullAuthentication holds the authentication information + for pulling the Docker images from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + pushAuthentication: + description: PullAuthentication holds the authentication information + for pulling the Docker images from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + removePreviousImage: + description: RemovePreviousImage describes if previous image should + be removed after successful build. This applies only to incremental + builds. + type: boolean + revisionId: + description: The RevisionId is a branch name or a SHA-1 hash of + every important thing about the commit + type: string + runImage: + description: RunImage will trigger a "docker run ..." invocation + of the produced image so the user can see if it operates as he + would expect + type: boolean + runtimeArtifacts: + description: RuntimeArtifacts specifies a list of source/destination + pairs that will be copied from builder to a runtime image. Source + can be a file or directory. Destination must be a directory. Regardless + whether it is an absolute or relative path, it will be placed + into image's WORKDIR. Destination also can be empty or equals + to ".", in this case it just refers to a root of WORKDIR. In case + it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files + label on a RuntimeImage. + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume to + - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + runtimeAuthentication: + description: RuntimeAuthentication holds the authentication information + for pulling the runtime Docker images from private repositories. + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + runtimeImage: + description: RuntimeImage specifies the image that will be a base + for resulting image and will be used for running an application. + By default, BuilderImage is used for building and running, but + the latter may be overridden. + type: string + runtimeImagePullPolicy: + description: RuntimeImagePullPolicy specifies when to pull a runtime + image. + type: string + scriptDownloadProxyConfig: + description: ScriptDownloadProxyConfig optionally specifies the + http and https proxy to use when downloading scripts + properties: + httpProxy: + type: string + httpsProxy: + type: string + type: object + scriptsUrl: + description: ScriptsURL is a URL describing where to fetch the S2I + scripts from during build process. This url can be a reference + within the builder image if the scheme is specified as image:// + type: string + secretCode: + description: SecretCode + type: string + securityOpt: + description: SecurityOpt are passed as options to the docker containers + launched by s2i. + items: + type: string + type: array + sourceUrl: + description: SourceURL is url of the codes such as https://github.com/a/b.git + type: string + tag: + description: Tag is a result image tag name. + type: string + taintKey: + description: The name of taint. + type: string + usage: + description: Usage allows for properly shortcircuiting s2i logic + when `s2i usage` is invoked + type: boolean + workingDir: + description: WorkingDir describes temporary directory used for downloading + sources, scripts and tar operations. + type: string + workingSourceDir: + description: WorkingSourceDir describes the subdirectory off of + WorkingDir set up during the repo download that is later used + as the root for ignore processing + type: string + required: + - imageName + - sourceUrl + type: object + fromTemplate: + description: FromTemplate define some inputs from user + properties: + builderImage: + description: BaseImage specify which version of this template to + use + type: string + name: + description: Name specify a template to use, so many fields in Config + can left empty + type: string + parameters: + description: Parameters must use with `template`, fill some parameters + which template will use + items: + properties: + defaultValue: + type: string + description: + type: string + key: + type: string + optValues: + items: + type: string + type: array + required: + type: boolean + type: + type: string + value: + type: string + type: object + type: array + type: object + type: object + status: + description: S2iBuilderStatus defines the observed state of S2iBuilder + properties: + lastRunName: + description: LastRunState return the name of the newest run of this + builder + type: string + lastRunStartTime: + description: LastRunStartTime return the startTime of the newest run + of this builder + format: date-time + type: string + lastRunState: + description: LastRunState return the state of the newest run of this + builder + type: string + runCount: + description: RunCount represent the sum of s2irun of this builder + type: integer + required: + - runCount + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_s2ibuildertemplates.yaml b/config/crd/bases/devops.kubesphere.io_s2ibuildertemplates.yaml new file mode 100644 index 000000000..50f0a4fed --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_s2ibuildertemplates.yaml @@ -0,0 +1,141 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: s2ibuildertemplates.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.codeFramework + name: Framework + type: string + - JSONPath: .spec.defaultBaseImage + name: DefaultBaseImage + type: string + - JSONPath: .spec.version + name: Version + type: string + group: devops.kubesphere.io + names: + categories: + - devops + kind: S2iBuilderTemplate + listKind: S2iBuilderTemplateList + plural: s2ibuildertemplates + shortNames: + - s2ibt + singular: s2ibuildertemplate + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: S2iBuilderTemplate is the Schema for the s2ibuildertemplates API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate + properties: + codeFramework: + description: CodeFramework means which language this template is designed + for and which framework is using if has framework. Like Java, NodeJS + etc + type: string + containerInfo: + description: Images are the images this template will use. + items: + properties: + buildVolumes: + description: BuildVolumes specifies a list of volumes to mount + to container running the build. + items: + type: string + type: array + builderImage: + description: BaseImage are the images this template will use. + type: string + runtimeArtifacts: + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume + to - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + runtimeImage: + type: string + type: object + type: array + defaultBaseImage: + description: DefaultBaseImage is the image that will be used by default + type: string + description: + description: Description illustrate the purpose of this template + type: string + environment: + description: Parameters is a set of environment variables to be passed + to the image. + items: + properties: + defaultValue: + type: string + description: + type: string + key: + type: string + optValues: + items: + type: string + type: array + required: + type: boolean + type: + type: string + value: + type: string + type: object + type: array + iconPath: + description: IconPath is used for frontend display + type: string + version: + description: Version of template + type: string + type: object + status: + description: S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/devops.kubesphere.io_s2iruns.yaml b/config/crd/bases/devops.kubesphere.io_s2iruns.yaml new file mode 100644 index 000000000..492908cd6 --- /dev/null +++ b/config/crd/bases/devops.kubesphere.io_s2iruns.yaml @@ -0,0 +1,181 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: s2iruns.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .status.runState + name: State + type: string + - JSONPath: .status.kubernetesJobName + name: K8sJobName + type: string + - JSONPath: .status.startTime + name: StartTime + type: date + - JSONPath: .status.completionTime + name: CompletionTime + type: date + - JSONPath: .status.s2iBuildResult.imageName + name: ImageName + type: string + group: devops.kubesphere.io + names: + kind: S2iRun + listKind: S2iRunList + plural: s2iruns + shortNames: + - s2ir + singular: s2irun + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: S2iRun is the Schema for the s2iruns API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iRunSpec defines the desired state of S2iRun + properties: + backoffLimit: + description: BackoffLimit limits the restart count of each s2irun. Default + is 0 + format: int32 + type: integer + builderName: + description: BuilderName specify the name of s2ibuilder, required + type: string + newRevisionId: + description: NewRevisionId override the default NewRevisionId in its + s2ibuilder. + type: string + newSourceURL: + description: NewSourceURL is used to download new binary artifacts + type: string + newTag: + description: NewTag override the default tag in its s2ibuilder, image + name cannot be changed. + type: string + secondsAfterFinished: + description: SecondsAfterFinished if is set and greater than zero, and + the job created by s2irun become successful or failed , the job will + be auto deleted after SecondsAfterFinished + format: int32 + type: integer + required: + - builderName + type: object + status: + description: S2iRunStatus defines the observed state of S2iRun + properties: + completionTime: + description: Represents time when the job was completed. It is not guaranteed + to be set in happens-before order across separate operations. It is + represented in RFC3339 form and is in UTC. + format: date-time + type: string + kubernetesJobName: + description: KubernetesJobName is the job name in k8s + type: string + logURL: + description: LogURL is uesd for external log handler to let user know + where is log located in + type: string + runState: + description: RunState indicates whether this job is done or failed + type: string + s2iBuildResult: + description: S2i build result info. + properties: + commandPull: + description: Command for pull image. + type: string + imageCreated: + description: Image created time. + type: string + imageID: + description: Image ID. + type: string + imageName: + description: ImageName is the name of artifact + type: string + imageRepoTags: + description: image tags. + items: + type: string + type: array + imageSize: + description: The size in bytes of the image + format: int64 + type: integer + type: object + s2iBuildSource: + description: S2i build source info. + properties: + binaryName: + description: Binary file Name + type: string + binarySize: + description: Binary file Size + format: int64 + type: integer + builderImage: + description: // BuilderImage describes which image is used for building + the result images. + type: string + commitID: + description: CommitID represents an arbitrary extended object reference + in Git as SHA-1 + type: string + committerEmail: + description: CommitterEmail contains the e-mail of the committer + type: string + committerName: + description: CommitterName contains the name of the committer + type: string + description: + description: Description is a result image description label. The + default is no description. + type: string + revisionId: + description: The RevisionId is a branch name or a SHA-1 hash of + every important thing about the commit + type: string + sourceUrl: + description: SourceURL is url of the codes such as https://github.com/a/b.git + type: string + type: object + startTime: + description: StartTime represent when this run began + format: date-time + type: string + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/iam.kubesphere.io_policyrules.yaml b/config/crd/bases/iam.kubesphere.io_policyrules.yaml new file mode 100644 index 000000000..c0eb6ed9c --- /dev/null +++ b/config/crd/bases/iam.kubesphere.io_policyrules.yaml @@ -0,0 +1,58 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: policyrules.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .scope + name: Scope + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: PolicyRule + listKind: PolicyRuleList + plural: policyrules + singular: policyrule + scope: Cluster + subresources: {} + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + rego: + type: string + scope: + type: string + required: + - rego + - scope + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/iam.kubesphere.io_rolebindings.yaml b/config/crd/bases/iam.kubesphere.io_rolebindings.yaml new file mode 100644 index 000000000..ede871b22 --- /dev/null +++ b/config/crd/bases/iam.kubesphere.io_rolebindings.yaml @@ -0,0 +1,104 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: rolebindings.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .scope + name: Scope + type: string + - JSONPath: .roleRef.name + name: RoleRef + type: string + - JSONPath: .subjects[*].name + name: Subjects + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: RoleBinding + listKind: RoleBindingList + plural: rolebindings + singular: rolebinding + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: RoleBinding is the Schema for the rolebindings API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + roleRef: + description: RoleRef contains information that points to the role being + used + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + scope: + type: string + subjects: + description: Subjects holds references to the users the role applies to. + items: + description: or a value for non-objects such as user and group names. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + type: string + kind: + description: Kind of object being referenced. Values defined by this + API group are "User", "Group", and "ServiceAccount". If the Authorizer + does not recognized the kind value, the Authorizer should report + an error. + type: string + name: + description: Name of the object being referenced. + type: string + required: + - apiGroup + - kind + - name + type: object + type: array + required: + - roleRef + - scope + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/iam.kubesphere.io_roles.yaml b/config/crd/bases/iam.kubesphere.io_roles.yaml new file mode 100644 index 000000000..91b01e128 --- /dev/null +++ b/config/crd/bases/iam.kubesphere.io_roles.yaml @@ -0,0 +1,87 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: roles.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .target.scope + name: Scope + type: string + - JSONPath: .target.name + name: Target + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: Role + listKind: RoleList + plural: roles + singular: role + scope: Cluster + subresources: {} + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + rules: + items: + description: RuleRef contains information that points to the role being + used + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + type: array + target: + properties: + name: + type: string + scope: + type: string + required: + - name + - scope + type: object + required: + - rules + - target + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/iam.kubesphere.io_users.yaml b/config/crd/bases/iam.kubesphere.io_users.yaml new file mode 100644 index 000000000..eec35e012 --- /dev/null +++ b/config/crd/bases/iam.kubesphere.io_users.yaml @@ -0,0 +1,117 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: users.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.email + name: Email + type: string + - JSONPath: .status.state + name: Status + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: User + listKind: UserList + plural: users + singular: user + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: User is the Schema for the users API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: UserSpec defines the desired state of User + properties: + description: + description: Description of the user. + type: string + displayName: + type: string + email: + description: Unique email address. + type: string + finalizers: + description: Finalizers is an opaque list of values that must be empty + to permanently remove object from storage. + items: + type: string + type: array + groups: + items: + type: string + type: array + lang: + description: The preferred written or spoken language for the user. + type: string + password: + type: string + required: + - email + - password + type: object + status: + description: UserStatus defines the observed state of User + properties: + conditions: + description: Represents the latest available observations of a namespace's + current state. + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of namespace controller condition. + type: string + required: + - status + - type + type: object + type: array + state: + description: The user status + type: string + type: object + required: + - spec + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/network.kubesphere.io_namespacenetworkpolicies.yaml b/config/crd/bases/network.kubesphere.io_namespacenetworkpolicies.yaml new file mode 100644 index 000000000..0be1db2ab --- /dev/null +++ b/config/crd/bases/network.kubesphere.io_namespacenetworkpolicies.yaml @@ -0,0 +1,762 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: namespacenetworkpolicies.network.kubesphere.io +spec: + group: network.kubesphere.io + names: + categories: + - networking + kind: NamespaceNetworkPolicy + listKind: NamespaceNetworkPolicyList + plural: namespacenetworkpolicies + shortNames: + - nsnp + singular: namespacenetworkpolicy + scope: Namespaced + validation: + openAPIV3Schema: + description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies + API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy + properties: + egress: + description: The ordered set of egress rules. Each rule contains a + set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an action. + \ Both selector-based security Policy and security Profiles reference + rules - separated out as a list of rules for both ingress and egress + packet matching. \n Each positive match criteria has a negated version, + prefixed with â€Notâ€. All the match criteria within a rule must be + satisfied for a packet to match. A single rule can contain the positive + and negative version of a match and both must be satisfied for the + rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected namespaces + will be matched. When both NamespaceSelector and Selector + are defined on the same rule, then only workload endpoints + that are matched by both selectors will be selected by the + rule. \n For NetworkPolicy, an empty NamespaceSelector implies + that the Selector is limited to selecting only workload + endpoints in the same namespace as the NetworkPolicy. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or terminates + at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated selectors. + type: string + ports: + description: "Ports is an optional field that restricts the + rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges of + ports. \n Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to \"TCP\" or \"UDP\"." + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). Only + traffic that originates from (terminates at) endpoints matching + the selector will be matched. \n Note that: in addition + to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports negation. + \ The two types of negation are subtly different. One negates + the set of matched endpoints, the other negates the whole + match: \n \tSelector = \"!has(my_label)\" matches packets + that are from other Calico-controlled \tendpoints that do + not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" + matches packets that are not from Calico-controlled \tendpoints + that do have the label “my_labelâ€. \n The effect is that + the latter will accept packets from non-Calico sources whereas + the former is limited to packets from Calico-controlled + endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from (or + terminates at) a pod running as a matching service account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + that matches the given label selector. If both Names + and Selector are specified then they are AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP requests. + properties: + methods: + description: Methods is an optional field that restricts the + rule to apply only to HTTP requests that use one of the + listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods + are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts the + rule to apply to HTTP requests that use one of the listed + HTTP Paths. Multiple paths are OR''d together. e.g: - exact: + /foo - prefix: /bar NOTE: Each entry may ONLY specify either + a `exact` or a `prefix` match. The validator will check + for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, which + Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example a + value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, which + Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example a + value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + description: NotProtocol is the negated version of the Protocol + field. + type: string + protocol: + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" + or an integer in the range 1-255." + type: string + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected namespaces + will be matched. When both NamespaceSelector and Selector + are defined on the same rule, then only workload endpoints + that are matched by both selectors will be selected by the + rule. \n For NetworkPolicy, an empty NamespaceSelector implies + that the Selector is limited to selecting only workload + endpoints in the same namespace as the NetworkPolicy. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or terminates + at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated selectors. + type: string + ports: + description: "Ports is an optional field that restricts the + rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges of + ports. \n Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to \"TCP\" or \"UDP\"." + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). Only + traffic that originates from (terminates at) endpoints matching + the selector will be matched. \n Note that: in addition + to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports negation. + \ The two types of negation are subtly different. One negates + the set of matched endpoints, the other negates the whole + match: \n \tSelector = \"!has(my_label)\" matches packets + that are from other Calico-controlled \tendpoints that do + not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" + matches packets that are not from Calico-controlled \tendpoints + that do have the label “my_labelâ€. \n The effect is that + the latter will accept packets from non-Calico sources whereas + the former is limited to packets from Calico-controlled + endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from (or + terminates at) a pod running as a matching service account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + that matches the given label selector. If both Names + and Selector are specified then they are AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + ingress: + description: The ordered set of ingress rules. Each rule contains a + set of packet match criteria and a corresponding action to apply. + items: + description: "A Rule encapsulates a set of match criteria and an action. + \ Both selector-based security Policy and security Profiles reference + rules - separated out as a list of rules for both ingress and egress + packet matching. \n Each positive match criteria has a negated version, + prefixed with â€Notâ€. All the match criteria within a rule must be + satisfied for a packet to match. A single rule can contain the positive + and negative version of a match and both must be satisfied for the + rule to match." + properties: + action: + type: string + destination: + description: Destination contains the match criteria that apply + to destination entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected namespaces + will be matched. When both NamespaceSelector and Selector + are defined on the same rule, then only workload endpoints + that are matched by both selectors will be selected by the + rule. \n For NetworkPolicy, an empty NamespaceSelector implies + that the Selector is limited to selecting only workload + endpoints in the same namespace as the NetworkPolicy. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or terminates + at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated selectors. + type: string + ports: + description: "Ports is an optional field that restricts the + rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges of + ports. \n Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to \"TCP\" or \"UDP\"." + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). Only + traffic that originates from (terminates at) endpoints matching + the selector will be matched. \n Note that: in addition + to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports negation. + \ The two types of negation are subtly different. One negates + the set of matched endpoints, the other negates the whole + match: \n \tSelector = \"!has(my_label)\" matches packets + that are from other Calico-controlled \tendpoints that do + not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" + matches packets that are not from Calico-controlled \tendpoints + that do have the label “my_labelâ€. \n The effect is that + the latter will accept packets from non-Calico sources whereas + the former is limited to packets from Calico-controlled + endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from (or + terminates at) a pod running as a matching service account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + that matches the given label selector. If both Names + and Selector are specified then they are AND'ed. + type: string + type: object + type: object + http: + description: HTTP contains match criteria that apply to HTTP requests. + properties: + methods: + description: Methods is an optional field that restricts the + rule to apply only to HTTP requests that use one of the + listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods + are OR'd together. + items: + type: string + type: array + paths: + description: 'Paths is an optional field that restricts the + rule to apply to HTTP requests that use one of the listed + HTTP Paths. Multiple paths are OR''d together. e.g: - exact: + /foo - prefix: /bar NOTE: Each entry may ONLY specify either + a `exact` or a `prefix` match. The validator will check + for it.' + items: + description: 'HTTPPath specifies an HTTP path to match. + It may be either of the form: exact: : which matches + the path exactly or prefix: : which matches + the path prefix' + properties: + exact: + type: string + prefix: + type: string + type: object + type: array + type: object + icmp: + description: ICMP is an optional field that restricts the rule + to apply to a specific type and code of ICMP traffic. This + should only be specified if the Protocol field is set to "ICMP" + or "ICMPv6". + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, which + Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example a + value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + ipVersion: + description: IPVersion is an optional field that restricts the + rule to only match a specific IP version. + type: integer + notICMP: + description: NotICMP is the negated version of the ICMP field. + properties: + code: + description: Match on a specific ICMP code. If specified, + the Type value must also be specified. This is a technical + limitation imposed by the kernel’s iptables firewall, which + Calico uses to enforce the rule. + type: integer + type: + description: Match on a specific ICMP type. For example a + value of 8 refers to ICMP Echo Request (i.e. pings). + type: integer + type: object + notProtocol: + description: NotProtocol is the negated version of the Protocol + field. + type: string + protocol: + description: "Protocol is an optional field that restricts the + rule to only apply to traffic of a specific IP protocol. Required + if any of the EntityRules contain Ports (because ports only + apply to certain protocols). \n Must be one of these string + values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" + or an integer in the range 1-255." + type: string + source: + description: Source contains the match criteria that apply to + source entity. + properties: + namespaceSelector: + description: "NamespaceSelector is an optional field that + contains a selector expression. Only traffic that originates + from (or terminates at) endpoints within the selected namespaces + will be matched. When both NamespaceSelector and Selector + are defined on the same rule, then only workload endpoints + that are matched by both selectors will be selected by the + rule. \n For NetworkPolicy, an empty NamespaceSelector implies + that the Selector is limited to selecting only workload + endpoints in the same namespace as the NetworkPolicy. \n + For GlobalNetworkPolicy, an empty NamespaceSelector implies + the Selector applies to workload endpoints across all namespaces." + type: string + nets: + description: Nets is an optional field that restricts the + rule to only apply to traffic that originates from (or terminates + at) IP addresses in any of the given subnets. + items: + type: string + type: array + notNets: + description: NotNets is the negated version of the Nets field. + items: + type: string + type: array + notPorts: + description: NotPorts is the negated version of the Ports + field. Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to "TCP" or "UDP". + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + notSelector: + description: NotSelector is the negated version of the Selector + field. See Selector field for subtleties with negated selectors. + type: string + ports: + description: "Ports is an optional field that restricts the + rule to only apply to traffic that has a source (destination) + port that matches one of these ranges/values. This value + is a list of integers or strings that represent ranges of + ports. \n Since only some protocols have ports, if any ports + are specified it requires the Protocol match in the Rule + to be set to \"TCP\" or \"UDP\"." + items: + description: "Port represents either a range of numeric + ports or a named port. \n - For a named port, set + the PortName, leaving MinPort and MaxPort as 0. - + For a port range, set MinPort and MaxPort to the (inclusive) + port numbers. Set PortName to \"\". - For a + single port, set MinPort = MaxPort and PortName = \"\"." + properties: + maxPort: + type: integer + minPort: + type: integer + portName: + type: string + type: object + type: array + selector: + description: "Selector is an optional field that contains + a selector expression (see Policy for sample syntax). Only + traffic that originates from (terminates at) endpoints matching + the selector will be matched. \n Note that: in addition + to the negated version of the Selector (see NotSelector + below), the selector expression syntax itself supports negation. + \ The two types of negation are subtly different. One negates + the set of matched endpoints, the other negates the whole + match: \n \tSelector = \"!has(my_label)\" matches packets + that are from other Calico-controlled \tendpoints that do + not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" + matches packets that are not from Calico-controlled \tendpoints + that do have the label “my_labelâ€. \n The effect is that + the latter will accept packets from non-Calico sources whereas + the former is limited to packets from Calico-controlled + endpoints." + type: string + serviceAccounts: + description: ServiceAccounts is an optional field that restricts + the rule to only apply to traffic that originates from (or + terminates at) a pod running as a matching service account. + properties: + names: + description: Names is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + whose name is in the list. + items: + type: string + type: array + selector: + description: Selector is an optional field that restricts + the rule to only apply to traffic that originates from + (or terminates at) a pod running as a service account + that matches the given label selector. If both Names + and Selector are specified then they are AND'ed. + type: string + type: object + type: object + required: + - action + type: object + type: array + order: + description: Order is an optional field that specifies the order in + which the policy is applied. Policies with higher "order" are applied + after those with lower order. If the order is omitted, it may be + considered to be "infinite" - i.e. the policy will be applied last. Policies + with identical order will be applied in alphanumerical order based + on the Policy "Name". + type: integer + selector: + description: "The selector is an expression used to pick pick out the + endpoints that the policy should be applied to. \n Selector expressions + follow this syntax: \n \tlabel == \"string_literal\" -> comparison, + e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" -> not + equal; also matches if label is not present \tlabel in { \"a\", \"b\", + \"c\", ... } -> true if the value of label X is one of \"a\", \"b\", + \"c\" \tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the + value of label X is not one of \"a\", \"b\", \"c\" \thas(label_name) + \ -> True if that label is present \t! expr -> negation of expr \texpr + && expr -> Short-circuit and \texpr || expr -> Short-circuit or + \t( expr ) -> parens for grouping \tall() or the empty selector -> + matches all endpoints. \n Label names are allowed to contain alphanumerics, + -, _ and /. String literals are more permissive but they do not support + escape characters. \n Examples (with made-up labels): \n \ttype == + \"webserver\" && deployment == \"prod\" \ttype in {\"frontend\", \"backend\"} + \tdeployment != \"dev\" \t! has(label_name)" + type: string + types: + description: "Types indicates whether this policy applies to ingress, + or to egress, or to both. When not explicitly specified (and so the + value on creation is empty or nil), Calico defaults Types according + to what Ingress and Egress are present in the policy. The default + is: \n - [ PolicyTypeIngress ], if there are no Egress rules (including + the case where there are also no Ingress rules) \n - [ PolicyTypeEgress + ], if there are Egress rules but no Ingress rules \n - [ PolicyTypeIngress, + PolicyTypeEgress ], if there are both Ingress and Egress rules. \n + When the policy is read back again, Types will always be one of these + values, never empty or nil." + items: + type: string + type: array + required: + - selector + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/servicemesh.kubesphere.io_servicepolicies.yaml b/config/crd/bases/servicemesh.kubesphere.io_servicepolicies.yaml new file mode 100644 index 000000000..252260dc6 --- /dev/null +++ b/config/crd/bases/servicemesh.kubesphere.io_servicepolicies.yaml @@ -0,0 +1,1605 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: servicepolicies.servicemesh.kubesphere.io +spec: + group: servicemesh.kubesphere.io + names: + kind: ServicePolicy + listKind: ServicePolicyList + plural: servicepolicies + singular: servicepolicy + scope: Namespaced + validation: + openAPIV3Schema: + description: ServicePolicy is the Schema for the servicepolicies API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServicePolicySpec defines the desired state of ServicePolicy + properties: + selector: + description: Label selector for destination rules. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + template: + description: Template used to create a destination rule + properties: + metadata: + description: Metadata of the virtual services created from this + template + type: object + spec: + description: Spec indicates the behavior of a destination rule. + properties: + export_to: + description: "A list of namespaces to which this destination + rule is exported. The resolution of a destination rule to + apply to a service occurs in the context of a hierarchy of + namespaces. Exporting a destination rule allows it to be included + in the resolution hierarchy for services in other namespaces. + This feature provides a mechanism for service owners and mesh + administrators to control the visibility of destination rules + across namespace boundaries. \n If no namespaces are specified + then the destination rule is exported to all namespaces by + default. \n The value \".\" is reserved and defines an export + to the same namespace that the destination rule is declared + in. Similarly, the value \"*\" is reserved and defines an + export to all namespaces. \n NOTE: in the current release, + the `exportTo` value is restricted to \".\" or \"*\" (i.e., + the current namespace or all namespaces)." + items: + type: string + type: array + host: + description: "The name of a service from the service registry. + Service names are looked up from the platform's service registry + (e.g., Kubernetes services, Consul services, etc.) and from + the hosts declared by [ServiceEntries](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry). + Rules defined for services that do not exist in the service + registry will be ignored. \n *Note for Kubernetes users*: + When short names are used (e.g. \"reviews\" instead of \"reviews.default.svc.cluster.local\"), + Istio will interpret the short name based on the namespace + of the rule, not the service. A rule in the \"default\" namespace + containing a host \"reviews\" will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated with the reviews + service. _To avoid potential misconfigurations, it is recommended + to always use fully qualified domain names over short names._ + \n Note that the host field applies to both HTTP and TCP services." + type: string + subsets: + description: One or more named sets that represent individual + versions of a service. Traffic policies can be overridden + at subset level. + items: + description: "A subset of endpoints of a service. Subsets + can be used for scenarios like A/B testing, or routing to + a specific version of a service. Refer to [VirtualService](https://istio.io/docs/reference/config/networking/virtual-service/#VirtualService) + documentation for examples of using subsets in these scenarios. + In addition, traffic policies defined at the service-level + can be overridden at a subset-level. The following rule + uses a round robin load balancing policy for all traffic + going to a subset named testversion that is composed of + endpoints (e.g., pods) with labels (version:v3). \n ```yaml + apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule + metadata: name: bookinfo-ratings spec: host: ratings.prod.svc.cluster.local + \ trafficPolicy: loadBalancer: simple: LEAST_CONN + \ subsets: - name: testversion labels: version: + v3 trafficPolicy: loadBalancer: simple: + ROUND_ROBIN ``` \n **Note:** Policies specified for subsets + will not take effect until a route rule explicitly sends + traffic to this subset. \n One or more labels are typically + required to identify the subset destination, however, when + the corresponding DestinationRule represents a host that + supports multiple SNI hosts (e.g., an egress gateway), a + subset without labels may be meaningful. In this case a + traffic policy with [TLSSettings](#TLSSettings) can be used + to identify a specific SNI host corresponding to the named + subset." + properties: + labels: + additionalProperties: + type: string + description: Labels apply a filter over the endpoints + of a service in the service registry. See route rules + for examples of usage. + type: object + name: + description: Name of the subset. The service name and + the subset name can be used for traffic splitting in + a route rule. + type: string + traffic_policy: + description: Traffic policies that apply to this subset. + Subsets inherit the traffic policies specified at the + DestinationRule level. Settings specified at the subset + level will override the corresponding settings specified + at the DestinationRule level. + properties: + connection_pool: + description: Settings controlling the volume of connections + to an upstream service + properties: + http: + description: HTTP connection pool settings. + properties: + h2_upgrade_policy: + description: Specify if http1.1 connection + should be upgraded to http2 for the associated + destination. + format: int32 + type: integer + http1_max_pending_requests: + description: Maximum number of pending HTTP + requests to a destination. Default 2^32-1. + format: int32 + type: integer + http2_max_requests: + description: Maximum number of requests to + a backend. Default 2^32-1. + format: int32 + type: integer + idle_timeout: + description: The idle timeout for upstream + connection pool connections. The idle timeout + is defined as the period in which there + are no active requests. If not set, there + is no idle timeout. When the idle timeout + is reached the connection will be closed. + Note that request based timeouts mean that + HTTP/2 PINGs will not keep the connection + alive. Applies to both HTTP1.1 and HTTP2 + connections. + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` field + and a positive or negative `nanos` field. + For durations of one second or more, + a non-zero value for the `nanos` field + must be of the same sign as the `seconds` + field. Must be from -999,999,999 to + +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + max_requests_per_connection: + description: Maximum number of requests per + connection to a backend. Setting this parameter + to 1 disables keep alive. Default 0, meaning + "unlimited", up to 2^29. + format: int32 + type: integer + max_retries: + description: Maximum number of retries that + can be outstanding to all hosts in a cluster + at a given time. Defaults to 2^32-1. + format: int32 + type: integer + type: object + tcp: + description: Settings common to both HTTP and + TCP upstream connections. + properties: + connect_timeout: + description: TCP connection timeout. + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` field + and a positive or negative `nanos` field. + For durations of one second or more, + a non-zero value for the `nanos` field + must be of the same sign as the `seconds` + field. Must be from -999,999,999 to + +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + max_connections: + description: Maximum number of HTTP1 /TCP + connections to a destination host. Default + 2^32-1. + format: int32 + type: integer + tcp_keepalive: + description: If set then set SO_KEEPALIVE + on the socket to enable TCP Keepalives. + properties: + interval: + description: The time duration between + keep-alive probes. Default is to use + the OS level configuration (unless overridden, + Linux defaults to 75s.) + properties: + nanos: + description: Signed fractions of a + second at nanosecond resolution + of the span of time. Durations less + than one second are represented + with a 0 `seconds` field and a positive + or negative `nanos` field. For durations + of one second or more, a non-zero + value for the `nanos` field must + be of the same sign as the `seconds` + field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + probes: + description: Maximum number of keepalive + probes to send without response before + deciding the connection is dead. Default + is to use the OS level configuration + (unless overridden, Linux defaults to + 9.) + format: int32 + type: integer + time: + description: The time duration a connection + needs to be idle before keep-alive probes + start being sent. Default is to use + the OS level configuration (unless overridden, + Linux defaults to 7200s (ie 2 hours.) + properties: + nanos: + description: Signed fractions of a + second at nanosecond resolution + of the span of time. Durations less + than one second are represented + with a 0 `seconds` field and a positive + or negative `nanos` field. For durations + of one second or more, a non-zero + value for the `nanos` field must + be of the same sign as the `seconds` + field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: object + load_balancer: + description: Settings controlling the load balancer + algorithms. + type: object + outlier_detection: + description: Settings controlling eviction of unhealthy + hosts from the load balancing pool + properties: + base_ejection_time: + description: 'Minimum ejection duration. A host + will remain ejected for a period equal to the + product of minimum ejection duration and the + number of times the host has been ejected. This + technique allows the system to automatically + increase the ejection period for unhealthy upstream + servers. format: 1h/1m/1s/1ms. MUST BE >=1ms. + Default is 30s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. For + durations of one second or more, a non-zero + value for the `nanos` field must be of the + same sign as the `seconds` field. Must be + from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + consecutive_errors: + description: Number of errors before a host is + ejected from the connection pool. Defaults to + 5. When the upstream host is accessed over HTTP, + a 502, 503, or 504 return code qualifies as + an error. When the upstream host is accessed + over an opaque TCP connection, connect timeouts + and connection error/failure events qualify + as an error. + format: int32 + type: integer + interval: + description: 'Time interval between ejection sweep + analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms. + Default is 10s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. For + durations of one second or more, a non-zero + value for the `nanos` field must be of the + same sign as the `seconds` field. Must be + from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + max_ejection_percent: + description: Maximum % of hosts in the load balancing + pool for the upstream service that can be ejected. + Defaults to 10%. + format: int32 + type: integer + min_health_percent: + description: Outlier detection will be enabled + as long as the associated load balancing pool + has at least min_health_percent hosts in healthy + mode. When the percentage of healthy hosts in + the load balancing pool drops below this threshold, + outlier detection will be disabled and the proxy + will load balance across all hosts in the pool + (healthy and unhealthy). The threshold can be + disabled by setting it to 0%. The default is + 0% as it's not typically applicable in k8s environments + with few pods per service. + format: int32 + type: integer + type: object + port_level_settings: + description: Traffic policies specific to individual + ports. Note that port level settings will override + the destination-level settings. Traffic settings + specified at the destination-level will not be inherited + when overridden by port-level settings, i.e. default + values will be applied to fields omitted in port-level + traffic policies. + items: + description: Traffic policies that apply to specific + ports of the service + properties: + connection_pool: + description: Settings controlling the volume + of connections to an upstream service + properties: + http: + description: HTTP connection pool settings. + properties: + h2_upgrade_policy: + description: Specify if http1.1 connection + should be upgraded to http2 for the + associated destination. + format: int32 + type: integer + http1_max_pending_requests: + description: Maximum number of pending + HTTP requests to a destination. Default + 2^32-1. + format: int32 + type: integer + http2_max_requests: + description: Maximum number of requests + to a backend. Default 2^32-1. + format: int32 + type: integer + idle_timeout: + description: The idle timeout for upstream + connection pool connections. The idle + timeout is defined as the period in + which there are no active requests. + If not set, there is no idle timeout. + When the idle timeout is reached the + connection will be closed. Note that + request based timeouts mean that HTTP/2 + PINGs will not keep the connection + alive. Applies to both HTTP1.1 and + HTTP2 connections. + properties: + nanos: + description: Signed fractions of + a second at nanosecond resolution + of the span of time. Durations + less than one second are represented + with a 0 `seconds` field and a + positive or negative `nanos` field. + For durations of one second or + more, a non-zero value for the + `nanos` field must be of the same + sign as the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of + the span of time. Must be from + -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds + are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 + days/year * 10000 years' + format: int64 + type: integer + type: object + max_requests_per_connection: + description: Maximum number of requests + per connection to a backend. Setting + this parameter to 1 disables keep + alive. Default 0, meaning "unlimited", + up to 2^29. + format: int32 + type: integer + max_retries: + description: Maximum number of retries + that can be outstanding to all hosts + in a cluster at a given time. Defaults + to 2^32-1. + format: int32 + type: integer + type: object + tcp: + description: Settings common to both HTTP + and TCP upstream connections. + properties: + connect_timeout: + description: TCP connection timeout. + properties: + nanos: + description: Signed fractions of + a second at nanosecond resolution + of the span of time. Durations + less than one second are represented + with a 0 `seconds` field and a + positive or negative `nanos` field. + For durations of one second or + more, a non-zero value for the + `nanos` field must be of the same + sign as the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of + the span of time. Must be from + -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds + are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 + days/year * 10000 years' + format: int64 + type: integer + type: object + max_connections: + description: Maximum number of HTTP1 + /TCP connections to a destination + host. Default 2^32-1. + format: int32 + type: integer + tcp_keepalive: + description: If set then set SO_KEEPALIVE + on the socket to enable TCP Keepalives. + properties: + interval: + description: The time duration between + keep-alive probes. Default is + to use the OS level configuration + (unless overridden, Linux defaults + to 75s.) + properties: + nanos: + description: Signed fractions + of a second at nanosecond + resolution of the span of + time. Durations less than + one second are represented + with a 0 `seconds` field and + a positive or negative `nanos` + field. For durations of one + second or more, a non-zero + value for the `nanos` field + must be of the same sign as + the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds + of the span of time. Must + be from -315,576,000,000 to + +315,576,000,000 inclusive. + Note: these bounds are computed + from: 60 sec/min * 60 min/hr + * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + probes: + description: Maximum number of keepalive + probes to send without response + before deciding the connection + is dead. Default is to use the + OS level configuration (unless + overridden, Linux defaults to + 9.) + format: int32 + type: integer + time: + description: The time duration a + connection needs to be idle before + keep-alive probes start being + sent. Default is to use the OS + level configuration (unless overridden, + Linux defaults to 7200s (ie 2 + hours.) + properties: + nanos: + description: Signed fractions + of a second at nanosecond + resolution of the span of + time. Durations less than + one second are represented + with a 0 `seconds` field and + a positive or negative `nanos` + field. For durations of one + second or more, a non-zero + value for the `nanos` field + must be of the same sign as + the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds + of the span of time. Must + be from -315,576,000,000 to + +315,576,000,000 inclusive. + Note: these bounds are computed + from: 60 sec/min * 60 min/hr + * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: object + load_balancer: + description: Settings controlling the load balancer + algorithms. + type: object + outlier_detection: + description: Settings controlling eviction of + unhealthy hosts from the load balancing pool + properties: + base_ejection_time: + description: 'Minimum ejection duration. + A host will remain ejected for a period + equal to the product of minimum ejection + duration and the number of times the host + has been ejected. This technique allows + the system to automatically increase the + ejection period for unhealthy upstream + servers. format: 1h/1m/1s/1ms. MUST BE + >=1ms. Default is 30s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` + field and a positive or negative `nanos` + field. For durations of one second + or more, a non-zero value for the + `nanos` field must be of the same + sign as the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 + sec/min * 60 min/hr * 24 hr/day * + 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + consecutive_errors: + description: Number of errors before a host + is ejected from the connection pool. Defaults + to 5. When the upstream host is accessed + over HTTP, a 502, 503, or 504 return code + qualifies as an error. When the upstream + host is accessed over an opaque TCP connection, + connect timeouts and connection error/failure + events qualify as an error. + format: int32 + type: integer + interval: + description: 'Time interval between ejection + sweep analysis. format: 1h/1m/1s/1ms. + MUST BE >=1ms. Default is 10s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` + field and a positive or negative `nanos` + field. For durations of one second + or more, a non-zero value for the + `nanos` field must be of the same + sign as the `seconds` field. Must + be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 + sec/min * 60 min/hr * 24 hr/day * + 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + max_ejection_percent: + description: Maximum % of hosts in the load + balancing pool for the upstream service + that can be ejected. Defaults to 10%. + format: int32 + type: integer + min_health_percent: + description: Outlier detection will be enabled + as long as the associated load balancing + pool has at least min_health_percent hosts + in healthy mode. When the percentage of + healthy hosts in the load balancing pool + drops below this threshold, outlier detection + will be disabled and the proxy will load + balance across all hosts in the pool (healthy + and unhealthy). The threshold can be disabled + by setting it to 0%. The default is 0% + as it's not typically applicable in k8s + environments with few pods per service. + format: int32 + type: integer + type: object + port: + description: Specifies the number of a port + on the destination service on which this policy + is being applied. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + tls: + description: TLS related settings for connections + to the upstream service. + properties: + ca_certificates: + description: 'OPTIONAL: The path to the + file containing certificate authority + certificates to use in verifying a presented + server certificate. If omitted, the proxy + will not verify the server''s certificate. + Should be empty if mode is `ISTIO_MUTUAL`.' + type: string + client_certificate: + description: REQUIRED if mode is `MUTUAL`. + The path to the file holding the client-side + TLS certificate to use. Should be empty + if mode is `ISTIO_MUTUAL`. + type: string + mode: + description: Indicates whether connections + to this port should be secured using TLS. + The value of this field determines how + TLS is enforced. + format: int32 + type: integer + private_key: + description: REQUIRED if mode is `MUTUAL`. + The path to the file holding the client's + private key. Should be empty if mode is + `ISTIO_MUTUAL`. + type: string + sni: + description: SNI string to present to the + server during TLS handshake. + type: string + subject_alt_names: + description: A list of alternate names to + verify the subject identity in the certificate. + If specified, the proxy will verify that + the server certificate's subject alt name + matches one of the specified values. If + specified, this list overrides the value + of subject_alt_names from the ServiceEntry. + items: + type: string + type: array + type: object + type: object + type: array + tls: + description: TLS related settings for connections + to the upstream service. + properties: + ca_certificates: + description: 'OPTIONAL: The path to the file containing + certificate authority certificates to use in + verifying a presented server certificate. If + omitted, the proxy will not verify the server''s + certificate. Should be empty if mode is `ISTIO_MUTUAL`.' + type: string + client_certificate: + description: REQUIRED if mode is `MUTUAL`. The + path to the file holding the client-side TLS + certificate to use. Should be empty if mode + is `ISTIO_MUTUAL`. + type: string + mode: + description: Indicates whether connections to + this port should be secured using TLS. The value + of this field determines how TLS is enforced. + format: int32 + type: integer + private_key: + description: REQUIRED if mode is `MUTUAL`. The + path to the file holding the client's private + key. Should be empty if mode is `ISTIO_MUTUAL`. + type: string + sni: + description: SNI string to present to the server + during TLS handshake. + type: string + subject_alt_names: + description: A list of alternate names to verify + the subject identity in the certificate. If + specified, the proxy will verify that the server + certificate's subject alt name matches one of + the specified values. If specified, this list + overrides the value of subject_alt_names from + the ServiceEntry. + items: + type: string + type: array + type: object + type: object + type: object + type: array + traffic_policy: + description: Traffic policies to apply (load balancing policy, + connection pool sizes, outlier detection). + properties: + connection_pool: + description: Settings controlling the volume of connections + to an upstream service + properties: + http: + description: HTTP connection pool settings. + properties: + h2_upgrade_policy: + description: Specify if http1.1 connection should + be upgraded to http2 for the associated destination. + format: int32 + type: integer + http1_max_pending_requests: + description: Maximum number of pending HTTP requests + to a destination. Default 2^32-1. + format: int32 + type: integer + http2_max_requests: + description: Maximum number of requests to a backend. + Default 2^32-1. + format: int32 + type: integer + idle_timeout: + description: The idle timeout for upstream connection + pool connections. The idle timeout is defined + as the period in which there are no active requests. + If not set, there is no idle timeout. When the + idle timeout is reached the connection will be + closed. Note that request based timeouts mean + that HTTP/2 PINGs will not keep the connection + alive. Applies to both HTTP1.1 and HTTP2 connections. + properties: + nanos: + description: Signed fractions of a second at + nanosecond resolution of the span of time. + Durations less than one second are represented + with a 0 `seconds` field and a positive or + negative `nanos` field. For durations of one + second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` + field. Must be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day * + 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + max_requests_per_connection: + description: Maximum number of requests per connection + to a backend. Setting this parameter to 1 disables + keep alive. Default 0, meaning "unlimited", up + to 2^29. + format: int32 + type: integer + max_retries: + description: Maximum number of retries that can + be outstanding to all hosts in a cluster at a + given time. Defaults to 2^32-1. + format: int32 + type: integer + type: object + tcp: + description: Settings common to both HTTP and TCP upstream + connections. + properties: + connect_timeout: + description: TCP connection timeout. + properties: + nanos: + description: Signed fractions of a second at + nanosecond resolution of the span of time. + Durations less than one second are represented + with a 0 `seconds` field and a positive or + negative `nanos` field. For durations of one + second or more, a non-zero value for the `nanos` + field must be of the same sign as the `seconds` + field. Must be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day * + 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + max_connections: + description: Maximum number of HTTP1 /TCP connections + to a destination host. Default 2^32-1. + format: int32 + type: integer + tcp_keepalive: + description: If set then set SO_KEEPALIVE on the + socket to enable TCP Keepalives. + properties: + interval: + description: The time duration between keep-alive + probes. Default is to use the OS level configuration + (unless overridden, Linux defaults to 75s.) + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. + For durations of one second or more, a + non-zero value for the `nanos` field must + be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these + bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + probes: + description: Maximum number of keepalive probes + to send without response before deciding the + connection is dead. Default is to use the + OS level configuration (unless overridden, + Linux defaults to 9.) + format: int32 + type: integer + time: + description: The time duration a connection + needs to be idle before keep-alive probes + start being sent. Default is to use the OS + level configuration (unless overridden, Linux + defaults to 7200s (ie 2 hours.) + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. + For durations of one second or more, a + non-zero value for the `nanos` field must + be of the same sign as the `seconds` field. + Must be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: these + bounds are computed from: 60 sec/min * + 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: object + load_balancer: + description: Settings controlling the load balancer algorithms. + type: object + outlier_detection: + description: Settings controlling eviction of unhealthy + hosts from the load balancing pool + properties: + base_ejection_time: + description: 'Minimum ejection duration. A host will + remain ejected for a period equal to the product of + minimum ejection duration and the number of times + the host has been ejected. This technique allows the + system to automatically increase the ejection period + for unhealthy upstream servers. format: 1h/1m/1s/1ms. + MUST BE >=1ms. Default is 30s.' + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less + than one second are represented with a 0 `seconds` + field and a positive or negative `nanos` field. + For durations of one second or more, a non-zero + value for the `nanos` field must be of the same + sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. + Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + consecutive_errors: + description: Number of errors before a host is ejected + from the connection pool. Defaults to 5. When the + upstream host is accessed over HTTP, a 502, 503, or + 504 return code qualifies as an error. When the upstream + host is accessed over an opaque TCP connection, connect + timeouts and connection error/failure events qualify + as an error. + format: int32 + type: integer + interval: + description: 'Time interval between ejection sweep analysis. + format: 1h/1m/1s/1ms. MUST BE >=1ms. Default is 10s.' + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less + than one second are represented with a 0 `seconds` + field and a positive or negative `nanos` field. + For durations of one second or more, a non-zero + value for the `nanos` field must be of the same + sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. + Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + max_ejection_percent: + description: Maximum % of hosts in the load balancing + pool for the upstream service that can be ejected. + Defaults to 10%. + format: int32 + type: integer + min_health_percent: + description: Outlier detection will be enabled as long + as the associated load balancing pool has at least + min_health_percent hosts in healthy mode. When the + percentage of healthy hosts in the load balancing + pool drops below this threshold, outlier detection + will be disabled and the proxy will load balance across + all hosts in the pool (healthy and unhealthy). The + threshold can be disabled by setting it to 0%. The + default is 0% as it's not typically applicable in + k8s environments with few pods per service. + format: int32 + type: integer + type: object + port_level_settings: + description: Traffic policies specific to individual ports. + Note that port level settings will override the destination-level + settings. Traffic settings specified at the destination-level + will not be inherited when overridden by port-level settings, + i.e. default values will be applied to fields omitted + in port-level traffic policies. + items: + description: Traffic policies that apply to specific ports + of the service + properties: + connection_pool: + description: Settings controlling the volume of connections + to an upstream service + properties: + http: + description: HTTP connection pool settings. + properties: + h2_upgrade_policy: + description: Specify if http1.1 connection + should be upgraded to http2 for the associated + destination. + format: int32 + type: integer + http1_max_pending_requests: + description: Maximum number of pending HTTP + requests to a destination. Default 2^32-1. + format: int32 + type: integer + http2_max_requests: + description: Maximum number of requests to + a backend. Default 2^32-1. + format: int32 + type: integer + idle_timeout: + description: The idle timeout for upstream + connection pool connections. The idle timeout + is defined as the period in which there + are no active requests. If not set, there + is no idle timeout. When the idle timeout + is reached the connection will be closed. + Note that request based timeouts mean that + HTTP/2 PINGs will not keep the connection + alive. Applies to both HTTP1.1 and HTTP2 + connections. + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` field + and a positive or negative `nanos` field. + For durations of one second or more, + a non-zero value for the `nanos` field + must be of the same sign as the `seconds` + field. Must be from -999,999,999 to + +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + max_requests_per_connection: + description: Maximum number of requests per + connection to a backend. Setting this parameter + to 1 disables keep alive. Default 0, meaning + "unlimited", up to 2^29. + format: int32 + type: integer + max_retries: + description: Maximum number of retries that + can be outstanding to all hosts in a cluster + at a given time. Defaults to 2^32-1. + format: int32 + type: integer + type: object + tcp: + description: Settings common to both HTTP and + TCP upstream connections. + properties: + connect_timeout: + description: TCP connection timeout. + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span + of time. Durations less than one second + are represented with a 0 `seconds` field + and a positive or negative `nanos` field. + For durations of one second or more, + a non-zero value for the `nanos` field + must be of the same sign as the `seconds` + field. Must be from -999,999,999 to + +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span + of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: 60 sec/min + * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + max_connections: + description: Maximum number of HTTP1 /TCP + connections to a destination host. Default + 2^32-1. + format: int32 + type: integer + tcp_keepalive: + description: If set then set SO_KEEPALIVE + on the socket to enable TCP Keepalives. + properties: + interval: + description: The time duration between + keep-alive probes. Default is to use + the OS level configuration (unless overridden, + Linux defaults to 75s.) + properties: + nanos: + description: Signed fractions of a + second at nanosecond resolution + of the span of time. Durations less + than one second are represented + with a 0 `seconds` field and a positive + or negative `nanos` field. For durations + of one second or more, a non-zero + value for the `nanos` field must + be of the same sign as the `seconds` + field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + probes: + description: Maximum number of keepalive + probes to send without response before + deciding the connection is dead. Default + is to use the OS level configuration + (unless overridden, Linux defaults to + 9.) + format: int32 + type: integer + time: + description: The time duration a connection + needs to be idle before keep-alive probes + start being sent. Default is to use + the OS level configuration (unless overridden, + Linux defaults to 7200s (ie 2 hours.) + properties: + nanos: + description: Signed fractions of a + second at nanosecond resolution + of the span of time. Durations less + than one second are represented + with a 0 `seconds` field and a positive + or negative `nanos` field. For durations + of one second or more, a non-zero + value for the `nanos` field must + be of the same sign as the `seconds` + field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the + span of time. Must be from -315,576,000,000 + to +315,576,000,000 inclusive. Note: + these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + type: object + type: object + load_balancer: + description: Settings controlling the load balancer + algorithms. + type: object + outlier_detection: + description: Settings controlling eviction of unhealthy + hosts from the load balancing pool + properties: + base_ejection_time: + description: 'Minimum ejection duration. A host + will remain ejected for a period equal to the + product of minimum ejection duration and the + number of times the host has been ejected. This + technique allows the system to automatically + increase the ejection period for unhealthy upstream + servers. format: 1h/1m/1s/1ms. MUST BE >=1ms. + Default is 30s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. For + durations of one second or more, a non-zero + value for the `nanos` field must be of the + same sign as the `seconds` field. Must be + from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + consecutive_errors: + description: Number of errors before a host is + ejected from the connection pool. Defaults to + 5. When the upstream host is accessed over HTTP, + a 502, 503, or 504 return code qualifies as + an error. When the upstream host is accessed + over an opaque TCP connection, connect timeouts + and connection error/failure events qualify + as an error. + format: int32 + type: integer + interval: + description: 'Time interval between ejection sweep + analysis. format: 1h/1m/1s/1ms. MUST BE >=1ms. + Default is 10s.' + properties: + nanos: + description: Signed fractions of a second + at nanosecond resolution of the span of + time. Durations less than one second are + represented with a 0 `seconds` field and + a positive or negative `nanos` field. For + durations of one second or more, a non-zero + value for the `nanos` field must be of the + same sign as the `seconds` field. Must be + from -999,999,999 to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of + time. Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed + from: 60 sec/min * 60 min/hr * 24 hr/day + * 365.25 days/year * 10000 years' + format: int64 + type: integer + type: object + max_ejection_percent: + description: Maximum % of hosts in the load balancing + pool for the upstream service that can be ejected. + Defaults to 10%. + format: int32 + type: integer + min_health_percent: + description: Outlier detection will be enabled + as long as the associated load balancing pool + has at least min_health_percent hosts in healthy + mode. When the percentage of healthy hosts in + the load balancing pool drops below this threshold, + outlier detection will be disabled and the proxy + will load balance across all hosts in the pool + (healthy and unhealthy). The threshold can be + disabled by setting it to 0%. The default is + 0% as it's not typically applicable in k8s environments + with few pods per service. + format: int32 + type: integer + type: object + port: + description: Specifies the number of a port on the + destination service on which this policy is being + applied. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + tls: + description: TLS related settings for connections + to the upstream service. + properties: + ca_certificates: + description: 'OPTIONAL: The path to the file containing + certificate authority certificates to use in + verifying a presented server certificate. If + omitted, the proxy will not verify the server''s + certificate. Should be empty if mode is `ISTIO_MUTUAL`.' + type: string + client_certificate: + description: REQUIRED if mode is `MUTUAL`. The + path to the file holding the client-side TLS + certificate to use. Should be empty if mode + is `ISTIO_MUTUAL`. + type: string + mode: + description: Indicates whether connections to + this port should be secured using TLS. The value + of this field determines how TLS is enforced. + format: int32 + type: integer + private_key: + description: REQUIRED if mode is `MUTUAL`. The + path to the file holding the client's private + key. Should be empty if mode is `ISTIO_MUTUAL`. + type: string + sni: + description: SNI string to present to the server + during TLS handshake. + type: string + subject_alt_names: + description: A list of alternate names to verify + the subject identity in the certificate. If + specified, the proxy will verify that the server + certificate's subject alt name matches one of + the specified values. If specified, this list + overrides the value of subject_alt_names from + the ServiceEntry. + items: + type: string + type: array + type: object + type: object + type: array + tls: + description: TLS related settings for connections to the + upstream service. + properties: + ca_certificates: + description: 'OPTIONAL: The path to the file containing + certificate authority certificates to use in verifying + a presented server certificate. If omitted, the proxy + will not verify the server''s certificate. Should + be empty if mode is `ISTIO_MUTUAL`.' + type: string + client_certificate: + description: REQUIRED if mode is `MUTUAL`. The path + to the file holding the client-side TLS certificate + to use. Should be empty if mode is `ISTIO_MUTUAL`. + type: string + mode: + description: Indicates whether connections to this port + should be secured using TLS. The value of this field + determines how TLS is enforced. + format: int32 + type: integer + private_key: + description: REQUIRED if mode is `MUTUAL`. The path + to the file holding the client's private key. Should + be empty if mode is `ISTIO_MUTUAL`. + type: string + sni: + description: SNI string to present to the server during + TLS handshake. + type: string + subject_alt_names: + description: A list of alternate names to verify the + subject identity in the certificate. If specified, + the proxy will verify that the server certificate's + subject alt name matches one of the specified values. + If specified, this list overrides the value of subject_alt_names + from the ServiceEntry. + items: + type: string + type: array + type: object + type: object + type: object + type: object + type: object + status: + description: ServicePolicyStatus defines the observed state of ServicePolicy + properties: + completionTime: + description: Represents time when the strategy was completed. It is + represented in RFC3339 form and is in UTC. + format: date-time + type: string + conditions: + description: The latest available observations of an object's current + state. + items: + description: StrategyCondition describes current state of a strategy. + properties: + lastProbeTime: + description: Last time the condition was checked. + format: date-time + type: string + lastTransitionTime: + description: Last time the condition transit from one status to + another + format: date-time + type: string + message: + description: Human readable message indicating details about last + transition. + type: string + reason: + description: reason for the condition's last transition + type: string + status: + description: Status of the condition, one of True, False, Unknown + type: string + type: + description: Type of strategy condition, Complete or Failed. + type: string + type: object + type: array + startTime: + description: Represents time when the strategy was acknowledged by the + controller. It is represented in RFC3339 form and is in UTC. + format: date-time + type: string + type: object + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/servicemesh.kubesphere.io_strategies.yaml b/config/crd/bases/servicemesh.kubesphere.io_strategies.yaml new file mode 100644 index 000000000..a02650a35 --- /dev/null +++ b/config/crd/bases/servicemesh.kubesphere.io_strategies.yaml @@ -0,0 +1,1166 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: strategies.servicemesh.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.type + description: type of strategy + name: Type + type: string + - JSONPath: .spec.template.spec.hosts + description: destination hosts + name: Hosts + type: string + - JSONPath: .metadata.creationTimestamp + description: 'CreationTimestamp is a timestamp representing the server time when + this object was created. It is not guaranteed to be set in happens-before order + across separate operations. Clients may not set this value. It is represented + in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for + lists. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' + name: Age + type: date + group: servicemesh.kubesphere.io + names: + kind: Strategy + listKind: StrategyList + plural: strategies + singular: strategy + scope: Namespaced + subresources: {} + validation: + openAPIV3Schema: + description: Strategy is the Schema for the strategies API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: StrategySpec defines the desired state of Strategy + properties: + governor: + description: Governor version, the version takes control of all incoming + traffic label version value + type: string + principal: + description: Principal version, the one as reference version label version + value + type: string + selector: + description: Label selector for virtual services. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains + values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to a + set of values. Valid operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator + is In or NotIn, the values array must be non-empty. If the + operator is Exists or DoesNotExist, the values array must + be empty. This array is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator is + "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + strategyPolicy: + description: strategy policy, how the strategy will be applied by the + strategy controller + type: string + template: + description: Template describes the virtual service that will be created. + properties: + metadata: + description: Metadata of the virtual services created from this + template + type: object + spec: + description: Spec indicates the behavior of a virtual service. + properties: + export_to: + description: "A list of namespaces to which this virtual service + is exported. Exporting a virtual service allows it to be used + by sidecars and gateways defined in other namespaces. This + feature provides a mechanism for service owners and mesh administrators + to control the visibility of virtual services across namespace + boundaries. \n If no namespaces are specified then the virtual + service is exported to all namespaces by default. \n The value + \".\" is reserved and defines an export to the same namespace + that the virtual service is declared in. Similarly the value + \"*\" is reserved and defines an export to all namespaces. + \n NOTE: in the current release, the `exportTo` value is restricted + to \".\" or \"*\" (i.e., the current namespace or all namespaces)." + items: + type: string + type: array + gateways: + description: The names of gateways and sidecars that should + apply these routes. A single VirtualService is used for sidecars + inside the mesh as well as for one or more gateways. The selection + condition imposed by this field can be overridden using the + source field in the match conditions of protocol-specific + routes. The reserved word `mesh` is used to imply all the + sidecars in the mesh. When this field is omitted, the default + gateway (`mesh`) will be used, which would apply the rule + to all sidecars in the mesh. If a list of gateway names is + provided, the rules will apply only to the gateways. To apply + the rules to both gateways and sidecars, specify `mesh` as + one of the gateway names. + items: + type: string + type: array + hosts: + description: "The destination hosts to which traffic is being + sent. Could be a DNS name with wildcard prefix or an IP address. + \ Depending on the platform, short-names can also be used + instead of a FQDN (i.e. has no dots in the name). In such + a scenario, the FQDN of the host would be derived based on + the underlying platform. \n A single VirtualService can be + used to describe all the traffic properties of the corresponding + hosts, including those for multiple HTTP and TCP ports. Alternatively, + the traffic properties of a host can be defined using more + than one VirtualService, with certain caveats. Refer to the + [Operations Guide](https://istio.io/docs/ops/traffic-management/deploy-guidelines/#multiple-virtual-services-and-destination-rules-for-the-same-host) + for details. \n *Note for Kubernetes users*: When short names + are used (e.g. \"reviews\" instead of \"reviews.default.svc.cluster.local\"), + Istio will interpret the short name based on the namespace + of the rule, not the service. A rule in the \"default\" namespace + containing a host \"reviews\" will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated with the reviews + service. _To avoid potential misconfigurations, it is recommended + to always use fully qualified domain names over short names._ + \n The hosts field applies to both HTTP and TCP services. + Service inside the mesh, i.e., those found in the service + registry, must always be referred to using their alphanumeric + names. IP addresses are allowed only for services defined + via the Gateway." + items: + type: string + type: array + http: + description: An ordered list of route rules for HTTP traffic. + HTTP routes will be applied to platform service ports named + 'http-*'/'http2-*'/'grpc-*', gateway ports with protocol HTTP/HTTP2/GRPC/ + TLS-terminated-HTTPS and service entry ports using HTTP/HTTP2/GRPC + protocols. The first rule matching an incoming request is + used. + items: + description: Describes match conditions and actions for routing + HTTP/1.1, HTTP2, and gRPC traffic. See VirtualService for + usage examples. + properties: + append_headers: + additionalProperties: + type: string + description: $hide_from_docs + type: object + append_request_headers: + additionalProperties: + type: string + description: $hide_from_docs + type: object + append_response_headers: + additionalProperties: + type: string + description: $hide_from_docs + type: object + cors_policy: + description: Cross-Origin Resource Sharing policy (CORS). + Refer to [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) + for further details about cross origin resource sharing. + properties: + allow_credentials: + description: Indicates whether the caller is allowed + to send the actual request (not the preflight) using + credentials. Translates to `Access-Control-Allow-Credentials` + header. + properties: + value: + description: The bool value. + type: boolean + type: object + allow_headers: + description: List of HTTP headers that can be used + when requesting the resource. Serialized to Access-Control-Allow-Headers + header. + items: + type: string + type: array + allow_methods: + description: List of HTTP methods allowed to access + the resource. The content will be serialized into + the Access-Control-Allow-Methods header. + items: + type: string + type: array + allow_origin: + description: The list of origins that are allowed + to perform CORS requests. The content will be serialized + into the Access-Control-Allow-Origin header. Wildcard + * will allow all origins. + items: + type: string + type: array + expose_headers: + description: A white list of HTTP headers that the + browsers are allowed to access. Serialized into + Access-Control-Expose-Headers header. + items: + type: string + type: array + max_age: + description: Specifies how long the results of a preflight + request can be cached. Translates to the `Access-Control-Max-Age` + header. + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less + than one second are represented with a 0 `seconds` + field and a positive or negative `nanos` field. + For durations of one second or more, a non-zero + value for the `nanos` field must be of the same + sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. + Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 + days/year * 10000 years' + format: int64 + type: integer + type: object + type: object + fault: + description: Fault injection policy to apply on HTTP traffic + at the client side. Note that timeouts or retries will + not be enabled when faults are enabled on the client + side. + properties: + abort: + description: Abort Http request attempts and return + error codes back to downstream service, giving the + impression that the upstream service is faulty. + properties: + percent: + description: Percentage of requests to be aborted + with the error code provided (0-100). Use of + integer `percent` value is deprecated. Use the + double `percentage` field instead. + format: int32 + type: integer + percentage: + description: Percentage of requests to be aborted + with the error code provided. + properties: + value: {} + type: object + type: object + delay: + description: Delay requests before forwarding, emulating + various failures such as network issues, overloaded + upstream service, etc. + properties: + percent: + description: Percentage of requests on which the + delay will be injected (0-100). Use of integer + `percent` value is deprecated. Use the double + `percentage` field instead. + format: int32 + type: integer + percentage: + description: Percentage of requests on which the + delay will be injected. + properties: + value: {} + type: object + type: object + type: object + headers: + description: Header manipulation rules + properties: + request: + description: Header manipulation rules to apply before + forwarding a request to the destination service + properties: + add: + additionalProperties: + type: string + description: Append the given values to the headers + specified by keys (will create a comma-separated + list of values) + type: object + remove: + description: Remove a the specified headers + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Overwrite the headers specified by + key with the given values + type: object + type: object + response: + description: Header manipulation rules to apply before + returning a response to the caller + properties: + add: + additionalProperties: + type: string + description: Append the given values to the headers + specified by keys (will create a comma-separated + list of values) + type: object + remove: + description: Remove a the specified headers + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Overwrite the headers specified by + key with the given values + type: object + type: object + type: object + match: + description: Match conditions to be satisfied for the + rule to be activated. All conditions inside a single + match block have AND semantics, while the list of match + blocks have OR semantics. The rule is matched if any + one of the match blocks succeed. + items: + description: "HttpMatchRequest specifies a set of criterion + to be met in order for the rule to be applied to the + HTTP request. For example, the following restricts + the rule to match only requests where the URL path + starts with /ratings/v2/ and the request contains + a custom `end-user` header with value `jason`. \n + ```yaml apiVersion: networking.istio.io/v1alpha3 kind: + VirtualService metadata: name: ratings-route spec: + \ hosts: - ratings.prod.svc.cluster.local http: + \ - match: - headers: end-user: exact: + jason uri: prefix: \"/ratings/v2/\" + \ ignoreUriCase: true route: - destination: + \ host: ratings.prod.svc.cluster.local ``` + \n HTTPMatchRequest CANNOT be empty." + properties: + authority: + description: "HTTP Authority values are case-sensitive + and formatted as follows: \n - `exact: \"value\"` + for exact string match \n - `prefix: \"value\"` + for prefix-based match \n - `regex: \"value\"` + for ECMAscript style regex-based match" + type: object + gateways: + description: $hide_from_docs + items: + type: string + type: array + headers: + description: "The header keys must be lowercase + and use hyphen as the separator, e.g. _x-request-id_. + \n Header values are case-sensitive and formatted + as follows: \n - `exact: \"value\"` for exact + string match \n - `prefix: \"value\"` for prefix-based + match \n - `regex: \"value\"` for ECMAscript style + regex-based match \n **Note:** The keys `uri`, + `scheme`, `method`, and `authority` will be ignored." + ignore_uri_case: + description: "Flag to specify whether the URI matching + should be case-insensitive. \n **Note:** The case + will be ignored only in the case of `exact` and + `prefix` URI matches." + type: boolean + method: + description: "HTTP Method values are case-sensitive + and formatted as follows: \n - `exact: \"value\"` + for exact string match \n - `prefix: \"value\"` + for prefix-based match \n - `regex: \"value\"` + for ECMAscript style regex-based match" + type: object + name: + description: The name assigned to a match. The match's + name will be concatenated with the parent route's + name and will be logged in the access logs for + requests matching this route. + type: string + port: + description: Specifies the ports on the host that + is being addressed. Many services only expose + a single port or label ports with the protocols + they support, in these cases it is not required + to explicitly select the port. + format: int32 + type: integer + query_params: + description: "Query parameters for matching. \n + Ex: - For a query parameter like \"?key=true\", + the map key would be \"key\" and the string + match could be defined as `exact: \"true\"`. - + For a query parameter like \"?key\", the map key + would be \"key\" and the string match could + be defined as `exact: \"\"`. - For a query parameter + like \"?key=123\", the map key would be \"key\" + and the string match could be defined as `regex: + \"\\d+$\"`. Note that this configuration will + only match values like \"123\" but not \"a123\" + or \"123a\". \n **Note:** `prefix` matching is + currently not supported." + scheme: + description: "URI Scheme values are case-sensitive + and formatted as follows: \n - `exact: \"value\"` + for exact string match \n - `prefix: \"value\"` + for prefix-based match \n - `regex: \"value\"` + for ECMAscript style regex-based match" + type: object + source_labels: + additionalProperties: + type: string + description: One or more labels that constrain the + applicability of a rule to workloads with the + given labels. If the VirtualService has a list + of gateways specified at the top, it must include + the reserved gateway `mesh` for this field to + be applicable. + type: object + uri: + description: "URI to match values are case-sensitive + and formatted as follows: \n - `exact: \"value\"` + for exact string match \n - `prefix: \"value\"` + for prefix-based match \n - `regex: \"value\"` + for ECMAscript style regex-based match \n **Note:** + Case-insensitive matching could be enabled via + the `ignore_uri_case` flag." + type: object + type: object + type: array + mirror: + description: Mirror HTTP traffic to a another destination + in addition to forwarding the requests to the intended + destination. Mirrored traffic is on a best effort basis + where the sidecar/gateway will not wait for the mirrored + cluster to respond before returning the response from + the original destination. Statistics will be generated + for the mirrored destination. + properties: + host: + description: "The name of a service from the service + registry. Service names are looked up from the platform's + service registry (e.g., Kubernetes services, Consul + services, etc.) and from the hosts declared by [ServiceEntry](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry). + Traffic forwarded to destinations that are not found + in either of the two, will be dropped. \n *Note + for Kubernetes users*: When short names are used + (e.g. \"reviews\" instead of \"reviews.default.svc.cluster.local\"), + Istio will interpret the short name based on the + namespace of the rule, not the service. A rule in + the \"default\" namespace containing a host \"reviews + will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated + with the reviews service. _To avoid potential misconfigurations, + it is recommended to always use fully qualified + domain names over short names._" + type: string + port: + description: Specifies the port on the host that is + being addressed. If a service exposes only a single + port it is not required to explicitly select the + port. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + subset: + description: The name of a subset within the service. + Applicable only to services within the mesh. The + subset must be defined in a corresponding DestinationRule. + type: string + type: object + mirror_percent: + description: Percentage of the traffic to be mirrored + by the `mirror` field. If this field is absent, all + the traffic (100%) will be mirrored. Max value is 100. + properties: + value: + description: The uint32 value. + format: int32 + type: integer + type: object + name: + description: The name assigned to the route for debugging + purposes. The route's name will be concatenated with + the match's name and will be logged in the access logs + for requests matching this route/match. + type: string + redirect: + description: A http rule can either redirect or forward + (default) traffic. If traffic passthrough option is + specified in the rule, route/redirect will be ignored. + The redirect primitive can be used to send a HTTP 301 + redirect to a different URI or Authority. + properties: + authority: + description: On a redirect, overwrite the Authority/Host + portion of the URL with this value. + type: string + redirect_code: + description: On a redirect, Specifies the HTTP status + code to use in the redirect response. The default + response code is MOVED_PERMANENTLY (301). + format: int32 + type: integer + uri: + description: On a redirect, overwrite the Path portion + of the URL with this value. Note that the entire + path will be replaced, irrespective of the request + URI being matched as an exact path or prefix. + type: string + type: object + remove_request_headers: + description: $hide_from_docs + items: + type: string + type: array + remove_response_headers: + description: $hide_from_docs + items: + type: string + type: array + retries: + description: Retry policy for HTTP requests. + properties: + attempts: + description: Number of retries for a given request. + The interval between retries will be determined + automatically (25ms+). Actual number of retries + attempted depends on the httpReqTimeout. + format: int32 + type: integer + per_try_timeout: + description: 'Timeout per retry attempt for a given + request. format: 1h/1m/1s/1ms. MUST BE >=1ms.' + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less + than one second are represented with a 0 `seconds` + field and a positive or negative `nanos` field. + For durations of one second or more, a non-zero + value for the `nanos` field must be of the same + sign as the `seconds` field. Must be from -999,999,999 + to +999,999,999 inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. + Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 + days/year * 10000 years' + format: int64 + type: integer + type: object + retry_on: + description: Specifies the conditions under which + retry takes place. One or more policies can be specified + using a ‘,’ delimited list. See the [retry policies](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on) + and [gRPC retry policies](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on) + for more details. + type: string + type: object + rewrite: + description: Rewrite HTTP URIs and Authority headers. + Rewrite cannot be used with Redirect primitive. Rewrite + will be performed before forwarding. + properties: + authority: + description: rewrite the Authority/Host header with + this value. + type: string + uri: + description: rewrite the path (or the prefix) portion + of the URI with this value. If the original URI + was matched based on prefix, the value provided + in this field will replace the corresponding matched + prefix. + type: string + type: object + route: + description: A http rule can either redirect or forward + (default) traffic. The forwarding target can be one + of several versions of a service (see glossary in beginning + of document). Weights associated with the service version + determine the proportion of traffic it receives. + items: + description: "Each routing rule is associated with one + or more service versions (see glossary in beginning + of document). Weights associated with the version + determine the proportion of traffic it receives. For + example, the following rule will route 25% of traffic + for the \"reviews\" service to instances with the + \"v2\" tag and the remaining traffic (i.e., 75%) to + \"v1\". \n ```yaml apiVersion: networking.istio.io/v1alpha3 + kind: VirtualService metadata: name: reviews-route + spec: hosts: - reviews.prod.svc.cluster.local + \ http: - route: - destination: host: + reviews.prod.svc.cluster.local subset: v2 + \ weight: 25 - destination: host: + reviews.prod.svc.cluster.local subset: v1 + \ weight: 75 ``` \n And the associated DestinationRule + \n ```yaml apiVersion: networking.istio.io/v1alpha3 + kind: DestinationRule metadata: name: reviews-destination + spec: host: reviews.prod.svc.cluster.local subsets: + \ - name: v1 labels: version: v1 - name: + v2 labels: version: v2 ``` \n Traffic can + also be split across two entirely different services + without having to define new subsets. For example, + the following rule forwards 25% of traffic to reviews.com + to dev.reviews.com \n ```yaml apiVersion: networking.istio.io/v1alpha3 + kind: VirtualService metadata: name: reviews-route-two-domains + spec: hosts: - reviews.com http: - route: + \ - destination: host: dev.reviews.com + \ weight: 25 - destination: host: + reviews.com weight: 75 ```" + properties: + append_request_headers: + additionalProperties: + type: string + description: Use of `append_request_headers` is + deprecated. Use the `headers` field instead. + type: object + append_response_headers: + additionalProperties: + type: string + description: Use of `append_response_headers` is + deprecated. Use the `headers` field instead. + type: object + destination: + description: Destination uniquely identifies the + instances of a service to which the request/connection + should be forwarded to. + properties: + host: + description: "The name of a service from the + service registry. Service names are looked + up from the platform's service registry (e.g., + Kubernetes services, Consul services, etc.) + and from the hosts declared by [ServiceEntry](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry). + Traffic forwarded to destinations that are + not found in either of the two, will be dropped. + \n *Note for Kubernetes users*: When short + names are used (e.g. \"reviews\" instead of + \"reviews.default.svc.cluster.local\"), Istio + will interpret the short name based on the + namespace of the rule, not the service. A + rule in the \"default\" namespace containing + a host \"reviews will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated + with the reviews service. _To avoid potential + misconfigurations, it is recommended to always + use fully qualified domain names over short + names._" + type: string + port: + description: Specifies the port on the host + that is being addressed. If a service exposes + only a single port it is not required to explicitly + select the port. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + subset: + description: The name of a subset within the + service. Applicable only to services within + the mesh. The subset must be defined in a + corresponding DestinationRule. + type: string + type: object + headers: + description: Header manipulation rules + properties: + request: + description: Header manipulation rules to apply + before forwarding a request to the destination + service + properties: + add: + additionalProperties: + type: string + description: Append the given values to + the headers specified by keys (will create + a comma-separated list of values) + type: object + remove: + description: Remove a the specified headers + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Overwrite the headers specified + by key with the given values + type: object + type: object + response: + description: Header manipulation rules to apply + before returning a response to the caller + properties: + add: + additionalProperties: + type: string + description: Append the given values to + the headers specified by keys (will create + a comma-separated list of values) + type: object + remove: + description: Remove a the specified headers + items: + type: string + type: array + set: + additionalProperties: + type: string + description: Overwrite the headers specified + by key with the given values + type: object + type: object + type: object + remove_request_headers: + description: Use of `remove_request_headers` is + deprecated. Use the `headers` field instead. + items: + type: string + type: array + remove_response_headers: + description: Use of `remove_response_header` is + deprecated. Use the `headers` field instead. + items: + type: string + type: array + weight: + description: The proportion of traffic to be forwarded + to the service version. (0-100). Sum of weights + across destinations SHOULD BE == 100. If there + is only one destination in a rule, the weight + value is assumed to be 100. + format: int32 + type: integer + type: object + type: array + timeout: + description: Timeout for HTTP requests. + properties: + nanos: + description: Signed fractions of a second at nanosecond + resolution of the span of time. Durations less than + one second are represented with a 0 `seconds` field + and a positive or negative `nanos` field. For durations + of one second or more, a non-zero value for the + `nanos` field must be of the same sign as the `seconds` + field. Must be from -999,999,999 to +999,999,999 + inclusive. + format: int32 + type: integer + seconds: + description: 'Signed seconds of the span of time. + Must be from -315,576,000,000 to +315,576,000,000 + inclusive. Note: these bounds are computed from: + 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year + * 10000 years' + format: int64 + type: integer + type: object + websocket_upgrade: + description: Deprecated. Websocket upgrades are done automatically + starting from Istio 1.0. $hide_from_docs + type: boolean + type: object + type: array + tcp: + description: An ordered list of route rules for opaque TCP traffic. + TCP routes will be applied to any port that is not a HTTP + or TLS port. The first rule matching an incoming request is + used. + items: + description: "Describes match conditions and actions for routing + TCP traffic. The following routing rule forwards traffic + arriving at port 27017 for mongo.prod.svc.cluster.local + to another Mongo server on port 5555. \n ```yaml apiVersion: + networking.istio.io/v1alpha3 kind: VirtualService metadata: + \ name: bookinfo-Mongo spec: hosts: - mongo.prod.svc.cluster.local + \ tcp: - match: - port: 27017 route: - destination: + \ host: mongo.backup.svc.cluster.local port: + \ number: 5555 ```" + properties: + match: + description: Match conditions to be satisfied for the + rule to be activated. All conditions inside a single + match block have AND semantics, while the list of match + blocks have OR semantics. The rule is matched if any + one of the match blocks succeed. + items: + description: L4 connection match attributes. Note that + L4 connection matching support is incomplete. + properties: + destination_subnets: + description: IPv4 or IPv6 ip addresses of destination + with optional subnet. E.g., a.b.c.d/xx form or + just a.b.c.d. + items: + type: string + type: array + gateways: + description: Names of gateways where the rule should + be applied to. Gateway names at the top of the + VirtualService (if any) are overridden. The gateway + match is independent of sourceLabels. + items: + type: string + type: array + port: + description: Specifies the port on the host that + is being addressed. Many services only expose + a single port or label ports with the protocols + they support, in these cases it is not required + to explicitly select the port. + format: int32 + type: integer + source_labels: + additionalProperties: + type: string + description: One or more labels that constrain the + applicability of a rule to workloads with the + given labels. If the VirtualService has a list + of gateways specified at the top, it should include + the reserved gateway `mesh` in order for this + field to be applicable. + type: object + source_subnet: + description: IPv4 or IPv6 ip address of source with + optional subnet. E.g., a.b.c.d/xx form or just + a.b.c.d $hide_from_docs + type: string + type: object + type: array + route: + description: The destination to which the connection should + be forwarded to. + items: + description: L4 routing rule weighted destination. + properties: + destination: + description: Destination uniquely identifies the + instances of a service to which the request/connection + should be forwarded to. + properties: + host: + description: "The name of a service from the + service registry. Service names are looked + up from the platform's service registry (e.g., + Kubernetes services, Consul services, etc.) + and from the hosts declared by [ServiceEntry](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry). + Traffic forwarded to destinations that are + not found in either of the two, will be dropped. + \n *Note for Kubernetes users*: When short + names are used (e.g. \"reviews\" instead of + \"reviews.default.svc.cluster.local\"), Istio + will interpret the short name based on the + namespace of the rule, not the service. A + rule in the \"default\" namespace containing + a host \"reviews will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated + with the reviews service. _To avoid potential + misconfigurations, it is recommended to always + use fully qualified domain names over short + names._" + type: string + port: + description: Specifies the port on the host + that is being addressed. If a service exposes + only a single port it is not required to explicitly + select the port. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + subset: + description: The name of a subset within the + service. Applicable only to services within + the mesh. The subset must be defined in a + corresponding DestinationRule. + type: string + type: object + weight: + description: The proportion of traffic to be forwarded + to the service version. If there is only one destination + in a rule, all traffic will be routed to it irrespective + of the weight. + format: int32 + type: integer + type: object + type: array + type: object + type: array + tls: + description: 'An ordered list of route rule for non-terminated + TLS & HTTPS traffic. Routing is typically performed using + the SNI value presented by the ClientHello message. TLS routes + will be applied to platform service ports named ''https-*'', + ''tls-*'', unterminated gateway ports using HTTPS/TLS protocols + (i.e. with "passthrough" TLS mode) and service entry ports + using HTTPS/TLS protocols. The first rule matching an incoming + request is used. NOTE: Traffic ''https-*'' or ''tls-*'' ports + without associated virtual service will be treated as opaque + TCP traffic.' + items: + description: "Describes match conditions and actions for routing + unterminated TLS traffic (TLS/HTTPS) The following routing + rule forwards unterminated TLS traffic arriving at port + 443 of gateway called \"mygateway\" to internal services + in the mesh based on the SNI value. \n ```yaml apiVersion: + networking.istio.io/v1alpha3 kind: VirtualService metadata: + \ name: bookinfo-sni spec: hosts: - \"*.bookinfo.com\" + \ gateways: - mygateway tls: - match: - port: + 443 sniHosts: - login.bookinfo.com route: + \ - destination: host: login.prod.svc.cluster.local + \ - match: - port: 443 sniHosts: - reviews.bookinfo.com + \ route: - destination: host: reviews.prod.svc.cluster.local + ```" + properties: + match: + description: Match conditions to be satisfied for the + rule to be activated. All conditions inside a single + match block have AND semantics, while the list of match + blocks have OR semantics. The rule is matched if any + one of the match blocks succeed. + items: + description: TLS connection match attributes. + properties: + destination_subnets: + description: IPv4 or IPv6 ip addresses of destination + with optional subnet. E.g., a.b.c.d/xx form or + just a.b.c.d. + items: + type: string + type: array + gateways: + description: Names of gateways where the rule should + be applied to. Gateway names at the top of the + VirtualService (if any) are overridden. The gateway + match is independent of sourceLabels. + items: + type: string + type: array + port: + description: Specifies the port on the host that + is being addressed. Many services only expose + a single port or label ports with the protocols + they support, in these cases it is not required + to explicitly select the port. + format: int32 + type: integer + sni_hosts: + description: SNI (server name indicator) to match + on. Wildcard prefixes can be used in the SNI value, + e.g., *.com will match foo.example.com as well + as example.com. An SNI value must be a subset + (i.e., fall within the domain) of the corresponding + virtual serivce's hosts. + items: + type: string + type: array + source_labels: + additionalProperties: + type: string + description: One or more labels that constrain the + applicability of a rule to workloads with the + given labels. If the VirtualService has a list + of gateways specified at the top, it should include + the reserved gateway `mesh` in order for this + field to be applicable. + type: object + source_subnet: + description: IPv4 or IPv6 ip address of source with + optional subnet. E.g., a.b.c.d/xx form or just + a.b.c.d $hide_from_docs + type: string + type: object + type: array + route: + description: The destination to which the connection should + be forwarded to. + items: + description: L4 routing rule weighted destination. + properties: + destination: + description: Destination uniquely identifies the + instances of a service to which the request/connection + should be forwarded to. + properties: + host: + description: "The name of a service from the + service registry. Service names are looked + up from the platform's service registry (e.g., + Kubernetes services, Consul services, etc.) + and from the hosts declared by [ServiceEntry](https://istio.io/docs/reference/config/networking/service-entry/#ServiceEntry). + Traffic forwarded to destinations that are + not found in either of the two, will be dropped. + \n *Note for Kubernetes users*: When short + names are used (e.g. \"reviews\" instead of + \"reviews.default.svc.cluster.local\"), Istio + will interpret the short name based on the + namespace of the rule, not the service. A + rule in the \"default\" namespace containing + a host \"reviews will be interpreted as \"reviews.default.svc.cluster.local\", + irrespective of the actual namespace associated + with the reviews service. _To avoid potential + misconfigurations, it is recommended to always + use fully qualified domain names over short + names._" + type: string + port: + description: Specifies the port on the host + that is being addressed. If a service exposes + only a single port it is not required to explicitly + select the port. + properties: + number: + description: Valid port number + format: int32 + type: integer + type: object + subset: + description: The name of a subset within the + service. Applicable only to services within + the mesh. The subset must be defined in a + corresponding DestinationRule. + type: string + type: object + weight: + description: The proportion of traffic to be forwarded + to the service version. If there is only one destination + in a rule, all traffic will be routed to it irrespective + of the weight. + format: int32 + type: integer + type: object + type: array + type: object + type: array + type: object + type: object + type: + description: Strategy type + type: string + type: object + status: + description: StrategyStatus defines the observed state of Strategy + properties: + completionTime: + description: Represents time when the strategy was completed. It is + represented in RFC3339 form and is in UTC. + format: date-time + type: string + conditions: + description: The latest available observations of an object's current + state. + items: + description: StrategyCondition describes current state of a strategy. + properties: + lastProbeTime: + description: Last time the condition was checked. + format: date-time + type: string + lastTransitionTime: + description: Last time the condition transit from one status to + another + format: date-time + type: string + message: + description: Human readable message indicating details about last + transition. + type: string + reason: + description: reason for the condition's last transition + type: string + status: + description: Status of the condition, one of True, False, Unknown + type: string + type: + description: Type of strategy condition, Complete or Failed. + type: string + type: object + type: array + startTime: + description: Represents time when the strategy was acknowledged by the + controller. It is represented in RFC3339 form and is in UTC. + format: date-time + type: string + type: object + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/tenant.kubesphere.io_workspaces.yaml b/config/crd/bases/tenant.kubesphere.io_workspaces.yaml new file mode 100644 index 000000000..2eb730876 --- /dev/null +++ b/config/crd/bases/tenant.kubesphere.io_workspaces.yaml @@ -0,0 +1,54 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspaces.tenant.kubesphere.io +spec: + group: tenant.kubesphere.io + names: + kind: Workspace + listKind: WorkspaceList + plural: workspaces + singular: workspace + scope: Namespaced + validation: + openAPIV3Schema: + description: Workspace is the Schema for the workspaces API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: WorkspaceSpec defines the desired state of Workspace + properties: + manager: + type: string + type: object + status: + description: WorkspaceStatus defines the observed state of Workspace + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_devopsprojects.yaml b/config/crds/devops.kubesphere.io_devopsprojects.yaml new file mode 100644 index 000000000..d5efd628d --- /dev/null +++ b/config/crds/devops.kubesphere.io_devopsprojects.yaml @@ -0,0 +1,59 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: devopsprojects.devops.kubesphere.io +spec: + group: devops.kubesphere.io + names: + categories: + - devops + kind: DevOpsProject + listKind: DevOpsProjectList + plural: devopsprojects + singular: devopsproject + scope: Cluster + validation: + openAPIV3Schema: + description: DevOpsProject is the Schema for the devopsprojects API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: DevOpsProjectSpec defines the desired state of DevOpsProject + type: object + status: + description: DevOpsProjectStatus defines the observed state of DevOpsProject + properties: + adminNamespace: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + version: v1alpha3 + versions: + - name: v1alpha3 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_pipelines.yaml b/config/crds/devops.kubesphere.io_pipelines.yaml new file mode 100644 index 000000000..e6e282776 --- /dev/null +++ b/config/crds/devops.kubesphere.io_pipelines.yaml @@ -0,0 +1,260 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: pipelines.devops.kubesphere.io +spec: + group: devops.kubesphere.io + names: + kind: Pipeline + listKind: PipelineList + plural: pipelines + singular: pipeline + scope: Namespaced + validation: + openAPIV3Schema: + description: Pipeline is the Schema for the pipelines API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PipelineSpec defines the desired state of Pipeline + properties: + multi_branch_pipeline: + properties: + bitbucket_server_source: + properties: + api_uri: + type: string + credential_id: + type: string + discover_branches: + type: integer + discover_pr_from_forks: + properties: + strategy: + type: integer + trust: + type: integer + type: object + discover_pr_from_origin: + type: integer + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + owner: + type: string + regex_filter: + type: string + repo: + type: string + scm_id: + type: string + type: object + descriptio: + type: string + discarder: + properties: + days_to_keep: + type: string + num_to_keep: + type: string + type: object + git_source: + properties: + credential_id: + type: string + discover_branches: + type: boolean + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + regex_filter: + type: string + scm_id: + type: string + url: + type: string + type: object + github_source: + properties: + api_uri: + type: string + credential_id: + type: string + discover_branches: + type: integer + discover_pr_from_forks: + properties: + strategy: + type: integer + trust: + type: integer + type: object + discover_pr_from_origin: + type: integer + git_clone_option: + properties: + depth: + type: integer + shallow: + type: boolean + timeout: + type: integer + type: object + owner: + type: string + regex_filter: + type: string + repo: + type: string + scm_id: + type: string + type: object + multibranch_job_trigger: + properties: + create_action_job_to_trigger: + type: string + delete_action_job_to_trigger: + type: string + type: object + name: + type: string + script_path: + type: string + single_svn_source: + properties: + credential_id: + type: string + remote: + type: string + scm_id: + type: string + type: object + source_type: + type: string + svn_source: + properties: + credential_id: + type: string + excludes: + type: string + includes: + type: string + remote: + type: string + scm_id: + type: string + type: object + timer_trigger: + properties: + cron: + description: user in no scm job + type: string + interval: + description: use in multi-branch job + type: string + type: object + required: + - name + - script_path + - source_type + type: object + pipeline: + properties: + descriptio: + type: string + disable_concurrent: + type: boolean + discarder: + properties: + days_to_keep: + type: string + num_to_keep: + type: string + type: object + jenkinsfile: + type: string + name: + type: string + parameters: + items: + properties: + default_value: + type: string + description: + type: string + name: + type: string + type: + type: string + required: + - name + - type + type: object + type: array + remote_trigger: + properties: + token: + type: string + type: object + timer_trigger: + properties: + cron: + description: user in no scm job + type: string + interval: + description: use in multi-branch job + type: string + type: object + required: + - name + type: object + type: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + type: string + required: + - type + type: object + status: + description: PipelineStatus defines the observed state of Pipeline + type: object + type: object + version: v1alpha3 + versions: + - name: v1alpha3 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_s2ibinaries.yaml b/config/crds/devops.kubesphere.io_s2ibinaries.yaml new file mode 100644 index 000000000..1c0dba8b4 --- /dev/null +++ b/config/crds/devops.kubesphere.io_s2ibinaries.yaml @@ -0,0 +1,86 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: s2ibinaries.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.fileName + name: FileName + type: string + - JSONPath: .spec.md5 + name: MD5 + type: string + - JSONPath: .spec.size + name: Size + type: string + - JSONPath: .status.phase + name: Phase + type: string + group: devops.kubesphere.io + names: + kind: S2iBinary + listKind: S2iBinaryList + plural: s2ibinaries + singular: s2ibinary + scope: Namespaced + subresources: {} + validation: + openAPIV3Schema: + description: S2iBinary is the Schema for the s2ibinaries API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBinarySpec defines the desired state of S2iBinary + properties: + downloadURL: + description: DownloadURL in KubeSphere + type: string + fileName: + description: FileName is filename of binary + type: string + md5: + description: MD5 is Binary's MD5 Hash + type: string + size: + description: Size is the file size of file + type: string + uploadTimeStamp: + description: UploadTime is last upload time + format: date-time + type: string + type: object + status: + description: S2iBinaryStatus defines the observed state of S2iBinary + properties: + phase: + description: Phase is status of S2iBinary . Possible value is "Ready","UnableToDownload" + type: string + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_s2ibuilders.yaml b/config/crds/devops.kubesphere.io_s2ibuilders.yaml new file mode 100644 index 000000000..cdd428f65 --- /dev/null +++ b/config/crds/devops.kubesphere.io_s2ibuilders.yaml @@ -0,0 +1,578 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: s2ibuilders.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .status.runCount + name: RunCount + type: integer + - JSONPath: .status.lastRunState + name: LastRunState + type: string + - JSONPath: .status.lastRunName + name: LastRunName + type: string + - JSONPath: .status.lastRunStartTime + name: LastRunStartTime + type: date + group: devops.kubesphere.io + names: + kind: S2iBuilder + listKind: S2iBuilderList + plural: s2ibuilders + shortNames: + - s2ib + singular: s2ibuilder + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: S2iBuilder is the Schema for the s2ibuilders API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBuilderSpec defines the desired state of S2iBuilder + properties: + config: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file' + properties: + addHost: + description: AddHost Add a line to /etc/hosts for test purpose or + private use in LAN. Its format is host:IP,muliple hosts can be + added by using multiple --add-host + items: + type: string + type: array + asDockerfile: + description: AsDockerfile indicates the path where the Dockerfile + should be written instead of building a new image. + type: string + assembleUser: + description: AssembleUser specifies the user to run the assemble + script in container + type: string + blockOnBuild: + description: BlockOnBuild prevents s2i from performing a docker + build operation if one is necessary to execute ONBUILD commands, + or to layer source code into the container for images that don't + have a tar binary available, if the image contains ONBUILD commands + that would be executed. + type: boolean + branchExpression: + description: Regular expressions, ignoring names that do not match + the provided regular expression + type: string + buildVolumes: + description: BuildVolumes specifies a list of volumes to mount to + container running the build. + items: + type: string + type: array + builderBaseImageVersion: + description: BuilderBaseImageVersion provides optional version information + about the builder base image. + type: string + builderImage: + description: BuilderImage describes which image is used for building + the result images. + type: string + builderImageVersion: + description: BuilderImageVersion provides optional version information + about the builder image. + type: string + builderPullPolicy: + description: BuilderPullPolicy specifies when to pull the builder + image + type: string + callbackUrl: + description: CallbackURL is a URL which is called upon successful + build to inform about that fact. + type: string + cgroupLimits: + description: CGroupLimits describes the cgroups limits that will + be applied to any containers run by s2i. + properties: + cpuPeriod: + format: int64 + type: integer + cpuQuota: + format: int64 + type: integer + cpuShares: + format: int64 + type: integer + memoryLimitBytes: + format: int64 + type: integer + memorySwap: + format: int64 + type: integer + parent: + type: string + required: + - cpuPeriod + - cpuQuota + - cpuShares + - memoryLimitBytes + - memorySwap + - parent + type: object + contextDir: + description: Specify a relative directory inside the application + repository that should be used as a root directory for the application. + type: string + description: + description: Description is a result image description label. The + default is no description. + type: string + destination: + description: Destination specifies a location where the untar operation + will place its artifacts. + type: string + displayName: + description: DisplayName is a result image display-name label. This + defaults to the output image name. + type: string + dockerConfig: + description: DockerConfig describes how to access host docker daemon. + properties: + caFile: + description: CAFile is the certificate authority file path for + a TLS connection + type: string + certFile: + description: CertFile is the certificate file path for a TLS + connection + type: string + endPoint: + description: Endpoint is the docker network endpoint or socket + type: string + keyFile: + description: KeyFile is the key file path for a TLS connection + type: string + tlsVerify: + description: TLSVerify indicates if TLS peer must be verified + type: boolean + useTLS: + description: UseTLS indicates if TLS must be used + type: boolean + required: + - caFile + - certFile + - endPoint + - keyFile + - tlsVerify + - useTLS + type: object + dockerNetworkMode: + description: DockerNetworkMode is used to set the docker network + setting to --net=container: when the builder is invoked from + a container. + type: string + dropCapabilities: + description: DropCapabilities contains a list of capabilities to + drop when executing containers + items: + type: string + type: array + environment: + description: Environment is a map of environment variables to be + passed to the image. + items: + description: EnvironmentSpec specifies a single environment variable. + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + excludeRegExp: + description: ExcludeRegExp contains a string representation of the + regular expression desired for deciding which files to exclude + from the tar stream + type: string + export: + description: Export Push the result image to specify image registry + in tag + type: boolean + gitSecretRef: + description: GitSecretRef is the BasicAuth Secret of Git Clone + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + hasOnBuild: + description: HasOnBuild will be set to true if the builder image + contains ONBUILD instructions + type: boolean + imageName: + description: ImageName Contains the registry address and reponame, + tag should set by field tag alone + type: string + imageScriptsUrl: + description: ImageScriptsURL is the default location to find the + assemble/run scripts for a builder image. This url can be a reference + within the builder image if the scheme is specified as image:// + type: string + imageWorkDir: + description: ImageWorkDir is the default working directory for the + builder image. + type: string + incremental: + description: Incremental describes whether to try to perform incremental + build. + type: boolean + incrementalAuthentication: + description: IncrementalAuthentication holds the authentication + information for pulling the previous image from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + incrementalFromTag: + description: IncrementalFromTag sets an alternative image tag to + look for existing artifacts. Tag is used by default if this is + not set. + type: string + injections: + description: Injections specifies a list source/destination folders + that are injected to the container that runs assemble. All files + we inject will be truncated after the assemble script finishes. + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume to + - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + isBinaryURL: + description: IsBinaryURL explain the type of SourceURL. If it is + IsBinaryURL, it will download the file directly without using + git. + type: boolean + keepSymlinks: + description: KeepSymlinks indicates to copy symlinks as symlinks. + Default behavior is to follow symlinks and copy files by content. + type: boolean + labelNamespace: + description: LabelNamespace provides the namespace under which the + labels will be generated. + type: string + labels: + additionalProperties: + type: string + description: Labels specify labels and their values to be applied + to the resulting image. Label keys must have non-zero length. + The labels defined here override generated labels in case they + have the same name. + type: object + layeredBuild: + description: LayeredBuild describes if this is build which layered + scripts and sources on top of BuilderImage. + type: boolean + nodeAffinityKey: + description: The key of Node Affinity. + type: string + nodeAffinityValues: + description: The values of Node Affinity. + items: + type: string + type: array + outputBuildResult: + description: Whether output build result to status. + type: boolean + outputImageName: + description: OutputImageName is a result image name without tag, + default is latest. tag will append to ImageName in the end + type: string + preserveWorkingDir: + description: PreserveWorkingDir describes if working directory should + be left after processing. + type: boolean + previousImagePullPolicy: + description: PreviousImagePullPolicy specifies when to pull the + previously build image when doing incremental build + type: string + pullAuthentication: + description: PullAuthentication holds the authentication information + for pulling the Docker images from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + pushAuthentication: + description: PullAuthentication holds the authentication information + for pulling the Docker images from private repositories + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + removePreviousImage: + description: RemovePreviousImage describes if previous image should + be removed after successful build. This applies only to incremental + builds. + type: boolean + revisionId: + description: The RevisionId is a branch name or a SHA-1 hash of + every important thing about the commit + type: string + runImage: + description: RunImage will trigger a "docker run ..." invocation + of the produced image so the user can see if it operates as he + would expect + type: boolean + runtimeArtifacts: + description: RuntimeArtifacts specifies a list of source/destination + pairs that will be copied from builder to a runtime image. Source + can be a file or directory. Destination must be a directory. Regardless + whether it is an absolute or relative path, it will be placed + into image's WORKDIR. Destination also can be empty or equals + to ".", in this case it just refers to a root of WORKDIR. In case + it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files + label on a RuntimeImage. + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume to + - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + runtimeAuthentication: + description: RuntimeAuthentication holds the authentication information + for pulling the runtime Docker images from private repositories. + properties: + email: + type: string + password: + type: string + secretRef: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + serverAddress: + type: string + username: + type: string + type: object + runtimeImage: + description: RuntimeImage specifies the image that will be a base + for resulting image and will be used for running an application. + By default, BuilderImage is used for building and running, but + the latter may be overridden. + type: string + runtimeImagePullPolicy: + description: RuntimeImagePullPolicy specifies when to pull a runtime + image. + type: string + scriptDownloadProxyConfig: + description: ScriptDownloadProxyConfig optionally specifies the + http and https proxy to use when downloading scripts + properties: + httpProxy: + type: string + httpsProxy: + type: string + type: object + scriptsUrl: + description: ScriptsURL is a URL describing where to fetch the S2I + scripts from during build process. This url can be a reference + within the builder image if the scheme is specified as image:// + type: string + secretCode: + description: SecretCode + type: string + securityOpt: + description: SecurityOpt are passed as options to the docker containers + launched by s2i. + items: + type: string + type: array + sourceUrl: + description: SourceURL is url of the codes such as https://github.com/a/b.git + type: string + tag: + description: Tag is a result image tag name. + type: string + taintKey: + description: The name of taint. + type: string + usage: + description: Usage allows for properly shortcircuiting s2i logic + when `s2i usage` is invoked + type: boolean + workingDir: + description: WorkingDir describes temporary directory used for downloading + sources, scripts and tar operations. + type: string + workingSourceDir: + description: WorkingSourceDir describes the subdirectory off of + WorkingDir set up during the repo download that is later used + as the root for ignore processing + type: string + required: + - imageName + - sourceUrl + type: object + fromTemplate: + description: FromTemplate define some inputs from user + properties: + builderImage: + description: BaseImage specify which version of this template to + use + type: string + name: + description: Name specify a template to use, so many fields in Config + can left empty + type: string + parameters: + description: Parameters must use with `template`, fill some parameters + which template will use + items: + properties: + defaultValue: + type: string + description: + type: string + key: + type: string + optValues: + items: + type: string + type: array + required: + type: boolean + type: + type: string + value: + type: string + type: object + type: array + type: object + type: object + status: + description: S2iBuilderStatus defines the observed state of S2iBuilder + properties: + lastRunName: + description: LastRunState return the name of the newest run of this + builder + type: string + lastRunStartTime: + description: LastRunStartTime return the startTime of the newest run + of this builder + format: date-time + type: string + lastRunState: + description: LastRunState return the state of the newest run of this + builder + type: string + runCount: + description: RunCount represent the sum of s2irun of this builder + type: integer + required: + - runCount + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_s2ibuildertemplates.yaml b/config/crds/devops.kubesphere.io_s2ibuildertemplates.yaml new file mode 100644 index 000000000..69d210892 --- /dev/null +++ b/config/crds/devops.kubesphere.io_s2ibuildertemplates.yaml @@ -0,0 +1,141 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: s2ibuildertemplates.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.codeFramework + name: Framework + type: string + - JSONPath: .spec.defaultBaseImage + name: DefaultBaseImage + type: string + - JSONPath: .spec.version + name: Version + type: string + group: devops.kubesphere.io + names: + categories: + - devops + kind: S2iBuilderTemplate + listKind: S2iBuilderTemplateList + plural: s2ibuildertemplates + shortNames: + - s2ibt + singular: s2ibuildertemplate + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: S2iBuilderTemplate is the Schema for the s2ibuildertemplates API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate + properties: + codeFramework: + description: CodeFramework means which language this template is designed + for and which framework is using if has framework. Like Java, NodeJS + etc + type: string + containerInfo: + description: Images are the images this template will use. + items: + properties: + buildVolumes: + description: BuildVolumes specifies a list of volumes to mount + to container running the build. + items: + type: string + type: array + builderImage: + description: BaseImage are the images this template will use. + type: string + runtimeArtifacts: + items: + description: VolumeSpec represents a single volume mount point. + properties: + destination: + description: Destination is the path to mount the volume + to - absolute or relative. + type: string + keep: + description: Keep indicates if the mounted data should be + kept in the final image. + type: boolean + source: + description: Source is a reference to the volume source. + type: string + type: object + type: array + runtimeImage: + type: string + type: object + type: array + defaultBaseImage: + description: DefaultBaseImage is the image that will be used by default + type: string + description: + description: Description illustrate the purpose of this template + type: string + environment: + description: Parameters is a set of environment variables to be passed + to the image. + items: + properties: + defaultValue: + type: string + description: + type: string + key: + type: string + optValues: + items: + type: string + type: array + required: + type: boolean + type: + type: string + value: + type: string + type: object + type: array + iconPath: + description: IconPath is used for frontend display + type: string + version: + description: Version of template + type: string + type: object + status: + description: S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops.kubesphere.io_s2iruns.yaml b/config/crds/devops.kubesphere.io_s2iruns.yaml new file mode 100644 index 000000000..af43f1f1e --- /dev/null +++ b/config/crds/devops.kubesphere.io_s2iruns.yaml @@ -0,0 +1,181 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: s2iruns.devops.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .status.runState + name: State + type: string + - JSONPath: .status.kubernetesJobName + name: K8sJobName + type: string + - JSONPath: .status.startTime + name: StartTime + type: date + - JSONPath: .status.completionTime + name: CompletionTime + type: date + - JSONPath: .status.s2iBuildResult.imageName + name: ImageName + type: string + group: devops.kubesphere.io + names: + kind: S2iRun + listKind: S2iRunList + plural: s2iruns + shortNames: + - s2ir + singular: s2irun + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + description: S2iRun is the Schema for the s2iruns API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: S2iRunSpec defines the desired state of S2iRun + properties: + backoffLimit: + description: BackoffLimit limits the restart count of each s2irun. Default + is 0 + format: int32 + type: integer + builderName: + description: BuilderName specify the name of s2ibuilder, required + type: string + newRevisionId: + description: NewRevisionId override the default NewRevisionId in its + s2ibuilder. + type: string + newSourceURL: + description: NewSourceURL is used to download new binary artifacts + type: string + newTag: + description: NewTag override the default tag in its s2ibuilder, image + name cannot be changed. + type: string + secondsAfterFinished: + description: SecondsAfterFinished if is set and greater than zero, and + the job created by s2irun become successful or failed , the job will + be auto deleted after SecondsAfterFinished + format: int32 + type: integer + required: + - builderName + type: object + status: + description: S2iRunStatus defines the observed state of S2iRun + properties: + completionTime: + description: Represents time when the job was completed. It is not guaranteed + to be set in happens-before order across separate operations. It is + represented in RFC3339 form and is in UTC. + format: date-time + type: string + kubernetesJobName: + description: KubernetesJobName is the job name in k8s + type: string + logURL: + description: LogURL is uesd for external log handler to let user know + where is log located in + type: string + runState: + description: RunState indicates whether this job is done or failed + type: string + s2iBuildResult: + description: S2i build result info. + properties: + commandPull: + description: Command for pull image. + type: string + imageCreated: + description: Image created time. + type: string + imageID: + description: Image ID. + type: string + imageName: + description: ImageName is the name of artifact + type: string + imageRepoTags: + description: image tags. + items: + type: string + type: array + imageSize: + description: The size in bytes of the image + format: int64 + type: integer + type: object + s2iBuildSource: + description: S2i build source info. + properties: + binaryName: + description: Binary file Name + type: string + binarySize: + description: Binary file Size + format: int64 + type: integer + builderImage: + description: // BuilderImage describes which image is used for building + the result images. + type: string + commitID: + description: CommitID represents an arbitrary extended object reference + in Git as SHA-1 + type: string + committerEmail: + description: CommitterEmail contains the e-mail of the committer + type: string + committerName: + description: CommitterName contains the name of the committer + type: string + description: + description: Description is a result image description label. The + default is no description. + type: string + revisionId: + description: The RevisionId is a branch name or a SHA-1 hash of + every important thing about the commit + type: string + sourceUrl: + description: SourceURL is url of the codes such as https://github.com/a/b.git + type: string + type: object + startTime: + description: StartTime represent when this run began + format: date-time + type: string + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/devops_v1alpha1_s2ibinary.yaml b/config/crds/devops_v1alpha1_s2ibinary.yaml deleted file mode 100644 index 332fa56ff..000000000 --- a/config/crds/devops_v1alpha1_s2ibinary.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - creationTimestamp: null - labels: - controller-tools.k8s.io: "1.0" - name: s2ibinaries.devops.kubesphere.io -spec: - additionalPrinterColumns: - - JSONPath: .spec.fileName - name: FileName - type: string - - JSONPath: .spec.md5 - name: MD5 - type: string - - JSONPath: .spec.size - name: Size - type: string - - JSONPath: .status.phase - name: Phase - type: string - group: devops.kubesphere.io - names: - kind: S2iBinary - plural: s2ibinaries - 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: - type: object - spec: - properties: - downloadURL: - description: DownloadURL in KubeSphere - type: string - fileName: - description: FileName is filename of binary - type: string - md5: - description: MD5 is Binary's MD5 Hash - type: string - size: - description: Size is the file size of file - type: string - uploadTimeStamp: - description: UploadTime is last upload time - format: date-time - type: string - type: object - status: - properties: - phase: - description: Phase is status of S2iBinary . Possible value is "Ready","UnableToDownload" - type: string - type: object - version: v1alpha1 -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_globalrolebindings.yaml b/config/crds/iam.kubesphere.io_globalrolebindings.yaml new file mode 100644 index 000000000..1884d5a62 --- /dev/null +++ b/config/crds/iam.kubesphere.io_globalrolebindings.yaml @@ -0,0 +1,99 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalrolebindings.iam.kubesphere.io +spec: + group: iam.kubesphere.io + names: + categories: + - iam + kind: GlobalRoleBinding + listKind: GlobalRoleBindingList + plural: globalrolebindings + singular: globalrolebinding + scope: Cluster + validation: + openAPIV3Schema: + description: RoleBinding is the Schema for the rolebindings API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + roleRef: + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + subjects: + description: Subjects holds references to the objects the role applies to. + items: + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. + type: string + kind: + description: Kind of object being referenced. Values defined by this + API group are "User", "Group", and "ServiceAccount". If the Authorizer + does not recognized the kind value, the Authorizer should report + an error. + type: string + name: + description: Name of the object being referenced. + type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string + required: + - kind + - name + type: object + type: array + required: + - roleRef + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_globalroles.yaml b/config/crds/iam.kubesphere.io_globalroles.yaml new file mode 100644 index 000000000..480516a60 --- /dev/null +++ b/config/crds/iam.kubesphere.io_globalroles.yaml @@ -0,0 +1,156 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: globalroles.iam.kubesphere.io +spec: + group: iam.kubesphere.io + names: + categories: + - iam + kind: GlobalRole + listKind: GlobalRoleList + plural: globalroles + singular: globalrole + scope: Cluster + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this GlobalRole. If AggregationRule is set, then the + Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: array + type: object + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_users.yaml b/config/crds/iam.kubesphere.io_users.yaml new file mode 100644 index 000000000..ce60e7e35 --- /dev/null +++ b/config/crds/iam.kubesphere.io_users.yaml @@ -0,0 +1,119 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: users.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .spec.email + name: Email + type: string + - JSONPath: .status.state + name: Status + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: User + listKind: UserList + plural: users + singular: user + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: User is the Schema for the users API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + spec: + description: UserSpec defines the desired state of User + properties: + description: + description: Description of the user. + type: string + displayName: + type: string + email: + description: Unique email address. + type: string + finalizers: + description: Finalizers is an opaque list of values that must be empty + to permanently remove object from storage. + items: + type: string + type: array + groups: + items: + type: string + type: array + lang: + description: The preferred written or spoken language for the user. + type: string + password: + description: password will be encrypted by mutating admission webhook + type: string + required: + - email + - password + type: object + status: + description: UserStatus defines the observed state of User + properties: + conditions: + description: Represents the latest available observations of a namespace's + current state. + items: + properties: + lastTransitionTime: + format: date-time + type: string + message: + type: string + reason: + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of namespace controller condition. + type: string + required: + - status + - type + type: object + type: array + state: + description: The user status + type: string + type: object + required: + - spec + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_workspacerolebindings.yaml b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml new file mode 100644 index 000000000..c06e75f2f --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspacerolebindings.yaml @@ -0,0 +1,103 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspacerolebindings.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRoleBinding + listKind: WorkspaceRoleBindingList + plural: workspacerolebindings + singular: workspacerolebinding + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + description: RoleBinding is the Schema for the rolebindings API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + roleRef: + description: RoleRef can only reference a ClusterRole in the global namespace. + If the RoleRef cannot be resolved, the Authorizer must return an error. + properties: + apiGroup: + description: APIGroup is the group for the resource being referenced + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - apiGroup + - kind + - name + type: object + subjects: + description: Subjects holds references to the objects the role applies to. + items: + description: Subject contains a reference to the object or user identities + a role binding applies to. This can either hold a direct API object + reference, or a value for non-objects such as user and group names. + properties: + apiGroup: + description: APIGroup holds the API group of the referenced subject. + Defaults to "" for ServiceAccount subjects. Defaults to "rbac.authorization.k8s.io" + for User and Group subjects. + type: string + kind: + description: Kind of object being referenced. Values defined by this + API group are "User", "Group", and "ServiceAccount". If the Authorizer + does not recognized the kind value, the Authorizer should report + an error. + type: string + name: + description: Name of the object being referenced. + type: string + namespace: + description: Namespace of the referenced object. If the object kind + is non-namespace, such as "User" or "Group", and this value is not + empty the Authorizer should report an error. + type: string + required: + - kind + - name + type: object + type: array + required: + - roleRef + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/iam.kubesphere.io_workspaceroles.yaml b/config/crds/iam.kubesphere.io_workspaceroles.yaml new file mode 100644 index 000000000..09d8b0b7d --- /dev/null +++ b/config/crds/iam.kubesphere.io_workspaceroles.yaml @@ -0,0 +1,164 @@ + +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: workspaceroles.iam.kubesphere.io +spec: + additionalPrinterColumns: + - JSONPath: .metadata.labels.kubesphere\.io/workspace + name: Workspace + type: string + - JSONPath: .metadata.labels.kubesphere\.io/alias-name + name: Alias + type: string + group: iam.kubesphere.io + names: + categories: + - iam + kind: WorkspaceRole + listKind: WorkspaceRoleList + plural: workspaceroles + singular: workspacerole + scope: Cluster + subresources: {} + validation: + openAPIV3Schema: + properties: + aggregationRule: + description: AggregationRule is an optional field that describes how to + build the Rules for this WorkspaceRole. If AggregationRule is set, then + the Rules are controller managed and direct changes to Rules will be stomped + by the controller. + properties: + roleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will + be used to find ClusterRoles and create the rules. If any of the selectors + match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. + The result of matchLabels and matchExpressions are ANDed. An empty + label selector matches all objects. A null label selector matches + no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector requirement is a selector that + contains values, a key, and an operator that relates the key + and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: operator represents a key's relationship to + a set of values. Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of string values. If the + operator is In or NotIn, the values array must be non-empty. + If the operator is Exists or DoesNotExist, the values + array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single + {key,value} in the matchLabels map is equivalent to an element + of matchExpressions, whose key field is "key", the operator + is "In", and the values array contains only "value". The requirements + are ANDed. + type: object + type: object + type: array + type: object + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: Standard object's metadata. + type: object + rules: + description: Rules holds all the PolicyRules for this ClusterRole + items: + description: PolicyRule holds information that describes a policy rule, + but does not contain information about who the rule applies to or which + namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the + resources. If multiple API groups are specified, any action requested + against one of the enumerated resources in any API group will be + allowed. + items: + type: string + type: array + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user + should have access to. *s are allowed, but only as the full, final + step in the path Since non-resource URLs are not namespaced, this + field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + Rules can either apply to API resources (such as "pods" or "secrets") + or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + resourceNames: + description: ResourceNames is an optional white list of names that + the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + resources: + description: Resources is a list of resources this rule applies to. ResourceAll + represents all resources. + items: + type: string + type: array + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds + and AttributeRestrictions contained in this rule. VerbAll represents + all kinds. + items: + type: string + type: array + required: + - verbs + type: object + type: array + required: + - rules + type: object + version: v1alpha2 + versions: + - name: v1alpha2 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crds/network.kubesphere.io_namespacenetworkpolicies.yaml b/config/crds/network.kubesphere.io_namespacenetworkpolicies.yaml new file mode 100644 index 000000000..36ce1b4ef --- /dev/null +++ b/config/crds/network.kubesphere.io_namespacenetworkpolicies.yaml @@ -0,0 +1,271 @@ +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (devel) + creationTimestamp: null + name: namespacenetworkpolicies.network.kubesphere.io +spec: + group: network.kubesphere.io + names: + categories: + - networking + kind: NamespaceNetworkPolicy + listKind: NamespaceNetworkPolicyList + plural: namespacenetworkpolicies + shortNames: + - nsnp + singular: namespacenetworkpolicy + scope: Namespaced + validation: + openAPIV3Schema: + description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies + API + 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/sig-architecture/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/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: NetworkPolicySpec provides the specification of a NetworkPolicy + properties: + egress: + description: List of egress rules to be applied to the selected pods. + Outgoing traffic is allowed if there are no NetworkPolicies selecting + the pod (and cluster policy otherwise allows the traffic), OR if the + traffic matches at least one egress rule across all of the NetworkPolicy + objects whose podSelector matches the pod. If this field is empty + then this NetworkPolicy limits all outgoing traffic (and serves solely + to ensure that the pods it selects are isolated by default). This + field is beta-level in 1.8 + items: + description: NetworkPolicyEgressRule describes a particular set of + traffic that is allowed out of pods matched by a NetworkPolicySpec's + podSelector. The traffic must match both ports and to. This type + is beta-level in 1.8 + properties: + ports: + description: List of destination ports for outgoing traffic. Each + item in this list is combined using a logical OR. If this field + is empty or missing, this rule matches all ports (traffic not + restricted by port). If this field is present and contains at + least one item, then this rule allows traffic only if the traffic + matches at least one port in the list. + items: + description: NetworkPolicyPort describes a port to allow traffic + on + properties: + port: + anyOf: + - type: integer + - type: string + description: The port on the given protocol. This can either + be a numerical or named port on a pod. If this field is + not provided, this matches all port names and numbers. + x-kubernetes-int-or-string: true + protocol: + description: The protocol (TCP, UDP, or SCTP) which traffic + must match. If not specified, this field defaults to TCP. + type: string + type: object + type: array + to: + description: List of destinations for outgoing traffic of pods + selected for this rule. Items in this list are combined using + a logical OR operation. If this field is empty or missing, this + rule matches all destinations (traffic not restricted by destination). + If this field is present and contains at least one item, this + rule allows traffic only if the traffic matches at least one + item in the to list. + items: + description: NetworkPolicyPeer describes a peer to allow traffic + from. Only certain combinations of fields are allowed + properties: + ipBlock: + description: IPBlock defines policy on a particular IPBlock. + If this field is set then neither of the other fields + can be. + properties: + cidr: + description: CIDR is a string representing the IP Block + Valid examples are "192.168.1.1/24" + type: string + except: + description: Except is a slice of CIDRs that should + not be included within an IP Block Valid examples + are "192.168.1.1/24" Except values will be rejected + if they are outside the CIDR range + items: + type: string + type: array + required: + - cidr + type: object + namespace: + description: "Selects Namespaces using cluster-scoped labels. + This field follows standard label selector semantics; + if present but empty, it selects all namespaces. \n If + PodSelector is also set, then the NetworkPolicyPeer as + a whole selects the Pods matching PodSelector in the Namespaces + selected by NamespaceSelector. Otherwise it selects all + Pods in the Namespaces selected by NamespaceSelector." + properties: + name: + type: string + required: + - name + type: object + service: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: object + type: array + type: object + type: array + ingress: + description: List of ingress rules to be applied to the selected pods. + Traffic is allowed to a pod if there are no NetworkPolicies selecting + the pod (and cluster policy otherwise allows the traffic), OR if the + traffic source is the pod's local node, OR if the traffic matches + at least one ingress rule across all of the NetworkPolicy objects + whose podSelector matches the pod. If this field is empty then this + NetworkPolicy does not allow any traffic (and serves solely to ensure + that the pods it selects are isolated by default) + items: + description: NetworkPolicyIngressRule describes a particular set of + traffic that is allowed to the pods matched by a NetworkPolicySpec's + podSelector. The traffic must match both ports and from. + properties: + from: + description: List of sources which should be able to access the + pods selected for this rule. Items in this list are combined + using a logical OR operation. If this field is empty or missing, + this rule matches all sources (traffic not restricted by source). + If this field is present and contains at least one item, this + rule allows traffic only if the traffic matches at least one + item in the from list. + items: + description: NetworkPolicyPeer describes a peer to allow traffic + from. Only certain combinations of fields are allowed + properties: + ipBlock: + description: IPBlock defines policy on a particular IPBlock. + If this field is set then neither of the other fields + can be. + properties: + cidr: + description: CIDR is a string representing the IP Block + Valid examples are "192.168.1.1/24" + type: string + except: + description: Except is a slice of CIDRs that should + not be included within an IP Block Valid examples + are "192.168.1.1/24" Except values will be rejected + if they are outside the CIDR range + items: + type: string + type: array + required: + - cidr + type: object + namespace: + description: "Selects Namespaces using cluster-scoped labels. + This field follows standard label selector semantics; + if present but empty, it selects all namespaces. \n If + PodSelector is also set, then the NetworkPolicyPeer as + a whole selects the Pods matching PodSelector in the Namespaces + selected by NamespaceSelector. Otherwise it selects all + Pods in the Namespaces selected by NamespaceSelector." + properties: + name: + type: string + required: + - name + type: object + service: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: object + type: array + ports: + description: List of ports which should be made accessible on + the pods selected for this rule. Each item in this list is combined + using a logical OR. If this field is empty or missing, this + rule matches all ports (traffic not restricted by port). If + this field is present and contains at least one item, then this + rule allows traffic only if the traffic matches at least one + port in the list. + items: + description: NetworkPolicyPort describes a port to allow traffic + on + properties: + port: + anyOf: + - type: integer + - type: string + description: The port on the given protocol. This can either + be a numerical or named port on a pod. If this field is + not provided, this matches all port names and numbers. + x-kubernetes-int-or-string: true + protocol: + description: The protocol (TCP, UDP, or SCTP) which traffic + must match. If not specified, this field defaults to TCP. + type: string + type: object + type: array + type: object + type: array + policyTypes: + description: List of rule types that the NetworkPolicy relates to. Valid + options are "Ingress", "Egress", or "Ingress,Egress". If this field + is not specified, it will default based on the existence of Ingress + or Egress rules; policies that contain an Egress section are assumed + to affect Egress, and all policies (whether or not they contain an + Ingress section) are assumed to affect Ingress. If you want to write + an egress-only policy, you must explicitly specify policyTypes [ "Egress" + ]. Likewise, if you want to write a policy that specifies that no + egress is allowed, you must specify a policyTypes value that include + "Egress" (since such a policy would not include an Egress section + and would otherwise default to just [ "Ingress" ]). This field is + beta-level in 1.8 + items: + description: Policy Type string describes the NetworkPolicy type This + type is beta-level in 1.8 + type: string + type: array + type: object + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml new file mode 100644 index 000000000..f3d4ba63a --- /dev/null +++ b/config/rbac/role.yaml @@ -0,0 +1,54 @@ + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: manager-role +rules: +- apiGroups: + - crd.projectcalico.org + resources: + - clusterinformations + - felixconfigurations + - globalfelixconfigs + - globalnetworkpolicies + - globalnetworksets + - hostendpoints + - ipamblocks + - ippools + - networkpolicies + - networksets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - network.kubesphere.io + resources: + - namespacenetworkpolicies + - workspacenetworkpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - tenant.kubesphere.io + resources: + - workspaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/config/samples/devops_v1alpha3_devopsproject.yaml b/config/samples/devops_v1alpha3_devopsproject.yaml new file mode 100644 index 000000000..b443b6d2c --- /dev/null +++ b/config/samples/devops_v1alpha3_devopsproject.yaml @@ -0,0 +1,9 @@ +apiVersion: devops.kubesphere.io/v1alpha3 +kind: DevOpsProject +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: devopsproject-sample +spec: + # Add fields here + foo: bar diff --git a/config/samples/devops_v1alpha3_pipeline.yaml b/config/samples/devops_v1alpha3_pipeline.yaml new file mode 100644 index 000000000..b9c9fbba3 --- /dev/null +++ b/config/samples/devops_v1alpha3_pipeline.yaml @@ -0,0 +1,9 @@ +apiVersion: devops.kubesphere.io/v1alpha3 +kind: Pipeline +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: pipeline-sample +spec: + # Add fields here + foo: bar diff --git a/config/samples/iam_v1alpha2_globalrole.yaml b/config/samples/iam_v1alpha2_globalrole.yaml new file mode 100644 index 000000000..268374d06 --- /dev/null +++ b/config/samples/iam_v1alpha2_globalrole.yaml @@ -0,0 +1,14 @@ +apiVersion: iam.kubesphere.io/v1alpha2 +kind: GlobalRole +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: global-admin +rules: + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' + diff --git a/config/samples/iam_v1alpha2_globalrolebinding.yaml b/config/samples/iam_v1alpha2_globalrolebinding.yaml new file mode 100644 index 000000000..11550dcdc --- /dev/null +++ b/config/samples/iam_v1alpha2_globalrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: iam.kubesphere.io/v1alpha2 +kind: GlobalRoleBinding +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: admin +roleRef: + apiGroup: iam.kubesphere.io/v1alpha2 + kind: GlobalRole + name: global-admin +subjects: + - apiGroup: iam.kubesphere.io/v1alpha2 + kind: User + name: admin diff --git a/config/samples/iam_v1alpha2_user.yaml b/config/samples/iam_v1alpha2_user.yaml new file mode 100644 index 000000000..88b72cb55 --- /dev/null +++ b/config/samples/iam_v1alpha2_user.yaml @@ -0,0 +1,9 @@ +apiVersion: iam.kubesphere.io/v1alpha2 +kind: User +metadata: + labels: + controller-tools.k8s.io: "1.0" + name: admin +spec: + email: admin@kubesphere.io + password: P@88w0rd diff --git a/config/webhook/iam.yaml b/config/webhook/iam.yaml new file mode 100644 index 000000000..c1ed3a1c9 --- /dev/null +++ b/config/webhook/iam.yaml @@ -0,0 +1,69 @@ +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: kubesphere-iam-validator +webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + caBundle: + service: + name: webhook-service + namespace: kubesphere-system + path: /validate-email-iam-kubesphere-io-v1alpha2-user + failurePolicy: Fail + name: vemail.iam.kubesphere.io + rules: + - apiGroups: + - iam.kubesphere.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - users + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: kubesphere-iam-injector +webhooks: + - admissionReviewVersions: + - v1beta1 + clientConfig: + caBundle: + service: + name: webhook-service + namespace: kubesphere-system + path: /mutating-encrypt-password-iam-kubesphere-io-v1alpha2-user + failurePolicy: Fail + name: mpassword.iam.kubesphere.io + reinvocationPolicy: Never + rules: + - apiGroups: + - iam.kubesphere.io + apiVersions: + - v1alpha2 + operations: + - CREATE + - UPDATE + resources: + - users + + +--- + +apiVersion: v1 +kind: Service +metadata: + name: webhook-service + namespace: kubesphere-system +spec: + ports: + - port: 443 + targetPort: 443 + selector: + app: ks-controller-manager + tier: backend \ No newline at end of file diff --git a/config/webhook/nsnp.yaml b/config/webhook/nsnp.yaml new file mode 100644 index 000000000..d02cd5a7c --- /dev/null +++ b/config/webhook/nsnp.yaml @@ -0,0 +1,24 @@ +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: kubesphere-nsnp-validate-service +webhooks: + - clientConfig: + caBundle: + service: + name: kubesphere-controller-manager-service + namespace: kubesphere-system + path: /validate-service-nsnp-kubesphere-io-v1alpha1-network + failurePolicy: Fail + name: validate.nsnp.kubesphere.io + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - services diff --git a/go.mod b/go.mod index 36a9f0e39..9d28c3a18 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/aws/aws-sdk-go v1.22.2 github.com/beevik/etree v1.1.0 - github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker v1.4.2-0.20190822205725-ed20165a37b4 @@ -31,77 +30,79 @@ require ( github.com/emirpasic/gods v1.12.0 // indirect github.com/fatih/structs v1.1.0 github.com/go-ldap/ldap v3.0.3+incompatible - github.com/go-logr/logr v0.1.0 + github.com/go-logr/zapr v0.1.1 // indirect + github.com/go-openapi/loads v0.19.2 github.com/go-openapi/spec v0.19.3 github.com/go-openapi/strfmt v0.19.0 - github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/go-openapi/validate v0.19.2 github.com/go-redis/redis v6.15.2+incompatible github.com/go-sql-driver/mysql v1.4.1 github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 github.com/golang/example v0.0.0-20170904185048-46695d81d1fa + github.com/golang/mock v1.2.0 github.com/golang/protobuf v1.3.2 + github.com/google/go-cmp v0.3.0 github.com/google/go-querystring v1.0.0 // indirect github.com/google/uuid v1.1.1 - github.com/gophercloud/gophercloud v0.3.0 // indirect github.com/gorilla/mux v1.7.1 // indirect github.com/gorilla/websocket v1.4.0 github.com/hashicorp/go-version v1.2.0 // indirect github.com/imdario/mergo v0.3.7 // indirect - github.com/json-iterator/go v1.1.8 + github.com/json-iterator/go v1.1.9 github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/kiali/kiali v0.15.1-0.20191210080139-edbbad1ef779 - github.com/klauspost/cpuid v1.2.1 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/kubernetes-sigs/application v0.0.0-20191210100950-18cc93526ab4 - github.com/kubesphere/s2ioperator v0.0.14 github.com/kubesphere/sonargo v0.0.2 - github.com/leodido/go-urn v1.1.0 // indirect github.com/lib/pq v1.2.0 // indirect - github.com/lucas-clemente/quic-go v0.11.1 // indirect github.com/mattn/go-sqlite3 v1.11.0 // indirect - github.com/mholt/caddy v1.0.0 - github.com/mholt/certmagic v0.5.1 // indirect - github.com/miekg/dns v1.1.9 // indirect github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect - github.com/onsi/ginkgo v1.8.0 - github.com/onsi/gomega v1.5.0 + github.com/onsi/ginkgo v1.12.0 + github.com/onsi/gomega v1.9.0 + github.com/open-policy-agent/opa v0.18.0 github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/openshift/api v3.9.0+incompatible // indirect + github.com/openshift/api v0.0.0-20180801171038-322a19404e37 // indirect + github.com/opentracing/opentracing-go v1.1.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pkg/errors v0.9.1 + github.com/projectcalico/kube-controllers v3.8.8+incompatible github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce - github.com/prometheus/common v0.4.0 + github.com/prometheus/client_golang v1.0.0 + github.com/prometheus/common v0.4.1 + github.com/prometheus/prometheus v1.8.2 github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 github.com/speps/go-hashids v2.0.0+incompatible github.com/spf13/cobra v0.0.5 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.4.0 github.com/stretchr/testify v1.4.0 + github.com/syndtr/goleveldb v1.0.0 // indirect github.com/xanzy/ssh-agent v0.2.1 // indirect - golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 + golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 + golang.org/x/net v0.0.0-20190923162816-aa69164e4478 google.golang.org/grpc v1.23.1 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect - gopkg.in/go-playground/validator.v9 v9.29.1 // indirect - gopkg.in/square/go-jose.v2 v2.3.1 // indirect gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect gopkg.in/src-d/go-git.v4 v4.11.0 - gopkg.in/yaml.v2 v2.2.4 + gopkg.in/yaml.v2 v2.2.8 istio.io/api v0.0.0-20191111210003-35e06ef8d838 istio.io/client-go v0.0.0-20191113122552-9bd0ba57c3d2 - k8s.io/api v0.0.0-20191114100352-16d7abae0d2a - k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833 - k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb - k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 - k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 - k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 - k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 + k8s.io/api v0.17.3 + k8s.io/apiextensions-apiserver v0.17.3 + k8s.io/apimachinery v0.17.3 + k8s.io/apiserver v0.17.3 + k8s.io/client-go v0.17.3 + k8s.io/code-generator v0.17.3 + k8s.io/component-base v0.17.3 + k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e // indirect k8s.io/klog v1.0.0 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a - k8s.io/utils v0.0.0-20191114184206-e782cd3c129f // indirect kubesphere.io/im v0.1.0 // indirect openpitrix.io/iam v0.1.0 // indirect openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c - sigs.k8s.io/controller-runtime v0.4.0 + sigs.k8s.io/controller-runtime v0.5.0 sigs.k8s.io/controller-tools v0.2.4 + sigs.k8s.io/kubefed v0.2.0-alpha.1 ) replace ( @@ -115,6 +116,7 @@ replace ( github.com/Azure/go-autorest/logger => github.com/Azure/go-autorest/logger v0.1.0 github.com/Azure/go-autorest/tracing => github.com/Azure/go-autorest/tracing v0.5.0 github.com/BurntSushi/toml => github.com/BurntSushi/toml v0.3.1 + github.com/MakeNowJust/heredoc => github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd github.com/Masterminds/semver => github.com/Masterminds/semver v1.5.0 github.com/Microsoft/go-winio => github.com/Microsoft/go-winio v0.4.12 github.com/NYTimes/gziphandler => github.com/NYTimes/gziphandler v1.1.1 @@ -136,9 +138,8 @@ replace ( github.com/bitly/go-simplejson => github.com/bitly/go-simplejson v0.5.0 github.com/blang/semver => github.com/blang/semver v3.5.0+incompatible github.com/bmizerany/assert => github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 - github.com/cenkalti/backoff => github.com/cenkalti/backoff v2.2.1+incompatible github.com/cespare/xxhash => github.com/cespare/xxhash v1.1.0 - github.com/cheekybits/genny => github.com/cheekybits/genny v1.0.0 + github.com/chai2010/gettext-go => github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 github.com/client9/misspell => github.com/client9/misspell v0.3.4 github.com/coreos/bbolt => github.com/coreos/bbolt v1.3.3 github.com/coreos/etcd => github.com/coreos/etcd v3.3.17+incompatible @@ -148,17 +149,16 @@ replace ( github.com/coreos/pkg => github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f github.com/cpuguy83/go-md2man => github.com/cpuguy83/go-md2man v1.0.10 github.com/davecgh/go-spew => github.com/davecgh/go-spew v1.1.1 + github.com/daviddengcn/go-colortext => github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd github.com/deckarep/golang-set => github.com/deckarep/golang-set v1.7.1 github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289 github.com/dgrijalva/jwt-go => github.com/dgrijalva/jwt-go v3.2.0+incompatible - github.com/dgryski/go-sip13 => github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 github.com/docker/distribution => github.com/docker/distribution v2.7.1+incompatible github.com/docker/docker => github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 github.com/docker/go-connections => github.com/docker/go-connections v0.3.0 github.com/docker/go-units => github.com/docker/go-units v0.3.3 github.com/docker/spdystream => github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c github.com/docopt/docopt-go => github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 - github.com/dustin/go-humanize => github.com/dustin/go-humanize v1.0.0 github.com/elastic/go-elasticsearch/v5 => github.com/elastic/go-elasticsearch/v5 v5.6.1 github.com/elastic/go-elasticsearch/v6 => github.com/elastic/go-elasticsearch/v6 v6.8.2 github.com/elastic/go-elasticsearch/v7 => github.com/elastic/go-elasticsearch/v7 v7.3.0 @@ -169,6 +169,7 @@ replace ( github.com/emirpasic/gods => github.com/emirpasic/gods v1.12.0 github.com/erikstmartin/go-testdb => github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 github.com/evanphx/json-patch => github.com/evanphx/json-patch v4.5.0+incompatible + github.com/exponent-io/jsonpath => github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d github.com/fatih/camelcase => github.com/fatih/camelcase v1.0.0 github.com/fatih/color => github.com/fatih/color v1.7.0 github.com/fatih/structs => github.com/fatih/structs v1.1.0 @@ -177,7 +178,6 @@ replace ( github.com/ghodss/yaml => github.com/ghodss/yaml v1.0.0 github.com/gliderlabs/ssh => github.com/gliderlabs/ssh v0.1.1 github.com/globalsign/mgo => github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 - github.com/go-acme/lego => github.com/go-acme/lego v2.5.0+incompatible github.com/go-kit/kit => github.com/go-kit/kit v0.8.0 github.com/go-ldap/ldap => github.com/go-ldap/ldap v3.0.3+incompatible github.com/go-logfmt/logfmt => github.com/go-logfmt/logfmt v0.4.0 @@ -200,6 +200,7 @@ replace ( github.com/go-sql-driver/mysql => github.com/go-sql-driver/mysql v1.4.1 github.com/go-stack/stack => github.com/go-stack/stack v1.8.0 github.com/gobuffalo/flect => github.com/gobuffalo/flect v0.1.5 + github.com/gobwas/glob => github.com/gobwas/glob v0.2.3 github.com/gocraft/dbr => github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 github.com/gofrs/uuid => github.com/gofrs/uuid v3.2.0+incompatible github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.0 @@ -208,6 +209,10 @@ replace ( github.com/golang/groupcache => github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 github.com/golang/mock => github.com/golang/mock v1.2.0 github.com/golang/protobuf => github.com/golang/protobuf v1.3.2 + github.com/golang/snappy => github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db + github.com/golangplus/bytes => github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450 + github.com/golangplus/fmt => github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 + github.com/golangplus/testing => github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e github.com/google/btree => github.com/google/btree v1.0.0 github.com/google/go-cmp => github.com/google/go-cmp v0.3.0 github.com/google/go-querystring => github.com/google/go-querystring v1.0.0 @@ -225,7 +230,6 @@ replace ( github.com/grpc-ecosystem/go-grpc-middleware => github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus => github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.9.6 - github.com/hashicorp/go-syslog => github.com/hashicorp/go-syslog v1.0.0 github.com/hashicorp/go-version => github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/golang-lru => github.com/hashicorp/golang-lru v0.5.3 github.com/hashicorp/hcl => github.com/hashicorp/hcl v1.0.0 @@ -234,7 +238,6 @@ replace ( github.com/inconshreveable/mousetrap => github.com/inconshreveable/mousetrap v1.0.0 github.com/jbenet/go-context => github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jessevdk/go-flags => github.com/jessevdk/go-flags v1.4.0 - github.com/jimstudt/http-authentication => github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a github.com/jinzhu/gorm => github.com/jinzhu/gorm v1.9.2 github.com/jinzhu/inflection => github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a github.com/jinzhu/now => github.com/jinzhu/now v1.0.0 @@ -250,7 +253,6 @@ replace ( github.com/kiali/kiali => github.com/kubesphere/kiali v0.15.1-0.20191210080139-edbbad1ef779 github.com/kisielk/errcheck => github.com/kisielk/errcheck v1.2.0 github.com/kisielk/gotool => github.com/kisielk/gotool v1.0.0 - github.com/klauspost/cpuid => github.com/klauspost/cpuid v1.2.1 github.com/koding/multiconfig => github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/konsorten/go-windows-terminal-sequences => github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/kr/logfmt => github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 @@ -258,42 +260,43 @@ replace ( github.com/kr/pty => github.com/kr/pty v1.1.5 github.com/kr/text => github.com/kr/text v0.1.0 github.com/kubernetes-sigs/application => github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4 - github.com/kubesphere/s2ioperator => github.com/kubesphere/s2ioperator v0.0.14 github.com/kubesphere/sonargo => github.com/kubesphere/sonargo v0.0.2 - github.com/kylelemons/godebug => github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/leodido/go-urn => github.com/leodido/go-urn v1.1.0 github.com/lib/pq => github.com/lib/pq v1.2.0 - github.com/lucas-clemente/quic-go => github.com/lucas-clemente/quic-go v0.11.1 + github.com/liggitt/tabwriter => github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de + github.com/lithammer/dedent => github.com/lithammer/dedent v1.1.0 github.com/magiconair/properties => github.com/magiconair/properties v1.8.0 github.com/mailru/easyjson => github.com/mailru/easyjson v0.7.0 - github.com/marten-seemann/qtls => github.com/marten-seemann/qtls v0.2.3 github.com/mattn/go-colorable => github.com/mattn/go-colorable v0.1.2 github.com/mattn/go-isatty => github.com/mattn/go-isatty v0.0.8 + github.com/mattn/go-runewidth => github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39 github.com/mattn/go-sqlite3 => github.com/mattn/go-sqlite3 v1.11.0 github.com/matttproud/golang_protobuf_extensions => github.com/matttproud/golang_protobuf_extensions v1.0.1 - github.com/mholt/caddy => github.com/mholt/caddy v1.0.0 - github.com/mholt/certmagic => github.com/mholt/certmagic v0.5.1 - github.com/miekg/dns => github.com/miekg/dns v1.1.9 github.com/mitchellh/go-homedir => github.com/mitchellh/go-homedir v1.1.0 + github.com/mitchellh/go-wordwrap => github.com/mitchellh/go-wordwrap v1.0.0 github.com/mitchellh/mapstructure => github.com/mitchellh/mapstructure v1.1.2 + github.com/mna/pigeon => github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae github.com/modern-go/concurrent => github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd github.com/modern-go/reflect2 => github.com/modern-go/reflect2 v1.0.1 github.com/morikuni/aec => github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c github.com/munnerz/goautoneg => github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d github.com/mwitkow/go-conntrack => github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 github.com/mxk/go-flowrate => github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f - github.com/naoina/go-stringutil => github.com/naoina/go-stringutil v0.1.0 - github.com/naoina/toml => github.com/naoina/toml v0.1.1 - github.com/oklog/ulid => github.com/oklog/ulid v1.3.1 + github.com/olekukonko/tablewriter => github.com/olekukonko/tablewriter v0.0.1 github.com/onsi/ginkgo => github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega => github.com/onsi/gomega v1.5.0 + github.com/open-policy-agent/opa => github.com/open-policy-agent/opa v0.18.0 github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec => github.com/opencontainers/image-spec v1.0.1 - github.com/openshift/api => github.com/openshift/api v3.9.0+incompatible + github.com/openshift/api => github.com/openshift/api v0.0.0-20180801171038-322a19404e37 + github.com/openshift/generic-admission-server => github.com/openshift/generic-admission-server v1.14.0 + github.com/opentracing/opentracing-go => github.com/opentracing/opentracing-go v1.1.0 + github.com/patrickmn/go-cache => github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pborman/uuid => github.com/pborman/uuid v1.2.0 github.com/pelletier/go-buffruneio => github.com/pelletier/go-buffruneio v0.2.0 github.com/pelletier/go-toml => github.com/pelletier/go-toml v1.2.0 github.com/peterbourgon/diskv => github.com/peterbourgon/diskv v2.0.1+incompatible + github.com/peterh/liner => github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d github.com/philhofer/fwd => github.com/philhofer/fwd v1.0.0 github.com/pkg/errors => github.com/pkg/errors v0.8.1 github.com/pmezard/go-difflib => github.com/pmezard/go-difflib v1.0.0 @@ -302,12 +305,14 @@ replace ( github.com/projectcalico/go-json => github.com/projectcalico/go-json v0.0.0-20161128004156-6219dc7339ba github.com/projectcalico/go-yaml => github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef github.com/projectcalico/go-yaml-wrapper => github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee + github.com/projectcalico/kube-controllers => github.com/projectcalico/kube-controllers v3.8.8+incompatible github.com/projectcalico/libcalico-go => github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce - github.com/prometheus/client_golang => github.com/prometheus/client_golang v0.9.3 + github.com/prometheus/client_golang => github.com/prometheus/client_golang v0.9.4 github.com/prometheus/client_model => github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 github.com/prometheus/common => github.com/prometheus/common v0.4.0 - github.com/prometheus/procfs => github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 - github.com/prometheus/tsdb => github.com/prometheus/tsdb v0.7.1 + github.com/prometheus/procfs => github.com/prometheus/procfs v0.0.2 + github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.8.2 + github.com/rcrowley/go-metrics => github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/remyoudompheng/bigfft => github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 github.com/rogpeppe/fastuuid => github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af github.com/rogpeppe/go-charset => github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4 @@ -330,14 +335,17 @@ replace ( github.com/src-d/gcfg => github.com/src-d/gcfg v1.4.0 github.com/stretchr/objx => github.com/stretchr/objx v0.2.0 github.com/stretchr/testify => github.com/stretchr/testify v1.4.0 + github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.0 github.com/tinylib/msgp => github.com/tinylib/msgp v1.1.0 github.com/tmc/grpc-websocket-proxy => github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 github.com/ugorji/go => github.com/ugorji/go v1.1.4 github.com/urfave/cli => github.com/urfave/cli v1.20.0 github.com/xanzy/ssh-agent => github.com/xanzy/ssh-agent v0.2.1 github.com/xiang90/probing => github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 + github.com/xlab/handysort => github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 github.com/xlab/treeprint => github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 github.com/xordataexchange/crypt => github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 + github.com/yashtewari/glob-intersection => github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b go.etcd.io/bbolt => go.etcd.io/bbolt v1.3.3 go.opencensus.io => go.opencensus.io v0.21.0 go.uber.org/atomic => go.uber.org/atomic v1.4.0 @@ -370,7 +378,6 @@ replace ( gopkg.in/go-playground/assert.v1 => gopkg.in/go-playground/assert.v1 v1.2.1 gopkg.in/go-playground/validator.v9 => gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/inf.v0 => gopkg.in/inf.v0 v0.9.1 - gopkg.in/mcuadros/go-syslog.v2 => gopkg.in/mcuadros/go-syslog.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 => gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/resty.v1 => gopkg.in/resty.v1 v1.12.0 gopkg.in/square/go-jose.v2 => gopkg.in/square/go-jose.v2 v2.3.1 @@ -392,12 +399,15 @@ replace ( k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20191114105449-027877536833 k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb k8s.io/apiserver => k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 + k8s.io/cli-runtime => k8s.io/cli-runtime v0.17.3 k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 k8s.io/code-generator => k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 k8s.io/component-base => k8s.io/component-base v0.0.0-20191114102325-35a9586014f7 k8s.io/gengo => k8s.io/gengo v0.0.0-20191120174120-e74f70b9b27e k8s.io/klog => k8s.io/klog v1.0.0 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a + k8s.io/kubectl => k8s.io/kubectl v0.17.3 + k8s.io/metrics => k8s.io/metrics v0.17.3 k8s.io/utils => k8s.io/utils v0.0.0-20191114184206-e782cd3c129f kubesphere.io/application => kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e kubesphere.io/im => kubesphere.io/im v0.1.0 @@ -412,7 +422,10 @@ replace ( rsc.io/goversion => rsc.io/goversion v1.0.0 sigs.k8s.io/controller-runtime => sigs.k8s.io/controller-runtime v0.4.0 sigs.k8s.io/controller-tools => sigs.k8s.io/controller-tools v0.2.4 + sigs.k8s.io/kubefed => sigs.k8s.io/kubefed v0.2.0-alpha.1 + sigs.k8s.io/kustomize => sigs.k8s.io/kustomize v2.0.3+incompatible sigs.k8s.io/structured-merge-diff => sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca sigs.k8s.io/testing_frameworks => sigs.k8s.io/testing_frameworks v0.1.2 sigs.k8s.io/yaml => sigs.k8s.io/yaml v1.1.0 + vbom.ml/util => vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc ) diff --git a/go.sum b/go.sum index f6b4405a8..c549ed4b4 100644 --- a/go.sum +++ b/go.sum @@ -18,12 +18,14 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk= github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= @@ -57,11 +59,9 @@ github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17 github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -77,12 +77,12 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 h1:+VAGRKyn9Ca+ckzV/PJsaRO7UXO9KQjFmSffcSDrWdE= @@ -94,8 +94,6 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elastic/go-elasticsearch/v5 v5.6.1 h1:RnL2wcXepOT5SdoKMMO1j1OBX0vxHYbBtkQNL2E3xs4= github.com/elastic/go-elasticsearch/v5 v5.6.1/go.mod h1:r7uV7HidpfkYh7D8SB4lkS13TNlNy3oa5GNmTZvuVqY= github.com/elastic/go-elasticsearch/v6 v6.8.2 h1:rp5DGrd63V5c6nHLjF6QEXUpZSvs0+QM3ld7m9VhV2g= @@ -115,6 +113,7 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3 github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= @@ -131,13 +130,9 @@ github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk= github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -172,10 +167,11 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/flect v0.1.5 h1:xpKq9ap8MbYfhuPCF0dBH854Gp9CxZjr/IocxELFflo= github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6 h1:kumyNm8Vr8cbVm/aLQYTbDE3SKCbbn5HEVoDp/Dyyfc= github.com/gocraft/dbr v0.0.0-20180507214907-a0fd650918f6/go.mod h1:K/9g3pPouf13kP5K7pdriQEJAy272R9yXuWuDIEWJTM= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -191,6 +187,11 @@ github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= +github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8= +github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= @@ -220,8 +221,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.6 h1:8p0pcgLlw2iuZVsdHdPaMUXFOA+6gDixcXbHEMzSyW8= github.com/grpc-ecosystem/grpc-gateway v1.9.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= @@ -237,8 +236,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a h1:BcF8coBl0QFVhe8vAMMlD+CV8EISiu9MGKLoj6ZEyJA= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.0/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= @@ -258,13 +255,10 @@ github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= -github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 h1:SWlt7BoQNASbhTUD0Oy5yysI2seJ7vWuGUp///OM4TM= github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7/go.mod h1:Y2SaZf2Rzd0pXkLVhLlCiAXFCLSXAIbTKDivVgff/AM= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -275,42 +269,33 @@ github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4 h1:pugSGmj8 github.com/kubesphere/application v0.0.0-20191210100950-18cc93526ab4/go.mod h1:sILRE7W0CquRyC51JNRj4U7OP7CJl3o62TcX5E6IcWs= github.com/kubesphere/kiali v0.15.1-0.20191210080139-edbbad1ef779 h1:52StEbBn6dRFF2DE9DBmVt26JQu9j4DOwagLUq6gZWg= github.com/kubesphere/kiali v0.15.1-0.20191210080139-edbbad1ef779/go.mod h1:Y1EqeixoXkKkU8I+yvOfhdh21+8+etFE6wYOVT2XFdI= -github.com/kubesphere/s2ioperator v0.0.14 h1:oShV/MSn8bwwnRzXU8bY3RH/V4k0TmCcKZ50B0Q9gEk= -github.com/kubesphere/s2ioperator v0.0.14/go.mod h1:6stEM/ocFZxYhLYl2d5LRYE5WdggHMIX5ngJwloWR4g= github.com/kubesphere/sonargo v0.0.2 h1:hsSRE3sv3mkPcUAeSABdp7rtfcNW2zzeHXzFa01CTkU= github.com/kubesphere/sonargo v0.0.2/go.mod h1:ww8n9ANlDXhX5PBZ18iaRnCgEkXN0GMml3/KZXOZ11w= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lucas-clemente/quic-go v0.11.1 h1:zasajC848Dqq/+WqfqBCkmPw+YHNe1MBts/z7y7nXf4= -github.com/lucas-clemente/quic-go v0.11.1/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mholt/caddy v1.0.0 h1:KI6RPGih2GFzWRPG8s9clKK28Ns4ZlVMKR/v7mxq6+c= -github.com/mholt/caddy v1.0.0/go.mod h1:PzUpQ3yGCTuEuy0KSxEeB4TZOi3zBZ8BR/zY0RBP414= -github.com/mholt/certmagic v0.5.1 h1:8Pf6Hwwlh5sbT3nwn3ovXyXWxHCEM54wvfLzTrQ+UiM= -github.com/mholt/certmagic v0.5.1/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= -github.com/miekg/dns v1.1.9 h1:OIdC9wT96RzuZMf2PfKRhFgsStHUUBZLM/lo1LqiM9E= -github.com/miekg/dns v1.1.9/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mna/pigeon v0.0.0-20180808201053-bb0192cfc2ae/go.mod h1:Iym28+kJVnC1hfQvv5MUtI6AiFFzvQjHcvI4RFTG/04= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= @@ -322,21 +307,24 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= -github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/open-policy-agent/opa v0.18.0 h1:EC81mO3/517Kq5brJHydqKE5MLzJ+4cdJvUQKxLzHy8= +github.com/open-policy-agent/opa v0.18.0/go.mod h1:6pC1cMYDI92i9EY/GoA2m+HcZlcCrh3jbfny5F7JVTA= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= -github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/openshift/api v0.0.0-20180801171038-322a19404e37 h1:05irGU4HK4IauGGDbsk+ZHrm1wOzMLYjMlfaiqMrBYc= +github.com/openshift/api v0.0.0-20180801171038-322a19404e37/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/openshift/generic-admission-server v1.14.0/go.mod h1:GD9KN/W4KxqRQGVMbqQHpHzb2XcQVvLCaBaSciqXvfM= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= @@ -344,6 +332,7 @@ github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtb github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -357,22 +346,25 @@ github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef h1:Di9BaA9ap github.com/projectcalico/go-yaml v0.0.0-20161201183616-955bc3e451ef/go.mod h1:1Ra2BftSa7Go38Gbq1q0bfmBFSSgUv+Cdc3SY8IL/C0= github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee h1:yVWsNSlAuYoJ0CznHsYRPiFgsotoj07k00k5rQvGlHM= github.com/projectcalico/go-yaml-wrapper v0.0.0-20161127220527-598e54215bee/go.mod h1:UgC0aTQ2KMDxlX3lU/stndk7DMUBJqzN40yFiILHgxc= +github.com/projectcalico/kube-controllers v3.8.8+incompatible h1:ZbCg0wJ+gd7i81CB6vOASiUN//oR4ZBl+wEdy0Vk1uI= +github.com/projectcalico/kube-controllers v3.8.8+incompatible/go.mod h1:ZEafKeKN5wiNARRw1LZP8l10uEfp04C7redU848MMZw= github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce h1:O/R67iwUe8TvZwgKbDB2cvF2/8L8PR4zVOcBtYEHD5Y= github.com/projectcalico/libcalico-go v1.7.2-0.20191104213956-8f81e1e344ce/go.mod h1:z4tuFqrAg/423AMSaDamY5LgqeOZ5ETui6iOxDwJ/ag= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v0.9.4 h1:Y8E/JaaPbmFSW2V81Ab/d8yZFYQQGbni1b1jPcG9Y6A= +github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/prometheus v1.8.2 h1:PAL466mnJw1VolZPm1OarpdUpqukUy/eX4tagia17DM= +github.com/prometheus/prometheus v1.8.2/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -386,6 +378,7 @@ github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009 h1:3wBL/e/qjpSYaXacpbIV+Bsj/nwQ4UO1llG/av54zzw= github.com/sony/sonyflake v0.0.0-20181109022403-6d5bd6181009/go.mod h1:dVvZuWJd174umvm5g8CmZD6S2GWwHKtpK/0ZPHswuNo= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/speps/go-hashids v2.0.0+incompatible h1:kSfxGfESueJKTx0mpER9Y/1XHl+FVQjtCqRyYcviFbw= github.com/speps/go-hashids v2.0.0+incompatible/go.mod h1:P7hqPzMdnZOfyIk+xrlG1QaSMw+gCBdHKsBDnhpaZvc= @@ -407,6 +400,8 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -416,8 +411,11 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70 github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY= +github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -429,15 +427,12 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f h1:hX65Cu3JDlGH3uEdK7I99Ii+9kjD6mvnnpfLdEAH0x4= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -451,7 +446,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+y golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -477,12 +471,9 @@ gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvR gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1 h1:60g8zx1BijSVSgLTzLCW9UC4/+i1Ih9jJ1DR5Tgp9vE= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.0 h1:KtlZ4c1OWbIs4jCv5ZXrTqG8EQocr0g/d4DjNg70aek= gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= @@ -517,6 +508,7 @@ k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWc k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 h1:+FvAOv/4JyYgZanQI8h+UW9FCmLzyEz7EZunuET6p5g= k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682/go.mod h1:Idob8Va6/sMX5SmwPLsU0pdvFlkwxuJ5x+fXMG8NbKE= +k8s.io/cli-runtime v0.17.3/go.mod h1:X7idckYphH4SZflgNpOOViSxetiMj6xI0viMAjM81TA= k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ= k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw= k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 h1:NMYlxaF7rYQJk2E2IyrUhaX81zX24+dmoZdkPw0gJqI= @@ -529,6 +521,9 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubectl v0.17.3 h1:9HHYj07kuFkM+sMJMOyQX29CKWq4lvKAG1UIPxNPMQ4= +k8s.io/kubectl v0.17.3/go.mod h1:NUn4IBY7f7yCMwSop2HCXlw/MVYP4HJBiUmOR3n9w28= +k8s.io/metrics v0.17.3/go.mod h1:HEJGy1fhHOjHggW9rMDBJBD3YuGroH3Y1pnIRw9FFaI= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:NhUQ0ZUdFz8NTQ+SvQG0JUKAn+q71v3TPExjsjRPIZI= @@ -549,9 +544,13 @@ sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9 sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA= sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= +sigs.k8s.io/kubefed v0.2.0-alpha.1 h1:nzaQ4HDReHLECXMv7iszHBLx3+GO3/Iwlw7dkS71qCw= +sigs.k8s.io/kubefed v0.2.0-alpha.1/go.mod h1:/X4yMEvaclI6CAeVwFBjtGJ1E3gwXcuVwNbGPXPz+CM= +sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM= sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/hack/docker_build.sh b/hack/docker_build.sh index 5d76fc10b..4b613c576 100755 --- a/hack/docker_build.sh +++ b/hack/docker_build.sh @@ -3,25 +3,23 @@ set -ex set -o pipefail -# Default image repo +# push to kubespheredev with default latest tag REPO=${REPO:-kubespheredev} -# Set tag to latest if no argument was given, normally was branch name -TAG=${TAG:-latest} +TAG=${TRAVIS_BRANCH:-latest} + +# check if build was triggered by a travis cronjob +if [[ -z "$TRAVIS_EVENT_TYPE" ]]; then + echo "TRAVIS_EVENT_TYPE is empty, also normaly build" +elif [[ $TRAVIS_EVENT_TYPE == "cron" ]]; then + TAG=dev-$(date +%Y%m%d) +fi + -docker build -f build/ks-apigateway/Dockerfile -t $REPO/ks-apigateway:$TAG . docker build -f build/ks-apiserver/Dockerfile -t $REPO/ks-apiserver:$TAG . -docker build -f build/ks-iam/Dockerfile -t $REPO/ks-account:$TAG . docker build -f build/ks-controller-manager/Dockerfile -t $REPO/ks-controller-manager:$TAG . -docker build -f build/hypersphere/Dockerfile -t $REPO/hypersphere:$TAG . -docker build -f ./pkg/db/Dockerfile -t $REPO/ks-devops:flyway-$TAG ./pkg/db/ - # Push image to dockerhub, need to support multiple push echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin -docker push $REPO/ks-apigateway:$TAG docker push $REPO/ks-apiserver:$TAG -docker push $REPO/ks-account:$TAG docker push $REPO/ks-controller-manager:$TAG -docker push $REPO/hypersphere:$TAG -docker push $REPO/ks-devops:flyway-$TAG diff --git a/hack/generate_client.sh b/hack/generate_client.sh index 9d094b8b0..7cf3f1c8d 100755 --- a/hack/generate_client.sh +++ b/hack/generate_client.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1" +GV="network:v1alpha1 servicemesh:v1alpha2 tenant:v1alpha1 devops:v1alpha1 iam:v1alpha2 devops:v1alpha3 cluster:v1alpha1" rm -rf ./pkg/client ./hack/generate_group.sh "client,lister,informer" kubesphere.io/kubesphere/pkg/client kubesphere.io/kubesphere/pkg/apis "$GV" --output-base=./ -h "$PWD/hack/boilerplate.go.txt" diff --git a/kustomize/network/OWNERS b/kustomize/network/OWNERS deleted file mode 100644 index 2dc932c86..000000000 --- a/kustomize/network/OWNERS +++ /dev/null @@ -1,11 +0,0 @@ -approvers: - - magicsong - - zheng1 - -reviewers: - - magicsong - - zheng1 - -labels: - - area/deploy - - area/networking \ No newline at end of file diff --git a/kustomize/network/calico-etcd/kustomization.yaml b/kustomize/network/calico-etcd/kustomization.yaml deleted file mode 100644 index a4f084ceb..000000000 --- a/kustomize/network/calico-etcd/kustomization.yaml +++ /dev/null @@ -1,23 +0,0 @@ -bases: -- ../crds - -resources: - - network.yaml - - rbac/role.yaml - - rbac/role_binding.yaml - -generatorOptions: - disableNameSuffixHash: true - -secretGenerator: - - name: calico-etcd-secrets - files: - - etcd-ca=etcd/ca - - etcd-key=etcd/key - - etcd-cert=etcd/crt - type: Opaque - -patchesStrategicMerge: - - patch_image_name.yaml - -namespace: network-test-f22e8ea9 diff --git a/kustomize/network/calico-etcd/network.yaml b/kustomize/network/calico-etcd/network.yaml deleted file mode 100644 index 31fd47396..000000000 --- a/kustomize/network/calico-etcd/network.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: network-system - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: network-manager - namespace: network-system - labels: - control-plane: network-manager -spec: - selector: - matchLabels: - control-plane: network-manager - replicas: 1 - template: - metadata: - labels: - control-plane: network-manager - spec: - nodeSelector: - node-role.kubernetes.io/master: "" - hostNetwork: true - tolerations: - - key: "CriticalAddonsOnly" - operator: "Exists" - - key: "node-role.kubernetes.io/master" - effect: NoSchedule - containers: - - command: - - /ks-network - args: - - -v=4 - - np-provider=calico - image: network:latest - imagePullPolicy: Always - name: manager - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - volumeMounts: - - mountPath: /calicocerts - name: etcd-certs - readOnly: true - terminationGracePeriodSeconds: 10 - volumes: - - name: etcd-certs - secret: - secretName: calico-etcd-secrets - defaultMode: 0400 diff --git a/kustomize/network/calico-etcd/patch_image_name.yaml b/kustomize/network/calico-etcd/patch_image_name.yaml deleted file mode 100644 index a6133c91b..000000000 --- a/kustomize/network/calico-etcd/patch_image_name.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: network-manager - namespace: network-system -spec: - template: - spec: - containers: - # Change the value of image field below to your controller image URL - - image: magicsong/ks-network:f22e8ea9 - name: manager diff --git a/kustomize/network/calico-etcd/patch_role_binding.yaml b/kustomize/network/calico-etcd/patch_role_binding.yaml deleted file mode 100644 index d5b4a3a04..000000000 --- a/kustomize/network/calico-etcd/patch_role_binding.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: manager-rolebinding -subjects: -- kind: ServiceAccount - name: default - namespace: network-test-f22e8ea9 diff --git a/kustomize/network/calico-etcd/role.yaml b/kustomize/network/calico-etcd/role.yaml deleted file mode 100644 index 2828ab542..000000000 --- a/kustomize/network/calico-etcd/role.yaml +++ /dev/null @@ -1,33 +0,0 @@ - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: network-manager -rules: -- apiGroups: - - network.kubesphere.io - resources: - - namespacenetworkpolicies - - workspacenetworkpolicies - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - tenant.kubesphere.io - resources: - - workspaces - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/kustomize/network/calico-etcd/role_binding.yaml b/kustomize/network/calico-etcd/role_binding.yaml deleted file mode 100644 index 9d4c056f7..000000000 --- a/kustomize/network/calico-etcd/role_binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: net-manager-role -subjects: -- kind: ServiceAccount - name: default - namespace: network-system \ No newline at end of file diff --git a/kustomize/network/calico-k8s/kustomization.yaml b/kustomize/network/calico-k8s/kustomization.yaml deleted file mode 100644 index 5739564d0..000000000 --- a/kustomize/network/calico-k8s/kustomization.yaml +++ /dev/null @@ -1,11 +0,0 @@ -bases: -- ../crds - -resources: -- network.yaml -- role.yaml - -patchesStrategicMerge: - - patch_image_name.yaml - -namespace: network-test-f22e8ea9 diff --git a/kustomize/network/calico-k8s/network.yaml b/kustomize/network/calico-k8s/network.yaml deleted file mode 100644 index 418a01b60..000000000 --- a/kustomize/network/calico-k8s/network.yaml +++ /dev/null @@ -1,69 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: network-system - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: network-manager - namespace: network-system - labels: - control-plane: network-manager -spec: - selector: - matchLabels: - control-plane: network-manager - replicas: 1 - template: - metadata: - labels: - control-plane: network-manager - spec: - nodeSelector: - node-role.kubernetes.io/master: "" - tolerations: - - key: "CriticalAddonsOnly" - operator: "Exists" - - key: "node-role.kubernetes.io/master" - effect: NoSchedule - serviceAccountName: network-manager - containers: - - command: - - /ks-network - args: - - -v=4 - - np-provider=calico - - datastore-type=k8s - image: network:latest - imagePullPolicy: Always - name: manager - resources: - limits: - cpu: 100m - memory: 30Mi - requests: - cpu: 100m - memory: 20Mi - terminationGracePeriodSeconds: 10 - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: net-role-binding - namespace: network-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: network-manager -subjects: -- kind: ServiceAccount - name: network-manager - ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: network-manager \ No newline at end of file diff --git a/kustomize/network/calico-k8s/patch_image_name.yaml b/kustomize/network/calico-k8s/patch_image_name.yaml deleted file mode 100644 index a6133c91b..000000000 --- a/kustomize/network/calico-k8s/patch_image_name.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: network-manager - namespace: network-system -spec: - template: - spec: - containers: - # Change the value of image field below to your controller image URL - - image: magicsong/ks-network:f22e8ea9 - name: manager diff --git a/kustomize/network/calico-k8s/patch_role_binding.yaml b/kustomize/network/calico-k8s/patch_role_binding.yaml deleted file mode 100644 index 07a52955d..000000000 --- a/kustomize/network/calico-k8s/patch_role_binding.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: net-role-binding -subjects: -- kind: ServiceAccount - name: network-manager - namespace: network-test-f22e8ea9 diff --git a/kustomize/network/calico-k8s/role.yaml b/kustomize/network/calico-k8s/role.yaml deleted file mode 100644 index b694ba90a..000000000 --- a/kustomize/network/calico-k8s/role.yaml +++ /dev/null @@ -1,54 +0,0 @@ - ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: network-manager -rules: -- apiGroups: - - crd.projectcalico.org - resources: - - clusterinformations - - felixconfigurations - - globalfelixconfigs - - globalnetworkpolicies - - globalnetworksets - - hostendpoints - - ipamblocks - - ippools - - networkpolicies - - networksets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - network.kubesphere.io - resources: - - namespacenetworkpolicies - - workspacenetworkpolicies - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - tenant.kubesphere.io - resources: - - workspaces - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/kustomize/network/crds/kustomization.yaml b/kustomize/network/crds/kustomization.yaml deleted file mode 100644 index 6b1ee9bf4..000000000 --- a/kustomize/network/crds/kustomization.yaml +++ /dev/null @@ -1,3 +0,0 @@ -resources: - - wsnp.yaml - - nsnp.yaml \ No newline at end of file diff --git a/kustomize/network/crds/nsnp.yaml b/kustomize/network/crds/nsnp.yaml deleted file mode 100644 index 7061216a7..000000000 --- a/kustomize/network/crds/nsnp.yaml +++ /dev/null @@ -1,711 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - creationTimestamp: null - name: namespacenetworkpolicies.network.kubesphere.io -spec: - group: network.kubesphere.io - names: - categories: - - networking - kind: NamespaceNetworkPolicy - plural: namespacenetworkpolicies - shortNames: - - nsnp - scope: Namespaced - validation: - openAPIV3Schema: - description: NamespaceNetworkPolicy is the Schema for the namespacenetworkpolicies - API - 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: - type: object - spec: - description: NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy - properties: - egress: - description: The ordered set of egress rules. Each rule contains a - set of packet match criteria and a corresponding action to apply. - items: - description: "A Rule encapsulates a set of match criteria and an action. - \ Both selector-based security Policy and security Profiles reference - rules - separated out as a list of rules for both ingress and egress - packet matching. \n Each positive match criteria has a negated version, - prefixed with â€Notâ€. All the match criteria within a rule must be - satisfied for a packet to match. A single rule can contain the positive - and negative version of a match and both must be satisfied for the - rule to match." - properties: - action: - type: string - destination: - description: Destination contains the match criteria that apply - to destination entity. - properties: - namespaceSelector: - description: "NamespaceSelector is an optional field that - contains a selector expression. Only traffic that originates - from (or terminates at) endpoints within the selected namespaces - will be matched. When both NamespaceSelector and Selector - are defined on the same rule, then only workload endpoints - that are matched by both selectors will be selected by the - rule. \n For NetworkPolicy, an empty NamespaceSelector implies - that the Selector is limited to selecting only workload - endpoints in the same namespace as the NetworkPolicy. \n - For GlobalNetworkPolicy, an empty NamespaceSelector implies - the Selector applies to workload endpoints across all namespaces." - type: string - nets: - description: Nets is an optional field that restricts the - rule to only apply to traffic that originates from (or terminates - at) IP addresses in any of the given subnets. - items: - type: string - type: array - notNets: - description: NotNets is the negated version of the Nets field. - items: - type: string - type: array - notPorts: - items: - type: object - x-kubernetes-int-or-string: true - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - description: NotPorts is the negated version of the Ports - field. Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to "TCP" or "UDP". - type: array - notSelector: - description: NotSelector is the negated version of the Selector - field. See Selector field for subtleties with negated selectors. - type: string - ports: - description: "Ports is an optional field that restricts the - rule to only apply to traffic that has a source (destination) - port that matches one of these ranges/values. This value - is a list of integers or strings that represent ranges of - ports. \n Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to \"TCP\" or \"UDP\"." - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - type: object - type: array - selector: - description: "Selector is an optional field that contains - a selector expression (see Policy for sample syntax). Only - traffic that originates from (terminates at) endpoints matching - the selector will be matched. \n Note that: in addition - to the negated version of the Selector (see NotSelector - below), the selector expression syntax itself supports negation. - \ The two types of negation are subtly different. One negates - the set of matched endpoints, the other negates the whole - match: \n \tSelector = \"!has(my_label)\" matches packets - that are from other Calico-controlled \tendpoints that do - not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" - matches packets that are not from Calico-controlled \tendpoints - that do have the label “my_labelâ€. \n The effect is that - the latter will accept packets from non-Calico sources whereas - the former is limited to packets from Calico-controlled - endpoints." - type: string - serviceAccounts: - description: ServiceAccounts is an optional field that restricts - the rule to only apply to traffic that originates from (or - terminates at) a pod running as a matching service account. - properties: - names: - description: Names is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - whose name is in the list. - items: - type: string - type: array - selector: - description: Selector is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - that matches the given label selector. If both Names - and Selector are specified then they are AND'ed. - type: string - type: object - type: object - http: - description: HTTP contains match criteria that apply to HTTP requests. - properties: - methods: - description: Methods is an optional field that restricts the - rule to apply only to HTTP requests that use one of the - listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods - are OR'd together. - items: - type: string - type: array - paths: - description: 'Paths is an optional field that restricts the - rule to apply to HTTP requests that use one of the listed - HTTP Paths. Multiple paths are OR''d together. e.g: - exact: - /foo - prefix: /bar NOTE: Each entry may ONLY specify either - a `exact` or a `prefix` match. The validator will check - for it.' - items: - description: 'HTTPPath specifies an HTTP path to match. - It may be either of the form: exact: : which matches - the path exactly or prefix: : which matches - the path prefix' - properties: - exact: - type: string - prefix: - type: string - type: object - type: array - type: object - icmp: - description: ICMP is an optional field that restricts the rule - to apply to a specific type and code of ICMP traffic. This - should only be specified if the Protocol field is set to "ICMP" - or "ICMPv6". - properties: - code: - description: Match on a specific ICMP code. If specified, - the Type value must also be specified. This is a technical - limitation imposed by the kernel’s iptables firewall, which - Calico uses to enforce the rule. - type: integer - type: - description: Match on a specific ICMP type. For example a - value of 8 refers to ICMP Echo Request (i.e. pings). - type: integer - type: object - ipVersion: - description: IPVersion is an optional field that restricts the - rule to only match a specific IP version. - type: integer - notICMP: - description: NotICMP is the negated version of the ICMP field. - properties: - code: - description: Match on a specific ICMP code. If specified, - the Type value must also be specified. This is a technical - limitation imposed by the kernel’s iptables firewall, which - Calico uses to enforce the rule. - type: integer - type: - description: Match on a specific ICMP type. For example a - value of 8 refers to ICMP Echo Request (i.e. pings). - type: integer - type: object - notProtocol: - description: NotProtocol is the negated version of the Protocol - field. - type: string - protocol: - description: "Protocol is an optional field that restricts the - rule to only apply to traffic of a specific IP protocol. Required - if any of the EntityRules contain Ports (because ports only - apply to certain protocols). \n Must be one of these string - values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" - or an integer in the range 1-255." - type: string - source: - description: Source contains the match criteria that apply to - source entity. - properties: - namespaceSelector: - description: "NamespaceSelector is an optional field that - contains a selector expression. Only traffic that originates - from (or terminates at) endpoints within the selected namespaces - will be matched. When both NamespaceSelector and Selector - are defined on the same rule, then only workload endpoints - that are matched by both selectors will be selected by the - rule. \n For NetworkPolicy, an empty NamespaceSelector implies - that the Selector is limited to selecting only workload - endpoints in the same namespace as the NetworkPolicy. \n - For GlobalNetworkPolicy, an empty NamespaceSelector implies - the Selector applies to workload endpoints across all namespaces." - type: string - nets: - description: Nets is an optional field that restricts the - rule to only apply to traffic that originates from (or terminates - at) IP addresses in any of the given subnets. - items: - type: string - type: array - notNets: - description: NotNets is the negated version of the Nets field. - items: - type: string - type: array - notPorts: - description: NotPorts is the negated version of the Ports - field. Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to "TCP" or "UDP". - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - type: object - type: array - notSelector: - description: NotSelector is the negated version of the Selector - field. See Selector field for subtleties with negated selectors. - type: string - ports: - description: "Ports is an optional field that restricts the - rule to only apply to traffic that has a source (destination) - port that matches one of these ranges/values. This value - is a list of integers or strings that represent ranges of - ports. \n Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to \"TCP\" or \"UDP\"." - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - type: object - type: array - selector: - description: "Selector is an optional field that contains - a selector expression (see Policy for sample syntax). Only - traffic that originates from (terminates at) endpoints matching - the selector will be matched. \n Note that: in addition - to the negated version of the Selector (see NotSelector - below), the selector expression syntax itself supports negation. - \ The two types of negation are subtly different. One negates - the set of matched endpoints, the other negates the whole - match: \n \tSelector = \"!has(my_label)\" matches packets - that are from other Calico-controlled \tendpoints that do - not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" - matches packets that are not from Calico-controlled \tendpoints - that do have the label “my_labelâ€. \n The effect is that - the latter will accept packets from non-Calico sources whereas - the former is limited to packets from Calico-controlled - endpoints." - type: string - serviceAccounts: - description: ServiceAccounts is an optional field that restricts - the rule to only apply to traffic that originates from (or - terminates at) a pod running as a matching service account. - properties: - names: - description: Names is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - whose name is in the list. - items: - type: string - type: array - selector: - description: Selector is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - that matches the given label selector. If both Names - and Selector are specified then they are AND'ed. - type: string - type: object - type: object - required: - - action - type: object - type: array - ingress: - description: The ordered set of ingress rules. Each rule contains a - set of packet match criteria and a corresponding action to apply. - items: - description: "A Rule encapsulates a set of match criteria and an action. - \ Both selector-based security Policy and security Profiles reference - rules - separated out as a list of rules for both ingress and egress - packet matching. \n Each positive match criteria has a negated version, - prefixed with â€Notâ€. All the match criteria within a rule must be - satisfied for a packet to match. A single rule can contain the positive - and negative version of a match and both must be satisfied for the - rule to match." - properties: - action: - type: string - destination: - description: Destination contains the match criteria that apply - to destination entity. - properties: - namespaceSelector: - description: "NamespaceSelector is an optional field that - contains a selector expression. Only traffic that originates - from (or terminates at) endpoints within the selected namespaces - will be matched. When both NamespaceSelector and Selector - are defined on the same rule, then only workload endpoints - that are matched by both selectors will be selected by the - rule. \n For NetworkPolicy, an empty NamespaceSelector implies - that the Selector is limited to selecting only workload - endpoints in the same namespace as the NetworkPolicy. \n - For GlobalNetworkPolicy, an empty NamespaceSelector implies - the Selector applies to workload endpoints across all namespaces." - type: string - nets: - description: Nets is an optional field that restricts the - rule to only apply to traffic that originates from (or terminates - at) IP addresses in any of the given subnets. - items: - type: string - type: array - notNets: - description: NotNets is the negated version of the Nets field. - items: - type: string - type: array - notPorts: - description: NotPorts is the negated version of the Ports - field. Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to "TCP" or "UDP". - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - anyOf: - - type: integer - - type: string - type: array - notSelector: - description: NotSelector is the negated version of the Selector - field. See Selector field for subtleties with negated selectors. - type: string - ports: - description: "Ports is an optional field that restricts the - rule to only apply to traffic that has a source (destination) - port that matches one of these ranges/values. This value - is a list of integers or strings that represent ranges of - ports. \n Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to \"TCP\" or \"UDP\"." - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - anyOf: - - type: integer - - type: string - type: array - selector: - description: "Selector is an optional field that contains - a selector expression (see Policy for sample syntax). Only - traffic that originates from (terminates at) endpoints matching - the selector will be matched. \n Note that: in addition - to the negated version of the Selector (see NotSelector - below), the selector expression syntax itself supports negation. - \ The two types of negation are subtly different. One negates - the set of matched endpoints, the other negates the whole - match: \n \tSelector = \"!has(my_label)\" matches packets - that are from other Calico-controlled \tendpoints that do - not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" - matches packets that are not from Calico-controlled \tendpoints - that do have the label “my_labelâ€. \n The effect is that - the latter will accept packets from non-Calico sources whereas - the former is limited to packets from Calico-controlled - endpoints." - type: string - serviceAccounts: - description: ServiceAccounts is an optional field that restricts - the rule to only apply to traffic that originates from (or - terminates at) a pod running as a matching service account. - properties: - names: - description: Names is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - whose name is in the list. - items: - type: string - type: array - selector: - description: Selector is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - that matches the given label selector. If both Names - and Selector are specified then they are AND'ed. - type: string - type: object - type: object - http: - description: HTTP contains match criteria that apply to HTTP requests. - properties: - methods: - description: Methods is an optional field that restricts the - rule to apply only to HTTP requests that use one of the - listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods - are OR'd together. - items: - type: string - type: array - paths: - description: 'Paths is an optional field that restricts the - rule to apply to HTTP requests that use one of the listed - HTTP Paths. Multiple paths are OR''d together. e.g: - exact: - /foo - prefix: /bar NOTE: Each entry may ONLY specify either - a `exact` or a `prefix` match. The validator will check - for it.' - items: - description: 'HTTPPath specifies an HTTP path to match. - It may be either of the form: exact: : which matches - the path exactly or prefix: : which matches - the path prefix' - properties: - exact: - type: string - prefix: - type: string - type: object - type: array - type: object - icmp: - description: ICMP is an optional field that restricts the rule - to apply to a specific type and code of ICMP traffic. This - should only be specified if the Protocol field is set to "ICMP" - or "ICMPv6". - properties: - code: - description: Match on a specific ICMP code. If specified, - the Type value must also be specified. This is a technical - limitation imposed by the kernel’s iptables firewall, which - Calico uses to enforce the rule. - type: integer - type: - description: Match on a specific ICMP type. For example a - value of 8 refers to ICMP Echo Request (i.e. pings). - type: integer - type: object - ipVersion: - description: IPVersion is an optional field that restricts the - rule to only match a specific IP version. - type: integer - notICMP: - description: NotICMP is the negated version of the ICMP field. - properties: - code: - description: Match on a specific ICMP code. If specified, - the Type value must also be specified. This is a technical - limitation imposed by the kernel’s iptables firewall, which - Calico uses to enforce the rule. - type: integer - type: - description: Match on a specific ICMP type. For example a - value of 8 refers to ICMP Echo Request (i.e. pings). - type: integer - type: object - notProtocol: - description: NotProtocol is the negated version of the Protocol - field. - type: string - protocol: - description: "Protocol is an optional field that restricts the - rule to only apply to traffic of a specific IP protocol. Required - if any of the EntityRules contain Ports (because ports only - apply to certain protocols). \n Must be one of these string - values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" - or an integer in the range 1-255." - type: string - source: - description: Source contains the match criteria that apply to - source entity. - properties: - namespaceSelector: - description: "NamespaceSelector is an optional field that - contains a selector expression. Only traffic that originates - from (or terminates at) endpoints within the selected namespaces - will be matched. When both NamespaceSelector and Selector - are defined on the same rule, then only workload endpoints - that are matched by both selectors will be selected by the - rule. \n For NetworkPolicy, an empty NamespaceSelector implies - that the Selector is limited to selecting only workload - endpoints in the same namespace as the NetworkPolicy. \n - For GlobalNetworkPolicy, an empty NamespaceSelector implies - the Selector applies to workload endpoints across all namespaces." - type: string - nets: - description: Nets is an optional field that restricts the - rule to only apply to traffic that originates from (or terminates - at) IP addresses in any of the given subnets. - items: - type: string - type: array - notNets: - description: NotNets is the negated version of the Nets field. - items: - type: string - type: array - notPorts: - description: NotPorts is the negated version of the Ports - field. Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to "TCP" or "UDP". - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - type: object - type: array - notSelector: - description: NotSelector is the negated version of the Selector - field. See Selector field for subtleties with negated selectors. - type: string - ports: - description: "Ports is an optional field that restricts the - rule to only apply to traffic that has a source (destination) - port that matches one of these ranges/values. This value - is a list of integers or strings that represent ranges of - ports. \n Since only some protocols have ports, if any ports - are specified it requires the Protocol match in the Rule - to be set to \"TCP\" or \"UDP\"." - items: - description: "Port represents either a range of numeric - ports or a named port. \n - For a named port, set - the PortName, leaving MinPort and MaxPort as 0. - - For a port range, set MinPort and MaxPort to the (inclusive) - port numbers. Set PortName to \"\". - For a - single port, set MinPort = MaxPort and PortName = \"\"." - x-kubernetes-int-or-string: true - anyOf: - - type: integer - - type: string - type: object - type: array - selector: - description: "Selector is an optional field that contains - a selector expression (see Policy for sample syntax). Only - traffic that originates from (terminates at) endpoints matching - the selector will be matched. \n Note that: in addition - to the negated version of the Selector (see NotSelector - below), the selector expression syntax itself supports negation. - \ The two types of negation are subtly different. One negates - the set of matched endpoints, the other negates the whole - match: \n \tSelector = \"!has(my_label)\" matches packets - that are from other Calico-controlled \tendpoints that do - not have the label “my_labelâ€. \n \tNotSelector = \"has(my_label)\" - matches packets that are not from Calico-controlled \tendpoints - that do have the label “my_labelâ€. \n The effect is that - the latter will accept packets from non-Calico sources whereas - the former is limited to packets from Calico-controlled - endpoints." - type: string - serviceAccounts: - description: ServiceAccounts is an optional field that restricts - the rule to only apply to traffic that originates from (or - terminates at) a pod running as a matching service account. - properties: - names: - description: Names is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - whose name is in the list. - items: - type: string - type: array - selector: - description: Selector is an optional field that restricts - the rule to only apply to traffic that originates from - (or terminates at) a pod running as a service account - that matches the given label selector. If both Names - and Selector are specified then they are AND'ed. - type: string - type: object - type: object - required: - - action - type: object - type: array - order: - description: Order is an optional field that specifies the order in - which the policy is applied. Policies with higher "order" are applied - after those with lower order. If the order is omitted, it may be - considered to be "infinite" - i.e. the policy will be applied last. Policies - with identical order will be applied in alphanumerical order based - on the Policy "Name". - type: integer - selector: - description: "The selector is an expression used to pick pick out the - endpoints that the policy should be applied to. \n Selector expressions - follow this syntax: \n \tlabel == \"string_literal\" -> comparison, - e.g. my_label == \"foo bar\" \tlabel != \"string_literal\" -> not - equal; also matches if label is not present \tlabel in { \"a\", \"b\", - \"c\", ... } -> true if the value of label X is one of \"a\", \"b\", - \"c\" \tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the - value of label X is not one of \"a\", \"b\", \"c\" \thas(label_name) - \ -> True if that label is present \t! expr -> negation of expr \texpr - && expr -> Short-circuit and \texpr || expr -> Short-circuit or - \t( expr ) -> parens for grouping \tall() or the empty selector -> - matches all endpoints. \n Label names are allowed to contain alphanumerics, - -, _ and /. String literals are more permissive but they do not support - escape characters. \n Examples (with made-up labels): \n \ttype == - \"webserver\" && deployment == \"prod\" \ttype in {\"frontend\", \"backend\"} - \tdeployment != \"dev\" \t! has(label_name)" - type: string - types: - description: "Types indicates whether this policy applies to ingress, - or to egress, or to both. When not explicitly specified (and so the - value on creation is empty or nil), Calico defaults Types according - to what Ingress and Egress are present in the policy. The default - is: \n - [ PolicyTypeIngress ], if there are no Egress rules (including - the case where there are also no Ingress rules) \n - [ PolicyTypeEgress - ], if there are Egress rules but no Ingress rules \n - [ PolicyTypeIngress, - PolicyTypeEgress ], if there are both Ingress and Egress rules. \n - When the policy is read back again, Types will always be one of these - values, never empty or nil." - items: - type: string - type: array - required: - - selector - type: object - type: object - version: v1alpha1 -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/kustomize/network/crds/wsnp.yaml b/kustomize/network/crds/wsnp.yaml deleted file mode 100644 index d434fe506..000000000 --- a/kustomize/network/crds/wsnp.yaml +++ /dev/null @@ -1,523 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - creationTimestamp: null - name: workspacenetworkpolicies.network.kubesphere.io -spec: - group: network.kubesphere.io - names: - categories: - - networking - kind: WorkspaceNetworkPolicy - plural: workspacenetworkpolicies - shortNames: - - wsnp - scope: Cluster - validation: - openAPIV3Schema: - description: WorkspaceNetworkPolicy is a set of network policies applied to - the scope to workspace - 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: - type: object - spec: - description: WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy - properties: - egress: - description: List of egress rules to be applied to the selected pods. - Outgoing traffic is allowed if there are no NetworkPolicies selecting - the pod (and cluster policy otherwise allows the traffic), OR if the - traffic matches at least one egress rule across all of the NetworkPolicy - objects whose podSelector matches the pod. If this field is empty - then this NetworkPolicy limits all outgoing traffic (and serves solely - to ensure that the pods it selects are isolated by default). This - field is beta-level in 1.8 - items: - description: WorkspaceNetworkPolicyEgressRule describes a particular - set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's - podSelector. The traffic must match both ports and to. - properties: - from: - description: List of sources which should be able to access the - pods selected for this rule. Items in this list are combined - using a logical OR operation. If this field is empty or missing, - this rule matches all sources (traffic not restricted by source). - If this field is present and contains at least on item, this - rule allows traffic only if the traffic matches at least one - item in the from list. - items: - description: WorkspaceNetworkPolicyPeer describes a peer to - allow traffic from. Only certain combinations of fields are - allowed. It is same as 'NetworkPolicyPeer' in k8s but with - an additional field 'WorkspaceSelector' - properties: - ipBlock: - description: IPBlock defines policy on a particular IPBlock. - If this field is set then neither of the other fields - can be. - properties: - cidr: - description: CIDR is a string representing the IP Block - Valid examples are "192.168.1.1/24" - type: string - except: - description: Except is a slice of CIDRs that should - not be included within an IP Block Valid examples - are "192.168.1.1/24" Except values will be rejected - if they are outside the CIDR range - items: - type: string - type: array - required: - - cidr - type: object - namespaceSelector: - description: "Selects Namespaces using cluster-scoped labels. - This field follows standard label selector semantics; - if present but empty, it selects all namespaces. \n If - PodSelector is also set, then the NetworkPolicyPeer as - a whole selects the Pods matching PodSelector in the Namespaces - selected by NamespaceSelector. Otherwise it selects all - Pods in the Namespaces selected by NamespaceSelector." - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - podSelector: - description: "This is a label selector which selects Pods. - This field follows standard label selector semantics; - if present but empty, it selects all pods. \n If NamespaceSelector - is also set, then the NetworkPolicyPeer as a whole selects - the Pods matching PodSelector in the Namespaces selected - by NamespaceSelector. Otherwise it selects the Pods matching - PodSelector in the policy's own Namespace." - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - workspaceSelector: - description: A label selector is a label query over a set - of resources. The result of matchLabels and matchExpressions - are ANDed. An empty label selector matches all objects. - A null label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: array - ports: - description: List of ports which should be made accessible on - the pods selected for this rule. Each item in this list is combined - using a logical OR. If this field is empty or missing, this - rule matches all ports (traffic not restricted by port). If - this field is present and contains at least one item, then this - rule allows traffic only if the traffic matches at least one - port in the list. - items: - description: NetworkPolicyPort describes a port to allow traffic - on - properties: - port: - anyOf: - - type: string - - type: integer - description: The port on the given protocol. This can either - be a numerical or named port on a pod. If this field is - not provided, this matches all port names and numbers. - protocol: - description: The protocol (TCP, UDP, or SCTP) which traffic - must match. If not specified, this field defaults to TCP. - type: string - type: object - type: array - type: object - type: array - ingress: - description: List of ingress rules to be applied to the selected pods. - Traffic is allowed to a pod if there are no NetworkPolicies selecting - the pod (and cluster policy otherwise allows the traffic), OR if the - traffic source is the pod's local node, OR if the traffic matches - at least one ingress rule across all of the NetworkPolicy objects - whose podSelector matches the pod. If this field is empty then this - NetworkPolicy does not allow any traffic (and serves solely to ensure - that the pods it selects are isolated by default) - items: - description: WorkspaceNetworkPolicyIngressRule describes a particular - set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's - podSelector. The traffic must match both ports and from. - properties: - from: - description: List of sources which should be able to access the - pods selected for this rule. Items in this list are combined - using a logical OR operation. If this field is empty or missing, - this rule matches all sources (traffic not restricted by source). - If this field is present and contains at least on item, this - rule allows traffic only if the traffic matches at least one - item in the from list. - items: - description: WorkspaceNetworkPolicyPeer describes a peer to - allow traffic from. Only certain combinations of fields are - allowed. It is same as 'NetworkPolicyPeer' in k8s but with - an additional field 'WorkspaceSelector' - properties: - ipBlock: - description: IPBlock defines policy on a particular IPBlock. - If this field is set then neither of the other fields - can be. - properties: - cidr: - description: CIDR is a string representing the IP Block - Valid examples are "192.168.1.1/24" - type: string - except: - description: Except is a slice of CIDRs that should - not be included within an IP Block Valid examples - are "192.168.1.1/24" Except values will be rejected - if they are outside the CIDR range - items: - type: string - type: array - required: - - cidr - type: object - namespaceSelector: - description: "Selects Namespaces using cluster-scoped labels. - This field follows standard label selector semantics; - if present but empty, it selects all namespaces. \n If - PodSelector is also set, then the NetworkPolicyPeer as - a whole selects the Pods matching PodSelector in the Namespaces - selected by NamespaceSelector. Otherwise it selects all - Pods in the Namespaces selected by NamespaceSelector." - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - podSelector: - description: "This is a label selector which selects Pods. - This field follows standard label selector semantics; - if present but empty, it selects all pods. \n If NamespaceSelector - is also set, then the NetworkPolicyPeer as a whole selects - the Pods matching PodSelector in the Namespaces selected - by NamespaceSelector. Otherwise it selects the Pods matching - PodSelector in the policy's own Namespace." - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - workspaceSelector: - description: A label selector is a label query over a set - of resources. The result of matchLabels and matchExpressions - are ANDed. An empty label selector matches all objects. - A null label selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector - that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: operator represents a key's relationship - to a set of values. Valid operators are In, - NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. - If the operator is In or NotIn, the values array - must be non-empty. If the operator is Exists - or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. - type: object - type: object - type: object - type: array - ports: - description: List of ports which should be made accessible on - the pods selected for this rule. Each item in this list is combined - using a logical OR. If this field is empty or missing, this - rule matches all ports (traffic not restricted by port). If - this field is present and contains at least one item, then this - rule allows traffic only if the traffic matches at least one - port in the list. - items: - description: NetworkPolicyPort describes a port to allow traffic - on - properties: - port: - anyOf: - - type: string - - type: integer - description: The port on the given protocol. This can either - be a numerical or named port on a pod. If this field is - not provided, this matches all port names and numbers. - protocol: - description: The protocol (TCP, UDP, or SCTP) which traffic - must match. If not specified, this field defaults to TCP. - type: string - type: object - type: array - type: object - type: array - policyTypes: - description: List of rule types that the WorkspaceNetworkPolicy relates - to. Valid options are Ingress, Egress, or Ingress,Egress. If this - field is not specified, it will default based on the existence of - Ingress or Egress rules; policies that contain an Egress section are - assumed to affect Egress, and all policies (whether or not they contain - an Ingress section) are assumed to affect Ingress. If you want to - write an egress-only policy, you must explicitly specify policyTypes - [ "Egress" ]. Likewise, if you want to write a policy that specifies - that no egress is allowed, you must specify a policyTypes value that - include "Egress" (since such a policy would not include an Egress - section and would otherwise default to just [ "Ingress" ]). - items: - description: Policy Type string describes the NetworkPolicy type This - type is beta-level in 1.8 - type: string - type: array - workspace: - description: Workspace specify the name of ws to apply this workspace - network policy - type: string - type: object - status: - description: WorkspaceNetworkPolicyStatus defines the observed state of - WorkspaceNetworkPolicy - type: object - type: object - version: v1alpha1 -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] diff --git a/kustomize/network/rbac/role.yaml b/kustomize/network/rbac/role.yaml deleted file mode 100644 index 41a5a7cf5..000000000 --- a/kustomize/network/rbac/role.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: net-manager-role -rules: -- apiGroups: - - network.kubesphere.io - resources: - - namespacenetworkpolicies - - workspacenetworkpolicies - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - tenant.kubesphere.io - resources: - - workspaces - verbs: - - create - - delete - - get - - list - - patch - - update - - watch diff --git a/pkg/api/auth/types.go b/pkg/api/auth/types.go new file mode 100644 index 000000000..63a80e747 --- /dev/null +++ b/pkg/api/auth/types.go @@ -0,0 +1,48 @@ +/* + * + * Copyright 2020 The KubeSphere 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 auth + +import "fmt" + +const ( + KindTokenReview = "TokenReview" +) + +type Spec struct { + Token string `json:"token" description:"access token"` +} + +type Status struct { + Authenticated bool `json:"authenticated" description:"is authenticated"` + User map[string]interface{} `json:"user,omitempty" description:"user info"` +} + +type TokenReview struct { + APIVersion string `json:"apiVersion" description:"Kubernetes API version"` + Kind string `json:"kind" description:"kind of the API object"` + Spec *Spec `json:"spec,omitempty"` + Status *Status `json:"status,omitempty" description:"token review status"` +} + +func (request *TokenReview) Validate() error { + if request.Spec == nil || request.Spec.Token == "" { + return fmt.Errorf("token must not be null") + } + return nil +} diff --git a/pkg/api/logging/v1alpha2/types.go b/pkg/api/logging/v1alpha2/types.go index 2164b9690..30a64ac68 100644 --- a/pkg/api/logging/v1alpha2/types.go +++ b/pkg/api/logging/v1alpha2/types.go @@ -1,229 +1,9 @@ package v1alpha2 -import ( - "encoding/json" - "time" -) +import "kubesphere.io/kubesphere/pkg/simple/client/logging" -const ( - OperationQuery int = iota - OperationStatistics - OperationHistogram - OperationExport -) - -// elasticsearch client config -type Config struct { - Host string - Port string - Index string - VersionMajor string -} - -type QueryParameters struct { - // when true, indicates the provided `namespaces` or `namespace_query` doesn't match any namespace - NamespaceNotFound bool - // a map of namespace with creation time - NamespaceWithCreationTime map[string]string - - // filter for literally matching - // query for fuzzy matching - WorkloadFilter []string - WorkloadQuery []string - PodFilter []string - PodQuery []string - ContainerFilter []string - ContainerQuery []string - LogQuery []string - - Operation int - Interval string - StartTime string - EndTime string - Sort string - From int64 - Size int64 - ScrollTimeout time.Duration -} - -// elasticsearch request body -type Request struct { - From int64 `json:"from"` - Size int64 `json:"size"` - Sorts []Sort `json:"sort,omitempty"` - MainQuery BoolQuery `json:"query"` - Aggs interface{} `json:"aggs,omitempty"` -} - -type Sort struct { - Order Order `json:"time"` -} - -type Order struct { - Order string `json:"order"` -} - -type BoolQuery struct { - Bool interface{} `json:"bool"` -} - -// user filter instead of must -// filter ignores scoring -type BoolFilter struct { - Filter []interface{} `json:"filter"` -} - -type BoolShould struct { - Should []interface{} `json:"should"` - MinimumShouldMatch int64 `json:"minimum_should_match"` -} - -type RangeQuery struct { - RangeSpec RangeSpec `json:"range"` -} - -type RangeSpec struct { - TimeRange TimeRange `json:"time"` -} - -type TimeRange struct { - Gte string `json:"gte,omitempty"` - Lte string `json:"lte,omitempty"` -} - -type MatchPhrase struct { - MatchPhrase map[string]string `json:"match_phrase"` -} - -type MatchPhrasePrefix struct { - MatchPhrasePrefix interface{} `json:"match_phrase_prefix"` -} - -type RegexpQuery struct { - Regexp interface{} `json:"regexp"` -} - -// StatisticsAggs, the struct for `aggs` of type Request, holds a cardinality aggregation for distinct container counting -type StatisticsAggs struct { - ContainerAgg ContainerAgg `json:"containers"` -} - -type ContainerAgg struct { - Cardinality AggField `json:"cardinality"` -} - -type AggField struct { - Field string `json:"field"` -} - -type HistogramAggs struct { - HistogramAgg HistogramAgg `json:"histogram"` -} - -type HistogramAgg struct { - DateHistogram DateHistogram `json:"date_histogram"` -} - -type DateHistogram struct { - Field string `json:"field"` - Interval string `json:"interval"` -} - -// Fore more info, refer to https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search-API.html -// Response body from the elasticsearch engine -type Response struct { - ScrollId string `json:"_scroll_id"` - Shards Shards `json:"_shards"` - Hits Hits `json:"hits"` - Aggregations json.RawMessage `json:"aggregations"` -} - -type Shards struct { - Total int64 `json:"total"` - Successful int64 `json:"successful"` - Skipped int64 `json:"skipped"` - Failed int64 `json:"failed"` -} - -type Hits struct { - // As of ElasticSearch v7.x, hits.total is changed - Total interface{} `json:"total"` - Hits []Hit `json:"hits"` -} - -type Hit struct { - Source Source `json:"_source"` - Sort []int64 `json:"sort"` -} - -type Source struct { - Log string `json:"log"` - Time string `json:"time"` - Kubernetes Kubernetes `json:"kubernetes"` -} - -type Kubernetes struct { - Namespace string `json:"namespace_name"` - Pod string `json:"pod_name"` - Container string `json:"container_name"` - Host string `json:"host"` -} - -type LogRecord struct { - Time string `json:"time,omitempty" description:"log timestamp"` - Log string `json:"log,omitempty" description:"log message"` - Namespace string `json:"namespace,omitempty" description:"namespace"` - Pod string `json:"pod,omitempty" description:"pod name"` - Container string `json:"container,omitempty" description:"container name"` - Host string `json:"host,omitempty" description:"node id"` -} - -type ReadResult struct { - ScrollID string `json:"_scroll_id,omitempty"` - Total int64 `json:"total" description:"total number of matched results"` - Records []LogRecord `json:"records,omitempty" description:"actual array of results"` -} - -// StatisticsResponseAggregations, the struct for `aggregations` of type Response, holds return results from the aggregation StatisticsAggs -type StatisticsResponseAggregations struct { - ContainerCount ContainerCount `json:"containers"` -} - -type ContainerCount struct { - Value int64 `json:"value"` -} - -type HistogramAggregations struct { - HistogramAggregation HistogramAggregation `json:"histogram"` -} - -type HistogramAggregation struct { - Histograms []HistogramStatistics `json:"buckets"` -} - -type HistogramStatistics struct { - Time int64 `json:"key"` - Count int64 `json:"doc_count"` -} - -type HistogramRecord struct { - Time int64 `json:"time" description:"timestamp"` - Count int64 `json:"count" description:"total number of logs at intervals"` -} - -type StatisticsResult struct { - Containers int64 `json:"containers" description:"total number of containers"` - Logs int64 `json:"logs" description:"total number of logs"` -} - -type HistogramResult struct { - Total int64 `json:"total" description:"total number of logs"` - Histograms []HistogramRecord `json:"histograms" description:"actual array of histogram results"` -} - -// Wrap elasticsearch response -type QueryResult struct { - Read *ReadResult `json:"query,omitempty" description:"query results"` - Statistics *StatisticsResult `json:"statistics,omitempty" description:"statistics results"` - Histogram *HistogramResult `json:"histogram,omitempty" description:"histogram results"` +type APIResponse struct { + Logs *logging.Logs `json:"query,omitempty" description:"query results"` + Statistics *logging.Statistics `json:"statistics,omitempty" description:"statistics results"` + Histogram *logging.Histogram `json:"histogram,omitempty" description:"histogram results"` } diff --git a/pkg/api/monitoring/v1alpha2/types.go b/pkg/api/monitoring/v1alpha2/types.go deleted file mode 100644 index 74700cb19..000000000 --- a/pkg/api/monitoring/v1alpha2/types.go +++ /dev/null @@ -1,23 +0,0 @@ -package v1alpha2 - -// Prometheus query api response -type APIResponse struct { - Status string `json:"status" description:"result status, one of error, success"` - Data QueryResult `json:"data" description:"actual metric result"` - ErrorType string `json:"errorType,omitempty"` - Error string `json:"error,omitempty"` - Warnings []string `json:"warnings,omitempty"` -} - -// QueryResult includes result data from a query. -type QueryResult struct { - ResultType string `json:"resultType" description:"result type, one of matrix, vector"` - Result []QueryValue `json:"result" description:"metric data including labels, time series and values"` -} - -// Time Series -type QueryValue struct { - Metric map[string]string `json:"metric,omitempty" description:"time series labels"` - Value []interface{} `json:"value,omitempty" description:"time series, values of vector type"` - Values [][]interface{} `json:"values,omitempty" description:"time series, values of matrix type"` -} diff --git a/pkg/api/resource/resource.go b/pkg/api/resource/resource.go new file mode 100644 index 000000000..958e3540f --- /dev/null +++ b/pkg/api/resource/resource.go @@ -0,0 +1 @@ +package resource diff --git a/pkg/api/resource/v1alpha2/types.go b/pkg/api/resource/v1alpha2/types.go new file mode 100644 index 000000000..a263140da --- /dev/null +++ b/pkg/api/resource/v1alpha2/types.go @@ -0,0 +1,29 @@ +package v1alpha2 + +import "time" + +// ComponentStatus represents system component status. +type ComponentStatus struct { + Name string `json:"name" description:"component name"` + Namespace string `json:"namespace" description:"the name of the namespace"` + SelfLink string `json:"selfLink" description:"self link"` + Label interface{} `json:"label" description:"labels"` + StartedAt time.Time `json:"startedAt" description:"started time"` + TotalBackends int `json:"totalBackends" description:"the total replicas of each backend system component"` + HealthyBackends int `json:"healthyBackends" description:"the number of healthy backend components"` +} + +// NodeStatus assembles cluster nodes status, simply wrap unhealthy and total nodes. +type NodeStatus struct { + // total nodes of cluster, including master nodes + TotalNodes int `json:"totalNodes" description:"total number of nodes"` + + // healthy nodes means nodes whose state is NodeReady + HealthyNodes int `json:"healthyNodes" description:"the number of healthy nodes"` +} + +// +type HealthStatus struct { + KubeSphereComponents []ComponentStatus `json:"kubesphereStatus" description:"kubesphere components status"` + NodeStatus NodeStatus `json:"nodeStatus" description:"nodes status"` +} diff --git a/pkg/api/types.go b/pkg/api/types.go new file mode 100644 index 000000000..42863886d --- /dev/null +++ b/pkg/api/types.go @@ -0,0 +1,161 @@ +package api + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +type ListResult struct { + Items []interface{} `json:"items"` + TotalItems int `json:"totalItems"` +} + +type ResourceQuota struct { + Namespace string `json:"namespace" description:"namespace"` + Data corev1.ResourceQuotaStatus `json:"data" description:"resource quota status"` +} + +type NamespacedResourceQuota struct { + Namespace string `json:"namespace,omitempty"` + + Data struct { + corev1.ResourceQuotaStatus + + // quota left status, do the math on the side, cause it's + // a lot easier with go-client library + Left corev1.ResourceList `json:"left,omitempty"` + } `json:"data,omitempty"` +} + +type Router struct { + RouterType string `json:"type"` + Annotations map[string]string `json:"annotations"` +} + +type GitCredential struct { + RemoteUrl string `json:"remoteUrl" description:"git server url"` + SecretRef *corev1.SecretReference `json:"secretRef,omitempty" description:"auth secret reference"` +} + +type RegistryCredential struct { + Username string `json:"username" description:"username"` + Password string `json:"password" description:"password"` + ServerHost string `json:"serverhost" description:"registry server host"` +} + +type Workloads struct { + Namespace string `json:"namespace" description:"the name of the namespace"` + Count map[string]int `json:"data" description:"the number of unhealthy workloads"` + Items map[string]interface{} `json:"items,omitempty" description:"unhealthy workloads"` +} + +type ClientType string + +const ( + ClientKubernetes ClientType = "Kubernetes" + ClientKubeSphere ClientType = "Kubesphere" + ClientIstio ClientType = "Istio" + ClientS2i ClientType = "S2i" + ClientApplication ClientType = "Application" + + StatusOK = "ok" +) + +var SupportedGroupVersionResources = map[ClientType][]schema.GroupVersionResource{ + // all supported kubernetes api objects + ClientKubernetes: { + {Group: "", Version: "v1", Resource: "namespaces"}, + {Group: "", Version: "v1", Resource: "nodes"}, + {Group: "", Version: "v1", Resource: "resourcequotas"}, + {Group: "", Version: "v1", Resource: "pods"}, + {Group: "", Version: "v1", Resource: "services"}, + {Group: "", Version: "v1", Resource: "persistentvolumeclaims"}, + {Group: "", Version: "v1", Resource: "secrets"}, + {Group: "", Version: "v1", Resource: "configmaps"}, + + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, + + {Group: "apps", Version: "v1", Resource: "deployments"}, + {Group: "apps", Version: "v1", Resource: "daemonsets"}, + {Group: "apps", Version: "v1", Resource: "replicasets"}, + {Group: "apps", Version: "v1", Resource: "statefulsets"}, + {Group: "apps", Version: "v1", Resource: "controllerrevisions"}, + + {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, + + {Group: "batch", Version: "v1", Resource: "jobs"}, + {Group: "batch", Version: "v1beta1", Resource: "cronjobs"}, + + {Group: "extensions", Version: "v1beta1", Resource: "ingresses"}, + + {Group: "autoscaling", Version: "v2beta2", Resource: "horizontalpodautoscalers"}, + }, + + // all supported kubesphere api objects + ClientKubeSphere: { + {Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibinaries"}, + + {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "strategies"}, + {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "servicepolicies"}, + }, + + // all supported istio api objects + ClientIstio: {}, + + // all supported s2i api objects + // TODO: move s2i clientset into kubesphere + ClientS2i: { + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuildertemplates"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2iruns"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"}, + }, + + // kubernetes-sigs application api objects + ClientApplication: { + {Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}, + }, +} + +// List of all resource kinds supported by the UI. +const ( + ResourceKindConfigMap = "configmaps" + ResourceKindDaemonSet = "daemonsets" + ResourceKindDeployment = "deployments" + ResourceKindEvent = "events" + ResourceKindHorizontalPodAutoscaler = "horizontalpodautoscalers" + ResourceKindIngress = "ingresses" + ResourceKindJob = "jobs" + ResourceKindCronJob = "cronjobs" + ResourceKindLimitRange = "limitranges" + ResourceKindNamespace = "namespaces" + ResourceKindNode = "nodes" + ResourceKindPersistentVolumeClaim = "persistentvolumeclaims" + ResourceKindPersistentVolume = "persistentvolumes" + ResourceKindCustomResourceDefinition = "customresourcedefinitions" + ResourceKindPod = "pods" + ResourceKindReplicaSet = "replicasets" + ResourceKindResourceQuota = "resourcequota" + ResourceKindSecret = "secrets" + ResourceKindService = "services" + ResourceKindStatefulSet = "statefulsets" + ResourceKindStorageClass = "storageclasses" + ResourceKindClusterRole = "clusterroles" + ResourceKindClusterRoleBinding = "clusterrolebindings" + ResourceKindRole = "roles" + ResourceKindRoleBinding = "rolebindings" + ResourceKindWorkspace = "workspaces" + ResourceKindS2iBinary = "s2ibinaries" + ResourceKindStrategy = "strategy" + ResourceKindServicePolicy = "servicepolicies" + ResourceKindS2iBuilderTemplate = "s2ibuildertemplates" + ResourceKindeS2iRun = "s2iruns" + ResourceKindS2iBuilder = "s2ibuilders" + ResourceKindApplication = "applications" + + WorkspaceNone = "" + ClusterNone = "" +) diff --git a/pkg/api/utils.go b/pkg/api/utils.go new file mode 100644 index 000000000..ac6de4b29 --- /dev/null +++ b/pkg/api/utils.go @@ -0,0 +1,23 @@ +package api + +import ( + "github.com/emicklei/go-restful" + "net/http" +) + +func HandleInternalError(response *restful.Response, req *restful.Request, err error) { + response.WriteError(http.StatusInternalServerError, err) +} + +// HandleBadRequest writes http.StatusBadRequest and log error +func HandleBadRequest(response *restful.Response, req *restful.Request, err error) { + response.WriteError(http.StatusBadRequest, err) +} + +func HandleNotFound(response *restful.Response, req *restful.Request, err error) { + response.WriteError(http.StatusNotFound, err) +} + +func HandleForbidden(response *restful.Response, req *restful.Request, err error) { + response.WriteError(http.StatusForbidden, err) +} diff --git a/pkg/apigateway/caddy-plugin/authenticate/authenticate.go b/pkg/apigateway/caddy-plugin/authenticate/authenticate.go deleted file mode 100644 index 6cb0aaff8..000000000 --- a/pkg/apigateway/caddy-plugin/authenticate/authenticate.go +++ /dev/null @@ -1,248 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authenticate - -import ( - "errors" - "fmt" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/internal" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "log" - "net/http" - "strconv" - "strings" - "time" - - "github.com/dgrijalva/jwt-go" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -type Auth struct { - Rule *Rule - Next httpserver.Handler -} - -type Rule struct { - Secret []byte - Path string - RedisOptions *redis.RedisOptions - TokenIdleTimeout time.Duration - RedisClient *redis.RedisClient - ExclusionRules []internal.ExclusionRule -} - -type User struct { - Username string `json:"username"` - UID string `json:"uid"` - Groups *[]string `json:"groups,omitempty"` - Extra *map[string]interface{} `json:"extra,omitempty"` -} - -var requestInfoFactory = request.RequestInfoFactory{ - APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), - GrouplessAPIPrefixes: sets.NewString("api")} - -func (h Auth) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) { - for _, rule := range h.Rule.ExclusionRules { - if httpserver.Path(req.URL.Path).Matches(rule.Path) && (rule.Method == internal.AllMethod || req.Method == rule.Method) { - return h.Next.ServeHTTP(resp, req) - } - } - - if httpserver.Path(req.URL.Path).Matches(h.Rule.Path) { - - uToken, err := h.ExtractToken(req) - - if err != nil { - return h.HandleUnauthorized(resp, err), nil - } - - token, err := h.Validate(uToken) - - if err != nil { - return h.HandleUnauthorized(resp, err), nil - } - - req, err = h.InjectContext(req, token) - - if err != nil { - return h.HandleUnauthorized(resp, err), nil - } - } - - return h.Next.ServeHTTP(resp, req) -} - -func (h Auth) InjectContext(req *http.Request, token *jwt.Token) (*http.Request, error) { - - payload, ok := token.Claims.(jwt.MapClaims) - - if !ok { - return nil, errors.New("invalid payload") - } - - for header := range req.Header { - if strings.HasPrefix(header, "X-Token-") { - req.Header.Del(header) - } - } - - usr := &user.DefaultInfo{} - - username, ok := payload["username"].(string) - - if ok && username != "" { - req.Header.Set("X-Token-Username", username) - usr.Name = username - } - - uid := payload["uid"] - - if uid != nil { - switch uid.(type) { - case int: - req.Header.Set("X-Token-UID", strconv.Itoa(uid.(int))) - usr.UID = strconv.Itoa(uid.(int)) - break - case string: - req.Header.Set("X-Token-UID", uid.(string)) - usr.UID = uid.(string) - break - } - } - - groups, ok := payload["groups"].([]string) - if ok && len(groups) > 0 { - req.Header.Set("X-Token-Groups", strings.Join(groups, ",")) - usr.Groups = groups - } - - // hard code, support jenkins auth plugin - if httpserver.Path(req.URL.Path).Matches("/kapis/jenkins.kubesphere.io") || - httpserver.Path(req.URL.Path).Matches("job") || - httpserver.Path(req.URL.Path).Matches("/kapis/devops.kubesphere.io/v1alpha2") { - req.SetBasicAuth(username, token.Raw) - } - - context := request.WithUser(req.Context(), usr) - - requestInfo, err := requestInfoFactory.NewRequestInfo(req) - - if err == nil { - context = request.WithRequestInfo(context, requestInfo) - } else { - return nil, err - } - - req = req.WithContext(context) - - return req, nil -} - -func (h Auth) Validate(uToken string) (*jwt.Token, error) { - - if len(uToken) == 0 { - return nil, fmt.Errorf("token length is zero") - } - - token, err := jwt.Parse(uToken, h.ProvideKey) - - if err != nil { - klog.Errorln(err) - return nil, err - } - - payload, ok := token.Claims.(jwt.MapClaims) - - if !ok { - err := fmt.Errorf("invalid payload") - klog.Errorln(err) - return nil, err - } - - username, ok := payload["username"].(string) - - if !ok { - err := fmt.Errorf("invalid payload") - klog.Errorln(err) - return nil, err - } - - if _, ok = payload["exp"]; ok { - // allow static token has expiration time - return token, nil - } - - tokenKey := fmt.Sprintf("kubesphere:users:%s:token:%s", username, uToken) - - exist, err := h.Rule.RedisClient.Redis().Exists(tokenKey).Result() - if err != nil { - klog.Error(err) - return nil, err - } - - if exist == 1 { - // reset expiration time if token exist - h.Rule.RedisClient.Redis().Expire(tokenKey, h.Rule.TokenIdleTimeout) - return token, nil - } else { - return nil, errors.New("illegal token") - } -} - -func (h Auth) HandleUnauthorized(w http.ResponseWriter, err error) int { - message := fmt.Sprintf("Unauthorized,%v", err) - w.Header().Add("WWW-Authenticate", message) - log.Println(message) - return http.StatusUnauthorized -} - -func (h Auth) ExtractToken(r *http.Request) (string, error) { - - jwtHeader := strings.Split(r.Header.Get("Authorization"), " ") - - if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 { - return jwtHeader[1], nil - } - - jwtCookie, err := r.Cookie("token") - - if err == nil { - return jwtCookie.Value, nil - } - - jwtQuery := r.URL.Query().Get("token") - - if jwtQuery != "" { - return jwtQuery, nil - } - - return "", fmt.Errorf("no token found") -} - -func (h Auth) ProvideKey(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { - return h.Rule.Secret, nil - } else { - return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"]) - } -} diff --git a/pkg/apigateway/caddy-plugin/authenticate/auto_load.go b/pkg/apigateway/caddy-plugin/authenticate/auto_load.go deleted file mode 100644 index 58ba4b485..000000000 --- a/pkg/apigateway/caddy-plugin/authenticate/auto_load.go +++ /dev/null @@ -1,152 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authenticate - -import ( - "fmt" - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/internal" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func Setup(c *caddy.Controller) error { - - rule, err := parse(c) - - if err != nil { - return err - } - - c.OnStartup(func() error { - rule.RedisClient, err = redis.NewRedisClient(rule.RedisOptions, nil) - // ensure redis is connected when startup - if err != nil { - return err - } - fmt.Println("Authenticate middleware is initiated") - return nil - }) - - c.OnShutdown(func() error { - return rule.RedisClient.Redis().Close() - }) - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return &Auth{Next: next, Rule: rule} - }) - - return nil -} - -func parse(c *caddy.Controller) (*Rule, error) { - - rule := &Rule{} - rule.ExclusionRules = make([]internal.ExclusionRule, 0) - if c.Next() { - args := c.RemainingArgs() - switch len(args) { - case 0: - for c.NextBlock() { - switch c.Val() { - case "path": - if !c.NextArg() { - return nil, c.ArgErr() - } - - rule.Path = c.Val() - - if c.NextArg() { - return nil, c.ArgErr() - } - case "token-idle-timeout": - if !c.NextArg() { - return nil, c.ArgErr() - } - - if timeout, err := time.ParseDuration(c.Val()); err != nil { - return nil, c.ArgErr() - } else { - rule.TokenIdleTimeout = timeout - } - - if c.NextArg() { - return nil, c.ArgErr() - } - case "redis-url": - if !c.NextArg() { - return nil, c.ArgErr() - } - - options := &redis.RedisOptions{RedisURL: c.Val()} - - if err := options.Validate(); len(err) > 0 { - return nil, c.ArgErr() - } else { - rule.RedisOptions = options - } - - if c.NextArg() { - return nil, c.ArgErr() - } - case "secret": - if !c.NextArg() { - return nil, c.ArgErr() - } - - rule.Secret = []byte(c.Val()) - - if c.NextArg() { - return nil, c.ArgErr() - } - case "except": - - if !c.NextArg() { - return nil, c.ArgErr() - } - - method := c.Val() - - if !sliceutil.HasString(internal.HttpMethods, method) { - return nil, c.ArgErr() - } - - for c.NextArg() { - path := c.Val() - rule.ExclusionRules = append(rule.ExclusionRules, internal.ExclusionRule{Method: method, Path: path}) - } - } - } - default: - return nil, c.ArgErr() - } - } - - if c.Next() { - return nil, c.ArgErr() - } - - if rule.RedisOptions == nil { - return nil, c.Err("redis-url must be specified") - } - - return rule, nil -} diff --git a/pkg/apigateway/caddy-plugin/authentication/authentication.go b/pkg/apigateway/caddy-plugin/authentication/authentication.go deleted file mode 100644 index d2e7a621d..000000000 --- a/pkg/apigateway/caddy-plugin/authentication/authentication.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authentication - -import ( - "context" - "errors" - "fmt" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/apiserver/pkg/endpoints/request" - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/internal" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "log" - "net/http" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" - "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" -) - -type Authentication struct { - Rule *Rule - Next httpserver.Handler -} - -type Rule struct { - Path string - ExclusionRules []internal.ExclusionRule -} - -func (c Authentication) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - - if httpserver.Path(r.URL.Path).Matches(c.Rule.Path) { - - for _, rule := range c.Rule.ExclusionRules { - if httpserver.Path(r.URL.Path).Matches(rule.Path) && (rule.Method == internal.AllMethod || r.Method == rule.Method) { - return c.Next.ServeHTTP(w, r) - } - } - - attrs, err := getAuthorizerAttributes(r.Context()) - - // without authenticate, no requestInfo found in the context - if err != nil { - return c.Next.ServeHTTP(w, r) - } - - permitted, err := permissionValidate(attrs) - - if err != nil { - return http.StatusInternalServerError, err - } - - if !permitted { - err = k8serr.NewForbidden(schema.GroupResource{Group: attrs.GetAPIGroup(), Resource: attrs.GetResource()}, attrs.GetName(), fmt.Errorf("permission undefined")) - return handleForbidden(w, err), nil - } - } - - return c.Next.ServeHTTP(w, r) - -} - -func handleForbidden(w http.ResponseWriter, err error) int { - message := fmt.Sprintf("Forbidden,%s", err.Error()) - w.Header().Add("WWW-Authenticate", message) - log.Println(message) - return http.StatusForbidden -} - -func permissionValidate(attrs authorizer.Attributes) (bool, error) { - - if attrs.GetResource() == "users" && attrs.GetUser().GetName() == attrs.GetName() { - return true, nil - } - - permitted, err := clusterRoleValidate(attrs) - - if err != nil { - log.Println("lister error", err) - return false, err - } - - if permitted { - return true, nil - } - - if attrs.GetNamespace() != "" { - permitted, err = roleValidate(attrs) - - if err != nil { - log.Println("lister error", err) - return false, err - } - - if permitted { - return true, nil - } - } - - return false, nil -} - -func roleValidate(attrs authorizer.Attributes) (bool, error) { - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() - roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister() - roleBindings, err := roleBindingLister.RoleBindings(attrs.GetNamespace()).List(labels.Everything()) - - if err != nil { - return false, err - } - - fullSource := attrs.GetResource() - - if attrs.GetSubresource() != "" { - fullSource = fullSource + "/" + attrs.GetSubresource() - } - - for _, roleBinding := range roleBindings { - if k8sutil.ContainsUser(roleBinding.Subjects, attrs.GetUser().GetName()) { - role, err := roleLister.Roles(attrs.GetNamespace()).Get(roleBinding.RoleRef.Name) - - if err != nil { - if k8serr.IsNotFound(err) { - continue - } - return false, err - } - - for _, rule := range role.Rules { - if ruleMatchesRequest(rule, attrs.GetAPIGroup(), "", attrs.GetResource(), attrs.GetSubresource(), attrs.GetName(), attrs.GetVerb()) { - return true, nil - } - } - } - } - - return false, nil -} - -func clusterRoleValidate(attrs authorizer.Attributes) (bool, error) { - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() - clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything()) - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - if err != nil { - return false, err - } - - for _, clusterRoleBinding := range clusterRoleBindings { - - if k8sutil.ContainsUser(clusterRoleBinding.Subjects, attrs.GetUser().GetName()) { - clusterRole, err := clusterRoleLister.Get(clusterRoleBinding.RoleRef.Name) - - if err != nil { - if k8serr.IsNotFound(err) { - continue - } - return false, err - } - - for _, rule := range clusterRole.Rules { - if attrs.IsResourceRequest() { - if ruleMatchesRequest(rule, attrs.GetAPIGroup(), "", attrs.GetResource(), attrs.GetSubresource(), attrs.GetName(), attrs.GetVerb()) { - return true, nil - } - } else { - if ruleMatchesRequest(rule, "", attrs.GetPath(), "", "", "", attrs.GetVerb()) { - return true, nil - } - } - } - - } - } - - return false, nil -} - -func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool { - - if resource == "" { - return false - } - - if !sliceutil.HasString(rule.APIGroups, apiGroup) && !sliceutil.HasString(rule.APIGroups, v1.ResourceAll) { - return false - } - - if len(rule.ResourceNames) > 0 && !sliceutil.HasString(rule.ResourceNames, resourceName) { - return false - } - - combinedResource := resource - - if subresource != "" { - combinedResource = combinedResource + "/" + subresource - } - - for _, res := range rule.Resources { - - // match "*" - if res == v1.ResourceAll || res == combinedResource { - return true - } - - // match "*/subresource" - if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") { - return true - } - // match "resource/*" - if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") { - return true - } - } - - return false -} - -func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool { - - if !sliceutil.HasString(rule.Verbs, verb) && !sliceutil.HasString(rule.Verbs, v1.VerbAll) { - return false - } - - if nonResourceURL == "" { - return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName) - } else { - return ruleMatchesNonResource(rule, nonResourceURL) - } -} - -func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool { - - if nonResourceURL == "" { - return false - } - - for _, spec := range rule.NonResourceURLs { - if pathMatches(nonResourceURL, spec) { - return true - } - } - - return false -} - -func pathMatches(path, spec string) bool { - if spec == "*" { - return true - } - if spec == path { - return true - } - if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) { - return true - } - return false -} - -func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) { - attribs := authorizer.AttributesRecord{} - - user, ok := request.UserFrom(ctx) - if ok { - attribs.User = user - } - - requestInfo, found := request.RequestInfoFrom(ctx) - if !found { - return nil, errors.New("no RequestInfo found in the context") - } - - // Start with common attributes that apply to resource and non-resource requests - attribs.ResourceRequest = requestInfo.IsResourceRequest - attribs.Path = requestInfo.Path - attribs.Verb = requestInfo.Verb - - attribs.APIGroup = requestInfo.APIGroup - attribs.APIVersion = requestInfo.APIVersion - attribs.Resource = requestInfo.Resource - attribs.Subresource = requestInfo.Subresource - attribs.Namespace = requestInfo.Namespace - attribs.Name = requestInfo.Name - - return &attribs, nil -} diff --git a/pkg/apigateway/caddy-plugin/authentication/auto_load.go b/pkg/apigateway/caddy-plugin/authentication/auto_load.go deleted file mode 100644 index ee94d9eaf..000000000 --- a/pkg/apigateway/caddy-plugin/authentication/auto_load.go +++ /dev/null @@ -1,117 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authentication - -import ( - "fmt" - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/internal" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - - "kubesphere.io/kubesphere/pkg/informers" -) - -// Setup is called by Caddy to parse the config block -func Setup(c *caddy.Controller) error { - - rule, err := parse(c) - - if err != nil { - return err - } - stopChan := make(chan struct{}, 0) - c.OnStartup(func() error { - informerFactory := informers.SharedInformerFactory() - informerFactory.Rbac().V1().Roles().Lister() - informerFactory.Rbac().V1().RoleBindings().Lister() - informerFactory.Rbac().V1().ClusterRoles().Lister() - informerFactory.Rbac().V1().ClusterRoleBindings().Lister() - informerFactory.Start(stopChan) - informerFactory.WaitForCacheSync(stopChan) - fmt.Println("Authentication middleware is initiated") - return nil - }) - - c.OnShutdown(func() error { - close(stopChan) - return nil - }) - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return &Authentication{Next: next, Rule: rule} - }) - return nil -} - -func parse(c *caddy.Controller) (*Rule, error) { - - rule := &Rule{} - rule.ExclusionRules = make([]internal.ExclusionRule, 0) - if c.Next() { - args := c.RemainingArgs() - switch len(args) { - case 0: - for c.NextBlock() { - switch c.Val() { - case "path": - if !c.NextArg() { - return rule, c.ArgErr() - } - - rule.Path = c.Val() - - if c.NextArg() { - return rule, c.ArgErr() - } - - break - case "except": - if !c.NextArg() { - return nil, c.ArgErr() - } - - method := c.Val() - - if !sliceutil.HasString(internal.HttpMethods, method) { - return nil, c.ArgErr() - } - - for c.NextArg() { - path := c.Val() - rule.ExclusionRules = append(rule.ExclusionRules, internal.ExclusionRule{Method: method, Path: path}) - } - break - } - } - case 1: - rule.Path = args[0] - if c.NextBlock() { - return rule, c.ArgErr() - } - default: - return rule, c.ArgErr() - } - } - - if c.Next() { - return rule, c.ArgErr() - } - - return rule, nil -} diff --git a/pkg/apigateway/caddy-plugin/internal/exclusion_rule.go b/pkg/apigateway/caddy-plugin/internal/exclusion_rule.go deleted file mode 100644 index 3b6054b3d..000000000 --- a/pkg/apigateway/caddy-plugin/internal/exclusion_rule.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - -package internal - -import "net/http" - -const AllMethod = "*" - -var HttpMethods = []string{AllMethod, http.MethodPost, http.MethodDelete, - http.MethodPatch, http.MethodPut, http.MethodGet, http.MethodOptions, http.MethodConnect} - -// Path exclusion rule -type ExclusionRule struct { - Method string - Path string -} diff --git a/pkg/apigateway/caddy-plugin/swagger/auto_load.go b/pkg/apigateway/caddy-plugin/swagger/auto_load.go deleted file mode 100644 index c71095281..000000000 --- a/pkg/apigateway/caddy-plugin/swagger/auto_load.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authenticate - -import ( - "fmt" - "net/http" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func Setup(c *caddy.Controller) error { - - handler, err := parse(c) - - if err != nil { - return err - } - - c.OnStartup(func() error { - fmt.Println("Swagger middleware is initiated") - return nil - }) - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return &Swagger{Next: next, Handler: handler} - }) - - return nil -} -func parse(c *caddy.Controller) (Handler, error) { - - handler := Handler{URL: "/swagger-ui", FilePath: "/var/static/swagger-ui"} - - if c.Next() { - args := c.RemainingArgs() - switch len(args) { - case 0: - for c.NextBlock() { - switch c.Val() { - case "url": - if !c.NextArg() { - return handler, c.ArgErr() - } - - handler.URL = c.Val() - - if c.NextArg() { - return handler, c.ArgErr() - } - case "filePath": - if !c.NextArg() { - return handler, c.ArgErr() - } - - handler.FilePath = c.Val() - - if c.NextArg() { - return handler, c.ArgErr() - } - default: - return handler, c.ArgErr() - } - } - default: - return handler, c.ArgErr() - } - } - - if c.Next() { - return handler, c.ArgErr() - } - - handler.Handler = http.StripPrefix(handler.URL, http.FileServer(http.Dir(handler.FilePath))) - - return handler, nil -} diff --git a/pkg/apigateway/caddy-plugin/swagger/swagger.go b/pkg/apigateway/caddy-plugin/swagger/swagger.go deleted file mode 100644 index 6c847a447..000000000 --- a/pkg/apigateway/caddy-plugin/swagger/swagger.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 authenticate - -import ( - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -type Swagger struct { - Handler Handler - Next httpserver.Handler -} - -type Handler struct { - URL string - FilePath string - Handler http.Handler -} - -func (h Swagger) ServeHTTP(resp http.ResponseWriter, req *http.Request) (int, error) { - - if httpserver.Path(req.URL.Path).Matches(h.Handler.URL) { - h.Handler.Handler.ServeHTTP(resp, req) - return http.StatusOK, nil - } - - return h.Next.ServeHTTP(resp, req) -} diff --git a/pkg/apigateway/plugins.go b/pkg/apigateway/plugins.go deleted file mode 100644 index a120e45d3..000000000 --- a/pkg/apigateway/plugins.go +++ /dev/null @@ -1,26 +0,0 @@ -package apigateway - -import ( - "github.com/mholt/caddy" - - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/authenticate" - "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/authentication" - swagger "kubesphere.io/kubesphere/pkg/apigateway/caddy-plugin/swagger" -) - -func RegisterPlugins() { - caddy.RegisterPlugin("swagger", caddy.Plugin{ - ServerType: "http", - Action: swagger.Setup, - }) - - caddy.RegisterPlugin("authenticate", caddy.Plugin{ - ServerType: "http", - Action: authenticate.Setup, - }) - - caddy.RegisterPlugin("authentication", caddy.Plugin{ - ServerType: "http", - Action: authentication.Setup, - }) -} diff --git a/pkg/apis/addtoscheme_cluster_v1alpha1.go b/pkg/apis/addtoscheme_cluster_v1alpha1.go new file mode 100644 index 000000000..8722b3516 --- /dev/null +++ b/pkg/apis/addtoscheme_cluster_v1alpha1.go @@ -0,0 +1,9 @@ +package apis + +import ( + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" +) + +func init() { + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/addtoscheme_devops_v1alpha3.go b/pkg/apis/addtoscheme_devops_v1alpha3.go new file mode 100644 index 000000000..1e169cc2d --- /dev/null +++ b/pkg/apis/addtoscheme_devops_v1alpha3.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The KubeSphere 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 apis + +import ( + api "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, api.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/addtoscheme_iam_v1alpha2.go b/pkg/apis/addtoscheme_iam_v1alpha2.go new file mode 100644 index 000000000..3f2a962a5 --- /dev/null +++ b/pkg/apis/addtoscheme_iam_v1alpha2.go @@ -0,0 +1,26 @@ +/* +Copyright 2019 The KubeSphere 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 apis + +import ( + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, iamv1alpha2.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/cluster/group.go b/pkg/apis/cluster/group.go new file mode 100644 index 000000000..916b1b53b --- /dev/null +++ b/pkg/apis/cluster/group.go @@ -0,0 +1 @@ +package cluster diff --git a/pkg/apis/cluster/v1alpha1/cluster_types.go b/pkg/apis/cluster/v1alpha1/cluster_types.go new file mode 100644 index 000000000..44715d96e --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/cluster_types.go @@ -0,0 +1,162 @@ +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ResourceKindCluster = "Cluster" + ResourcesSingularCluster = "cluster" + ResourcesPluralCluster = "clusters" + + IsHostCluster = "cluster.kubesphere.io/is-host-cluster" + // Description of which region the cluster been placed + ClusterRegion = "cluster.kubesphere.io/region" + // Name of the cluster group + ClusterGroup = "cluster.kubesphere.io/group" + + Finalizer = "finalizer.cluster.kubesphere.io" +) + +type ClusterSpec struct { + + // Join cluster as a kubefed cluster + JoinFederation bool `json:"joinFederation,omitempty"` + + // Desired state of the cluster + Enable bool `json:"enable,omitempty"` + + // Provider of the cluster, this field is just for description + Provider string `json:"provider,omitempty"` + + // Connection holds info to connect to the member cluster + Connection Connection `json:"connection,omitempty"` +} + +type ConnectionType string + +const ( + ConnectionTypeDirect ConnectionType = "direct" + ConnectionTypeProxy ConnectionType = "proxy" +) + +type Connection struct { + + // type defines how host cluster will connect to host cluster + // ConnectionTypeDirect means direct connection, this requires + // kubeconfig and kubesphere apiserver endpoint provided + // ConnectionTypeProxy means using kubesphere proxy, no kubeconfig + // or kubesphere apiserver endpoint required + Type ConnectionType `json:"type,omitempty"` + + // KubeSphere API Server endpoint. Example: http://10.10.0.11:8080 + // Should provide this field explicitly if connection type is direct. + // Will be populated by ks-apiserver if connection type is proxy. + KubeSphereAPIEndpoint string `json:"kubesphereAPIEndpoint,omitempty"` + + // Kubernetes API Server endpoint. Example: https://10.10.0.1:6443 + // Should provide this field explicitly if connection type is direct. + // Will be populated by ks-apiserver if connection type is proxy. + KubernetesAPIEndpoint string `json:"kubernetesAPIEndpoint,omitempty"` + + // KubeConfig content used to connect to cluster api server + // Should provide this field explicitly if connection type is direct. + // Will be populated by ks-proxy if connection type is proxy. + KubeConfig []byte `json:"kubeconfig,omitempty"` + + // Token used by agents of member cluster to connect to host cluster proxy. + // This field is populated by apiserver only if connection type is proxy. + Token string `json:"token,omitempty"` + + // KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic + // Only applicable when connection type is proxy. + KubernetesAPIServerPort uint16 `json:"kubernetesAPIServerPort,omitempty"` + + // KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic + // Only applicable when connection type is proxy. + KubeSphereAPIServerPort uint16 `json:"kubesphereAPIServerPort,omitempty"` +} + +type ClusterConditionType string + +const ( + // Cluster agent is initialized and waiting for connecting + ClusterInitialized ClusterConditionType = "Initialized" + + // Cluster agent is available + ClusterAgentAvailable ClusterConditionType = "AgentAvailable" + + // Cluster has been one of federated clusters + ClusterFederated ClusterConditionType = "Federated" + + // Cluster is all available for requests + ClusterReady ClusterConditionType = "Ready" +) + +type ClusterCondition struct { + // Type of the condition + Type ClusterConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status v1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +type ClusterStatus struct { + + // Represents the latest available observations of a cluster's current state. + Conditions []ClusterCondition `json:"conditions,omitempty"` + + // GitVersion of the kubernetes cluster, this field is populated by cluster controller + KubernetesVersion string `json:"kubernetesVersion,omitempty"` + + // Count of the kubernetes cluster nodes + // This field may not reflect the instant status of the cluster. + NodeCount int `json:"nodeCount,omitempty"` + + // Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'. + // +optional + Zones []string `json:"zones,omitempty"` + + // Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'. + // +optional + Region *string `json:"region,omitempty"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// +genclient:nonNamespaced +// +kubebuilder:printcolumn:name="Federated",type="boolean",JSONPath=".spec.joinFederation" +// +kubebuilder:printcolumn:name="Provider",type="string",JSONPath=".spec.provider" +// +kubebuilder:printcolumn:name="Active",type="boolean",JSONPath=".spec.enable" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.kubernetesVersion" +// +kubebuilder:resource:scope=Cluster + +// Cluster is the schema for the clusters API +type Cluster struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterSpec `json:"spec,omitempty"` + Status ClusterStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type ClusterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Cluster `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Cluster{}, &ClusterList{}) +} diff --git a/pkg/apis/cluster/v1alpha1/doc.go b/pkg/apis/cluster/v1alpha1/doc.go new file mode 100644 index 000000000..4a05e1696 --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/doc.go @@ -0,0 +1,8 @@ +// Package v1alpha1 contains API Schema definitions for the tower v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/cluster +// +k8s:defaulter-gen=TypeMeta +// +groupName=cluster.kubesphere.io + +package v1alpha1 diff --git a/pkg/apis/cluster/v1alpha1/openapi_generated.go b/pkg/apis/cluster/v1alpha1/openapi_generated.go new file mode 100644 index 000000000..520e7e862 --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/openapi_generated.go @@ -0,0 +1,13595 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The KubeSphere 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 openapi-gen. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1alpha1 + +import ( + spec "github.com/go-openapi/spec" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + common "k8s.io/kube-openapi/pkg/common" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource": schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref), + "k8s.io/api/core/v1.Affinity": schema_k8sio_api_core_v1_Affinity(ref), + "k8s.io/api/core/v1.AttachedVolume": schema_k8sio_api_core_v1_AttachedVolume(ref), + "k8s.io/api/core/v1.AvoidPods": schema_k8sio_api_core_v1_AvoidPods(ref), + "k8s.io/api/core/v1.AzureDiskVolumeSource": schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref), + "k8s.io/api/core/v1.AzureFilePersistentVolumeSource": schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref), + "k8s.io/api/core/v1.AzureFileVolumeSource": schema_k8sio_api_core_v1_AzureFileVolumeSource(ref), + "k8s.io/api/core/v1.Binding": schema_k8sio_api_core_v1_Binding(ref), + "k8s.io/api/core/v1.CSIPersistentVolumeSource": schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CSIVolumeSource": schema_k8sio_api_core_v1_CSIVolumeSource(ref), + "k8s.io/api/core/v1.Capabilities": schema_k8sio_api_core_v1_Capabilities(ref), + "k8s.io/api/core/v1.CephFSPersistentVolumeSource": schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CephFSVolumeSource": schema_k8sio_api_core_v1_CephFSVolumeSource(ref), + "k8s.io/api/core/v1.CinderPersistentVolumeSource": schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CinderVolumeSource": schema_k8sio_api_core_v1_CinderVolumeSource(ref), + "k8s.io/api/core/v1.ClientIPConfig": schema_k8sio_api_core_v1_ClientIPConfig(ref), + "k8s.io/api/core/v1.ComponentCondition": schema_k8sio_api_core_v1_ComponentCondition(ref), + "k8s.io/api/core/v1.ComponentStatus": schema_k8sio_api_core_v1_ComponentStatus(ref), + "k8s.io/api/core/v1.ComponentStatusList": schema_k8sio_api_core_v1_ComponentStatusList(ref), + "k8s.io/api/core/v1.ConfigMap": schema_k8sio_api_core_v1_ConfigMap(ref), + "k8s.io/api/core/v1.ConfigMapEnvSource": schema_k8sio_api_core_v1_ConfigMapEnvSource(ref), + "k8s.io/api/core/v1.ConfigMapKeySelector": schema_k8sio_api_core_v1_ConfigMapKeySelector(ref), + "k8s.io/api/core/v1.ConfigMapList": schema_k8sio_api_core_v1_ConfigMapList(ref), + "k8s.io/api/core/v1.ConfigMapNodeConfigSource": schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref), + "k8s.io/api/core/v1.ConfigMapProjection": schema_k8sio_api_core_v1_ConfigMapProjection(ref), + "k8s.io/api/core/v1.ConfigMapVolumeSource": schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref), + "k8s.io/api/core/v1.Container": schema_k8sio_api_core_v1_Container(ref), + "k8s.io/api/core/v1.ContainerImage": schema_k8sio_api_core_v1_ContainerImage(ref), + "k8s.io/api/core/v1.ContainerPort": schema_k8sio_api_core_v1_ContainerPort(ref), + "k8s.io/api/core/v1.ContainerState": schema_k8sio_api_core_v1_ContainerState(ref), + "k8s.io/api/core/v1.ContainerStateRunning": schema_k8sio_api_core_v1_ContainerStateRunning(ref), + "k8s.io/api/core/v1.ContainerStateTerminated": schema_k8sio_api_core_v1_ContainerStateTerminated(ref), + "k8s.io/api/core/v1.ContainerStateWaiting": schema_k8sio_api_core_v1_ContainerStateWaiting(ref), + "k8s.io/api/core/v1.ContainerStatus": schema_k8sio_api_core_v1_ContainerStatus(ref), + "k8s.io/api/core/v1.DaemonEndpoint": schema_k8sio_api_core_v1_DaemonEndpoint(ref), + "k8s.io/api/core/v1.DownwardAPIProjection": schema_k8sio_api_core_v1_DownwardAPIProjection(ref), + "k8s.io/api/core/v1.DownwardAPIVolumeFile": schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref), + "k8s.io/api/core/v1.DownwardAPIVolumeSource": schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref), + "k8s.io/api/core/v1.EmptyDirVolumeSource": schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref), + "k8s.io/api/core/v1.EndpointAddress": schema_k8sio_api_core_v1_EndpointAddress(ref), + "k8s.io/api/core/v1.EndpointPort": schema_k8sio_api_core_v1_EndpointPort(ref), + "k8s.io/api/core/v1.EndpointSubset": schema_k8sio_api_core_v1_EndpointSubset(ref), + "k8s.io/api/core/v1.Endpoints": schema_k8sio_api_core_v1_Endpoints(ref), + "k8s.io/api/core/v1.EndpointsList": schema_k8sio_api_core_v1_EndpointsList(ref), + "k8s.io/api/core/v1.EnvFromSource": schema_k8sio_api_core_v1_EnvFromSource(ref), + "k8s.io/api/core/v1.EnvVar": schema_k8sio_api_core_v1_EnvVar(ref), + "k8s.io/api/core/v1.EnvVarSource": schema_k8sio_api_core_v1_EnvVarSource(ref), + "k8s.io/api/core/v1.EphemeralContainer": schema_k8sio_api_core_v1_EphemeralContainer(ref), + "k8s.io/api/core/v1.EphemeralContainerCommon": schema_k8sio_api_core_v1_EphemeralContainerCommon(ref), + "k8s.io/api/core/v1.EphemeralContainers": schema_k8sio_api_core_v1_EphemeralContainers(ref), + "k8s.io/api/core/v1.Event": schema_k8sio_api_core_v1_Event(ref), + "k8s.io/api/core/v1.EventList": schema_k8sio_api_core_v1_EventList(ref), + "k8s.io/api/core/v1.EventSeries": schema_k8sio_api_core_v1_EventSeries(ref), + "k8s.io/api/core/v1.EventSource": schema_k8sio_api_core_v1_EventSource(ref), + "k8s.io/api/core/v1.ExecAction": schema_k8sio_api_core_v1_ExecAction(ref), + "k8s.io/api/core/v1.FCVolumeSource": schema_k8sio_api_core_v1_FCVolumeSource(ref), + "k8s.io/api/core/v1.FlexPersistentVolumeSource": schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref), + "k8s.io/api/core/v1.FlexVolumeSource": schema_k8sio_api_core_v1_FlexVolumeSource(ref), + "k8s.io/api/core/v1.FlockerVolumeSource": schema_k8sio_api_core_v1_FlockerVolumeSource(ref), + "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource": schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref), + "k8s.io/api/core/v1.GitRepoVolumeSource": schema_k8sio_api_core_v1_GitRepoVolumeSource(ref), + "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource": schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref), + "k8s.io/api/core/v1.GlusterfsVolumeSource": schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref), + "k8s.io/api/core/v1.HTTPGetAction": schema_k8sio_api_core_v1_HTTPGetAction(ref), + "k8s.io/api/core/v1.HTTPHeader": schema_k8sio_api_core_v1_HTTPHeader(ref), + "k8s.io/api/core/v1.Handler": schema_k8sio_api_core_v1_Handler(ref), + "k8s.io/api/core/v1.HostAlias": schema_k8sio_api_core_v1_HostAlias(ref), + "k8s.io/api/core/v1.HostPathVolumeSource": schema_k8sio_api_core_v1_HostPathVolumeSource(ref), + "k8s.io/api/core/v1.ISCSIPersistentVolumeSource": schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref), + "k8s.io/api/core/v1.ISCSIVolumeSource": schema_k8sio_api_core_v1_ISCSIVolumeSource(ref), + "k8s.io/api/core/v1.KeyToPath": schema_k8sio_api_core_v1_KeyToPath(ref), + "k8s.io/api/core/v1.Lifecycle": schema_k8sio_api_core_v1_Lifecycle(ref), + "k8s.io/api/core/v1.LimitRange": schema_k8sio_api_core_v1_LimitRange(ref), + "k8s.io/api/core/v1.LimitRangeItem": schema_k8sio_api_core_v1_LimitRangeItem(ref), + "k8s.io/api/core/v1.LimitRangeList": schema_k8sio_api_core_v1_LimitRangeList(ref), + "k8s.io/api/core/v1.LimitRangeSpec": schema_k8sio_api_core_v1_LimitRangeSpec(ref), + "k8s.io/api/core/v1.List": schema_k8sio_api_core_v1_List(ref), + "k8s.io/api/core/v1.LoadBalancerIngress": schema_k8sio_api_core_v1_LoadBalancerIngress(ref), + "k8s.io/api/core/v1.LoadBalancerStatus": schema_k8sio_api_core_v1_LoadBalancerStatus(ref), + "k8s.io/api/core/v1.LocalObjectReference": schema_k8sio_api_core_v1_LocalObjectReference(ref), + "k8s.io/api/core/v1.LocalVolumeSource": schema_k8sio_api_core_v1_LocalVolumeSource(ref), + "k8s.io/api/core/v1.NFSVolumeSource": schema_k8sio_api_core_v1_NFSVolumeSource(ref), + "k8s.io/api/core/v1.Namespace": schema_k8sio_api_core_v1_Namespace(ref), + "k8s.io/api/core/v1.NamespaceCondition": schema_k8sio_api_core_v1_NamespaceCondition(ref), + "k8s.io/api/core/v1.NamespaceList": schema_k8sio_api_core_v1_NamespaceList(ref), + "k8s.io/api/core/v1.NamespaceSpec": schema_k8sio_api_core_v1_NamespaceSpec(ref), + "k8s.io/api/core/v1.NamespaceStatus": schema_k8sio_api_core_v1_NamespaceStatus(ref), + "k8s.io/api/core/v1.Node": schema_k8sio_api_core_v1_Node(ref), + "k8s.io/api/core/v1.NodeAddress": schema_k8sio_api_core_v1_NodeAddress(ref), + "k8s.io/api/core/v1.NodeAffinity": schema_k8sio_api_core_v1_NodeAffinity(ref), + "k8s.io/api/core/v1.NodeCondition": schema_k8sio_api_core_v1_NodeCondition(ref), + "k8s.io/api/core/v1.NodeConfigSource": schema_k8sio_api_core_v1_NodeConfigSource(ref), + "k8s.io/api/core/v1.NodeConfigStatus": schema_k8sio_api_core_v1_NodeConfigStatus(ref), + "k8s.io/api/core/v1.NodeDaemonEndpoints": schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref), + "k8s.io/api/core/v1.NodeList": schema_k8sio_api_core_v1_NodeList(ref), + "k8s.io/api/core/v1.NodeProxyOptions": schema_k8sio_api_core_v1_NodeProxyOptions(ref), + "k8s.io/api/core/v1.NodeResources": schema_k8sio_api_core_v1_NodeResources(ref), + "k8s.io/api/core/v1.NodeSelector": schema_k8sio_api_core_v1_NodeSelector(ref), + "k8s.io/api/core/v1.NodeSelectorRequirement": schema_k8sio_api_core_v1_NodeSelectorRequirement(ref), + "k8s.io/api/core/v1.NodeSelectorTerm": schema_k8sio_api_core_v1_NodeSelectorTerm(ref), + "k8s.io/api/core/v1.NodeSpec": schema_k8sio_api_core_v1_NodeSpec(ref), + "k8s.io/api/core/v1.NodeStatus": schema_k8sio_api_core_v1_NodeStatus(ref), + "k8s.io/api/core/v1.NodeSystemInfo": schema_k8sio_api_core_v1_NodeSystemInfo(ref), + "k8s.io/api/core/v1.ObjectFieldSelector": schema_k8sio_api_core_v1_ObjectFieldSelector(ref), + "k8s.io/api/core/v1.ObjectReference": schema_k8sio_api_core_v1_ObjectReference(ref), + "k8s.io/api/core/v1.PersistentVolume": schema_k8sio_api_core_v1_PersistentVolume(ref), + "k8s.io/api/core/v1.PersistentVolumeClaim": schema_k8sio_api_core_v1_PersistentVolumeClaim(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimCondition": schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimList": schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimSpec": schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimStatus": schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref), + "k8s.io/api/core/v1.PersistentVolumeList": schema_k8sio_api_core_v1_PersistentVolumeList(ref), + "k8s.io/api/core/v1.PersistentVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeSource(ref), + "k8s.io/api/core/v1.PersistentVolumeSpec": schema_k8sio_api_core_v1_PersistentVolumeSpec(ref), + "k8s.io/api/core/v1.PersistentVolumeStatus": schema_k8sio_api_core_v1_PersistentVolumeStatus(ref), + "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource": schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref), + "k8s.io/api/core/v1.Pod": schema_k8sio_api_core_v1_Pod(ref), + "k8s.io/api/core/v1.PodAffinity": schema_k8sio_api_core_v1_PodAffinity(ref), + "k8s.io/api/core/v1.PodAffinityTerm": schema_k8sio_api_core_v1_PodAffinityTerm(ref), + "k8s.io/api/core/v1.PodAntiAffinity": schema_k8sio_api_core_v1_PodAntiAffinity(ref), + "k8s.io/api/core/v1.PodAttachOptions": schema_k8sio_api_core_v1_PodAttachOptions(ref), + "k8s.io/api/core/v1.PodCondition": schema_k8sio_api_core_v1_PodCondition(ref), + "k8s.io/api/core/v1.PodDNSConfig": schema_k8sio_api_core_v1_PodDNSConfig(ref), + "k8s.io/api/core/v1.PodDNSConfigOption": schema_k8sio_api_core_v1_PodDNSConfigOption(ref), + "k8s.io/api/core/v1.PodExecOptions": schema_k8sio_api_core_v1_PodExecOptions(ref), + "k8s.io/api/core/v1.PodIP": schema_k8sio_api_core_v1_PodIP(ref), + "k8s.io/api/core/v1.PodList": schema_k8sio_api_core_v1_PodList(ref), + "k8s.io/api/core/v1.PodLogOptions": schema_k8sio_api_core_v1_PodLogOptions(ref), + "k8s.io/api/core/v1.PodPortForwardOptions": schema_k8sio_api_core_v1_PodPortForwardOptions(ref), + "k8s.io/api/core/v1.PodProxyOptions": schema_k8sio_api_core_v1_PodProxyOptions(ref), + "k8s.io/api/core/v1.PodReadinessGate": schema_k8sio_api_core_v1_PodReadinessGate(ref), + "k8s.io/api/core/v1.PodSecurityContext": schema_k8sio_api_core_v1_PodSecurityContext(ref), + "k8s.io/api/core/v1.PodSignature": schema_k8sio_api_core_v1_PodSignature(ref), + "k8s.io/api/core/v1.PodSpec": schema_k8sio_api_core_v1_PodSpec(ref), + "k8s.io/api/core/v1.PodStatus": schema_k8sio_api_core_v1_PodStatus(ref), + "k8s.io/api/core/v1.PodStatusResult": schema_k8sio_api_core_v1_PodStatusResult(ref), + "k8s.io/api/core/v1.PodTemplate": schema_k8sio_api_core_v1_PodTemplate(ref), + "k8s.io/api/core/v1.PodTemplateList": schema_k8sio_api_core_v1_PodTemplateList(ref), + "k8s.io/api/core/v1.PodTemplateSpec": schema_k8sio_api_core_v1_PodTemplateSpec(ref), + "k8s.io/api/core/v1.PortworxVolumeSource": schema_k8sio_api_core_v1_PortworxVolumeSource(ref), + "k8s.io/api/core/v1.PreferAvoidPodsEntry": schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref), + "k8s.io/api/core/v1.PreferredSchedulingTerm": schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref), + "k8s.io/api/core/v1.Probe": schema_k8sio_api_core_v1_Probe(ref), + "k8s.io/api/core/v1.ProjectedVolumeSource": schema_k8sio_api_core_v1_ProjectedVolumeSource(ref), + "k8s.io/api/core/v1.QuobyteVolumeSource": schema_k8sio_api_core_v1_QuobyteVolumeSource(ref), + "k8s.io/api/core/v1.RBDPersistentVolumeSource": schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref), + "k8s.io/api/core/v1.RBDVolumeSource": schema_k8sio_api_core_v1_RBDVolumeSource(ref), + "k8s.io/api/core/v1.RangeAllocation": schema_k8sio_api_core_v1_RangeAllocation(ref), + "k8s.io/api/core/v1.ReplicationController": schema_k8sio_api_core_v1_ReplicationController(ref), + "k8s.io/api/core/v1.ReplicationControllerCondition": schema_k8sio_api_core_v1_ReplicationControllerCondition(ref), + "k8s.io/api/core/v1.ReplicationControllerList": schema_k8sio_api_core_v1_ReplicationControllerList(ref), + "k8s.io/api/core/v1.ReplicationControllerSpec": schema_k8sio_api_core_v1_ReplicationControllerSpec(ref), + "k8s.io/api/core/v1.ReplicationControllerStatus": schema_k8sio_api_core_v1_ReplicationControllerStatus(ref), + "k8s.io/api/core/v1.ResourceFieldSelector": schema_k8sio_api_core_v1_ResourceFieldSelector(ref), + "k8s.io/api/core/v1.ResourceQuota": schema_k8sio_api_core_v1_ResourceQuota(ref), + "k8s.io/api/core/v1.ResourceQuotaList": schema_k8sio_api_core_v1_ResourceQuotaList(ref), + "k8s.io/api/core/v1.ResourceQuotaSpec": schema_k8sio_api_core_v1_ResourceQuotaSpec(ref), + "k8s.io/api/core/v1.ResourceQuotaStatus": schema_k8sio_api_core_v1_ResourceQuotaStatus(ref), + "k8s.io/api/core/v1.ResourceRequirements": schema_k8sio_api_core_v1_ResourceRequirements(ref), + "k8s.io/api/core/v1.SELinuxOptions": schema_k8sio_api_core_v1_SELinuxOptions(ref), + "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource": schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref), + "k8s.io/api/core/v1.ScaleIOVolumeSource": schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref), + "k8s.io/api/core/v1.ScopeSelector": schema_k8sio_api_core_v1_ScopeSelector(ref), + "k8s.io/api/core/v1.ScopedResourceSelectorRequirement": schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref), + "k8s.io/api/core/v1.Secret": schema_k8sio_api_core_v1_Secret(ref), + "k8s.io/api/core/v1.SecretEnvSource": schema_k8sio_api_core_v1_SecretEnvSource(ref), + "k8s.io/api/core/v1.SecretKeySelector": schema_k8sio_api_core_v1_SecretKeySelector(ref), + "k8s.io/api/core/v1.SecretList": schema_k8sio_api_core_v1_SecretList(ref), + "k8s.io/api/core/v1.SecretProjection": schema_k8sio_api_core_v1_SecretProjection(ref), + "k8s.io/api/core/v1.SecretReference": schema_k8sio_api_core_v1_SecretReference(ref), + "k8s.io/api/core/v1.SecretVolumeSource": schema_k8sio_api_core_v1_SecretVolumeSource(ref), + "k8s.io/api/core/v1.SecurityContext": schema_k8sio_api_core_v1_SecurityContext(ref), + "k8s.io/api/core/v1.SerializedReference": schema_k8sio_api_core_v1_SerializedReference(ref), + "k8s.io/api/core/v1.Service": schema_k8sio_api_core_v1_Service(ref), + "k8s.io/api/core/v1.ServiceAccount": schema_k8sio_api_core_v1_ServiceAccount(ref), + "k8s.io/api/core/v1.ServiceAccountList": schema_k8sio_api_core_v1_ServiceAccountList(ref), + "k8s.io/api/core/v1.ServiceAccountTokenProjection": schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref), + "k8s.io/api/core/v1.ServiceList": schema_k8sio_api_core_v1_ServiceList(ref), + "k8s.io/api/core/v1.ServicePort": schema_k8sio_api_core_v1_ServicePort(ref), + "k8s.io/api/core/v1.ServiceProxyOptions": schema_k8sio_api_core_v1_ServiceProxyOptions(ref), + "k8s.io/api/core/v1.ServiceSpec": schema_k8sio_api_core_v1_ServiceSpec(ref), + "k8s.io/api/core/v1.ServiceStatus": schema_k8sio_api_core_v1_ServiceStatus(ref), + "k8s.io/api/core/v1.SessionAffinityConfig": schema_k8sio_api_core_v1_SessionAffinityConfig(ref), + "k8s.io/api/core/v1.StorageOSPersistentVolumeSource": schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref), + "k8s.io/api/core/v1.StorageOSVolumeSource": schema_k8sio_api_core_v1_StorageOSVolumeSource(ref), + "k8s.io/api/core/v1.Sysctl": schema_k8sio_api_core_v1_Sysctl(ref), + "k8s.io/api/core/v1.TCPSocketAction": schema_k8sio_api_core_v1_TCPSocketAction(ref), + "k8s.io/api/core/v1.Taint": schema_k8sio_api_core_v1_Taint(ref), + "k8s.io/api/core/v1.Toleration": schema_k8sio_api_core_v1_Toleration(ref), + "k8s.io/api/core/v1.TopologySelectorLabelRequirement": schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref), + "k8s.io/api/core/v1.TopologySelectorTerm": schema_k8sio_api_core_v1_TopologySelectorTerm(ref), + "k8s.io/api/core/v1.TopologySpreadConstraint": schema_k8sio_api_core_v1_TopologySpreadConstraint(ref), + "k8s.io/api/core/v1.TypedLocalObjectReference": schema_k8sio_api_core_v1_TypedLocalObjectReference(ref), + "k8s.io/api/core/v1.Volume": schema_k8sio_api_core_v1_Volume(ref), + "k8s.io/api/core/v1.VolumeDevice": schema_k8sio_api_core_v1_VolumeDevice(ref), + "k8s.io/api/core/v1.VolumeMount": schema_k8sio_api_core_v1_VolumeMount(ref), + "k8s.io/api/core/v1.VolumeNodeAffinity": schema_k8sio_api_core_v1_VolumeNodeAffinity(ref), + "k8s.io/api/core/v1.VolumeProjection": schema_k8sio_api_core_v1_VolumeProjection(ref), + "k8s.io/api/core/v1.VolumeSource": schema_k8sio_api_core_v1_VolumeSource(ref), + "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource": schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref), + "k8s.io/api/core/v1.WeightedPodAffinityTerm": schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref), + "k8s.io/api/core/v1.WindowsSecurityContextOptions": schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Cluster": schema_pkg_apis_cluster_v1alpha1_Cluster(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterCondition": schema_pkg_apis_cluster_v1alpha1_ClusterCondition(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterList": schema_pkg_apis_cluster_v1alpha1_ClusterList(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterSpec": schema_pkg_apis_cluster_v1alpha1_ClusterSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterStatus": schema_pkg_apis_cluster_v1alpha1_ClusterStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Connection": schema_pkg_apis_cluster_v1alpha1_Connection(ref), + } +} + +func schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"string"}, + Format: "", + }, + }, + "partition": { + SchemaProps: spec.SchemaProps{ + Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Affinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Affinity is a group of affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes node affinity scheduling rules for the pod.", + Ref: ref("k8s.io/api/core/v1.NodeAffinity"), + }, + }, + "podAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).", + Ref: ref("k8s.io/api/core/v1.PodAffinity"), + }, + }, + "podAntiAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).", + Ref: ref("k8s.io/api/core/v1.PodAntiAffinity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeAffinity", "k8s.io/api/core/v1.PodAffinity", "k8s.io/api/core/v1.PodAntiAffinity"}, + } +} + +func schema_k8sio_api_core_v1_AttachedVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AttachedVolume describes a volume attached to a node", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the attached volume", + Type: []string{"string"}, + Format: "", + }, + }, + "devicePath": { + SchemaProps: spec.SchemaProps{ + Description: "DevicePath represents the device path where the volume should be available", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "devicePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AvoidPods(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AvoidPods describes pods that should avoid this node. This is the value for a Node annotation with key scheduler.alpha.kubernetes.io/preferAvoidPods and will eventually become a field of NodeStatus.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "preferAvoidPods": { + SchemaProps: spec.SchemaProps{ + Description: "Bounded-sized list of signatures of pods that should avoid this node, sorted in timestamp order from oldest to newest. Size of the slice is unspecified.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PreferAvoidPodsEntry"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PreferAvoidPodsEntry"}, + } +} + +func schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "diskName": { + SchemaProps: spec.SchemaProps{ + Description: "The Name of the data disk in the blob storage", + Type: []string{"string"}, + Format: "", + }, + }, + "diskURI": { + SchemaProps: spec.SchemaProps{ + Description: "The URI the data disk in the blob storage", + Type: []string{"string"}, + Format: "", + }, + }, + "cachingMode": { + SchemaProps: spec.SchemaProps{ + Description: "Host Caching mode: None, Read Only, Read Write.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"diskName", "diskURI"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "the name of secret that contains Azure Storage Account Name and Key", + Type: []string{"string"}, + Format: "", + }, + }, + "shareName": { + SchemaProps: spec.SchemaProps{ + Description: "Share Name", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "the namespace of the secret that contains Azure Storage Account Name and Key default is the same as the Pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"secretName", "shareName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AzureFileVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "the name of secret that contains Azure Storage Account Name and Key", + Type: []string{"string"}, + Format: "", + }, + }, + "shareName": { + SchemaProps: spec.SchemaProps{ + Description: "Share Name", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"secretName", "shareName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Binding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Binding ties one object to another; for example, a pod is bound to a node by a scheduler. Deprecated in 1.7, please use the bindings subresource of pods instead.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "target": { + SchemaProps: spec.SchemaProps{ + Description: "The target object that you want to bind to the standard object.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + Required: []string{"target"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents storage that is managed by an external CSI volume driver (Beta feature)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume. Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeHandle": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeHandle is the unique volume name returned by the CSI volume plugin’s CreateVolume to refer to the volume on all subsequent calls. Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeAttributes": { + SchemaProps: spec.SchemaProps{ + Description: "Attributes of the volume to publish.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "controllerPublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "nodeStageSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "nodePublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "controllerExpandSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "ControllerExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerExpandVolume call. This is an alpha field and requires enabling ExpandCSIVolumes feature gate. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + }, + Required: []string{"driver", "volumeHandle"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a source location of a volume to mount, managed by an external CSI driver", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies a read-only configuration for the volume. Defaults to false (read/write).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeAttributes": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "nodePublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Capabilities(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adds and removes POSIX capabilities from running containers.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "add": { + SchemaProps: spec.SchemaProps{ + Description: "Added capabilities", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "drop": { + SchemaProps: spec.SchemaProps{ + Description: "Removed capabilities", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretFile": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CephFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretFile": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CinderVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_ClientIPConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClientIPConfig represents the configurations of Client IP based session affinity.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\". Default value is 10800(for 3 hours).", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ComponentCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Information about the condition of a component.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of condition for a component. Valid value: \"Healthy\"", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message about the condition for a component. For example, information about a health check.", + Type: []string{"string"}, + Format: "", + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "Condition error code for a component. For example, a health check error code.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ComponentStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of component conditions observed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ComponentCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ComponentCondition", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ComponentStatusList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Status of all the conditions for the component as a list of ComponentStatus objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of ComponentStatus objects.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ComponentStatus"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ComponentStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap holds configuration data for pods to consume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "binaryData": { + SchemaProps: spec.SchemaProps{ + Description: "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Selects a key from a ConfigMap.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key to select.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"key"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapList is a resource containing a list of ConfigMap objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is the list of ConfigMaps.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ConfigMap"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMap", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeletConfigKey": { + SchemaProps: spec.SchemaProps{ + Description: "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"namespace", "name", "kubeletConfigKey"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A single application container that you want to run within a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "containerPort", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is an alpha feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_ContainerImage(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describe a container image", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "names": { + SchemaProps: spec.SchemaProps{ + Description: "Names by which this image is known. e.g. [\"k8s.gcr.io/hyperkube:v1.0.7\", \"dockerhub.io/google_containers/hyperkube:v1.0.7\"]", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "sizeBytes": { + SchemaProps: spec.SchemaProps{ + Description: "The size of the image in bytes.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"names"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerPort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerPort represents a network port in a single container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "containerPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".", + Type: []string{"string"}, + Format: "", + }, + }, + "hostIP": { + SchemaProps: spec.SchemaProps{ + Description: "What host IP to bind the external port to.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"containerPort"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateRunning(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateRunning is a running state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "startedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which the container was last (re-)started", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateTerminated(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateTerminated is a terminated state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exitCode": { + SchemaProps: spec.SchemaProps{ + Description: "Exit status from the last termination of the container", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "signal": { + SchemaProps: spec.SchemaProps{ + Description: "Signal from the last termination of the container", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason from the last termination of the container", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message regarding the last termination of the container", + Type: []string{"string"}, + Format: "", + }, + }, + "startedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which previous execution of the container started", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "finishedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which the container last terminated", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "containerID": { + SchemaProps: spec.SchemaProps{ + Description: "Container's ID in the format 'docker://'", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"exitCode"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateWaiting(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateWaiting is a waiting state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason the container is not yet running.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message regarding why the container is not yet running.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStatus contains details for the current status of this container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "Details about the container's current condition.", + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + "lastState": { + SchemaProps: spec.SchemaProps{ + Description: "Details about the container's last termination condition.", + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + "ready": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies whether the container has passed its readiness probe.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "restartCount": { + SchemaProps: spec.SchemaProps{ + Description: "The number of times the container has been restarted, currently based on the number of dead containers that have not yet been removed. Note that this is calculated from dead containers. But those containers are subject to garbage collection. This value will get capped at 5 by GC.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Description: "ImageID of the container's image.", + Type: []string{"string"}, + Format: "", + }, + }, + "containerID": { + SchemaProps: spec.SchemaProps{ + Description: "Container's ID in the format 'docker://'.", + Type: []string{"string"}, + Format: "", + }, + }, + "started": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name", "ready", "restartCount", "image", "imageID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerState"}, + } +} + +func schema_k8sio_api_core_v1_DaemonEndpoint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DaemonEndpoint contains information about a single Daemon endpoint.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Port": { + SchemaProps: spec.SchemaProps{ + Description: "Port number of the given endpoint.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"Port"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of DownwardAPIVolume file", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPIVolumeFile represents information to create the file containing the pod field", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.", + Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), + }, + }, + "resourceFieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.", + Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), + }, + }, + "mode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"path"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector"}, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of downward API volume file", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, + } +} + +func schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "medium": { + SchemaProps: spec.SchemaProps{ + Description: "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Type: []string{"string"}, + Format: "", + }, + }, + "sizeLimit": { + SchemaProps: spec.SchemaProps{ + Description: "Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_EndpointAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointAddress is a tuple that describes single IP address.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "The IP of this endpoint. May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24). IPv6 is also accepted but not fully supported on all platforms. Also, certain kubernetes components, like kube-proxy, are not IPv6 ready.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "The Hostname of this endpoint", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeName": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Node hosting this endpoint. This can be used to determine endpoints local to a node.", + Type: []string{"string"}, + Format: "", + }, + }, + "targetRef": { + SchemaProps: spec.SchemaProps{ + Description: "Reference to object providing the endpoint.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + Required: []string{"ip"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_EndpointPort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointPort is a tuple that describes a single port.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of this port. This must match the 'name' field in the corresponding ServicePort. Must be a DNS_LABEL. Optional only if one port is defined.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "The port number of the endpoint.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"port"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_EndpointSubset(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n }\nThe resulting set of endpoints can be viewed as:\n a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n b: [ 10.10.1.1:309, 10.10.2.2:309 ]", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "addresses": { + SchemaProps: spec.SchemaProps{ + Description: "IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointAddress"), + }, + }, + }, + }, + }, + "notReadyAddresses": { + SchemaProps: spec.SchemaProps{ + Description: "IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointAddress"), + }, + }, + }, + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Port numbers available on the related IP addresses.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointPort"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EndpointAddress", "k8s.io/api/core/v1.EndpointPort"}, + } +} + +func schema_k8sio_api_core_v1_Endpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "subsets": { + SchemaProps: spec.SchemaProps{ + Description: "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointSubset"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EndpointSubset", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_EndpointsList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointsList is a list of endpoints.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of endpoints.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Endpoints"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Endpoints", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_EnvFromSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvFromSource represents the source of a set of ConfigMaps", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "prefix": { + SchemaProps: spec.SchemaProps{ + Description: "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.", + Type: []string{"string"}, + Format: "", + }, + }, + "configMapRef": { + SchemaProps: spec.SchemaProps{ + Description: "The ConfigMap to select from", + Ref: ref("k8s.io/api/core/v1.ConfigMapEnvSource"), + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "The Secret to select from", + Ref: ref("k8s.io/api/core/v1.SecretEnvSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapEnvSource", "k8s.io/api/core/v1.SecretEnvSource"}, + } +} + +func schema_k8sio_api_core_v1_EnvVar(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvVar represents an environment variable present in a Container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the environment variable. Must be a C_IDENTIFIER.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", + Type: []string{"string"}, + Format: "", + }, + }, + "valueFrom": { + SchemaProps: spec.SchemaProps{ + Description: "Source for the environment variable's value. Cannot be used if value is not empty.", + Ref: ref("k8s.io/api/core/v1.EnvVarSource"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EnvVarSource"}, + } +} + +func schema_k8sio_api_core_v1_EnvVarSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvVarSource represents a source for the value of an EnvVar.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "fieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP.", + Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), + }, + }, + "resourceFieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.", + Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), + }, + }, + "configMapKeyRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a key of a ConfigMap.", + Ref: ref("k8s.io/api/core/v1.ConfigMapKeySelector"), + }, + }, + "secretKeyRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a key of a secret in the pod's namespace", + Ref: ref("k8s.io/api/core/v1.SecretKeySelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapKeySelector", "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector", "k8s.io/api/core/v1.SecretKeySelector"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "An EphemeralContainer is a container that may be added temporarily to an existing pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a pod is removed or restarted. If an ephemeral container causes a pod to exceed its resource allocation, the pod may be evicted. Ephemeral containers may not be added by directly updating the pod spec. They must be added via the pod's ephemeralcontainers subresource, and they will appear in the pod spec once added. This is an alpha feature enabled by the EphemeralContainers feature flag.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Ports are not allowed for ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "targetContainerName": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container is run in whatever namespaces are shared for the pod. Note that the container runtime must support this feature.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EphemeralContainerCommon is a copy of all fields in Container to be inlined in EphemeralContainer. This separate type allows easy conversion from EphemeralContainer to Container and allows separate documentation for the fields of EphemeralContainer. When a new field is added to Container it must be added here as well.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Ports are not allowed for ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainers(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "ephemeralContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "A list of ephemeral containers associated with this pod. New ephemeral containers may be appended to this list, but existing ephemeral containers may not be removed or modified.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), + }, + }, + }, + }, + }, + }, + Required: []string{"ephemeralContainers"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_Event(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Event is a report of an event somewhere in the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "involvedObject": { + SchemaProps: spec.SchemaProps{ + Description: "The object that this event is about.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the status of this operation.", + Type: []string{"string"}, + Format: "", + }, + }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The component reporting this event. Should be a short machine understandable string.", + Ref: ref("k8s.io/api/core/v1.EventSource"), + }, + }, + "firstTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "The time at which the event was first recorded. (Time of server receipt is in TypeMeta.)", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "The time at which the most recent occurrence of this event was recorded.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "count": { + SchemaProps: spec.SchemaProps{ + Description: "The number of times this event has occurred.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of this event (Normal, Warning), new types could be added in the future", + Type: []string{"string"}, + Format: "", + }, + }, + "eventTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time when this Event was first observed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), + }, + }, + "series": { + SchemaProps: spec.SchemaProps{ + Description: "Data about the Event series this event represents or nil if it's a singleton Event.", + Ref: ref("k8s.io/api/core/v1.EventSeries"), + }, + }, + "action": { + SchemaProps: spec.SchemaProps{ + Description: "What action was taken/failed regarding to the Regarding object.", + Type: []string{"string"}, + Format: "", + }, + }, + "related": { + SchemaProps: spec.SchemaProps{ + Description: "Optional secondary object for more complex actions.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "reportingComponent": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", + Type: []string{"string"}, + Format: "", + }, + }, + "reportingInstance": { + SchemaProps: spec.SchemaProps{ + Description: "ID of the controller instance, e.g. `kubelet-xyzf`.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"metadata", "involvedObject"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EventSeries", "k8s.io/api/core/v1.EventSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_EventList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventList is a list of events.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of events", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Event"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Event", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_EventSeries(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "count": { + SchemaProps: spec.SchemaProps{ + Description: "Number of occurrences in this series up to the last heartbeat time", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "lastObservedTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time of the last occurrence observed", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "State of this Series: Ongoing or Finished Deprecated. Planned removal for 1.18", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"}, + } +} + +func schema_k8sio_api_core_v1_EventSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventSource contains information for an event.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "component": { + SchemaProps: spec.SchemaProps{ + Description: "Component from which the event is generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Node name on which the event is generated.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ExecAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ExecAction describes a \"run in container\" action.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_FCVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetWWNs": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC target worldwide names (WWNs)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC target lun number", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "wwids": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FlexPersistentVolumeSource represents a generic persistent volume resource that is provisioned/attached using an exec based plugin.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Extra command options if any.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_FlexVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Extra command options if any.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_FlockerVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "datasetName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated", + Type: []string{"string"}, + Format: "", + }, + }, + "datasetUUID": { + SchemaProps: spec.SchemaProps{ + Description: "UUID of the dataset. This is unique identifier of a Flocker dataset", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pdName": { + SchemaProps: spec.SchemaProps{ + Description: "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"string"}, + Format: "", + }, + }, + "partition": { + SchemaProps: spec.SchemaProps{ + Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"pdName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GitRepoVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "repository": { + SchemaProps: spec.SchemaProps{ + Description: "Repository URL", + Type: []string{"string"}, + Format: "", + }, + }, + "revision": { + SchemaProps: spec.SchemaProps{ + Description: "Commit hash for the specified revision.", + Type: []string{"string"}, + Format: "", + }, + }, + "directory": { + SchemaProps: spec.SchemaProps{ + Description: "Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"repository"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "endpoints": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"boolean"}, + Format: "", + }, + }, + "endpointsNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsNamespace is the namespace that contains Glusterfs endpoint. If this field is empty, the EndpointNamespace defaults to the same namespace as the bound PVC. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"endpoints", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "endpoints": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"endpoints", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_HTTPGetAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HTTPGetAction describes an action based on HTTP Get requests.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path to access on the HTTP server.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.", + Type: []string{"string"}, + Format: "", + }, + }, + "scheme": { + SchemaProps: spec.SchemaProps{ + Description: "Scheme to use for connecting to the host. Defaults to HTTP.", + Type: []string{"string"}, + Format: "", + }, + }, + "httpHeaders": { + SchemaProps: spec.SchemaProps{ + Description: "Custom headers to set in the request. HTTP allows repeated headers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.HTTPHeader"), + }, + }, + }, + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.HTTPHeader", "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_HTTPHeader(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HTTPHeader describes a custom header to be used in HTTP probes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The header field name", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "The header field value", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Handler(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Handler defines a specific action that should be taken", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exec": { + SchemaProps: spec.SchemaProps{ + Description: "One and only one of the following should be specified. Exec specifies the action to take.", + Ref: ref("k8s.io/api/core/v1.ExecAction"), + }, + }, + "httpGet": { + SchemaProps: spec.SchemaProps{ + Description: "HTTPGet specifies the http request to perform.", + Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), + }, + }, + "tcpSocket": { + SchemaProps: spec.SchemaProps{ + Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", + Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, + } +} + +func schema_k8sio_api_core_v1_HostAlias(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "IP address of the host file entry.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostnames": { + SchemaProps: spec.SchemaProps{ + Description: "Hostnames for the above IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_HostPathVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ISCSIPersistentVolumeSource represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetPortal": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"string"}, + Format: "", + }, + }, + "iqn": { + SchemaProps: spec.SchemaProps{ + Description: "Target iSCSI Qualified Name.", + Type: []string{"string"}, + Format: "", + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Lun number.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "iscsiInterface": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "portals": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "chapAuthDiscovery": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Discovery CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "chapAuthSession": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Session CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "CHAP Secret for iSCSI target and initiator authentication", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "initiatorName": { + SchemaProps: spec.SchemaProps{ + Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"targetPortal", "iqn", "lun"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_ISCSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetPortal": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"string"}, + Format: "", + }, + }, + "iqn": { + SchemaProps: spec.SchemaProps{ + Description: "Target iSCSI Qualified Name.", + Type: []string{"string"}, + Format: "", + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Lun number.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "iscsiInterface": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "portals": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "chapAuthDiscovery": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Discovery CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "chapAuthSession": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Session CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "CHAP Secret for iSCSI target and initiator authentication", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "initiatorName": { + SchemaProps: spec.SchemaProps{ + Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"targetPortal", "iqn", "lun"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_KeyToPath(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps a string key to a path within a volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key to project.", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.", + Type: []string{"string"}, + Format: "", + }, + }, + "mode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"key", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Lifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "postStart": { + SchemaProps: spec.SchemaProps{ + Description: "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", + Ref: ref("k8s.io/api/core/v1.Handler"), + }, + }, + "preStop": { + SchemaProps: spec.SchemaProps{ + Description: "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", + Ref: ref("k8s.io/api/core/v1.Handler"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Handler"}, + } +} + +func schema_k8sio_api_core_v1_LimitRange(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRange sets resource usage limits for each kind of resource in a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the limits enforced. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.LimitRangeSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRangeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeItem(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of resource that this limit applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Max usage constraints on this kind by resource name.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Min usage constraints on this kind by resource name.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "default": { + SchemaProps: spec.SchemaProps{ + Description: "Default resource requirement limit value by resource name if resource limit is omitted.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "defaultRequest": { + SchemaProps: spec.SchemaProps{ + Description: "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "maxLimitRequestRatio": { + SchemaProps: spec.SchemaProps{ + Description: "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeList is a list of LimitRange items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of LimitRange objects. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LimitRange"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRange", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeSpec defines a min/max usage limit for resources that match on kind.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "limits": { + SchemaProps: spec.SchemaProps{ + Description: "Limits is the list of LimitRangeItem objects that are enforced.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LimitRangeItem"), + }, + }, + }, + }, + }, + }, + Required: []string{"limits"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRangeItem"}, + } +} + +func schema_k8sio_api_core_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "List holds a list of objects, which may not be known by the server.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of objects", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_k8sio_api_core_v1_LoadBalancerIngress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancerIngress represents the status of a load-balancer ingress point: traffic intended for the service should be sent to an ingress point.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)", + Type: []string{"string"}, + Format: "", + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "Hostname is set for load-balancer ingress points that are DNS based (typically AWS load-balancers)", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_LoadBalancerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancerStatus represents the status of a load-balancer.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ingress": { + SchemaProps: spec.SchemaProps{ + Description: "Ingress is a list containing ingress points for the load-balancer. Traffic intended for the service should be sent to these ingress points.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LoadBalancerIngress"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LoadBalancerIngress"}, + } +} + +func schema_k8sio_api_core_v1_LocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_LocalVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity (Beta feature)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "The full path to the volume on the node. It can be either a directory or block device (disk, partition, ...).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. It applies only when the Path is a block device. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default value is to auto-select a fileystem if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "server": { + SchemaProps: spec.SchemaProps{ + Description: "Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"server", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Namespace(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Namespace provides a scope for Names. Use of multiple namespaces is optional.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NamespaceSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NamespaceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NamespaceSpec", "k8s.io/api/core/v1.NamespaceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceCondition contains details about state of namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of namespace controller condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceList is a list of Namespaces.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is the list of Namespace objects in the list. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Namespace"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Namespace", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceSpec describes the attributes on a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "finalizers": { + SchemaProps: spec.SchemaProps{ + Description: "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NamespaceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceStatus is information about the current status of a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents the latest available observations of a namespace's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NamespaceCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NamespaceCondition"}, + } +} + +func schema_k8sio_api_core_v1_Node(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of a node. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NodeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the node. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NodeStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSpec", "k8s.io/api/core/v1.NodeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_NodeAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeAddress contains information for the node's address.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Node address type, one of Hostname, ExternalIP or InternalIP.", + Type: []string{"string"}, + Format: "", + }, + }, + "address": { + SchemaProps: spec.SchemaProps{ + Description: "The node address.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "address"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Node affinity is a group of node affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node.", + Ref: ref("k8s.io/api/core/v1.NodeSelector"), + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PreferredSchedulingTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelector", "k8s.io/api/core/v1.PreferredSchedulingTerm"}, + } +} + +func schema_k8sio_api_core_v1_NodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeCondition contains condition information for a node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of node condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastHeartbeatTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we got an update on a given condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transit from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_NodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap is a reference to a Node's ConfigMap", + Ref: ref("k8s.io/api/core/v1.ConfigMapNodeConfigSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapNodeConfigSource"}, + } +} + +func schema_k8sio_api_core_v1_NodeConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeConfigStatus describes the status of the config assigned by Node.Spec.ConfigSource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "assigned": { + SchemaProps: spec.SchemaProps{ + Description: "Assigned reports the checkpointed config the node will try to use. When Node.Spec.ConfigSource is updated, the node checkpoints the associated config payload to local disk, along with a record indicating intended config. The node refers to this record to choose its config checkpoint, and reports this record in Assigned. Assigned only updates in the status after the record has been checkpointed to disk. When the Kubelet is restarted, it tries to make the Assigned config the Active config by loading and validating the checkpointed payload identified by Assigned.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "active": { + SchemaProps: spec.SchemaProps{ + Description: "Active reports the checkpointed config the node is actively using. Active will represent either the current version of the Assigned config, or the current LastKnownGood config, depending on whether attempting to use the Assigned config results in an error.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "lastKnownGood": { + SchemaProps: spec.SchemaProps{ + Description: "LastKnownGood reports the checkpointed config the node will fall back to when it encounters an error attempting to use the Assigned config. The Assigned config becomes the LastKnownGood config when the node determines that the Assigned config is stable and correct. This is currently implemented as a 10-minute soak period starting when the local record of Assigned config is updated. If the Assigned config is Active at the end of this period, it becomes the LastKnownGood. Note that if Spec.ConfigSource is reset to nil (use local defaults), the LastKnownGood is also immediately reset to nil, because the local default config is always assumed good. You should not make assumptions about the node's method of determining config stability and correctness, as this may change or become configurable in the future.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "Error describes any problems reconciling the Spec.ConfigSource to the Active config. Errors may occur, for example, attempting to checkpoint Spec.ConfigSource to the local Assigned record, attempting to checkpoint the payload associated with Spec.ConfigSource, attempting to load or validate the Assigned config, etc. Errors may occur at different points while syncing config. Earlier errors (e.g. download or checkpointing errors) will not result in a rollback to LastKnownGood, and may resolve across Kubelet retries. Later errors (e.g. loading or validating a checkpointed config) will result in a rollback to LastKnownGood. In the latter case, it is usually possible to resolve the error by fixing the config assigned in Spec.ConfigSource. You can find additional information for debugging by searching the error message in the Kubelet log. Error is a human-readable description of the error state; machines can check whether or not Error is empty, but should not rely on the stability of the Error text across Kubelet versions.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeConfigSource"}, + } +} + +func schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeDaemonEndpoints lists ports opened by daemons running on the Node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kubeletEndpoint": { + SchemaProps: spec.SchemaProps{ + Description: "Endpoint on which Kubelet is listening.", + Ref: ref("k8s.io/api/core/v1.DaemonEndpoint"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DaemonEndpoint"}, + } +} + +func schema_k8sio_api_core_v1_NodeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeList is the whole list of all Nodes which have been registered with master.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of nodes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Node"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Node", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_NodeProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeProxyOptions is the query options to a Node's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the URL path to use for the current proxy request to node.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeResources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeResources is an object for conveying resource information about a node. see http://releases.k8s.io/HEAD/docs/design/resources.md for more details.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Capacity represents the available resources of a node", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + Required: []string{"Capacity"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_NodeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeSelectorTerms": { + SchemaProps: spec.SchemaProps{ + Description: "Required. A list of node selector terms. The terms are ORed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), + }, + }, + }, + }, + }, + }, + Required: []string{"nodeSelectorTerms"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorTerm"}, + } +} + +func schema_k8sio_api_core_v1_NodeSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "operator"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeSelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of node selector requirements by node's labels.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), + }, + }, + }, + }, + }, + "matchFields": { + SchemaProps: spec.SchemaProps{ + Description: "A list of node selector requirements by node's fields.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorRequirement"}, + } +} + +func schema_k8sio_api_core_v1_NodeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSpec describes the attributes that a node is created with.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podCIDR": { + SchemaProps: spec.SchemaProps{ + Description: "PodCIDR represents the pod IP range assigned to the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "podCIDRs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "podCIDRs represents the IP ranges assigned to the node for usage by Pods on that node. If this field is specified, the 0th entry must match the podCIDR field. It may contain at most 1 value for each of IPv4 and IPv6.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "providerID": { + SchemaProps: spec.SchemaProps{ + Description: "ID of the node assigned by the cloud provider in the format: ://", + Type: []string{"string"}, + Format: "", + }, + }, + "unschedulable": { + SchemaProps: spec.SchemaProps{ + Description: "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", + Type: []string{"boolean"}, + Format: "", + }, + }, + "taints": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the node's taints.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Taint"), + }, + }, + }, + }, + }, + "configSource": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "externalID": { + SchemaProps: spec.SchemaProps{ + Description: "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeConfigSource", "k8s.io/api/core/v1.Taint"}, + } +} + +func schema_k8sio_api_core_v1_NodeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeStatus is information about the current status of a node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Capacity represents the total resources of a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "allocatable": { + SchemaProps: spec.SchemaProps{ + Description: "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "NodePhase is the recently observed lifecycle phase of the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#phase The field is never populated, and now is deprecated.", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions is an array of current observed node conditions. More info: https://kubernetes.io/docs/concepts/nodes/node/#condition", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeCondition"), + }, + }, + }, + }, + }, + "addresses": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of addresses reachable to the node. Queried from cloud provider, if available. More info: https://kubernetes.io/docs/concepts/nodes/node/#addresses Note: This field is declared as mergeable, but the merge key is not sufficiently unique, which can cause data corruption when it is merged. Callers should instead use a full-replacement patch. See http://pr.k8s.io/79391 for an example.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeAddress"), + }, + }, + }, + }, + }, + "daemonEndpoints": { + SchemaProps: spec.SchemaProps{ + Description: "Endpoints of daemons running on the Node.", + Ref: ref("k8s.io/api/core/v1.NodeDaemonEndpoints"), + }, + }, + "nodeInfo": { + SchemaProps: spec.SchemaProps{ + Description: "Set of ids/uuids to uniquely identify the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#info", + Ref: ref("k8s.io/api/core/v1.NodeSystemInfo"), + }, + }, + "images": { + SchemaProps: spec.SchemaProps{ + Description: "List of container images on this node", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerImage"), + }, + }, + }, + }, + }, + "volumesInUse": { + SchemaProps: spec.SchemaProps{ + Description: "List of attachable volumes in use (mounted) by the node.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "volumesAttached": { + SchemaProps: spec.SchemaProps{ + Description: "List of volumes that are attached to the node.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.AttachedVolume"), + }, + }, + }, + }, + }, + "config": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the config assigned to the node via the dynamic Kubelet config feature.", + Ref: ref("k8s.io/api/core/v1.NodeConfigStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AttachedVolume", "k8s.io/api/core/v1.ContainerImage", "k8s.io/api/core/v1.NodeAddress", "k8s.io/api/core/v1.NodeCondition", "k8s.io/api/core/v1.NodeConfigStatus", "k8s.io/api/core/v1.NodeDaemonEndpoints", "k8s.io/api/core/v1.NodeSystemInfo", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_NodeSystemInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSystemInfo is a set of ids/uuids to uniquely identify the node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "machineID": { + SchemaProps: spec.SchemaProps{ + Description: "MachineID reported by the node. For unique machine identification in the cluster this field is preferred. Learn more from man(5) machine-id: http://man7.org/linux/man-pages/man5/machine-id.5.html", + Type: []string{"string"}, + Format: "", + }, + }, + "systemUUID": { + SchemaProps: spec.SchemaProps{ + Description: "SystemUUID reported by the node. For unique machine identification MachineID is preferred. This field is specific to Red Hat hosts https://access.redhat.com/documentation/en-US/Red_Hat_Subscription_Management/1/html/RHSM/getting-system-uuid.html", + Type: []string{"string"}, + Format: "", + }, + }, + "bootID": { + SchemaProps: spec.SchemaProps{ + Description: "Boot ID reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "kernelVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Kernel Version reported by the node from 'uname -r' (e.g. 3.16.0-0.bpo.4-amd64).", + Type: []string{"string"}, + Format: "", + }, + }, + "osImage": { + SchemaProps: spec.SchemaProps{ + Description: "OS Image reported by the node from /etc/os-release (e.g. Debian GNU/Linux 7 (wheezy)).", + Type: []string{"string"}, + Format: "", + }, + }, + "containerRuntimeVersion": { + SchemaProps: spec.SchemaProps{ + Description: "ContainerRuntime Version reported by the node through runtime remote API (e.g. docker://1.5.0).", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeletVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Kubelet Version reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeProxyVersion": { + SchemaProps: spec.SchemaProps{ + Description: "KubeProxy Version reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "operatingSystem": { + SchemaProps: spec.SchemaProps{ + Description: "The Operating System reported by the node", + Type: []string{"string"}, + Format: "", + }, + }, + "architecture": { + SchemaProps: spec.SchemaProps{ + Description: "The Architecture reported by the node", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"machineID", "systemUUID", "bootID", "kernelVersion", "osImage", "containerRuntimeVersion", "kubeletVersion", "kubeProxyVersion", "operatingSystem", "architecture"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ObjectFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectFieldSelector selects an APIVersioned field of an object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path of the field to select in the specified API version.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"fieldPath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectReference contains enough information to let you inspect or modify the referred object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldPath": { + SchemaProps: spec.SchemaProps{ + Description: "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PersistentVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeSpec", "k8s.io/api/core/v1.PersistentVolumeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaim(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaim is a user's request for and claim to a persistent volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaimSpec", "k8s.io/api/core/v1.PersistentVolumeClaimStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimCondition contails details about state of pvc", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "lastProbeTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we probed the condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human-readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "A list of persistent volume claims. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaim"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "A label query over volumes to consider for binding.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the binding reference to the PersistentVolume backing this claim.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageClassName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeMode": { + SchemaProps: spec.SchemaProps{ + Description: "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "dataSource": { + SchemaProps: spec.SchemaProps{ + Description: "This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.", + Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.TypedLocalObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase represents the current phase of PersistentVolumeClaim.", + Type: []string{"string"}, + Format: "", + }, + }, + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the actual resources of the underlying volume.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaimCondition", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "claimName": { + SchemaProps: spec.SchemaProps{ + Description: "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Will force the ReadOnly setting in VolumeMounts. Default false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"claimName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeList is a list of PersistentVolume items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of persistent volumes. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolume"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolume", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeSource is similar to VolumeSource but meant for the administrator who creates PVs. Exactly one of its members must be set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", + Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity", + Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", + Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", + Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeSpec is the specification of a persistent volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "A description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", + Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity", + Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", + Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", + Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), + }, + }, + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "claimRef": { + SchemaProps: spec.SchemaProps{ + Description: "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "persistentVolumeReclaimPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "What happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", + Type: []string{"string"}, + Format: "", + }, + }, + "storageClassName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.", + Type: []string{"string"}, + Format: "", + }, + }, + "mountOptions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "volumeMode": { + SchemaProps: spec.SchemaProps{ + Description: "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "NodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume.", + Ref: ref("k8s.io/api/core/v1.VolumeNodeAffinity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VolumeNodeAffinity", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeStatus is the current status of a persistent volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable message indicating details about why the volume is in this state.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Photon Controller persistent disk resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pdID": { + SchemaProps: spec.SchemaProps{ + Description: "ID that identifies Photon Controller persistent disk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"pdID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Pod(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSpec", "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod affinity is a group of inter pod affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_PodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A label query over a set of resources, in this case pods.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + "namespaces": { + SchemaProps: spec.SchemaProps{ + Description: "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\"", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "topologyKey": { + SchemaProps: spec.SchemaProps{ + Description: "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"topologyKey"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_PodAntiAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_PodAttachOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodAttachOptions is the query options to a Pod's remote attach call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Stdin if true, redirects the standard input stream of the pod for this call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdout": { + SchemaProps: spec.SchemaProps{ + Description: "Stdout if true indicates that stdout is to be redirected for the attach call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stderr": { + SchemaProps: spec.SchemaProps{ + Description: "Stderr if true indicates that stderr is to be redirected for the attach call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "TTY if true indicates that a tty will be allocated for the attach call. This is passed through the container runtime so the tty is allocated on the worker node by the container runtime. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "The container in which to execute the command. Defaults to only container if there is only one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodCondition contains details for the current condition of this pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"string"}, + Format: "", + }, + }, + "lastProbeTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we probed the condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Unique, one-word, CamelCase reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human-readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodDNSConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nameservers": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "searches": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodDNSConfigOption"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodDNSConfigOption"}, + } +} + +func schema_k8sio_api_core_v1_PodDNSConfigOption(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodDNSConfigOption defines DNS resolver options of a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodExecOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodExecOptions is the query options to a Pod's remote exec call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard input stream of the pod for this call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdout": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard output stream of the pod for this call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stderr": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard error stream of the pod for this call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "TTY if true indicates that a tty will be allocated for the exec call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "Container in which to execute the command. Defaults to only container if there is only one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Command is the remote command to execute. argv array. Not executed within a shell.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"command"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodIP(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "IP address information for entries in the (plural) PodIPs field. Each entry includes:\n IP: An IP address allocated to the pod. Routable at least within the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "ip is an IP address (IPv4 or IPv6) assigned to the pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodList is a list of Pods.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of pods. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Pod"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Pod", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodLogOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodLogOptions is the query options for a Pod's logs REST call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "The container for which to stream logs. Defaults to only container if there is one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + "follow": { + SchemaProps: spec.SchemaProps{ + Description: "Follow the log stream of the pod. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "previous": { + SchemaProps: spec.SchemaProps{ + Description: "Return previous terminated container logs. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "sinceSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "A relative time in seconds before the current time from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "sinceTime": { + SchemaProps: spec.SchemaProps{ + Description: "An RFC3339 timestamp from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "timestamps": { + SchemaProps: spec.SchemaProps{ + Description: "If true, add an RFC3339 or RFC3339Nano timestamp at the beginning of every line of log output. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tailLines": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the number of lines from the end of the logs to show. If not specified, logs are shown from the creation of the container or sinceSeconds or sinceTime", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "limitBytes": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the number of bytes to read from the server before terminating the log output. This may not display a complete final line of logging, and may return slightly more or slightly less than the specified limit.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodPortForwardOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodPortForwardOptions is the query options to a Pod's port forward call when using WebSockets. The `port` query parameter must specify the port or ports (comma separated) to forward over. Port forwarding over SPDY does not use these options. It requires the port to be passed in the `port` header as part of request.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "List of ports to forward Required when using WebSockets", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodProxyOptions is the query options to a Pod's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the URL path to use for the current proxy request to pod.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodReadinessGate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodReadinessGate contains the reference to a pod condition", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditionType": { + SchemaProps: spec.SchemaProps{ + Description: "ConditionType refers to a condition in the pod's condition list with matching type.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"conditionType"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodSecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "seLinuxOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), + }, + }, + "windowsOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), + }, + }, + "runAsUser": { + SchemaProps: spec.SchemaProps{ + Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsNonRoot": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "supplementalGroups": { + SchemaProps: spec.SchemaProps{ + Description: "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + "fsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "sysctls": { + SchemaProps: spec.SchemaProps{ + Description: "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Sysctl"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.Sysctl", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, + } +} + +func schema_k8sio_api_core_v1_PodSignature(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describes the class of pods that should avoid this node. Exactly one field should be set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podController": { + SchemaProps: spec.SchemaProps{ + Description: "Reference to controller whose pods should avoid this node.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"}, + } +} + +func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodSpec is a description of a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "initContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + }, + }, + }, + "containers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + }, + }, + }, + "ephemeralContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is alpha-level and is only honored by servers that enable the EphemeralContainers feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), + }, + }, + }, + }, + }, + "restartPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationGracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "activeDeadlineSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "dnsPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + 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: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + Type: []string{"string"}, + Format: "", + }, + }, + "serviceAccount": { + SchemaProps: spec.SchemaProps{ + Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Type: []string{"string"}, + Format: "", + }, + }, + "automountServiceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "nodeName": { + SchemaProps: spec.SchemaProps{ + Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostNetwork": { + SchemaProps: spec.SchemaProps{ + Description: "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "hostPID": { + SchemaProps: spec.SchemaProps{ + Description: "Use the host's pid namespace. Optional: Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "hostIPC": { + SchemaProps: spec.SchemaProps{ + Description: "Use the host's ipc namespace. Optional: Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "shareProcessNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. This field is beta-level and may be disabled with the PodShareProcessNamespace feature.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", + Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), + }, + }, + "imagePullSecrets": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + 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", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", + Type: []string{"string"}, + Format: "", + }, + }, + "subdomain": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.", + Type: []string{"string"}, + Format: "", + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's scheduling constraints", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "schedulerName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.", + Type: []string{"string"}, + Format: "", + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's tolerations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "hostAliases": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.HostAlias"), + }, + }, + }, + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + Type: []string{"string"}, + Format: "", + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "dnsConfig": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", + Ref: ref("k8s.io/api/core/v1.PodDNSConfig"), + }, + }, + "readinessGates": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodReadinessGate"), + }, + }, + }, + }, + }, + "runtimeClassName": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + Type: []string{"string"}, + Format: "", + }, + }, + "enableServiceLinks": { + SchemaProps: spec.SchemaProps{ + Description: "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "preemptionPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is alpha-level and is only honored by servers that enable the NonPreemptingPriority feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "overhead": { + SchemaProps: spec.SchemaProps{ + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "topologySpreadConstraints": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "topologyKey", + "whenUnsatisfiable", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "topologyKey", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. This field is alpha-level and is only honored by clusters that enables the EvenPodsSpread feature. All topologySpreadConstraints are ANDed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.TopologySpreadConstraint"), + }, + }, + }, + }, + }, + }, + Required: []string{"containers"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values:\n\nPending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodCondition"), + }, + }, + }, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about why the pod is in this condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'", + Type: []string{"string"}, + Format: "", + }, + }, + "nominatedNodeName": { + SchemaProps: spec.SchemaProps{ + Description: "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostIP": { + SchemaProps: spec.SchemaProps{ + Description: "IP address of the host to which the pod is assigned. Empty if not yet scheduled.", + Type: []string{"string"}, + Format: "", + }, + }, + "podIP": { + SchemaProps: spec.SchemaProps{ + Description: "IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", + Type: []string{"string"}, + Format: "", + }, + }, + "podIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list is empty if no IPs have been allocated yet.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodIP"), + }, + }, + }, + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "initContainerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + "containerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + "qosClass": { + SchemaProps: spec.SchemaProps{ + Description: "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md", + Type: []string{"string"}, + Format: "", + }, + }, + "ephemeralContainerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "Status for any ephemeral containers that have run in this pod. This field is alpha-level and is only populated by servers that enable the EphemeralContainers feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStatus", "k8s.io/api/core/v1.PodCondition", "k8s.io/api/core/v1.PodIP", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodStatusResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate describes a template for creating copies of a predefined pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Description: "Template defines the pods that will be created from this pod template. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplateSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplateList is a list of PodTemplates.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of pod templates", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodTemplate"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplateSpec describes the data a pod should have when created from a template", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PortworxVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolumeSource represents a Portworx volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeID uniquely identifies a Portworx volume", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describes a class of pods that should avoid this node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podSignature": { + SchemaProps: spec.SchemaProps{ + Description: "The class of pods.", + Ref: ref("k8s.io/api/core/v1.PodSignature"), + }, + }, + "evictionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which this entry was added to the list.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason why this entry was added to the list.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating why this entry was added to the list.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"podSignature"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSignature", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "weight": { + SchemaProps: spec.SchemaProps{ + Description: "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "preference": { + SchemaProps: spec.SchemaProps{ + Description: "A node selector term, associated with the corresponding weight.", + Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), + }, + }, + }, + Required: []string{"weight", "preference"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorTerm"}, + } +} + +func schema_k8sio_api_core_v1_Probe(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exec": { + SchemaProps: spec.SchemaProps{ + Description: "One and only one of the following should be specified. Exec specifies the action to take.", + Ref: ref("k8s.io/api/core/v1.ExecAction"), + }, + }, + "httpGet": { + SchemaProps: spec.SchemaProps{ + Description: "HTTPGet specifies the http request to perform.", + Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), + }, + }, + "tcpSocket": { + SchemaProps: spec.SchemaProps{ + Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", + Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), + }, + }, + "initialDelaySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "periodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "successThreshold": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "failureThreshold": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, + } +} + +func schema_k8sio_api_core_v1_ProjectedVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a projected volume source", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "sources": { + SchemaProps: spec.SchemaProps{ + Description: "list of volume projections", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeProjection"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"sources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.VolumeProjection"}, + } +} + +func schema_k8sio_api_core_v1_QuobyteVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "registry": { + SchemaProps: spec.SchemaProps{ + Description: "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", + Type: []string{"string"}, + Format: "", + }, + }, + "volume": { + SchemaProps: spec.SchemaProps{ + Description: "Volume is a string that references an already created Quobyte volume by name.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "User to map volume access to Defaults to serivceaccount user", + Type: []string{"string"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "Group to map volume access to Default is no group", + Type: []string{"string"}, + Format: "", + }, + }, + "tenant": { + SchemaProps: spec.SchemaProps{ + Description: "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"registry", "volume"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + Type: []string{"string"}, + Format: "", + }, + }, + "pool": { + SchemaProps: spec.SchemaProps{ + Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "keyring": { + SchemaProps: spec.SchemaProps{ + Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors", "image"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_RBDVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + Type: []string{"string"}, + Format: "", + }, + }, + "pool": { + SchemaProps: spec.SchemaProps{ + Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "keyring": { + SchemaProps: spec.SchemaProps{ + Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors", "image"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_RangeAllocation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RangeAllocation is not a public type.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "range": { + SchemaProps: spec.SchemaProps{ + Description: "Range is string that identifies the range represented by 'data'.", + Type: []string{"string"}, + Format: "", + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data is a bit array containing all allocated addresses in the previous segment.", + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + Required: []string{"range", "data"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationController(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationController represents the configuration of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ReplicationControllerSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ReplicationControllerStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationControllerSpec", "k8s.io/api/core/v1.ReplicationControllerStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerCondition describes the state of a replication controller at a certain point.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of replication controller condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "The last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "The reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about the transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerList is a collection of replication controllers.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of replication controllers. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ReplicationController"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationController", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerSpec is the specification of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "minReadySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Description: "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", + Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplateSpec"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerStatus represents the current status of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "fullyLabeledReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of pods that have labels matching the labels of the pod template of the replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readyReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of ready replicas for this replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "availableReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of available replicas (ready for at least minReadySeconds) for this replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration reflects the generation of the most recently observed replication controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents the latest available observations of a replication controller's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ReplicationControllerCondition"), + }, + }, + }, + }, + }, + }, + Required: []string{"replicas"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationControllerCondition"}, + } +} + +func schema_k8sio_api_core_v1_ResourceFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceFieldSelector represents container resources (cpu, memory) and their output format", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "containerName": { + SchemaProps: spec.SchemaProps{ + Description: "Container name: required for volumes, optional for env vars", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Required: resource to select", + Type: []string{"string"}, + Format: "", + }, + }, + "divisor": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the output format of the exposed resources, defaults to \"1\"", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + Required: []string{"resource"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuota(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuota sets aggregate quota restrictions enforced per namespace", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the desired quota. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ResourceQuotaSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status defines the actual enforced quota and its current usage. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ResourceQuotaStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceQuotaSpec", "k8s.io/api/core/v1.ResourceQuotaStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaList is a list of ResourceQuota items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of ResourceQuota objects. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ResourceQuota"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceQuota", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hard": { + SchemaProps: spec.SchemaProps{ + Description: "hard is the set of desired hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "scopes": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "scopeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota but expressed using ScopeSelectorOperator in combination with possible values. For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched.", + Ref: ref("k8s.io/api/core/v1.ScopeSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ScopeSelector", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaStatus defines the enforced hard limits and observed use.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hard": { + SchemaProps: spec.SchemaProps{ + Description: "Hard is the set of enforced hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "used": { + SchemaProps: spec.SchemaProps{ + Description: "Used is the current observed total usage of the resource in the namespace.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceRequirements(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceRequirements describes the compute resource requirements.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "limits": { + SchemaProps: spec.SchemaProps{ + Description: "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "requests": { + SchemaProps: spec.SchemaProps{ + Description: "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_SELinuxOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SELinuxOptions are the labels to be applied to the container", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "user": { + SchemaProps: spec.SchemaProps{ + Description: "User is a SELinux user label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "role": { + SchemaProps: spec.SchemaProps{ + Description: "Role is a SELinux role label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is a SELinux type label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "level": { + SchemaProps: spec.SchemaProps{ + Description: "Level is SELinux level label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gateway": { + SchemaProps: spec.SchemaProps{ + Description: "The host address of the ScaleIO API Gateway.", + Type: []string{"string"}, + Format: "", + }, + }, + "system": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the storage system as configured in ScaleIO.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "sslEnabled": { + SchemaProps: spec.SchemaProps{ + Description: "Flag to enable/disable SSL communication with Gateway, default false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "protectionDomain": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the ScaleIO Protection Domain for the configured storage.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePool": { + SchemaProps: spec.SchemaProps{ + Description: "The ScaleIO Storage Pool associated with the protection domain.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageMode": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\"", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"gateway", "system", "secretRef"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ScaleIOVolumeSource represents a persistent ScaleIO volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gateway": { + SchemaProps: spec.SchemaProps{ + Description: "The host address of the ScaleIO API Gateway.", + Type: []string{"string"}, + Format: "", + }, + }, + "system": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the storage system as configured in ScaleIO.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "sslEnabled": { + SchemaProps: spec.SchemaProps{ + Description: "Flag to enable/disable SSL communication with Gateway, default false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "protectionDomain": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the ScaleIO Protection Domain for the configured storage.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePool": { + SchemaProps: spec.SchemaProps{ + Description: "The ScaleIO Storage Pool associated with the protection domain.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageMode": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"gateway", "system", "secretRef"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_ScopeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A scope selector represents the AND of the selectors represented by the scoped-resource selector requirements.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of scope selector requirements by scope of the resources.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ScopedResourceSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ScopedResourceSelectorRequirement"}, + } +} + +func schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator that relates the scope name and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scopeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the scope that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Represents a scope's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"scopeName", "operator"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Secret(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + "stringData": { + SchemaProps: spec.SchemaProps{ + Description: "stringData allows specifying non-binary secret data in string form. It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Used to facilitate programmatic handling of secret data.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_SecretEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretKeySelector selects a key of a Secret.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key of the secret to select from. Must be a valid secret key.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"key"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretList is a list of Secret.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of secret objects. More info: https://kubernetes.io/docs/concepts/configuration/secret", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Secret"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Secret", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_SecretProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_SecretReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is unique within a namespace to reference a secret resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace defines the space within which the secret name must be unique.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_SecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capabilities": { + SchemaProps: spec.SchemaProps{ + Description: "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.", + Ref: ref("k8s.io/api/core/v1.Capabilities"), + }, + }, + "privileged": { + SchemaProps: spec.SchemaProps{ + Description: "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "seLinuxOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), + }, + }, + "windowsOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), + }, + }, + "runAsUser": { + SchemaProps: spec.SchemaProps{ + Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsNonRoot": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "readOnlyRootFilesystem": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container has a read-only root filesystem. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "allowPrivilegeEscalation": { + SchemaProps: spec.SchemaProps{ + Description: "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN", + Type: []string{"boolean"}, + Format: "", + }, + }, + "procMount": { + SchemaProps: spec.SchemaProps{ + Description: "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Capabilities", "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, + } +} + +func schema_k8sio_api_core_v1_SerializedReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SerializedReference is a reference to serialized object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "reference": { + SchemaProps: spec.SchemaProps{ + Description: "The reference to an object in the system.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Service(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ServiceSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ServiceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServiceSpec", "k8s.io/api/core/v1.ServiceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "secrets": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: https://kubernetes.io/docs/concepts/configuration/secret", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "automountServiceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccountList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountList is a list of ServiceAccount objects", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of ServiceAccounts. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ServiceAccount"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServiceAccount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "audience": { + SchemaProps: spec.SchemaProps{ + Description: "Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.", + Type: []string{"string"}, + Format: "", + }, + }, + "expirationSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the path relative to the mount point of the file to project the token into.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ServiceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceList holds a list of services.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of services", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Service"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Service", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServicePort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServicePort contains information on service's port.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", + Type: []string{"string"}, + Format: "", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "The port that will be exposed by this service.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "targetPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "nodePort": { + SchemaProps: spec.SchemaProps{ + Description: "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_ServiceProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceProxyOptions is the query options to a Service's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the part of URLs that include service endpoints, suffixes, and parameters to use for the current proxy request to service. For example, the whole request URL is http://localhost/api/v1/namespaces/kube-system/services/elasticsearch-logging/_search?q=user:kimchy. Path is _search?q=user:kimchy.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ServiceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceSpec describes the attributes that a user creates on a service.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "port", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "port", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ServicePort"), + }, + }, + }, + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "clusterIP": { + SchemaProps: spec.SchemaProps{ + Description: "clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \"None\", empty string (\"\"), or a valid IP address. \"None\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", + Type: []string{"string"}, + Format: "", + }, + }, + "externalIPs": { + SchemaProps: spec.SchemaProps{ + 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.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "sessionAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"string"}, + Format: "", + }, + }, + "loadBalancerIP": { + SchemaProps: spec.SchemaProps{ + Description: "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "loadBalancerSourceRanges": { + SchemaProps: spec.SchemaProps{ + Description: "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.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "externalName": { + SchemaProps: spec.SchemaProps{ + Description: "externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.", + Type: []string{"string"}, + Format: "", + }, + }, + "externalTrafficPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \"Local\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \"Cluster\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.", + Type: []string{"string"}, + Format: "", + }, + }, + "healthCheckNodePort": { + SchemaProps: spec.SchemaProps{ + Description: "healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "publishNotReadyAddresses": { + SchemaProps: spec.SchemaProps{ + Description: "publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "sessionAffinityConfig": { + SchemaProps: spec.SchemaProps{ + Description: "sessionAffinityConfig contains the configurations of session affinity.", + Ref: ref("k8s.io/api/core/v1.SessionAffinityConfig"), + }, + }, + "ipFamily": { + SchemaProps: spec.SchemaProps{ + Description: "ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServicePort", "k8s.io/api/core/v1.SessionAffinityConfig"}, + } +} + +func schema_k8sio_api_core_v1_ServiceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceStatus represents the current status of a service.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "loadBalancer": { + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancer contains the current status of the load-balancer, if one is present.", + Ref: ref("k8s.io/api/core/v1.LoadBalancerStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LoadBalancerStatus"}, + } +} + +func schema_k8sio_api_core_v1_SessionAffinityConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SessionAffinityConfig represents the configurations of session affinity.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "clientIP": { + SchemaProps: spec.SchemaProps{ + Description: "clientIP contains the configurations of Client IP based session affinity.", + Ref: ref("k8s.io/api/core/v1.ClientIPConfig"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ClientIPConfig"}, + } +} + +func schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a StorageOS persistent volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_StorageOSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a StorageOS persistent volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Sysctl(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Sysctl defines a kernel parameter to be set", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of a property to set", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value of a property to set", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TCPSocketAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TCPSocketAction describes an action based on opening a socket", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "port": { + SchemaProps: spec.SchemaProps{ + Description: "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Host name to connect to, defaults to the pod IP.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_Taint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The node this Taint is attached to has the \"effect\" on any pod that does not tolerate the Taint.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The taint key to be applied to a node.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The taint value corresponding to the taint key.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeAdded": { + SchemaProps: spec.SchemaProps{ + Description: "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"key", "effect"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_Toleration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "tolerationSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. One value must match the label to be selected. Each entry in Values is ORed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "values"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TopologySelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchLabelExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of topology selector requirements by labels.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.TopologySelectorLabelRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.TopologySelectorLabelRequirement"}, + } +} + +func schema_k8sio_api_core_v1_TopologySpreadConstraint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TopologySpreadConstraint specifies how to spread matching pods among the given topology.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "maxSkew": { + SchemaProps: spec.SchemaProps{ + Description: "MaxSkew describes the degree to which pods may be unevenly distributed. It's the maximum permitted difference between the number of matching pods in any two topology domains of a given topology type. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 1/1/1; scheduling it onto zone1(zone2) would make the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. It's a required field. Default value is 1 and 0 is not allowed.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "topologyKey": { + SchemaProps: spec.SchemaProps{ + Description: "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. It's a required field.", + Type: []string{"string"}, + Format: "", + }, + }, + "whenUnsatisfiable": { + SchemaProps: spec.SchemaProps{ + Description: "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it - ScheduleAnyway tells the scheduler to still schedule it It's considered as \"Unsatisfiable\" if and only if placing incoming pod on any topology violates \"MaxSkew\". For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.", + Type: []string{"string"}, + Format: "", + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + }, + Required: []string{"maxSkew", "topologyKey", "whenUnsatisfiable"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_TypedLocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiGroup": { + SchemaProps: spec.SchemaProps{ + Description: "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is the type of resource being referenced", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of resource being referenced", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Volume represents a named volume in a pod that may be accessed by any container in the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "emptyDir": { + SchemaProps: spec.SchemaProps{ + Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "gitRepo": { + SchemaProps: spec.SchemaProps{ + Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), + }, + }, + "persistentVolumeClaim": { + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPI represents downward API about the pod that should populate this volume", + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap represents a configMap that should populate this volume", + Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "projected": { + SchemaProps: spec.SchemaProps{ + Description: "Items for all in one resources secrets, configmaps, and downward API", + Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", + Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_VolumeDevice(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "volumeDevice describes a mapping of a raw block device within a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name must match the name of a persistentVolumeClaim in the pod", + Type: []string{"string"}, + Format: "", + }, + }, + "devicePath": { + SchemaProps: spec.SchemaProps{ + Description: "devicePath is the path inside of the container that the device will be mapped to.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "devicePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_VolumeMount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VolumeMount describes a mounting of a Volume within a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "This must match the Name of a Volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "mountPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path within the container at which the volume should be mounted. Must not contain ':'.", + Type: []string{"string"}, + Format: "", + }, + }, + "subPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", + Type: []string{"string"}, + Format: "", + }, + }, + "mountPropagation": { + SchemaProps: spec.SchemaProps{ + Description: "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.", + Type: []string{"string"}, + Format: "", + }, + }, + "subPathExpr": { + SchemaProps: spec.SchemaProps{ + Description: "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "mountPath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_VolumeNodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VolumeNodeAffinity defines constraints that limit what nodes this volume can be accessed from.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "required": { + SchemaProps: spec.SchemaProps{ + Description: "Required specifies hard node constraints that must be met.", + Ref: ref("k8s.io/api/core/v1.NodeSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelector"}, + } +} + +func schema_k8sio_api_core_v1_VolumeProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Projection that may be projected along with other supported volume types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "information about the secret data to project", + Ref: ref("k8s.io/api/core/v1.SecretProjection"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "information about the downwardAPI data to project", + Ref: ref("k8s.io/api/core/v1.DownwardAPIProjection"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "information about the configMap data to project", + Ref: ref("k8s.io/api/core/v1.ConfigMapProjection"), + }, + }, + "serviceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "information about the serviceAccountToken data to project", + Ref: ref("k8s.io/api/core/v1.ServiceAccountTokenProjection"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapProjection", "k8s.io/api/core/v1.DownwardAPIProjection", "k8s.io/api/core/v1.SecretProjection", "k8s.io/api/core/v1.ServiceAccountTokenProjection"}, + } +} + +func schema_k8sio_api_core_v1_VolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents the source of a volume to mount. Only one of its members may be specified.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "emptyDir": { + SchemaProps: spec.SchemaProps{ + Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "gitRepo": { + SchemaProps: spec.SchemaProps{ + Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), + }, + }, + "persistentVolumeClaim": { + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPI represents downward API about the pod that should populate this volume", + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap represents a configMap that should populate this volume", + Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "projected": { + SchemaProps: spec.SchemaProps{ + Description: "Items for all in one resources secrets, configmaps, and downward API", + Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", + Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a vSphere volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumePath": { + SchemaProps: spec.SchemaProps{ + Description: "Path that identifies vSphere volume vmdk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePolicyName": { + SchemaProps: spec.SchemaProps{ + Description: "Storage Policy Based Management (SPBM) profile name.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePolicyID": { + SchemaProps: spec.SchemaProps{ + Description: "Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"volumePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "weight": { + SchemaProps: spec.SchemaProps{ + Description: "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "podAffinityTerm": { + SchemaProps: spec.SchemaProps{ + Description: "Required. A pod affinity term, associated with the corresponding weight.", + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + Required: []string{"weight", "podAffinityTerm"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WindowsSecurityContextOptions contain Windows-specific options and credentials.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gmsaCredentialSpecName": { + SchemaProps: spec.SchemaProps{ + Description: "GMSACredentialSpecName is the name of the GMSA credential spec to use. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + "gmsaCredentialSpec": { + SchemaProps: spec.SchemaProps{ + Description: "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + "runAsUserName": { + SchemaProps: spec.SchemaProps{ + Description: "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. This field is alpha-level and it is only honored by servers that enable the WindowsRunAsUserName feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIGroup contains the name, the supported versions, and the preferred version of a group.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the name of the group.", + Type: []string{"string"}, + Format: "", + }, + }, + "versions": { + SchemaProps: spec.SchemaProps{ + Description: "versions are the versions supported in this group.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + }, + }, + }, + }, + }, + "preferredVersion": { + SchemaProps: spec.SchemaProps{ + Description: "preferredVersion is the version preferred by the API server, which probably is the storage version.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + }, + }, + "serverAddressByClientCIDRs": { + SchemaProps: spec.SchemaProps{ + Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "versions"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery", "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + } +} + +func schema_pkg_apis_meta_v1_APIGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "groups": { + SchemaProps: spec.SchemaProps{ + Description: "groups is a list of APIGroup.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"), + }, + }, + }, + }, + }, + }, + Required: []string{"groups"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"}, + } +} + +func schema_pkg_apis_meta_v1_APIResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIResource specifies the name of a resource and whether it is namespaced.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the plural name of the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "singularName": { + SchemaProps: spec.SchemaProps{ + Description: "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + Type: []string{"string"}, + Format: "", + }, + }, + "namespaced": { + SchemaProps: spec.SchemaProps{ + Description: "namespaced indicates if a resource is namespaced or not.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + Type: []string{"string"}, + Format: "", + }, + }, + "verbs": { + SchemaProps: spec.SchemaProps{ + Description: "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "shortNames": { + SchemaProps: spec.SchemaProps{ + Description: "shortNames is a list of suggested short names of the resource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "categories": { + SchemaProps: spec.SchemaProps{ + Description: "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "storageVersionHash": { + SchemaProps: spec.SchemaProps{ + Description: "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "singularName", "namespaced", "kind", "verbs"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_APIResourceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "groupVersion": { + SchemaProps: spec.SchemaProps{ + Description: "groupVersion is the group and version this APIResourceList is for.", + Type: []string{"string"}, + Format: "", + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "resources contains the name of the resources and if they are namespaced.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"), + }, + }, + }, + }, + }, + }, + Required: []string{"groupVersion", "resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"}, + } +} + +func schema_pkg_apis_meta_v1_APIVersions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "versions": { + SchemaProps: spec.SchemaProps{ + Description: "versions are the api versions that are available.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "serverAddressByClientCIDRs": { + SchemaProps: spec.SchemaProps{ + Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + }, + }, + }, + }, + }, + }, + Required: []string{"versions", "serverAddressByClientCIDRs"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + } +} + +func schema_pkg_apis_meta_v1_CreateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CreateOptions may be provided when creating an API object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DeleteOptions may be provided when deleting an API object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "gracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "preconditions": { + SchemaProps: spec.SchemaProps{ + Description: "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"), + }, + }, + "orphanDependents": { + SchemaProps: spec.SchemaProps{ + Description: "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagationPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"}, + } +} + +func schema_pkg_apis_meta_v1_Duration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Duration is a wrapper around time.Duration which supports correct marshaling to YAML and JSON. In particular, it marshals into strings, which can be used as map keys in json.", + Type: v1.Duration{}.OpenAPISchemaType(), + Format: v1.Duration{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ExportOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ExportOptions is the query options to the standard REST get call. Deprecated. Planned for removal in 1.18.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "export": { + SchemaProps: spec.SchemaProps{ + Description: "Should this value be exported. Export strips fields that a user can not specify. Deprecated. Planned for removal in 1.18.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "exact": { + SchemaProps: spec.SchemaProps{ + Description: "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'. Deprecated. Planned for removal in 1.18.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"export", "exact"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_FieldsV1(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GetOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GetOptions is the standard query options to the standard REST get call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "kind"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersion(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersion contains the \"group\" and the \"version\", which uniquely identifies the API.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "groupVersion": { + SchemaProps: spec.SchemaProps{ + Description: "groupVersion specifies the API group and version in the form \"group/version\"", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"groupVersion", "version"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version", "kind"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_InternalEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "InternalEvent makes watch.Event versioned", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Object": { + SchemaProps: spec.SchemaProps{ + Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Bookmark: the object (instance of a type being watched) where\n only ResourceVersion field is set. On successful restart of watch from a\n bookmark resourceVersion, client is guaranteed to not get repeat event\n nor miss any events.\n * If Type is Error: *api.Status is recommended; other types may make sense\n depending on context.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.Object"), + }, + }, + }, + Required: []string{"Type", "Object"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.Object"}, + } +} + +func schema_pkg_apis_meta_v1_LabelSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchLabels": { + SchemaProps: spec.SchemaProps{ + Description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"}, + } +} + +func schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "key", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "key is the label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "operator"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "List holds a list of objects, which may not be known by the server.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of objects", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_pkg_apis_meta_v1_ListMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "selfLink": { + SchemaProps: spec.SchemaProps{ + Description: "selfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Description: "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + Type: []string{"string"}, + Format: "", + }, + }, + "remainingItemCount": { + SchemaProps: spec.SchemaProps{ + Description: "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ListOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ListOptions is the query options to a standard REST list call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + Type: []string{"string"}, + Format: "", + }, + }, + "watch": { + SchemaProps: spec.SchemaProps{ + Description: "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "allowWatchBookmarks": { + SchemaProps: spec.SchemaProps{ + Description: "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "limit": { + SchemaProps: spec.SchemaProps{ + Description: "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Description: "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "manager": { + SchemaProps: spec.SchemaProps{ + Description: "Manager is an identifier of the workflow managing these fields.", + Type: []string{"string"}, + Format: "", + }, + }, + "operation": { + SchemaProps: spec.SchemaProps{ + Description: "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + Type: []string{"string"}, + Format: "", + }, + }, + "time": { + SchemaProps: spec.SchemaProps{ + Description: "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "fieldsType": { + SchemaProps: spec.SchemaProps{ + Description: "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldsV1": { + SchemaProps: spec.SchemaProps{ + Description: "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_meta_v1_MicroTime(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MicroTime is version of Time with microsecond level precision.", + Type: v1.MicroTime{}.OpenAPISchemaType(), + Format: v1.MicroTime{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "generateName": { + SchemaProps: spec.SchemaProps{ + Description: "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces", + Type: []string{"string"}, + Format: "", + }, + }, + "selfLink": { + SchemaProps: spec.SchemaProps{ + Description: "SelfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "generation": { + SchemaProps: spec.SchemaProps{ + Description: "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "creationTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "deletionTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "deletionGracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Description: "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "ownerReferences": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), + }, + }, + }, + }, + }, + "finalizers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "clusterName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.", + Type: []string{"string"}, + Format: "", + }, + }, + "managedFields": { + SchemaProps: spec.SchemaProps{ + Description: "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry", "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_meta_v1_OwnerReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "controller": { + SchemaProps: spec.SchemaProps{ + Description: "If true, this reference points to the managing controller.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "blockOwnerDeletion": { + SchemaProps: spec.SchemaProps{ + Description: "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"apiVersion", "kind", "name", "uid"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_PartialObjectMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PartialObjectMetadata is a generic representation of any object with ObjectMeta. It allows clients to get access to a particular ObjectMeta schema without knowing the details of the version.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PartialObjectMetadataList contains a list of objects containing only their metadata", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items contains each of the included items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"}, + } +} + +func schema_pkg_apis_meta_v1_Patch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_PatchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "force": { + SchemaProps: spec.SchemaProps{ + Description: "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Preconditions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the target UID.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the target ResourceVersion", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_RootPaths(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RootPaths lists the paths available at root. For example: \"/healthz\", \"/apis\".", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "paths": { + SchemaProps: spec.SchemaProps{ + Description: "paths are the paths available at root.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"paths"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "clientCIDR": { + SchemaProps: spec.SchemaProps{ + Description: "The CIDR with which clients can match their IP to figure out the server address that they should use.", + Type: []string{"string"}, + Format: "", + }, + }, + "serverAddress": { + SchemaProps: spec.SchemaProps{ + Description: "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"clientCIDR", "serverAddress"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Status is a return value for calls that don't return other objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the status of this operation.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"), + }, + }, + "code": { + SchemaProps: spec.SchemaProps{ + Description: "Suggested HTTP return code for this status, 0 if not set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"}, + } +} + +func schema_pkg_apis_meta_v1_StatusCause(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + Type: []string{"string"}, + Format: "", + }, + }, + "field": { + SchemaProps: spec.SchemaProps{ + Description: "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_StatusDetails(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + Type: []string{"string"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "The group attribute of the resource associated with the status StatusReason.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "causes": { + SchemaProps: spec.SchemaProps{ + Description: "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"), + }, + }, + }, + }, + }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"}, + } +} + +func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Table is a tabular representation of a set of API resources. The server transforms the object into a set of preferred columns for quickly reviewing the objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "columnDefinitions": { + SchemaProps: spec.SchemaProps{ + Description: "columnDefinitions describes each column in the returned items array. The number of cells per row will always match the number of column definitions.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition"), + }, + }, + }, + }, + }, + "rows": { + SchemaProps: spec.SchemaProps{ + Description: "rows is the list of items in the table.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"), + }, + }, + }, + }, + }, + }, + Required: []string{"columnDefinitions", "rows"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition", "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"}, + } +} + +func schema_pkg_apis_meta_v1_TableColumnDefinition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableColumnDefinition contains information about a column returned in the Table.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is a human readable name for the column.", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type is an OpenAPI type definition for this column, such as number, integer, string, or array. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + Type: []string{"string"}, + Format: "", + }, + }, + "format": { + SchemaProps: spec.SchemaProps{ + Description: "format is an optional OpenAPI type modifier for this column. A format modifies the type and imposes additional rules, like date or time formatting for a string. The 'name' format is applied to the primary identifier column which has type 'string' to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human readable description of this column.", + Type: []string{"string"}, + Format: "", + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a higher priority.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"name", "type", "format", "description", "priority"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TableOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableOptions are used when a Table is requested by the caller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "includeObject": { + SchemaProps: spec.SchemaProps{ + Description: "includeObject decides whether to include each object along with its columnar information. Specifying \"None\" will return no object, specifying \"Object\" will return the full object contents, and specifying \"Metadata\" (the default) will return the object's metadata in the PartialObjectMetadata kind in version v1beta1 of the meta.k8s.io API group.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableRow is an individual row in a table.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "cells": { + SchemaProps: spec.SchemaProps{ + Description: "cells will be as wide as the column definitions array and may contain strings, numbers (float64 or int64), booleans, simple maps, lists, or null. See the type field of the column definition for a more detailed description.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "conditions describe additional status of a row that are relevant for a human user. These conditions apply to the row, not to the object, and will be specific to table output. The only defined condition type is 'Completed', for a row that indicates a resource that has run to completion and can be given less visual priority.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition"), + }, + }, + }, + }, + }, + "object": { + SchemaProps: spec.SchemaProps{ + Description: "This field contains the requested additional information about each object based on the includeObject policy when requesting the Table. If \"None\", this field is empty, if \"Object\" this will be the default serialization of the object for the current API version, and if \"Metadata\" (the default) will contain the object metadata. Check the returned kind and apiVersion of the object before parsing. The media type of the object will always match the enclosing list - if this as a JSON table, these will be JSON encoded objects.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"cells"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_pkg_apis_meta_v1_TableRowCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableRowCondition allows a row to be marked with additional information.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of row condition. The only defined value is 'Completed' indicating that the object this row represents has reached a completed state and may be given less visual priority than other rows. Clients are not required to honor any conditions but should be consistent where possible about handling the conditions.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) machine readable reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Time(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + Type: v1.Time{}.OpenAPISchemaType(), + Format: v1.Time{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Timestamp(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Timestamp is a struct that is equivalent to Time, but intended for protobuf marshalling/unmarshalling. It is generated into a serialization that matches Time. Do not use in Go structs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "seconds": { + SchemaProps: spec.SchemaProps{ + Description: "Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "nanos": { + SchemaProps: spec.SchemaProps{ + Description: "Non-negative fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values that count forward in time. Must be from 0 to 999,999,999 inclusive. This field may be limited in precision depending on context.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"seconds", "nanos"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypeMeta describes an individual object in an API response or request with strings representing the type of the object and its API schema version. Structures that are versioned or persisted should inline TypeMeta.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_UpdateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Event represents a single event to a watched resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "object": { + SchemaProps: spec.SchemaProps{ + Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"type", "object"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + Type: []string{"object"}, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this: type MyAwesomeAPIObject struct {\n runtime.TypeMeta `json:\",inline\"`\n ... // other fields\n} func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_Unknown(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Unknown allows api objects with unknown types to be passed-through. This can be used to deal with the API objects from a plug-in. Unknown objects still have functioning TypeMeta features-- kind, version, etc. metadata and field mutatation.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Raw": { + SchemaProps: spec.SchemaProps{ + Description: "Raw will hold the complete serialized object which couldn't be matched with a registered type. Most likely, nothing should be done with this except for passing it through the system.", + Type: []string{"string"}, + Format: "byte", + }, + }, + "ContentEncoding": { + SchemaProps: spec.SchemaProps{ + Description: "ContentEncoding is encoding used to encode 'Raw' data. Unspecified means no encoding.", + Type: []string{"string"}, + Format: "", + }, + }, + "ContentType": { + SchemaProps: spec.SchemaProps{ + Description: "ContentType is serialization method used to serialize 'Raw'. Unspecified means ContentTypeJSON.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"Raw", "ContentEncoding", "ContentType"}, + }, + }, + } +} + +func schema_pkg_apis_cluster_v1alpha1_Cluster(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Cluster is the schema for the clusters API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterSpec", "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterStatus"}, + } +} + +func schema_pkg_apis_cluster_v1alpha1_ClusterCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of the condition", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastUpdateTime": { + SchemaProps: spec.SchemaProps{ + Description: "The last time this condition was updated.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "The reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about the transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_cluster_v1alpha1_ClusterList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Cluster"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Cluster"}, + } +} + +func schema_pkg_apis_cluster_v1alpha1_ClusterSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "joinFederation": { + SchemaProps: spec.SchemaProps{ + Description: "Join cluster as a kubefed cluster", + Type: []string{"boolean"}, + Format: "", + }, + }, + "enable": { + SchemaProps: spec.SchemaProps{ + Description: "Desired state of the cluster", + Type: []string{"boolean"}, + Format: "", + }, + }, + "provider": { + SchemaProps: spec.SchemaProps{ + Description: "Provider of the cluster, this field is just for description", + Type: []string{"string"}, + Format: "", + }, + }, + "connection": { + SchemaProps: spec.SchemaProps{ + Description: "Connection holds info to connect to the member cluster", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Connection"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.Connection"}, + } +} + +func schema_pkg_apis_cluster_v1alpha1_ClusterStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the latest available observations of a cluster's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterCondition"), + }, + }, + }, + }, + }, + "kubernetesVersion": { + SchemaProps: spec.SchemaProps{ + Description: "GitVersion of the kubernetes cluster, this field is populated by cluster controller", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeCount": { + SchemaProps: spec.SchemaProps{ + Description: "Count of the kubernetes cluster nodes This field may not reflect the instant status of the cluster.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "zones": { + SchemaProps: spec.SchemaProps{ + Description: "Zones are the names of availability zones in which the nodes of the cluster exist, e.g. 'us-east1-a'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "region": { + SchemaProps: spec.SchemaProps{ + Description: "Region is the name of the region in which all of the nodes in the cluster exist. e.g. 'us-east1'.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1.ClusterCondition"}, + } +} + +func schema_pkg_apis_cluster_v1alpha1_Connection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type defines how host cluster will connect to host cluster ConnectionTypeDirect means direct connection, this requires\n kubeconfig and kubesphere apiserver endpoint provided\nConnectionTypeProxy means using kubesphere proxy, no kubeconfig\n or kubesphere apiserver endpoint required", + Type: []string{"string"}, + Format: "", + }, + }, + "kubesphereAPIEndpoint": { + SchemaProps: spec.SchemaProps{ + Description: "KubeSphere API Server endpoint. Example: http://10.10.0.11:8080 Should provide this field explicitly if connection type is direct. Will be populated by ks-apiserver if connection type is proxy.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubernetesAPIEndpoint": { + SchemaProps: spec.SchemaProps{ + Description: "Kubernetes API Server endpoint. Example: https://10.10.0.1:6443 Should provide this field explicitly if connection type is direct. Will be populated by ks-apiserver if connection type is proxy.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeconfig": { + SchemaProps: spec.SchemaProps{ + Description: "KubeConfig content used to connect to cluster api server Should provide this field explicitly if connection type is direct. Will be populated by ks-proxy if connection type is proxy.", + Type: []string{"string"}, + Format: "byte", + }, + }, + "token": { + SchemaProps: spec.SchemaProps{ + Description: "Token used by agents of member cluster to connect to host cluster proxy. This field is populated by apiserver only if connection type is proxy.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubernetesAPIServerPort": { + SchemaProps: spec.SchemaProps{ + Description: "KubeAPIServerPort is the port which listens for forwarding kube-apiserver traffic Only applicable when connection type is proxy.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "kubesphereAPIServerPort": { + SchemaProps: spec.SchemaProps{ + Description: "KubeSphereAPIServerPort is the port which listens for forwarding kubesphere apigateway traffic Only applicable when connection type is proxy.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/apis/cluster/v1alpha1/register.go b/pkg/apis/cluster/v1alpha1/register.go new file mode 100644 index 000000000..6cb43d6bc --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/register.go @@ -0,0 +1,41 @@ +/* + +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 v1alpha1 contains API Schema definitions for the tower v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:defaulter-gen=TypeMeta +// +groupName=cluster.kubesphere.io +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "cluster.kubesphere.io", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..a5d35bd86 --- /dev/null +++ b/pkg/apis/cluster/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,169 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The KubeSphere 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 controller-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 *Cluster) DeepCopyInto(out *Cluster) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cluster. +func (in *Cluster) DeepCopy() *Cluster { + if in == nil { + return nil + } + out := new(Cluster) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Cluster) 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 *ClusterCondition) DeepCopyInto(out *ClusterCondition) { + *out = *in + in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCondition. +func (in *ClusterCondition) DeepCopy() *ClusterCondition { + if in == nil { + return nil + } + out := new(ClusterCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterList) DeepCopyInto(out *ClusterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Cluster, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterList. +func (in *ClusterList) DeepCopy() *ClusterList { + if in == nil { + return nil + } + out := new(ClusterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterList) 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 *ClusterSpec) DeepCopyInto(out *ClusterSpec) { + *out = *in + in.Connection.DeepCopyInto(&out.Connection) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSpec. +func (in *ClusterSpec) DeepCopy() *ClusterSpec { + if in == nil { + return nil + } + out := new(ClusterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterStatus) DeepCopyInto(out *ClusterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]ClusterCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Zones != nil { + in, out := &in.Zones, &out.Zones + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Region != nil { + in, out := &in.Region, &out.Region + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterStatus. +func (in *ClusterStatus) DeepCopy() *ClusterStatus { + if in == nil { + return nil + } + out := new(ClusterStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Connection) DeepCopyInto(out *Connection) { + *out = *in + if in.KubeConfig != nil { + in, out := &in.KubeConfig, &out.KubeConfig + *out = make([]byte, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection. +func (in *Connection) DeepCopy() *Connection { + if in == nil { + return nil + } + out := new(Connection) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/devops/crdinstall/install.go b/pkg/apis/devops/crdinstall/install.go index a8334655b..6cd0092a2 100644 --- a/pkg/apis/devops/crdinstall/install.go +++ b/pkg/apis/devops/crdinstall/install.go @@ -22,9 +22,11 @@ import ( k8sruntime "k8s.io/apimachinery/pkg/runtime" urlruntime "k8s.io/apimachinery/pkg/util/runtime" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" ) func Install(scheme *k8sruntime.Scheme) { urlruntime.Must(devopsv1alpha1.AddToScheme(scheme)) - urlruntime.Must(scheme.SetVersionPriority(devopsv1alpha1.SchemeGroupVersion)) + urlruntime.Must(devopsv1alpha3.AddToScheme(scheme)) + urlruntime.Must(scheme.SetVersionPriority(devopsv1alpha3.SchemeGroupVersion, devopsv1alpha1.SchemeGroupVersion)) } diff --git a/pkg/apis/devops/v1alpha1/openapi_generated.go b/pkg/apis/devops/v1alpha1/openapi_generated.go index 6e0e460a2..1992a77b2 100644 --- a/pkg/apis/devops/v1alpha1/openapi_generated.go +++ b/pkg/apis/devops/v1alpha1/openapi_generated.go @@ -30,57 +30,11126 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinary": schema_pkg_apis_devops_v1alpha1_S2iBinary(ref), - "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinaryList": schema_pkg_apis_devops_v1alpha1_S2iBinaryList(ref), - "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinarySpec": schema_pkg_apis_devops_v1alpha1_S2iBinarySpec(ref), - "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinaryStatus": schema_pkg_apis_devops_v1alpha1_S2iBinaryStatus(ref), + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource": schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref), + "k8s.io/api/core/v1.Affinity": schema_k8sio_api_core_v1_Affinity(ref), + "k8s.io/api/core/v1.AttachedVolume": schema_k8sio_api_core_v1_AttachedVolume(ref), + "k8s.io/api/core/v1.AvoidPods": schema_k8sio_api_core_v1_AvoidPods(ref), + "k8s.io/api/core/v1.AzureDiskVolumeSource": schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref), + "k8s.io/api/core/v1.AzureFilePersistentVolumeSource": schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref), + "k8s.io/api/core/v1.AzureFileVolumeSource": schema_k8sio_api_core_v1_AzureFileVolumeSource(ref), + "k8s.io/api/core/v1.Binding": schema_k8sio_api_core_v1_Binding(ref), + "k8s.io/api/core/v1.CSIPersistentVolumeSource": schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CSIVolumeSource": schema_k8sio_api_core_v1_CSIVolumeSource(ref), + "k8s.io/api/core/v1.Capabilities": schema_k8sio_api_core_v1_Capabilities(ref), + "k8s.io/api/core/v1.CephFSPersistentVolumeSource": schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CephFSVolumeSource": schema_k8sio_api_core_v1_CephFSVolumeSource(ref), + "k8s.io/api/core/v1.CinderPersistentVolumeSource": schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref), + "k8s.io/api/core/v1.CinderVolumeSource": schema_k8sio_api_core_v1_CinderVolumeSource(ref), + "k8s.io/api/core/v1.ClientIPConfig": schema_k8sio_api_core_v1_ClientIPConfig(ref), + "k8s.io/api/core/v1.ComponentCondition": schema_k8sio_api_core_v1_ComponentCondition(ref), + "k8s.io/api/core/v1.ComponentStatus": schema_k8sio_api_core_v1_ComponentStatus(ref), + "k8s.io/api/core/v1.ComponentStatusList": schema_k8sio_api_core_v1_ComponentStatusList(ref), + "k8s.io/api/core/v1.ConfigMap": schema_k8sio_api_core_v1_ConfigMap(ref), + "k8s.io/api/core/v1.ConfigMapEnvSource": schema_k8sio_api_core_v1_ConfigMapEnvSource(ref), + "k8s.io/api/core/v1.ConfigMapKeySelector": schema_k8sio_api_core_v1_ConfigMapKeySelector(ref), + "k8s.io/api/core/v1.ConfigMapList": schema_k8sio_api_core_v1_ConfigMapList(ref), + "k8s.io/api/core/v1.ConfigMapNodeConfigSource": schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref), + "k8s.io/api/core/v1.ConfigMapProjection": schema_k8sio_api_core_v1_ConfigMapProjection(ref), + "k8s.io/api/core/v1.ConfigMapVolumeSource": schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref), + "k8s.io/api/core/v1.Container": schema_k8sio_api_core_v1_Container(ref), + "k8s.io/api/core/v1.ContainerImage": schema_k8sio_api_core_v1_ContainerImage(ref), + "k8s.io/api/core/v1.ContainerPort": schema_k8sio_api_core_v1_ContainerPort(ref), + "k8s.io/api/core/v1.ContainerState": schema_k8sio_api_core_v1_ContainerState(ref), + "k8s.io/api/core/v1.ContainerStateRunning": schema_k8sio_api_core_v1_ContainerStateRunning(ref), + "k8s.io/api/core/v1.ContainerStateTerminated": schema_k8sio_api_core_v1_ContainerStateTerminated(ref), + "k8s.io/api/core/v1.ContainerStateWaiting": schema_k8sio_api_core_v1_ContainerStateWaiting(ref), + "k8s.io/api/core/v1.ContainerStatus": schema_k8sio_api_core_v1_ContainerStatus(ref), + "k8s.io/api/core/v1.DaemonEndpoint": schema_k8sio_api_core_v1_DaemonEndpoint(ref), + "k8s.io/api/core/v1.DownwardAPIProjection": schema_k8sio_api_core_v1_DownwardAPIProjection(ref), + "k8s.io/api/core/v1.DownwardAPIVolumeFile": schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref), + "k8s.io/api/core/v1.DownwardAPIVolumeSource": schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref), + "k8s.io/api/core/v1.EmptyDirVolumeSource": schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref), + "k8s.io/api/core/v1.EndpointAddress": schema_k8sio_api_core_v1_EndpointAddress(ref), + "k8s.io/api/core/v1.EndpointPort": schema_k8sio_api_core_v1_EndpointPort(ref), + "k8s.io/api/core/v1.EndpointSubset": schema_k8sio_api_core_v1_EndpointSubset(ref), + "k8s.io/api/core/v1.Endpoints": schema_k8sio_api_core_v1_Endpoints(ref), + "k8s.io/api/core/v1.EndpointsList": schema_k8sio_api_core_v1_EndpointsList(ref), + "k8s.io/api/core/v1.EnvFromSource": schema_k8sio_api_core_v1_EnvFromSource(ref), + "k8s.io/api/core/v1.EnvVar": schema_k8sio_api_core_v1_EnvVar(ref), + "k8s.io/api/core/v1.EnvVarSource": schema_k8sio_api_core_v1_EnvVarSource(ref), + "k8s.io/api/core/v1.EphemeralContainer": schema_k8sio_api_core_v1_EphemeralContainer(ref), + "k8s.io/api/core/v1.EphemeralContainerCommon": schema_k8sio_api_core_v1_EphemeralContainerCommon(ref), + "k8s.io/api/core/v1.EphemeralContainers": schema_k8sio_api_core_v1_EphemeralContainers(ref), + "k8s.io/api/core/v1.Event": schema_k8sio_api_core_v1_Event(ref), + "k8s.io/api/core/v1.EventList": schema_k8sio_api_core_v1_EventList(ref), + "k8s.io/api/core/v1.EventSeries": schema_k8sio_api_core_v1_EventSeries(ref), + "k8s.io/api/core/v1.EventSource": schema_k8sio_api_core_v1_EventSource(ref), + "k8s.io/api/core/v1.ExecAction": schema_k8sio_api_core_v1_ExecAction(ref), + "k8s.io/api/core/v1.FCVolumeSource": schema_k8sio_api_core_v1_FCVolumeSource(ref), + "k8s.io/api/core/v1.FlexPersistentVolumeSource": schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref), + "k8s.io/api/core/v1.FlexVolumeSource": schema_k8sio_api_core_v1_FlexVolumeSource(ref), + "k8s.io/api/core/v1.FlockerVolumeSource": schema_k8sio_api_core_v1_FlockerVolumeSource(ref), + "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource": schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref), + "k8s.io/api/core/v1.GitRepoVolumeSource": schema_k8sio_api_core_v1_GitRepoVolumeSource(ref), + "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource": schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref), + "k8s.io/api/core/v1.GlusterfsVolumeSource": schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref), + "k8s.io/api/core/v1.HTTPGetAction": schema_k8sio_api_core_v1_HTTPGetAction(ref), + "k8s.io/api/core/v1.HTTPHeader": schema_k8sio_api_core_v1_HTTPHeader(ref), + "k8s.io/api/core/v1.Handler": schema_k8sio_api_core_v1_Handler(ref), + "k8s.io/api/core/v1.HostAlias": schema_k8sio_api_core_v1_HostAlias(ref), + "k8s.io/api/core/v1.HostPathVolumeSource": schema_k8sio_api_core_v1_HostPathVolumeSource(ref), + "k8s.io/api/core/v1.ISCSIPersistentVolumeSource": schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref), + "k8s.io/api/core/v1.ISCSIVolumeSource": schema_k8sio_api_core_v1_ISCSIVolumeSource(ref), + "k8s.io/api/core/v1.KeyToPath": schema_k8sio_api_core_v1_KeyToPath(ref), + "k8s.io/api/core/v1.Lifecycle": schema_k8sio_api_core_v1_Lifecycle(ref), + "k8s.io/api/core/v1.LimitRange": schema_k8sio_api_core_v1_LimitRange(ref), + "k8s.io/api/core/v1.LimitRangeItem": schema_k8sio_api_core_v1_LimitRangeItem(ref), + "k8s.io/api/core/v1.LimitRangeList": schema_k8sio_api_core_v1_LimitRangeList(ref), + "k8s.io/api/core/v1.LimitRangeSpec": schema_k8sio_api_core_v1_LimitRangeSpec(ref), + "k8s.io/api/core/v1.List": schema_k8sio_api_core_v1_List(ref), + "k8s.io/api/core/v1.LoadBalancerIngress": schema_k8sio_api_core_v1_LoadBalancerIngress(ref), + "k8s.io/api/core/v1.LoadBalancerStatus": schema_k8sio_api_core_v1_LoadBalancerStatus(ref), + "k8s.io/api/core/v1.LocalObjectReference": schema_k8sio_api_core_v1_LocalObjectReference(ref), + "k8s.io/api/core/v1.LocalVolumeSource": schema_k8sio_api_core_v1_LocalVolumeSource(ref), + "k8s.io/api/core/v1.NFSVolumeSource": schema_k8sio_api_core_v1_NFSVolumeSource(ref), + "k8s.io/api/core/v1.Namespace": schema_k8sio_api_core_v1_Namespace(ref), + "k8s.io/api/core/v1.NamespaceCondition": schema_k8sio_api_core_v1_NamespaceCondition(ref), + "k8s.io/api/core/v1.NamespaceList": schema_k8sio_api_core_v1_NamespaceList(ref), + "k8s.io/api/core/v1.NamespaceSpec": schema_k8sio_api_core_v1_NamespaceSpec(ref), + "k8s.io/api/core/v1.NamespaceStatus": schema_k8sio_api_core_v1_NamespaceStatus(ref), + "k8s.io/api/core/v1.Node": schema_k8sio_api_core_v1_Node(ref), + "k8s.io/api/core/v1.NodeAddress": schema_k8sio_api_core_v1_NodeAddress(ref), + "k8s.io/api/core/v1.NodeAffinity": schema_k8sio_api_core_v1_NodeAffinity(ref), + "k8s.io/api/core/v1.NodeCondition": schema_k8sio_api_core_v1_NodeCondition(ref), + "k8s.io/api/core/v1.NodeConfigSource": schema_k8sio_api_core_v1_NodeConfigSource(ref), + "k8s.io/api/core/v1.NodeConfigStatus": schema_k8sio_api_core_v1_NodeConfigStatus(ref), + "k8s.io/api/core/v1.NodeDaemonEndpoints": schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref), + "k8s.io/api/core/v1.NodeList": schema_k8sio_api_core_v1_NodeList(ref), + "k8s.io/api/core/v1.NodeProxyOptions": schema_k8sio_api_core_v1_NodeProxyOptions(ref), + "k8s.io/api/core/v1.NodeResources": schema_k8sio_api_core_v1_NodeResources(ref), + "k8s.io/api/core/v1.NodeSelector": schema_k8sio_api_core_v1_NodeSelector(ref), + "k8s.io/api/core/v1.NodeSelectorRequirement": schema_k8sio_api_core_v1_NodeSelectorRequirement(ref), + "k8s.io/api/core/v1.NodeSelectorTerm": schema_k8sio_api_core_v1_NodeSelectorTerm(ref), + "k8s.io/api/core/v1.NodeSpec": schema_k8sio_api_core_v1_NodeSpec(ref), + "k8s.io/api/core/v1.NodeStatus": schema_k8sio_api_core_v1_NodeStatus(ref), + "k8s.io/api/core/v1.NodeSystemInfo": schema_k8sio_api_core_v1_NodeSystemInfo(ref), + "k8s.io/api/core/v1.ObjectFieldSelector": schema_k8sio_api_core_v1_ObjectFieldSelector(ref), + "k8s.io/api/core/v1.ObjectReference": schema_k8sio_api_core_v1_ObjectReference(ref), + "k8s.io/api/core/v1.PersistentVolume": schema_k8sio_api_core_v1_PersistentVolume(ref), + "k8s.io/api/core/v1.PersistentVolumeClaim": schema_k8sio_api_core_v1_PersistentVolumeClaim(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimCondition": schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimList": schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimSpec": schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimStatus": schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref), + "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref), + "k8s.io/api/core/v1.PersistentVolumeList": schema_k8sio_api_core_v1_PersistentVolumeList(ref), + "k8s.io/api/core/v1.PersistentVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeSource(ref), + "k8s.io/api/core/v1.PersistentVolumeSpec": schema_k8sio_api_core_v1_PersistentVolumeSpec(ref), + "k8s.io/api/core/v1.PersistentVolumeStatus": schema_k8sio_api_core_v1_PersistentVolumeStatus(ref), + "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource": schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref), + "k8s.io/api/core/v1.Pod": schema_k8sio_api_core_v1_Pod(ref), + "k8s.io/api/core/v1.PodAffinity": schema_k8sio_api_core_v1_PodAffinity(ref), + "k8s.io/api/core/v1.PodAffinityTerm": schema_k8sio_api_core_v1_PodAffinityTerm(ref), + "k8s.io/api/core/v1.PodAntiAffinity": schema_k8sio_api_core_v1_PodAntiAffinity(ref), + "k8s.io/api/core/v1.PodAttachOptions": schema_k8sio_api_core_v1_PodAttachOptions(ref), + "k8s.io/api/core/v1.PodCondition": schema_k8sio_api_core_v1_PodCondition(ref), + "k8s.io/api/core/v1.PodDNSConfig": schema_k8sio_api_core_v1_PodDNSConfig(ref), + "k8s.io/api/core/v1.PodDNSConfigOption": schema_k8sio_api_core_v1_PodDNSConfigOption(ref), + "k8s.io/api/core/v1.PodExecOptions": schema_k8sio_api_core_v1_PodExecOptions(ref), + "k8s.io/api/core/v1.PodIP": schema_k8sio_api_core_v1_PodIP(ref), + "k8s.io/api/core/v1.PodList": schema_k8sio_api_core_v1_PodList(ref), + "k8s.io/api/core/v1.PodLogOptions": schema_k8sio_api_core_v1_PodLogOptions(ref), + "k8s.io/api/core/v1.PodPortForwardOptions": schema_k8sio_api_core_v1_PodPortForwardOptions(ref), + "k8s.io/api/core/v1.PodProxyOptions": schema_k8sio_api_core_v1_PodProxyOptions(ref), + "k8s.io/api/core/v1.PodReadinessGate": schema_k8sio_api_core_v1_PodReadinessGate(ref), + "k8s.io/api/core/v1.PodSecurityContext": schema_k8sio_api_core_v1_PodSecurityContext(ref), + "k8s.io/api/core/v1.PodSignature": schema_k8sio_api_core_v1_PodSignature(ref), + "k8s.io/api/core/v1.PodSpec": schema_k8sio_api_core_v1_PodSpec(ref), + "k8s.io/api/core/v1.PodStatus": schema_k8sio_api_core_v1_PodStatus(ref), + "k8s.io/api/core/v1.PodStatusResult": schema_k8sio_api_core_v1_PodStatusResult(ref), + "k8s.io/api/core/v1.PodTemplate": schema_k8sio_api_core_v1_PodTemplate(ref), + "k8s.io/api/core/v1.PodTemplateList": schema_k8sio_api_core_v1_PodTemplateList(ref), + "k8s.io/api/core/v1.PodTemplateSpec": schema_k8sio_api_core_v1_PodTemplateSpec(ref), + "k8s.io/api/core/v1.PortworxVolumeSource": schema_k8sio_api_core_v1_PortworxVolumeSource(ref), + "k8s.io/api/core/v1.PreferAvoidPodsEntry": schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref), + "k8s.io/api/core/v1.PreferredSchedulingTerm": schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref), + "k8s.io/api/core/v1.Probe": schema_k8sio_api_core_v1_Probe(ref), + "k8s.io/api/core/v1.ProjectedVolumeSource": schema_k8sio_api_core_v1_ProjectedVolumeSource(ref), + "k8s.io/api/core/v1.QuobyteVolumeSource": schema_k8sio_api_core_v1_QuobyteVolumeSource(ref), + "k8s.io/api/core/v1.RBDPersistentVolumeSource": schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref), + "k8s.io/api/core/v1.RBDVolumeSource": schema_k8sio_api_core_v1_RBDVolumeSource(ref), + "k8s.io/api/core/v1.RangeAllocation": schema_k8sio_api_core_v1_RangeAllocation(ref), + "k8s.io/api/core/v1.ReplicationController": schema_k8sio_api_core_v1_ReplicationController(ref), + "k8s.io/api/core/v1.ReplicationControllerCondition": schema_k8sio_api_core_v1_ReplicationControllerCondition(ref), + "k8s.io/api/core/v1.ReplicationControllerList": schema_k8sio_api_core_v1_ReplicationControllerList(ref), + "k8s.io/api/core/v1.ReplicationControllerSpec": schema_k8sio_api_core_v1_ReplicationControllerSpec(ref), + "k8s.io/api/core/v1.ReplicationControllerStatus": schema_k8sio_api_core_v1_ReplicationControllerStatus(ref), + "k8s.io/api/core/v1.ResourceFieldSelector": schema_k8sio_api_core_v1_ResourceFieldSelector(ref), + "k8s.io/api/core/v1.ResourceQuota": schema_k8sio_api_core_v1_ResourceQuota(ref), + "k8s.io/api/core/v1.ResourceQuotaList": schema_k8sio_api_core_v1_ResourceQuotaList(ref), + "k8s.io/api/core/v1.ResourceQuotaSpec": schema_k8sio_api_core_v1_ResourceQuotaSpec(ref), + "k8s.io/api/core/v1.ResourceQuotaStatus": schema_k8sio_api_core_v1_ResourceQuotaStatus(ref), + "k8s.io/api/core/v1.ResourceRequirements": schema_k8sio_api_core_v1_ResourceRequirements(ref), + "k8s.io/api/core/v1.SELinuxOptions": schema_k8sio_api_core_v1_SELinuxOptions(ref), + "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource": schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref), + "k8s.io/api/core/v1.ScaleIOVolumeSource": schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref), + "k8s.io/api/core/v1.ScopeSelector": schema_k8sio_api_core_v1_ScopeSelector(ref), + "k8s.io/api/core/v1.ScopedResourceSelectorRequirement": schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref), + "k8s.io/api/core/v1.Secret": schema_k8sio_api_core_v1_Secret(ref), + "k8s.io/api/core/v1.SecretEnvSource": schema_k8sio_api_core_v1_SecretEnvSource(ref), + "k8s.io/api/core/v1.SecretKeySelector": schema_k8sio_api_core_v1_SecretKeySelector(ref), + "k8s.io/api/core/v1.SecretList": schema_k8sio_api_core_v1_SecretList(ref), + "k8s.io/api/core/v1.SecretProjection": schema_k8sio_api_core_v1_SecretProjection(ref), + "k8s.io/api/core/v1.SecretReference": schema_k8sio_api_core_v1_SecretReference(ref), + "k8s.io/api/core/v1.SecretVolumeSource": schema_k8sio_api_core_v1_SecretVolumeSource(ref), + "k8s.io/api/core/v1.SecurityContext": schema_k8sio_api_core_v1_SecurityContext(ref), + "k8s.io/api/core/v1.SerializedReference": schema_k8sio_api_core_v1_SerializedReference(ref), + "k8s.io/api/core/v1.Service": schema_k8sio_api_core_v1_Service(ref), + "k8s.io/api/core/v1.ServiceAccount": schema_k8sio_api_core_v1_ServiceAccount(ref), + "k8s.io/api/core/v1.ServiceAccountList": schema_k8sio_api_core_v1_ServiceAccountList(ref), + "k8s.io/api/core/v1.ServiceAccountTokenProjection": schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref), + "k8s.io/api/core/v1.ServiceList": schema_k8sio_api_core_v1_ServiceList(ref), + "k8s.io/api/core/v1.ServicePort": schema_k8sio_api_core_v1_ServicePort(ref), + "k8s.io/api/core/v1.ServiceProxyOptions": schema_k8sio_api_core_v1_ServiceProxyOptions(ref), + "k8s.io/api/core/v1.ServiceSpec": schema_k8sio_api_core_v1_ServiceSpec(ref), + "k8s.io/api/core/v1.ServiceStatus": schema_k8sio_api_core_v1_ServiceStatus(ref), + "k8s.io/api/core/v1.SessionAffinityConfig": schema_k8sio_api_core_v1_SessionAffinityConfig(ref), + "k8s.io/api/core/v1.StorageOSPersistentVolumeSource": schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref), + "k8s.io/api/core/v1.StorageOSVolumeSource": schema_k8sio_api_core_v1_StorageOSVolumeSource(ref), + "k8s.io/api/core/v1.Sysctl": schema_k8sio_api_core_v1_Sysctl(ref), + "k8s.io/api/core/v1.TCPSocketAction": schema_k8sio_api_core_v1_TCPSocketAction(ref), + "k8s.io/api/core/v1.Taint": schema_k8sio_api_core_v1_Taint(ref), + "k8s.io/api/core/v1.Toleration": schema_k8sio_api_core_v1_Toleration(ref), + "k8s.io/api/core/v1.TopologySelectorLabelRequirement": schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref), + "k8s.io/api/core/v1.TopologySelectorTerm": schema_k8sio_api_core_v1_TopologySelectorTerm(ref), + "k8s.io/api/core/v1.TopologySpreadConstraint": schema_k8sio_api_core_v1_TopologySpreadConstraint(ref), + "k8s.io/api/core/v1.TypedLocalObjectReference": schema_k8sio_api_core_v1_TypedLocalObjectReference(ref), + "k8s.io/api/core/v1.Volume": schema_k8sio_api_core_v1_Volume(ref), + "k8s.io/api/core/v1.VolumeDevice": schema_k8sio_api_core_v1_VolumeDevice(ref), + "k8s.io/api/core/v1.VolumeMount": schema_k8sio_api_core_v1_VolumeMount(ref), + "k8s.io/api/core/v1.VolumeNodeAffinity": schema_k8sio_api_core_v1_VolumeNodeAffinity(ref), + "k8s.io/api/core/v1.VolumeProjection": schema_k8sio_api_core_v1_VolumeProjection(ref), + "k8s.io/api/core/v1.VolumeSource": schema_k8sio_api_core_v1_VolumeSource(ref), + "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource": schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref), + "k8s.io/api/core/v1.WeightedPodAffinityTerm": schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref), + "k8s.io/api/core/v1.WindowsSecurityContextOptions": schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig": schema_pkg_apis_devops_v1alpha1_AuthConfig(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.CGroupLimits": schema_pkg_apis_devops_v1alpha1_CGroupLimits(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ContainerConfig": schema_pkg_apis_devops_v1alpha1_ContainerConfig(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ContainerInfo": schema_pkg_apis_devops_v1alpha1_ContainerInfo(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfig": schema_pkg_apis_devops_v1alpha1_DockerConfig(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfigEntry": schema_pkg_apis_devops_v1alpha1_DockerConfigEntry(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfigJson": schema_pkg_apis_devops_v1alpha1_DockerConfigJson(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.EnvironmentSpec": schema_pkg_apis_devops_v1alpha1_EnvironmentSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.Parameter": schema_pkg_apis_devops_v1alpha1_Parameter(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ProxyConfig": schema_pkg_apis_devops_v1alpha1_ProxyConfig(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iAutoScale": schema_pkg_apis_devops_v1alpha1_S2iAutoScale(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinary": schema_pkg_apis_devops_v1alpha1_S2iBinary(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinaryList": schema_pkg_apis_devops_v1alpha1_S2iBinaryList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinarySpec": schema_pkg_apis_devops_v1alpha1_S2iBinarySpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBinaryStatus": schema_pkg_apis_devops_v1alpha1_S2iBinaryStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildResult": schema_pkg_apis_devops_v1alpha1_S2iBuildResult(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildSource": schema_pkg_apis_devops_v1alpha1_S2iBuildSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilder": schema_pkg_apis_devops_v1alpha1_S2iBuilder(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderList": schema_pkg_apis_devops_v1alpha1_S2iBuilderList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderSpec": schema_pkg_apis_devops_v1alpha1_S2iBuilderSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderStatus": schema_pkg_apis_devops_v1alpha1_S2iBuilderStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplate": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplate(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateList": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iConfig": schema_pkg_apis_devops_v1alpha1_S2iConfig(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRun": schema_pkg_apis_devops_v1alpha1_S2iRun(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunList": schema_pkg_apis_devops_v1alpha1_S2iRunList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunSpec": schema_pkg_apis_devops_v1alpha1_S2iRunSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunStatus": schema_pkg_apis_devops_v1alpha1_S2iRunStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.UserDefineTemplate": schema_pkg_apis_devops_v1alpha1_UserDefineTemplate(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec": schema_pkg_apis_devops_v1alpha1_VolumeSpec(ref), + } +} + +func schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"string"}, + Format: "", + }, + }, + "partition": { + SchemaProps: spec.SchemaProps{ + Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Affinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Affinity is a group of affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes node affinity scheduling rules for the pod.", + Ref: ref("k8s.io/api/core/v1.NodeAffinity"), + }, + }, + "podAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).", + Ref: ref("k8s.io/api/core/v1.PodAffinity"), + }, + }, + "podAntiAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).", + Ref: ref("k8s.io/api/core/v1.PodAntiAffinity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeAffinity", "k8s.io/api/core/v1.PodAffinity", "k8s.io/api/core/v1.PodAntiAffinity"}, + } +} + +func schema_k8sio_api_core_v1_AttachedVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AttachedVolume describes a volume attached to a node", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the attached volume", + Type: []string{"string"}, + Format: "", + }, + }, + "devicePath": { + SchemaProps: spec.SchemaProps{ + Description: "DevicePath represents the device path where the volume should be available", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "devicePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AvoidPods(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AvoidPods describes pods that should avoid this node. This is the value for a Node annotation with key scheduler.alpha.kubernetes.io/preferAvoidPods and will eventually become a field of NodeStatus.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "preferAvoidPods": { + SchemaProps: spec.SchemaProps{ + Description: "Bounded-sized list of signatures of pods that should avoid this node, sorted in timestamp order from oldest to newest. Size of the slice is unspecified.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PreferAvoidPodsEntry"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PreferAvoidPodsEntry"}, + } +} + +func schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "diskName": { + SchemaProps: spec.SchemaProps{ + Description: "The Name of the data disk in the blob storage", + Type: []string{"string"}, + Format: "", + }, + }, + "diskURI": { + SchemaProps: spec.SchemaProps{ + Description: "The URI the data disk in the blob storage", + Type: []string{"string"}, + Format: "", + }, + }, + "cachingMode": { + SchemaProps: spec.SchemaProps{ + Description: "Host Caching mode: None, Read Only, Read Write.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"diskName", "diskURI"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "the name of secret that contains Azure Storage Account Name and Key", + Type: []string{"string"}, + Format: "", + }, + }, + "shareName": { + SchemaProps: spec.SchemaProps{ + Description: "Share Name", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "the namespace of the secret that contains Azure Storage Account Name and Key default is the same as the Pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"secretName", "shareName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_AzureFileVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "the name of secret that contains Azure Storage Account Name and Key", + Type: []string{"string"}, + Format: "", + }, + }, + "shareName": { + SchemaProps: spec.SchemaProps{ + Description: "Share Name", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"secretName", "shareName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Binding(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Binding ties one object to another; for example, a pod is bound to a node by a scheduler. Deprecated in 1.7, please use the bindings subresource of pods instead.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "target": { + SchemaProps: spec.SchemaProps{ + Description: "The target object that you want to bind to the standard object.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + Required: []string{"target"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents storage that is managed by an external CSI volume driver (Beta feature)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume. Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeHandle": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeHandle is the unique volume name returned by the CSI volume plugin’s CreateVolume to refer to the volume on all subsequent calls. Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeAttributes": { + SchemaProps: spec.SchemaProps{ + Description: "Attributes of the volume to publish.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "controllerPublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "nodeStageSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "nodePublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "controllerExpandSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "ControllerExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerExpandVolume call. This is an alpha field and requires enabling ExpandCSIVolumes feature gate. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + }, + Required: []string{"driver", "volumeHandle"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a source location of a volume to mount, managed by an external CSI driver", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies a read-only configuration for the volume. Defaults to false (read/write).", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeAttributes": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "nodePublishSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Capabilities(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adds and removes POSIX capabilities from running containers.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "add": { + SchemaProps: spec.SchemaProps{ + Description: "Added capabilities", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "drop": { + SchemaProps: spec.SchemaProps{ + Description: "Removed capabilities", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretFile": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CephFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretFile": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_CinderVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_ClientIPConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ClientIPConfig represents the configurations of Client IP based session affinity.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\". Default value is 10800(for 3 hours).", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ComponentCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Information about the condition of a component.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of condition for a component. Valid value: \"Healthy\"", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message about the condition for a component. For example, information about a health check.", + Type: []string{"string"}, + Format: "", + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "Condition error code for a component. For example, a health check error code.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ComponentStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of component conditions observed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ComponentCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ComponentCondition", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ComponentStatusList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Status of all the conditions for the component as a list of ComponentStatus objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of ComponentStatus objects.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ComponentStatus"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ComponentStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMap(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap holds configuration data for pods to consume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "binaryData": { + SchemaProps: spec.SchemaProps{ + Description: "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Selects a key from a ConfigMap.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key to select.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"key"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapList is a resource containing a list of ConfigMap objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is the list of ConfigMaps.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ConfigMap"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMap", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeletConfigKey": { + SchemaProps: spec.SchemaProps{ + Description: "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"namespace", "name", "kubeletConfigKey"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ConfigMapProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the ConfigMap or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A single application container that you want to run within a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "containerPort", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "containerPort", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is an alpha feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_ContainerImage(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describe a container image", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "names": { + SchemaProps: spec.SchemaProps{ + Description: "Names by which this image is known. e.g. [\"k8s.gcr.io/hyperkube:v1.0.7\", \"dockerhub.io/google_containers/hyperkube:v1.0.7\"]", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "sizeBytes": { + SchemaProps: spec.SchemaProps{ + Description: "The size of the image in bytes.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + Required: []string{"names"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerPort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerPort represents a network port in a single container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "containerPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".", + Type: []string{"string"}, + Format: "", + }, + }, + "hostIP": { + SchemaProps: spec.SchemaProps{ + Description: "What host IP to bind the external port to.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"containerPort"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateRunning(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateRunning is a running state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "startedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which the container was last (re-)started", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateTerminated(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateTerminated is a terminated state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exitCode": { + SchemaProps: spec.SchemaProps{ + Description: "Exit status from the last termination of the container", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "signal": { + SchemaProps: spec.SchemaProps{ + Description: "Signal from the last termination of the container", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason from the last termination of the container", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message regarding the last termination of the container", + Type: []string{"string"}, + Format: "", + }, + }, + "startedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which previous execution of the container started", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "finishedAt": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which the container last terminated", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "containerID": { + SchemaProps: spec.SchemaProps{ + Description: "Container's ID in the format 'docker://'", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"exitCode"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ContainerStateWaiting(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStateWaiting is a waiting state of a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason the container is not yet running.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Message regarding why the container is not yet running.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ContainerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerStatus contains details for the current status of this container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "Details about the container's current condition.", + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + "lastState": { + SchemaProps: spec.SchemaProps{ + Description: "Details about the container's last termination condition.", + Ref: ref("k8s.io/api/core/v1.ContainerState"), + }, + }, + "ready": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies whether the container has passed its readiness probe.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "restartCount": { + SchemaProps: spec.SchemaProps{ + Description: "The number of times the container has been restarted, currently based on the number of dead containers that have not yet been removed. Note that this is calculated from dead containers. But those containers are subject to garbage collection. This value will get capped at 5 by GC.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Description: "ImageID of the container's image.", + Type: []string{"string"}, + Format: "", + }, + }, + "containerID": { + SchemaProps: spec.SchemaProps{ + Description: "Container's ID in the format 'docker://'.", + Type: []string{"string"}, + Format: "", + }, + }, + "started": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name", "ready", "restartCount", "image", "imageID"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerState"}, + } +} + +func schema_k8sio_api_core_v1_DaemonEndpoint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DaemonEndpoint contains information about a single Daemon endpoint.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Port": { + SchemaProps: spec.SchemaProps{ + Description: "Port number of the given endpoint.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"Port"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of DownwardAPIVolume file", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPIVolumeFile represents information to create the file containing the pod field", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.", + Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), + }, + }, + "resourceFieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.", + Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), + }, + }, + "mode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"path"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector"}, + } +} + +func schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of downward API volume file", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, + } +} + +func schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "medium": { + SchemaProps: spec.SchemaProps{ + Description: "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Type: []string{"string"}, + Format: "", + }, + }, + "sizeLimit": { + SchemaProps: spec.SchemaProps{ + Description: "Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_EndpointAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointAddress is a tuple that describes single IP address.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "The IP of this endpoint. May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24). IPv6 is also accepted but not fully supported on all platforms. Also, certain kubernetes components, like kube-proxy, are not IPv6 ready.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "The Hostname of this endpoint", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeName": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Node hosting this endpoint. This can be used to determine endpoints local to a node.", + Type: []string{"string"}, + Format: "", + }, + }, + "targetRef": { + SchemaProps: spec.SchemaProps{ + Description: "Reference to object providing the endpoint.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + Required: []string{"ip"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_EndpointPort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointPort is a tuple that describes a single port.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of this port. This must match the 'name' field in the corresponding ServicePort. Must be a DNS_LABEL. Optional only if one port is defined.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "The port number of the endpoint.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"port"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_EndpointSubset(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n }\nThe resulting set of endpoints can be viewed as:\n a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n b: [ 10.10.1.1:309, 10.10.2.2:309 ]", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "addresses": { + SchemaProps: spec.SchemaProps{ + Description: "IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointAddress"), + }, + }, + }, + }, + }, + "notReadyAddresses": { + SchemaProps: spec.SchemaProps{ + Description: "IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointAddress"), + }, + }, + }, + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Port numbers available on the related IP addresses.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointPort"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EndpointAddress", "k8s.io/api/core/v1.EndpointPort"}, + } +} + +func schema_k8sio_api_core_v1_Endpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "subsets": { + SchemaProps: spec.SchemaProps{ + Description: "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EndpointSubset"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EndpointSubset", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_EndpointsList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EndpointsList is a list of endpoints.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of endpoints.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Endpoints"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Endpoints", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_EnvFromSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvFromSource represents the source of a set of ConfigMaps", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "prefix": { + SchemaProps: spec.SchemaProps{ + Description: "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.", + Type: []string{"string"}, + Format: "", + }, + }, + "configMapRef": { + SchemaProps: spec.SchemaProps{ + Description: "The ConfigMap to select from", + Ref: ref("k8s.io/api/core/v1.ConfigMapEnvSource"), + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "The Secret to select from", + Ref: ref("k8s.io/api/core/v1.SecretEnvSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapEnvSource", "k8s.io/api/core/v1.SecretEnvSource"}, + } +} + +func schema_k8sio_api_core_v1_EnvVar(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvVar represents an environment variable present in a Container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the environment variable. Must be a C_IDENTIFIER.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", + Type: []string{"string"}, + Format: "", + }, + }, + "valueFrom": { + SchemaProps: spec.SchemaProps{ + Description: "Source for the environment variable's value. Cannot be used if value is not empty.", + Ref: ref("k8s.io/api/core/v1.EnvVarSource"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EnvVarSource"}, + } +} + +func schema_k8sio_api_core_v1_EnvVarSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvVarSource represents a source for the value of an EnvVar.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "fieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP.", + Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), + }, + }, + "resourceFieldRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.", + Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), + }, + }, + "configMapKeyRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a key of a ConfigMap.", + Ref: ref("k8s.io/api/core/v1.ConfigMapKeySelector"), + }, + }, + "secretKeyRef": { + SchemaProps: spec.SchemaProps{ + Description: "Selects a key of a secret in the pod's namespace", + Ref: ref("k8s.io/api/core/v1.SecretKeySelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapKeySelector", "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector", "k8s.io/api/core/v1.SecretKeySelector"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "An EphemeralContainer is a container that may be added temporarily to an existing pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a pod is removed or restarted. If an ephemeral container causes a pod to exceed its resource allocation, the pod may be evicted. Ephemeral containers may not be added by directly updating the pod spec. They must be added via the pod's ephemeralcontainers subresource, and they will appear in the pod spec once added. This is an alpha feature enabled by the EphemeralContainers feature flag.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Ports are not allowed for ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "targetContainerName": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container is run in whatever namespaces are shared for the pod. Note that the container runtime must support this feature.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EphemeralContainerCommon is a copy of all fields in Container to be inlined in EphemeralContainer. This separate type allows easy conversion from EphemeralContainer to Container and allows separate documentation for the fields of EphemeralContainer. When a new field is added to Container it must be added here as well.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", + Type: []string{"string"}, + Format: "", + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "args": { + SchemaProps: spec.SchemaProps{ + Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "Ports are not allowed for ephemeral containers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerPort"), + }, + }, + }, + }, + }, + "envFrom": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvFromSource"), + }, + }, + }, + }, + }, + "env": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeMounts": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "mountPath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeDevices": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "devicePath", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeDevice"), + }, + }, + }, + }, + }, + "livenessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "readinessProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "startupProbe": { + SchemaProps: spec.SchemaProps{ + Description: "Probes are not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Probe"), + }, + }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.Lifecycle"), + }, + }, + "terminationMessagePath": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationMessagePolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", + Type: []string{"string"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext is not allowed for ephemeral containers.", + Ref: ref("k8s.io/api/core/v1.SecurityContext"), + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdinOnce": { + SchemaProps: spec.SchemaProps{ + Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, + } +} + +func schema_k8sio_api_core_v1_EphemeralContainers(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "ephemeralContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "A list of ephemeral containers associated with this pod. New ephemeral containers may be appended to this list, but existing ephemeral containers may not be removed or modified.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), + }, + }, + }, + }, + }, + }, + Required: []string{"ephemeralContainers"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_Event(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Event is a report of an event somewhere in the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "involvedObject": { + SchemaProps: spec.SchemaProps{ + Description: "The object that this event is about.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the status of this operation.", + Type: []string{"string"}, + Format: "", + }, + }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The component reporting this event. Should be a short machine understandable string.", + Ref: ref("k8s.io/api/core/v1.EventSource"), + }, + }, + "firstTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "The time at which the event was first recorded. (Time of server receipt is in TypeMeta.)", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "The time at which the most recent occurrence of this event was recorded.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "count": { + SchemaProps: spec.SchemaProps{ + Description: "The number of times this event has occurred.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of this event (Normal, Warning), new types could be added in the future", + Type: []string{"string"}, + Format: "", + }, + }, + "eventTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time when this Event was first observed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), + }, + }, + "series": { + SchemaProps: spec.SchemaProps{ + Description: "Data about the Event series this event represents or nil if it's a singleton Event.", + Ref: ref("k8s.io/api/core/v1.EventSeries"), + }, + }, + "action": { + SchemaProps: spec.SchemaProps{ + Description: "What action was taken/failed regarding to the Regarding object.", + Type: []string{"string"}, + Format: "", + }, + }, + "related": { + SchemaProps: spec.SchemaProps{ + Description: "Optional secondary object for more complex actions.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "reportingComponent": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", + Type: []string{"string"}, + Format: "", + }, + }, + "reportingInstance": { + SchemaProps: spec.SchemaProps{ + Description: "ID of the controller instance, e.g. `kubelet-xyzf`.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"metadata", "involvedObject"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.EventSeries", "k8s.io/api/core/v1.EventSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_EventList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventList is a list of events.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of events", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Event"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Event", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_EventSeries(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "count": { + SchemaProps: spec.SchemaProps{ + Description: "Number of occurrences in this series up to the last heartbeat time", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "lastObservedTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time of the last occurrence observed", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), + }, + }, + "state": { + SchemaProps: spec.SchemaProps{ + Description: "State of this Series: Ongoing or Finished Deprecated. Planned removal for 1.18", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"}, + } +} + +func schema_k8sio_api_core_v1_EventSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EventSource contains information for an event.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "component": { + SchemaProps: spec.SchemaProps{ + Description: "Component from which the event is generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Node name on which the event is generated.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ExecAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ExecAction describes a \"run in container\" action.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_FCVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetWWNs": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC target worldwide names (WWNs)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC target lun number", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "wwids": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FlexPersistentVolumeSource represents a generic persistent volume resource that is provisioned/attached using an exec based plugin.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Extra command options if any.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_FlexVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "driver": { + SchemaProps: spec.SchemaProps{ + Description: "Driver is the name of the driver to use for this volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Extra command options if any.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"driver"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_FlockerVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "datasetName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated", + Type: []string{"string"}, + Format: "", + }, + }, + "datasetUUID": { + SchemaProps: spec.SchemaProps{ + Description: "UUID of the dataset. This is unique identifier of a Flocker dataset", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pdName": { + SchemaProps: spec.SchemaProps{ + Description: "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"string"}, + Format: "", + }, + }, + "partition": { + SchemaProps: spec.SchemaProps{ + Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"pdName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GitRepoVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "repository": { + SchemaProps: spec.SchemaProps{ + Description: "Repository URL", + Type: []string{"string"}, + Format: "", + }, + }, + "revision": { + SchemaProps: spec.SchemaProps{ + Description: "Commit hash for the specified revision.", + Type: []string{"string"}, + Format: "", + }, + }, + "directory": { + SchemaProps: spec.SchemaProps{ + Description: "Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"repository"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "endpoints": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"boolean"}, + Format: "", + }, + }, + "endpointsNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsNamespace is the namespace that contains Glusterfs endpoint. If this field is empty, the EndpointNamespace defaults to the same namespace as the bound PVC. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"endpoints", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "endpoints": { + SchemaProps: spec.SchemaProps{ + Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"endpoints", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_HTTPGetAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HTTPGetAction describes an action based on HTTP Get requests.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path to access on the HTTP server.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.", + Type: []string{"string"}, + Format: "", + }, + }, + "scheme": { + SchemaProps: spec.SchemaProps{ + Description: "Scheme to use for connecting to the host. Defaults to HTTP.", + Type: []string{"string"}, + Format: "", + }, + }, + "httpHeaders": { + SchemaProps: spec.SchemaProps{ + Description: "Custom headers to set in the request. HTTP allows repeated headers.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.HTTPHeader"), + }, + }, + }, + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.HTTPHeader", "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_HTTPHeader(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HTTPHeader describes a custom header to be used in HTTP probes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The header field name", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "The header field value", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Handler(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Handler defines a specific action that should be taken", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exec": { + SchemaProps: spec.SchemaProps{ + Description: "One and only one of the following should be specified. Exec specifies the action to take.", + Ref: ref("k8s.io/api/core/v1.ExecAction"), + }, + }, + "httpGet": { + SchemaProps: spec.SchemaProps{ + Description: "HTTPGet specifies the http request to perform.", + Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), + }, + }, + "tcpSocket": { + SchemaProps: spec.SchemaProps{ + Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", + Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, + } +} + +func schema_k8sio_api_core_v1_HostAlias(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "IP address of the host file entry.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostnames": { + SchemaProps: spec.SchemaProps{ + Description: "Hostnames for the above IP address.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_HostPathVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ISCSIPersistentVolumeSource represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetPortal": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"string"}, + Format: "", + }, + }, + "iqn": { + SchemaProps: spec.SchemaProps{ + Description: "Target iSCSI Qualified Name.", + Type: []string{"string"}, + Format: "", + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Lun number.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "iscsiInterface": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "portals": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "chapAuthDiscovery": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Discovery CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "chapAuthSession": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Session CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "CHAP Secret for iSCSI target and initiator authentication", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "initiatorName": { + SchemaProps: spec.SchemaProps{ + Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"targetPortal", "iqn", "lun"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_ISCSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "targetPortal": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"string"}, + Format: "", + }, + }, + "iqn": { + SchemaProps: spec.SchemaProps{ + Description: "Target iSCSI Qualified Name.", + Type: []string{"string"}, + Format: "", + }, + }, + "lun": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Lun number.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "iscsiInterface": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "portals": { + SchemaProps: spec.SchemaProps{ + Description: "iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "chapAuthDiscovery": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Discovery CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "chapAuthSession": { + SchemaProps: spec.SchemaProps{ + Description: "whether support iSCSI Session CHAP authentication", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "CHAP Secret for iSCSI target and initiator authentication", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "initiatorName": { + SchemaProps: spec.SchemaProps{ + Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"targetPortal", "iqn", "lun"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_KeyToPath(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Maps a string key to a path within a volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key to project.", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.", + Type: []string{"string"}, + Format: "", + }, + }, + "mode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"key", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Lifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "postStart": { + SchemaProps: spec.SchemaProps{ + Description: "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", + Ref: ref("k8s.io/api/core/v1.Handler"), + }, + }, + "preStop": { + SchemaProps: spec.SchemaProps{ + Description: "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", + Ref: ref("k8s.io/api/core/v1.Handler"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Handler"}, + } +} + +func schema_k8sio_api_core_v1_LimitRange(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRange sets resource usage limits for each kind of resource in a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the limits enforced. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.LimitRangeSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRangeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeItem(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of resource that this limit applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "max": { + SchemaProps: spec.SchemaProps{ + Description: "Max usage constraints on this kind by resource name.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "min": { + SchemaProps: spec.SchemaProps{ + Description: "Min usage constraints on this kind by resource name.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "default": { + SchemaProps: spec.SchemaProps{ + Description: "Default resource requirement limit value by resource name if resource limit is omitted.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "defaultRequest": { + SchemaProps: spec.SchemaProps{ + Description: "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "maxLimitRequestRatio": { + SchemaProps: spec.SchemaProps{ + Description: "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeList is a list of LimitRange items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of LimitRange objects. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LimitRange"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRange", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_LimitRangeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LimitRangeSpec defines a min/max usage limit for resources that match on kind.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "limits": { + SchemaProps: spec.SchemaProps{ + Description: "Limits is the list of LimitRangeItem objects that are enforced.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LimitRangeItem"), + }, + }, + }, + }, + }, + }, + Required: []string{"limits"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LimitRangeItem"}, + } +} + +func schema_k8sio_api_core_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "List holds a list of objects, which may not be known by the server.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of objects", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_k8sio_api_core_v1_LoadBalancerIngress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancerIngress represents the status of a load-balancer ingress point: traffic intended for the service should be sent to an ingress point.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)", + Type: []string{"string"}, + Format: "", + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "Hostname is set for load-balancer ingress points that are DNS based (typically AWS load-balancers)", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_LoadBalancerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancerStatus represents the status of a load-balancer.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ingress": { + SchemaProps: spec.SchemaProps{ + Description: "Ingress is a list containing ingress points for the load-balancer. Traffic intended for the service should be sent to these ingress points.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LoadBalancerIngress"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LoadBalancerIngress"}, + } +} + +func schema_k8sio_api_core_v1_LocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_LocalVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity (Beta feature)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "path": { + SchemaProps: spec.SchemaProps{ + Description: "The full path to the volume on the node. It can be either a directory or block device (disk, partition, ...).", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. It applies only when the Path is a block device. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default value is to auto-select a fileystem if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "server": { + SchemaProps: spec.SchemaProps{ + Description: "Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"server", "path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Namespace(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Namespace provides a scope for Names. Use of multiple namespaces is optional.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NamespaceSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NamespaceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NamespaceSpec", "k8s.io/api/core/v1.NamespaceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceCondition contains details about state of namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of namespace controller condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceList is a list of Namespaces.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is the list of Namespace objects in the list. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Namespace"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Namespace", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_NamespaceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceSpec describes the attributes on a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "finalizers": { + SchemaProps: spec.SchemaProps{ + Description: "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NamespaceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NamespaceStatus is information about the current status of a Namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents the latest available observations of a namespace's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NamespaceCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NamespaceCondition"}, + } +} + +func schema_k8sio_api_core_v1_Node(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of a node. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NodeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the node. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.NodeStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSpec", "k8s.io/api/core/v1.NodeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_NodeAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeAddress contains information for the node's address.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Node address type, one of Hostname, ExternalIP or InternalIP.", + Type: []string{"string"}, + Format: "", + }, + }, + "address": { + SchemaProps: spec.SchemaProps{ + Description: "The node address.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "address"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Node affinity is a group of node affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node.", + Ref: ref("k8s.io/api/core/v1.NodeSelector"), + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PreferredSchedulingTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelector", "k8s.io/api/core/v1.PreferredSchedulingTerm"}, + } +} + +func schema_k8sio_api_core_v1_NodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeCondition contains condition information for a node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of node condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastHeartbeatTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we got an update on a given condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transit from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_NodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap is a reference to a Node's ConfigMap", + Ref: ref("k8s.io/api/core/v1.ConfigMapNodeConfigSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapNodeConfigSource"}, + } +} + +func schema_k8sio_api_core_v1_NodeConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeConfigStatus describes the status of the config assigned by Node.Spec.ConfigSource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "assigned": { + SchemaProps: spec.SchemaProps{ + Description: "Assigned reports the checkpointed config the node will try to use. When Node.Spec.ConfigSource is updated, the node checkpoints the associated config payload to local disk, along with a record indicating intended config. The node refers to this record to choose its config checkpoint, and reports this record in Assigned. Assigned only updates in the status after the record has been checkpointed to disk. When the Kubelet is restarted, it tries to make the Assigned config the Active config by loading and validating the checkpointed payload identified by Assigned.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "active": { + SchemaProps: spec.SchemaProps{ + Description: "Active reports the checkpointed config the node is actively using. Active will represent either the current version of the Assigned config, or the current LastKnownGood config, depending on whether attempting to use the Assigned config results in an error.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "lastKnownGood": { + SchemaProps: spec.SchemaProps{ + Description: "LastKnownGood reports the checkpointed config the node will fall back to when it encounters an error attempting to use the Assigned config. The Assigned config becomes the LastKnownGood config when the node determines that the Assigned config is stable and correct. This is currently implemented as a 10-minute soak period starting when the local record of Assigned config is updated. If the Assigned config is Active at the end of this period, it becomes the LastKnownGood. Note that if Spec.ConfigSource is reset to nil (use local defaults), the LastKnownGood is also immediately reset to nil, because the local default config is always assumed good. You should not make assumptions about the node's method of determining config stability and correctness, as this may change or become configurable in the future.", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "error": { + SchemaProps: spec.SchemaProps{ + Description: "Error describes any problems reconciling the Spec.ConfigSource to the Active config. Errors may occur, for example, attempting to checkpoint Spec.ConfigSource to the local Assigned record, attempting to checkpoint the payload associated with Spec.ConfigSource, attempting to load or validate the Assigned config, etc. Errors may occur at different points while syncing config. Earlier errors (e.g. download or checkpointing errors) will not result in a rollback to LastKnownGood, and may resolve across Kubelet retries. Later errors (e.g. loading or validating a checkpointed config) will result in a rollback to LastKnownGood. In the latter case, it is usually possible to resolve the error by fixing the config assigned in Spec.ConfigSource. You can find additional information for debugging by searching the error message in the Kubelet log. Error is a human-readable description of the error state; machines can check whether or not Error is empty, but should not rely on the stability of the Error text across Kubelet versions.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeConfigSource"}, + } +} + +func schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeDaemonEndpoints lists ports opened by daemons running on the Node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kubeletEndpoint": { + SchemaProps: spec.SchemaProps{ + Description: "Endpoint on which Kubelet is listening.", + Ref: ref("k8s.io/api/core/v1.DaemonEndpoint"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.DaemonEndpoint"}, + } +} + +func schema_k8sio_api_core_v1_NodeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeList is the whole list of all Nodes which have been registered with master.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of nodes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Node"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Node", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_NodeProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeProxyOptions is the query options to a Node's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the URL path to use for the current proxy request to node.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeResources(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeResources is an object for conveying resource information about a node. see http://releases.k8s.io/HEAD/docs/design/resources.md for more details.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Capacity represents the available resources of a node", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + Required: []string{"Capacity"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_NodeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nodeSelectorTerms": { + SchemaProps: spec.SchemaProps{ + Description: "Required. A list of node selector terms. The terms are ORed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), + }, + }, + }, + }, + }, + }, + Required: []string{"nodeSelectorTerms"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorTerm"}, + } +} + +func schema_k8sio_api_core_v1_NodeSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "operator"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_NodeSelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of node selector requirements by node's labels.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), + }, + }, + }, + }, + }, + "matchFields": { + SchemaProps: spec.SchemaProps{ + Description: "A list of node selector requirements by node's fields.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorRequirement"}, + } +} + +func schema_k8sio_api_core_v1_NodeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSpec describes the attributes that a node is created with.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podCIDR": { + SchemaProps: spec.SchemaProps{ + Description: "PodCIDR represents the pod IP range assigned to the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "podCIDRs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "podCIDRs represents the IP ranges assigned to the node for usage by Pods on that node. If this field is specified, the 0th entry must match the podCIDR field. It may contain at most 1 value for each of IPv4 and IPv6.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "providerID": { + SchemaProps: spec.SchemaProps{ + Description: "ID of the node assigned by the cloud provider in the format: ://", + Type: []string{"string"}, + Format: "", + }, + }, + "unschedulable": { + SchemaProps: spec.SchemaProps{ + Description: "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", + Type: []string{"boolean"}, + Format: "", + }, + }, + "taints": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the node's taints.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Taint"), + }, + }, + }, + }, + }, + "configSource": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field", + Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), + }, + }, + "externalID": { + SchemaProps: spec.SchemaProps{ + Description: "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeConfigSource", "k8s.io/api/core/v1.Taint"}, + } +} + +func schema_k8sio_api_core_v1_NodeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeStatus is information about the current status of a node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Capacity represents the total resources of a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "allocatable": { + SchemaProps: spec.SchemaProps{ + Description: "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "NodePhase is the recently observed lifecycle phase of the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#phase The field is never populated, and now is deprecated.", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions is an array of current observed node conditions. More info: https://kubernetes.io/docs/concepts/nodes/node/#condition", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeCondition"), + }, + }, + }, + }, + }, + "addresses": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of addresses reachable to the node. Queried from cloud provider, if available. More info: https://kubernetes.io/docs/concepts/nodes/node/#addresses Note: This field is declared as mergeable, but the merge key is not sufficiently unique, which can cause data corruption when it is merged. Callers should instead use a full-replacement patch. See http://pr.k8s.io/79391 for an example.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.NodeAddress"), + }, + }, + }, + }, + }, + "daemonEndpoints": { + SchemaProps: spec.SchemaProps{ + Description: "Endpoints of daemons running on the Node.", + Ref: ref("k8s.io/api/core/v1.NodeDaemonEndpoints"), + }, + }, + "nodeInfo": { + SchemaProps: spec.SchemaProps{ + Description: "Set of ids/uuids to uniquely identify the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#info", + Ref: ref("k8s.io/api/core/v1.NodeSystemInfo"), + }, + }, + "images": { + SchemaProps: spec.SchemaProps{ + Description: "List of container images on this node", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerImage"), + }, + }, + }, + }, + }, + "volumesInUse": { + SchemaProps: spec.SchemaProps{ + Description: "List of attachable volumes in use (mounted) by the node.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "volumesAttached": { + SchemaProps: spec.SchemaProps{ + Description: "List of volumes that are attached to the node.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.AttachedVolume"), + }, + }, + }, + }, + }, + "config": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the config assigned to the node via the dynamic Kubelet config feature.", + Ref: ref("k8s.io/api/core/v1.NodeConfigStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AttachedVolume", "k8s.io/api/core/v1.ContainerImage", "k8s.io/api/core/v1.NodeAddress", "k8s.io/api/core/v1.NodeCondition", "k8s.io/api/core/v1.NodeConfigStatus", "k8s.io/api/core/v1.NodeDaemonEndpoints", "k8s.io/api/core/v1.NodeSystemInfo", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_NodeSystemInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NodeSystemInfo is a set of ids/uuids to uniquely identify the node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "machineID": { + SchemaProps: spec.SchemaProps{ + Description: "MachineID reported by the node. For unique machine identification in the cluster this field is preferred. Learn more from man(5) machine-id: http://man7.org/linux/man-pages/man5/machine-id.5.html", + Type: []string{"string"}, + Format: "", + }, + }, + "systemUUID": { + SchemaProps: spec.SchemaProps{ + Description: "SystemUUID reported by the node. For unique machine identification MachineID is preferred. This field is specific to Red Hat hosts https://access.redhat.com/documentation/en-US/Red_Hat_Subscription_Management/1/html/RHSM/getting-system-uuid.html", + Type: []string{"string"}, + Format: "", + }, + }, + "bootID": { + SchemaProps: spec.SchemaProps{ + Description: "Boot ID reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "kernelVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Kernel Version reported by the node from 'uname -r' (e.g. 3.16.0-0.bpo.4-amd64).", + Type: []string{"string"}, + Format: "", + }, + }, + "osImage": { + SchemaProps: spec.SchemaProps{ + Description: "OS Image reported by the node from /etc/os-release (e.g. Debian GNU/Linux 7 (wheezy)).", + Type: []string{"string"}, + Format: "", + }, + }, + "containerRuntimeVersion": { + SchemaProps: spec.SchemaProps{ + Description: "ContainerRuntime Version reported by the node through runtime remote API (e.g. docker://1.5.0).", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeletVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Kubelet Version reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "kubeProxyVersion": { + SchemaProps: spec.SchemaProps{ + Description: "KubeProxy Version reported by the node.", + Type: []string{"string"}, + Format: "", + }, + }, + "operatingSystem": { + SchemaProps: spec.SchemaProps{ + Description: "The Operating System reported by the node", + Type: []string{"string"}, + Format: "", + }, + }, + "architecture": { + SchemaProps: spec.SchemaProps{ + Description: "The Architecture reported by the node", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"machineID", "systemUUID", "bootID", "kernelVersion", "osImage", "containerRuntimeVersion", "kubeletVersion", "kubeProxyVersion", "operatingSystem", "architecture"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ObjectFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectFieldSelector selects an APIVersioned field of an object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path of the field to select in the specified API version.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"fieldPath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectReference contains enough information to let you inspect or modify the referred object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldPath": { + SchemaProps: spec.SchemaProps{ + Description: "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PersistentVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeSpec", "k8s.io/api/core/v1.PersistentVolumeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaim(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaim is a user's request for and claim to a persistent volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaimSpec", "k8s.io/api/core/v1.PersistentVolumeClaimStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimCondition contails details about state of pvc", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "lastProbeTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we probed the condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human-readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "A list of persistent volume claims. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaim"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "A label query over volumes to consider for binding.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the binding reference to the PersistentVolume backing this claim.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageClassName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeMode": { + SchemaProps: spec.SchemaProps{ + Description: "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "dataSource": { + SchemaProps: spec.SchemaProps{ + Description: "This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.", + Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.TypedLocalObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase represents the current phase of PersistentVolumeClaim.", + Type: []string{"string"}, + Format: "", + }, + }, + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "Represents the actual resources of the underlying volume.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimCondition"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolumeClaimCondition", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "claimName": { + SchemaProps: spec.SchemaProps{ + Description: "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Will force the ReadOnly setting in VolumeMounts. Default false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"claimName"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeList is a list of PersistentVolume items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of persistent volumes. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PersistentVolume"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PersistentVolume", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeSource is similar to VolumeSource but meant for the administrator who creates PVs. Exactly one of its members must be set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", + Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity", + Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", + Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", + Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeSpec is the specification of a persistent volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capacity": { + SchemaProps: spec.SchemaProps{ + Description: "A description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", + Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Description: "Local represents directly-attached storage with node affinity", + Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", + Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", + Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), + }, + }, + "accessModes": { + SchemaProps: spec.SchemaProps{ + Description: "AccessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "claimRef": { + SchemaProps: spec.SchemaProps{ + Description: "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + "persistentVolumeReclaimPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "What happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", + Type: []string{"string"}, + Format: "", + }, + }, + "storageClassName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.", + Type: []string{"string"}, + Format: "", + }, + }, + "mountOptions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "volumeMode": { + SchemaProps: spec.SchemaProps{ + Description: "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "NodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume.", + Ref: ref("k8s.io/api/core/v1.VolumeNodeAffinity"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VolumeNodeAffinity", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PersistentVolumeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeStatus is the current status of a persistent volume.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable message indicating details about why the volume is in this state.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Photon Controller persistent disk resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "pdID": { + SchemaProps: spec.SchemaProps{ + Description: "ID that identifies Photon Controller persistent disk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"pdID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Pod(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSpec", "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod affinity is a group of inter pod affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_PodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A label query over a set of resources, in this case pods.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + "namespaces": { + SchemaProps: spec.SchemaProps{ + Description: "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\"", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "topologyKey": { + SchemaProps: spec.SchemaProps{ + Description: "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"topologyKey"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_PodAntiAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "requiredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + }, + }, + "preferredDuringSchedulingIgnoredDuringExecution": { + SchemaProps: spec.SchemaProps{ + Description: "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_PodAttachOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodAttachOptions is the query options to a Pod's remote attach call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Stdin if true, redirects the standard input stream of the pod for this call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdout": { + SchemaProps: spec.SchemaProps{ + Description: "Stdout if true indicates that stdout is to be redirected for the attach call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stderr": { + SchemaProps: spec.SchemaProps{ + Description: "Stderr if true indicates that stderr is to be redirected for the attach call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "TTY if true indicates that a tty will be allocated for the attach call. This is passed through the container runtime so the tty is allocated on the worker node by the container runtime. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "The container in which to execute the command. Defaults to only container if there is only one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodCondition contains details for the current condition of this pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"string"}, + Format: "", + }, + }, + "lastProbeTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time we probed the condition.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "Unique, one-word, CamelCase reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human-readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodDNSConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "nameservers": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "searches": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "options": { + SchemaProps: spec.SchemaProps{ + Description: "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodDNSConfigOption"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodDNSConfigOption"}, + } +} + +func schema_k8sio_api_core_v1_PodDNSConfigOption(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodDNSConfigOption defines DNS resolver options of a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Required.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodExecOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodExecOptions is the query options to a Pod's remote exec call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "stdin": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard input stream of the pod for this call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stdout": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard output stream of the pod for this call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "stderr": { + SchemaProps: spec.SchemaProps{ + Description: "Redirect the standard error stream of the pod for this call. Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tty": { + SchemaProps: spec.SchemaProps{ + Description: "TTY if true indicates that a tty will be allocated for the exec call. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "Container in which to execute the command. Defaults to only container if there is only one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + "command": { + SchemaProps: spec.SchemaProps{ + Description: "Command is the remote command to execute. argv array. Not executed within a shell.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"command"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodIP(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "IP address information for entries in the (plural) PodIPs field. Each entry includes:\n IP: An IP address allocated to the pod. Routable at least within the cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ip": { + SchemaProps: spec.SchemaProps{ + Description: "ip is an IP address (IPv4 or IPv6) assigned to the pod", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodList is a list of Pods.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of pods. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Pod"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Pod", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodLogOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodLogOptions is the query options for a Pod's logs REST call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Description: "The container for which to stream logs. Defaults to only container if there is one container in the pod.", + Type: []string{"string"}, + Format: "", + }, + }, + "follow": { + SchemaProps: spec.SchemaProps{ + Description: "Follow the log stream of the pod. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "previous": { + SchemaProps: spec.SchemaProps{ + Description: "Return previous terminated container logs. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "sinceSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "A relative time in seconds before the current time from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "sinceTime": { + SchemaProps: spec.SchemaProps{ + Description: "An RFC3339 timestamp from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "timestamps": { + SchemaProps: spec.SchemaProps{ + Description: "If true, add an RFC3339 or RFC3339Nano timestamp at the beginning of every line of log output. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tailLines": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the number of lines from the end of the logs to show. If not specified, logs are shown from the creation of the container or sinceSeconds or sinceTime", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "limitBytes": { + SchemaProps: spec.SchemaProps{ + Description: "If set, the number of bytes to read from the server before terminating the log output. This may not display a complete final line of logging, and may return slightly more or slightly less than the specified limit.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodPortForwardOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodPortForwardOptions is the query options to a Pod's port forward call when using WebSockets. The `port` query parameter must specify the port or ports (comma separated) to forward over. Port forwarding over SPDY does not use these options. It requires the port to be passed in the `port` header as part of request.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "List of ports to forward Required when using WebSockets", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodProxyOptions is the query options to a Pod's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the URL path to use for the current proxy request to pod.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodReadinessGate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodReadinessGate contains the reference to a pod condition", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "conditionType": { + SchemaProps: spec.SchemaProps{ + Description: "ConditionType refers to a condition in the pod's condition list with matching type.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"conditionType"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PodSecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "seLinuxOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), + }, + }, + "windowsOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), + }, + }, + "runAsUser": { + SchemaProps: spec.SchemaProps{ + Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsNonRoot": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "supplementalGroups": { + SchemaProps: spec.SchemaProps{ + Description: "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + "fsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "sysctls": { + SchemaProps: spec.SchemaProps{ + Description: "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Sysctl"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.Sysctl", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, + } +} + +func schema_k8sio_api_core_v1_PodSignature(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describes the class of pods that should avoid this node. Exactly one field should be set.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podController": { + SchemaProps: spec.SchemaProps{ + Description: "Reference to controller whose pods should avoid this node.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"}, + } +} + +func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodSpec is a description of a pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumes": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge,retainKeys", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "initContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + }, + }, + }, + "containers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + }, + }, + }, + "ephemeralContainers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is alpha-level and is only honored by servers that enable the EphemeralContainers feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), + }, + }, + }, + }, + }, + "restartPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", + Type: []string{"string"}, + Format: "", + }, + }, + "terminationGracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "activeDeadlineSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "dnsPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + 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: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + Type: []string{"string"}, + Format: "", + }, + }, + "serviceAccount": { + SchemaProps: spec.SchemaProps{ + Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", + Type: []string{"string"}, + Format: "", + }, + }, + "automountServiceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "nodeName": { + SchemaProps: spec.SchemaProps{ + Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostNetwork": { + SchemaProps: spec.SchemaProps{ + Description: "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "hostPID": { + SchemaProps: spec.SchemaProps{ + Description: "Use the host's pid namespace. Optional: Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "hostIPC": { + SchemaProps: spec.SchemaProps{ + Description: "Use the host's ipc namespace. Optional: Default to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "shareProcessNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. This field is beta-level and may be disabled with the PodShareProcessNamespace feature.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "securityContext": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", + Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), + }, + }, + "imagePullSecrets": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + 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", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "hostname": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", + Type: []string{"string"}, + Format: "", + }, + }, + "subdomain": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.", + Type: []string{"string"}, + Format: "", + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's scheduling constraints", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "schedulerName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.", + Type: []string{"string"}, + Format: "", + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the pod's tolerations.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "hostAliases": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.HostAlias"), + }, + }, + }, + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", + Type: []string{"string"}, + Format: "", + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "dnsConfig": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", + Ref: ref("k8s.io/api/core/v1.PodDNSConfig"), + }, + }, + "readinessGates": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodReadinessGate"), + }, + }, + }, + }, + }, + "runtimeClassName": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + Type: []string{"string"}, + Format: "", + }, + }, + "enableServiceLinks": { + SchemaProps: spec.SchemaProps{ + Description: "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "preemptionPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is alpha-level and is only honored by servers that enable the NonPreemptingPriority feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "overhead": { + SchemaProps: spec.SchemaProps{ + Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "topologySpreadConstraints": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "topologyKey", + "whenUnsatisfiable", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "topologyKey", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. This field is alpha-level and is only honored by clusters that enables the EvenPodsSpread feature. All topologySpreadConstraints are ANDed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.TopologySpreadConstraint"), + }, + }, + }, + }, + }, + }, + Required: []string{"containers"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "phase": { + SchemaProps: spec.SchemaProps{ + Description: "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values:\n\nPending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase", + Type: []string{"string"}, + Format: "", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodCondition"), + }, + }, + }, + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about why the pod is in this condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'", + Type: []string{"string"}, + Format: "", + }, + }, + "nominatedNodeName": { + SchemaProps: spec.SchemaProps{ + Description: "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", + Type: []string{"string"}, + Format: "", + }, + }, + "hostIP": { + SchemaProps: spec.SchemaProps{ + Description: "IP address of the host to which the pod is assigned. Empty if not yet scheduled.", + Type: []string{"string"}, + Format: "", + }, + }, + "podIP": { + SchemaProps: spec.SchemaProps{ + Description: "IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", + Type: []string{"string"}, + Format: "", + }, + }, + "podIPs": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "ip", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list is empty if no IPs have been allocated yet.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodIP"), + }, + }, + }, + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "initContainerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + "containerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + "qosClass": { + SchemaProps: spec.SchemaProps{ + Description: "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md", + Type: []string{"string"}, + Format: "", + }, + }, + "ephemeralContainerStatuses": { + SchemaProps: spec.SchemaProps{ + Description: "Status for any ephemeral containers that have run in this pod. This field is alpha-level and is only populated by servers that enable the EphemeralContainers feature.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ContainerStatus"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStatus", "k8s.io/api/core/v1.PodCondition", "k8s.io/api/core/v1.PodIP", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PodStatusResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate describes a template for creating copies of a predefined pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Description: "Template defines the pods that will be created from this pod template. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplateSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplateList is a list of PodTemplates.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of pod templates", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.PodTemplate"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_PodTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PodTemplateSpec describes the data a pod should have when created from a template", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.PodSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_PortworxVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolumeSource represents a Portworx volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeID": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeID uniquely identifies a Portworx volume", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"volumeID"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Describes a class of pods that should avoid this node.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podSignature": { + SchemaProps: spec.SchemaProps{ + Description: "The class of pods.", + Ref: ref("k8s.io/api/core/v1.PodSignature"), + }, + }, + "evictionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Time at which this entry was added to the list.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) reason why this entry was added to the list.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating why this entry was added to the list.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"podSignature"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodSignature", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "weight": { + SchemaProps: spec.SchemaProps{ + Description: "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "preference": { + SchemaProps: spec.SchemaProps{ + Description: "A node selector term, associated with the corresponding weight.", + Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), + }, + }, + }, + Required: []string{"weight", "preference"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelectorTerm"}, + } +} + +func schema_k8sio_api_core_v1_Probe(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "exec": { + SchemaProps: spec.SchemaProps{ + Description: "One and only one of the following should be specified. Exec specifies the action to take.", + Ref: ref("k8s.io/api/core/v1.ExecAction"), + }, + }, + "httpGet": { + SchemaProps: spec.SchemaProps{ + Description: "HTTPGet specifies the http request to perform.", + Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), + }, + }, + "tcpSocket": { + SchemaProps: spec.SchemaProps{ + Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", + Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), + }, + }, + "initialDelaySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "periodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "successThreshold": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "failureThreshold": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, + } +} + +func schema_k8sio_api_core_v1_ProjectedVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a projected volume source", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "sources": { + SchemaProps: spec.SchemaProps{ + Description: "list of volume projections", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.VolumeProjection"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"sources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.VolumeProjection"}, + } +} + +func schema_k8sio_api_core_v1_QuobyteVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "registry": { + SchemaProps: spec.SchemaProps{ + Description: "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", + Type: []string{"string"}, + Format: "", + }, + }, + "volume": { + SchemaProps: spec.SchemaProps{ + Description: "Volume is a string that references an already created Quobyte volume by name.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "User to map volume access to Defaults to serivceaccount user", + Type: []string{"string"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "Group to map volume access to Default is no group", + Type: []string{"string"}, + Format: "", + }, + }, + "tenant": { + SchemaProps: spec.SchemaProps{ + Description: "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"registry", "volume"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + Type: []string{"string"}, + Format: "", + }, + }, + "pool": { + SchemaProps: spec.SchemaProps{ + Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "keyring": { + SchemaProps: spec.SchemaProps{ + Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors", "image"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_RBDVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "monitors": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "image": { + SchemaProps: spec.SchemaProps{ + Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", + Type: []string{"string"}, + Format: "", + }, + }, + "pool": { + SchemaProps: spec.SchemaProps{ + Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "user": { + SchemaProps: spec.SchemaProps{ + Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "keyring": { + SchemaProps: spec.SchemaProps{ + Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"monitors", "image"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_RangeAllocation(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RangeAllocation is not a public type.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "range": { + SchemaProps: spec.SchemaProps{ + Description: "Range is string that identifies the range represented by 'data'.", + Type: []string{"string"}, + Format: "", + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data is a bit array containing all allocated addresses in the previous segment.", + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + Required: []string{"range", "data"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationController(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationController represents the configuration of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ReplicationControllerSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ReplicationControllerStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationControllerSpec", "k8s.io/api/core/v1.ReplicationControllerStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerCondition describes the state of a replication controller at a certain point.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of replication controller condition.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "lastTransitionTime": { + SchemaProps: spec.SchemaProps{ + Description: "The last time the condition transitioned from one status to another.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "The reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human readable message indicating details about the transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerList is a collection of replication controllers.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of replication controllers. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ReplicationController"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationController", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerSpec is the specification of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "minReadySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "template": { + SchemaProps: spec.SchemaProps{ + Description: "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", + Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodTemplateSpec"}, + } +} + +func schema_k8sio_api_core_v1_ReplicationControllerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ReplicationControllerStatus represents the current status of a replication controller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "fullyLabeledReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of pods that have labels matching the labels of the pod template of the replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "readyReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of ready replicas for this replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "availableReplicas": { + SchemaProps: spec.SchemaProps{ + Description: "The number of available replicas (ready for at least minReadySeconds) for this replication controller.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration reflects the generation of the most recently observed replication controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Represents the latest available observations of a replication controller's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ReplicationControllerCondition"), + }, + }, + }, + }, + }, + }, + Required: []string{"replicas"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ReplicationControllerCondition"}, + } +} + +func schema_k8sio_api_core_v1_ResourceFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceFieldSelector represents container resources (cpu, memory) and their output format", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "containerName": { + SchemaProps: spec.SchemaProps{ + Description: "Container name: required for volumes, optional for env vars", + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Description: "Required: resource to select", + Type: []string{"string"}, + Format: "", + }, + }, + "divisor": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the output format of the exposed resources, defaults to \"1\"", + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + Required: []string{"resource"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuota(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuota sets aggregate quota restrictions enforced per namespace", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the desired quota. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ResourceQuotaSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status defines the actual enforced quota and its current usage. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ResourceQuotaStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceQuotaSpec", "k8s.io/api/core/v1.ResourceQuotaStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaList is a list of ResourceQuota items.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of ResourceQuota objects. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ResourceQuota"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceQuota", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hard": { + SchemaProps: spec.SchemaProps{ + Description: "hard is the set of desired hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "scopes": { + SchemaProps: spec.SchemaProps{ + Description: "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "scopeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota but expressed using ScopeSelectorOperator in combination with possible values. For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched.", + Ref: ref("k8s.io/api/core/v1.ScopeSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ScopeSelector", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceQuotaStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceQuotaStatus defines the enforced hard limits and observed use.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hard": { + SchemaProps: spec.SchemaProps{ + Description: "Hard is the set of enforced hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "used": { + SchemaProps: spec.SchemaProps{ + Description: "Used is the current observed total usage of the resource in the namespace.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_ResourceRequirements(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ResourceRequirements describes the compute resource requirements.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "limits": { + SchemaProps: spec.SchemaProps{ + Description: "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "requests": { + SchemaProps: spec.SchemaProps{ + Description: "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + +func schema_k8sio_api_core_v1_SELinuxOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SELinuxOptions are the labels to be applied to the container", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "user": { + SchemaProps: spec.SchemaProps{ + Description: "User is a SELinux user label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "role": { + SchemaProps: spec.SchemaProps{ + Description: "Role is a SELinux role label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type is a SELinux type label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + "level": { + SchemaProps: spec.SchemaProps{ + Description: "Level is SELinux level label that applies to the container.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gateway": { + SchemaProps: spec.SchemaProps{ + Description: "The host address of the ScaleIO API Gateway.", + Type: []string{"string"}, + Format: "", + }, + }, + "system": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the storage system as configured in ScaleIO.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", + Ref: ref("k8s.io/api/core/v1.SecretReference"), + }, + }, + "sslEnabled": { + SchemaProps: spec.SchemaProps{ + Description: "Flag to enable/disable SSL communication with Gateway, default false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "protectionDomain": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the ScaleIO Protection Domain for the configured storage.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePool": { + SchemaProps: spec.SchemaProps{ + Description: "The ScaleIO Storage Pool associated with the protection domain.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageMode": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\"", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"gateway", "system", "secretRef"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.SecretReference"}, + } +} + +func schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ScaleIOVolumeSource represents a persistent ScaleIO volume", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gateway": { + SchemaProps: spec.SchemaProps{ + Description: "The host address of the ScaleIO API Gateway.", + Type: []string{"string"}, + Format: "", + }, + }, + "system": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the storage system as configured in ScaleIO.", + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "sslEnabled": { + SchemaProps: spec.SchemaProps{ + Description: "Flag to enable/disable SSL communication with Gateway, default false", + Type: []string{"boolean"}, + Format: "", + }, + }, + "protectionDomain": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the ScaleIO Protection Domain for the configured storage.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePool": { + SchemaProps: spec.SchemaProps{ + Description: "The ScaleIO Storage Pool associated with the protection domain.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageMode": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"gateway", "system", "secretRef"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_ScopeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A scope selector represents the AND of the selectors represented by the scoped-resource selector requirements.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of scope selector requirements by scope of the resources.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ScopedResourceSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ScopedResourceSelectorRequirement"}, + } +} + +func schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator that relates the scope name and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scopeName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the scope that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Represents a scope's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"scopeName", "operator"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Secret(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "data": { + SchemaProps: spec.SchemaProps{ + Description: "Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + "stringData": { + SchemaProps: spec.SchemaProps{ + Description: "stringData allows specifying non-binary secret data in string form. It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Used to facilitate programmatic handling of secret data.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_SecretEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretKeySelector selects a key of a Secret.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The key of the secret to select from. Must be a valid secret key.", + Type: []string{"string"}, + Format: "", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"key"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretList is a list of Secret.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "Items is a list of secret objects. More info: https://kubernetes.io/docs/concepts/configuration/secret", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Secret"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Secret", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_SecretProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its key must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_SecretReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is unique within a namespace to reference a secret resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace defines the space within which the secret name must be unique.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_SecretVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secretName": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.KeyToPath"), + }, + }, + }, + }, + }, + "defaultMode": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "optional": { + SchemaProps: spec.SchemaProps{ + Description: "Specify whether the Secret or its keys must be defined", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.KeyToPath"}, + } +} + +func schema_k8sio_api_core_v1_SecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "capabilities": { + SchemaProps: spec.SchemaProps{ + Description: "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.", + Ref: ref("k8s.io/api/core/v1.Capabilities"), + }, + }, + "privileged": { + SchemaProps: spec.SchemaProps{ + Description: "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "seLinuxOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), + }, + }, + "windowsOptions": { + SchemaProps: spec.SchemaProps{ + Description: "The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), + }, + }, + "runAsUser": { + SchemaProps: spec.SchemaProps{ + Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsGroup": { + SchemaProps: spec.SchemaProps{ + Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "runAsNonRoot": { + SchemaProps: spec.SchemaProps{ + Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "readOnlyRootFilesystem": { + SchemaProps: spec.SchemaProps{ + Description: "Whether this container has a read-only root filesystem. Default is false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "allowPrivilegeEscalation": { + SchemaProps: spec.SchemaProps{ + Description: "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN", + Type: []string{"boolean"}, + Format: "", + }, + }, + "procMount": { + SchemaProps: spec.SchemaProps{ + Description: "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Capabilities", "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, + } +} + +func schema_k8sio_api_core_v1_SerializedReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SerializedReference is a reference to serialized object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "reference": { + SchemaProps: spec.SchemaProps{ + Description: "The reference to an object in the system.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Service(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Description: "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ServiceSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Ref: ref("k8s.io/api/core/v1.ServiceStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServiceSpec", "k8s.io/api/core/v1.ServiceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "secrets": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "name", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: https://kubernetes.io/docs/concepts/configuration/secret", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "automountServiceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccountList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountList is a list of ServiceAccount objects", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of ServiceAccounts. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ServiceAccount"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServiceAccount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "audience": { + SchemaProps: spec.SchemaProps{ + Description: "Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.", + Type: []string{"string"}, + Format: "", + }, + }, + "expirationSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the path relative to the mount point of the file to project the token into.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"path"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ServiceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceList holds a list of services.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of services", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Service"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Service", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_k8sio_api_core_v1_ServicePort(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServicePort contains information on service's port.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", + Type: []string{"string"}, + Format: "", + }, + }, + "protocol": { + SchemaProps: spec.SchemaProps{ + Description: "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", + Type: []string{"string"}, + Format: "", + }, + }, + "port": { + SchemaProps: spec.SchemaProps{ + Description: "The port that will be exposed by this service.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "targetPort": { + SchemaProps: spec.SchemaProps{ + Description: "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "nodePort": { + SchemaProps: spec.SchemaProps{ + Description: "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_ServiceProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceProxyOptions is the query options to a Service's proxy call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "path": { + SchemaProps: spec.SchemaProps{ + Description: "Path is the part of URLs that include service endpoints, suffixes, and parameters to use for the current proxy request to service. For example, the whole request URL is http://localhost/api/v1/namespaces/kube-system/services/elasticsearch-logging/_search?q=user:kimchy. Path is _search?q=user:kimchy.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_ServiceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceSpec describes the attributes that a user creates on a service.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ports": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-map-keys": []interface{}{ + "port", + "protocol", + }, + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "port", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ServicePort"), + }, + }, + }, + }, + }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "clusterIP": { + SchemaProps: spec.SchemaProps{ + Description: "clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \"None\", empty string (\"\"), or a valid IP address. \"None\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", + Type: []string{"string"}, + Format: "", + }, + }, + "externalIPs": { + SchemaProps: spec.SchemaProps{ + 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.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "sessionAffinity": { + SchemaProps: spec.SchemaProps{ + Description: "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", + Type: []string{"string"}, + Format: "", + }, + }, + "loadBalancerIP": { + SchemaProps: spec.SchemaProps{ + Description: "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", + Type: []string{"string"}, + Format: "", + }, + }, + "loadBalancerSourceRanges": { + SchemaProps: spec.SchemaProps{ + Description: "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.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "externalName": { + SchemaProps: spec.SchemaProps{ + Description: "externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.", + Type: []string{"string"}, + Format: "", + }, + }, + "externalTrafficPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \"Local\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \"Cluster\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.", + Type: []string{"string"}, + Format: "", + }, + }, + "healthCheckNodePort": { + SchemaProps: spec.SchemaProps{ + Description: "healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "publishNotReadyAddresses": { + SchemaProps: spec.SchemaProps{ + Description: "publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "sessionAffinityConfig": { + SchemaProps: spec.SchemaProps{ + Description: "sessionAffinityConfig contains the configurations of session affinity.", + Ref: ref("k8s.io/api/core/v1.SessionAffinityConfig"), + }, + }, + "ipFamily": { + SchemaProps: spec.SchemaProps{ + Description: "ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ServicePort", "k8s.io/api/core/v1.SessionAffinityConfig"}, + } +} + +func schema_k8sio_api_core_v1_ServiceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServiceStatus represents the current status of a service.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "loadBalancer": { + SchemaProps: spec.SchemaProps{ + Description: "LoadBalancer contains the current status of the load-balancer, if one is present.", + Ref: ref("k8s.io/api/core/v1.LoadBalancerStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LoadBalancerStatus"}, + } +} + +func schema_k8sio_api_core_v1_SessionAffinityConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SessionAffinityConfig represents the configurations of session affinity.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "clientIP": { + SchemaProps: spec.SchemaProps{ + Description: "clientIP contains the configurations of Client IP based session affinity.", + Ref: ref("k8s.io/api/core/v1.ClientIPConfig"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ClientIPConfig"}, + } +} + +func schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a StorageOS persistent volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_StorageOSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a StorageOS persistent volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumeName": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", + Type: []string{"string"}, + Format: "", + }, + }, + "volumeNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_k8sio_api_core_v1_Sysctl(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Sysctl defines a kernel parameter to be set", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of a property to set", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value of a property to set", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TCPSocketAction(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TCPSocketAction describes an action based on opening a socket", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "port": { + SchemaProps: spec.SchemaProps{ + Description: "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", + Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), + }, + }, + "host": { + SchemaProps: spec.SchemaProps{ + Description: "Optional: Host name to connect to, defaults to the pod IP.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"port"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, + } +} + +func schema_k8sio_api_core_v1_Taint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The node this Taint is attached to has the \"effect\" on any pod that does not tolerate the Taint.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The taint key to be applied to a node.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The taint value corresponding to the taint key.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeAdded": { + SchemaProps: spec.SchemaProps{ + Description: "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"key", "effect"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_k8sio_api_core_v1_Toleration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Description: "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.", + Type: []string{"string"}, + Format: "", + }, + }, + "effect": { + SchemaProps: spec.SchemaProps{ + Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.", + Type: []string{"string"}, + Format: "", + }, + }, + "tolerationSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + SchemaProps: spec.SchemaProps{ + Description: "The label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "An array of string values. One value must match the label to be selected. Each entry in Values is ORed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "values"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_TopologySelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchLabelExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "A list of topology selector requirements by labels.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.TopologySelectorLabelRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.TopologySelectorLabelRequirement"}, + } +} + +func schema_k8sio_api_core_v1_TopologySpreadConstraint(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TopologySpreadConstraint specifies how to spread matching pods among the given topology.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "maxSkew": { + SchemaProps: spec.SchemaProps{ + Description: "MaxSkew describes the degree to which pods may be unevenly distributed. It's the maximum permitted difference between the number of matching pods in any two topology domains of a given topology type. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 1/1/1; scheduling it onto zone1(zone2) would make the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. It's a required field. Default value is 1 and 0 is not allowed.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "topologyKey": { + SchemaProps: spec.SchemaProps{ + Description: "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. It's a required field.", + Type: []string{"string"}, + Format: "", + }, + }, + "whenUnsatisfiable": { + SchemaProps: spec.SchemaProps{ + Description: "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it - ScheduleAnyway tells the scheduler to still schedule it It's considered as \"Unsatisfiable\" if and only if placing incoming pod on any topology violates \"MaxSkew\". For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.", + Type: []string{"string"}, + Format: "", + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, + }, + Required: []string{"maxSkew", "topologyKey", "whenUnsatisfiable"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, + } +} + +func schema_k8sio_api_core_v1_TypedLocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiGroup": { + SchemaProps: spec.SchemaProps{ + Description: "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is the type of resource being referenced", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name is the name of resource being referenced", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Volume represents a named volume in a pod that may be accessed by any container in the pod.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + Type: []string{"string"}, + Format: "", + }, + }, + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "emptyDir": { + SchemaProps: spec.SchemaProps{ + Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "gitRepo": { + SchemaProps: spec.SchemaProps{ + Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), + }, + }, + "persistentVolumeClaim": { + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPI represents downward API about the pod that should populate this volume", + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap represents a configMap that should populate this volume", + Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "projected": { + SchemaProps: spec.SchemaProps{ + Description: "Items for all in one resources secrets, configmaps, and downward API", + Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", + Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_VolumeDevice(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "volumeDevice describes a mapping of a raw block device within a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name must match the name of a persistentVolumeClaim in the pod", + Type: []string{"string"}, + Format: "", + }, + }, + "devicePath": { + SchemaProps: spec.SchemaProps{ + Description: "devicePath is the path inside of the container that the device will be mapped to.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "devicePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_VolumeMount(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VolumeMount describes a mounting of a Volume within a container.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "This must match the Name of a Volume.", + Type: []string{"string"}, + Format: "", + }, + }, + "readOnly": { + SchemaProps: spec.SchemaProps{ + Description: "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "mountPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path within the container at which the volume should be mounted. Must not contain ':'.", + Type: []string{"string"}, + Format: "", + }, + }, + "subPath": { + SchemaProps: spec.SchemaProps{ + Description: "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", + Type: []string{"string"}, + Format: "", + }, + }, + "mountPropagation": { + SchemaProps: spec.SchemaProps{ + Description: "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.", + Type: []string{"string"}, + Format: "", + }, + }, + "subPathExpr": { + SchemaProps: spec.SchemaProps{ + Description: "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "mountPath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_VolumeNodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VolumeNodeAffinity defines constraints that limit what nodes this volume can be accessed from.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "required": { + SchemaProps: spec.SchemaProps{ + Description: "Required specifies hard node constraints that must be met.", + Ref: ref("k8s.io/api/core/v1.NodeSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.NodeSelector"}, + } +} + +func schema_k8sio_api_core_v1_VolumeProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Projection that may be projected along with other supported volume types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "information about the secret data to project", + Ref: ref("k8s.io/api/core/v1.SecretProjection"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "information about the downwardAPI data to project", + Ref: ref("k8s.io/api/core/v1.DownwardAPIProjection"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "information about the configMap data to project", + Ref: ref("k8s.io/api/core/v1.ConfigMapProjection"), + }, + }, + "serviceAccountToken": { + SchemaProps: spec.SchemaProps{ + Description: "information about the serviceAccountToken data to project", + Ref: ref("k8s.io/api/core/v1.ServiceAccountTokenProjection"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ConfigMapProjection", "k8s.io/api/core/v1.DownwardAPIProjection", "k8s.io/api/core/v1.SecretProjection", "k8s.io/api/core/v1.ServiceAccountTokenProjection"}, + } +} + +func schema_k8sio_api_core_v1_VolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents the source of a volume to mount. Only one of its members may be specified.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "hostPath": { + SchemaProps: spec.SchemaProps{ + Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", + Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), + }, + }, + "emptyDir": { + SchemaProps: spec.SchemaProps{ + Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", + Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), + }, + }, + "gcePersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", + Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), + }, + }, + "awsElasticBlockStore": { + SchemaProps: spec.SchemaProps{ + Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", + Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), + }, + }, + "gitRepo": { + SchemaProps: spec.SchemaProps{ + Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", + Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), + }, + }, + "secret": { + SchemaProps: spec.SchemaProps{ + Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", + Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), + }, + }, + "nfs": { + SchemaProps: spec.SchemaProps{ + Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", + Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), + }, + }, + "iscsi": { + SchemaProps: spec.SchemaProps{ + Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", + Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), + }, + }, + "glusterfs": { + SchemaProps: spec.SchemaProps{ + Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", + Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), + }, + }, + "persistentVolumeClaim": { + SchemaProps: spec.SchemaProps{ + Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", + Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), + }, + }, + "rbd": { + SchemaProps: spec.SchemaProps{ + Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", + Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), + }, + }, + "flexVolume": { + SchemaProps: spec.SchemaProps{ + Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", + Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), + }, + }, + "cinder": { + SchemaProps: spec.SchemaProps{ + Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", + Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), + }, + }, + "cephfs": { + SchemaProps: spec.SchemaProps{ + Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), + }, + }, + "flocker": { + SchemaProps: spec.SchemaProps{ + Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", + Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), + }, + }, + "downwardAPI": { + SchemaProps: spec.SchemaProps{ + Description: "DownwardAPI represents downward API about the pod that should populate this volume", + Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), + }, + }, + "fc": { + SchemaProps: spec.SchemaProps{ + Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", + Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), + }, + }, + "azureFile": { + SchemaProps: spec.SchemaProps{ + Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), + }, + }, + "configMap": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigMap represents a configMap that should populate this volume", + Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), + }, + }, + "vsphereVolume": { + SchemaProps: spec.SchemaProps{ + Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), + }, + }, + "quobyte": { + SchemaProps: spec.SchemaProps{ + Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", + Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), + }, + }, + "azureDisk": { + SchemaProps: spec.SchemaProps{ + Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", + Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), + }, + }, + "photonPersistentDisk": { + SchemaProps: spec.SchemaProps{ + Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), + }, + }, + "projected": { + SchemaProps: spec.SchemaProps{ + Description: "Items for all in one resources secrets, configmaps, and downward API", + Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), + }, + }, + "portworxVolume": { + SchemaProps: spec.SchemaProps{ + Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", + Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), + }, + }, + "scaleIO": { + SchemaProps: spec.SchemaProps{ + Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), + }, + }, + "storageos": { + SchemaProps: spec.SchemaProps{ + Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", + Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), + }, + }, + "csi": { + SchemaProps: spec.SchemaProps{ + Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", + Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, + } +} + +func schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Represents a vSphere volume resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "volumePath": { + SchemaProps: spec.SchemaProps{ + Description: "Path that identifies vSphere volume vmdk", + Type: []string{"string"}, + Format: "", + }, + }, + "fsType": { + SchemaProps: spec.SchemaProps{ + Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePolicyName": { + SchemaProps: spec.SchemaProps{ + Description: "Storage Policy Based Management (SPBM) profile name.", + Type: []string{"string"}, + Format: "", + }, + }, + "storagePolicyID": { + SchemaProps: spec.SchemaProps{ + Description: "Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"volumePath"}, + }, + }, + } +} + +func schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "weight": { + SchemaProps: spec.SchemaProps{ + Description: "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "podAffinityTerm": { + SchemaProps: spec.SchemaProps{ + Description: "Required. A pod affinity term, associated with the corresponding weight.", + Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), + }, + }, + }, + Required: []string{"weight", "podAffinityTerm"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.PodAffinityTerm"}, + } +} + +func schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "WindowsSecurityContextOptions contain Windows-specific options and credentials.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "gmsaCredentialSpecName": { + SchemaProps: spec.SchemaProps{ + Description: "GMSACredentialSpecName is the name of the GMSA credential spec to use. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + "gmsaCredentialSpec": { + SchemaProps: spec.SchemaProps{ + Description: "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + "runAsUserName": { + SchemaProps: spec.SchemaProps{ + Description: "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. This field is alpha-level and it is only honored by servers that enable the WindowsRunAsUserName feature flag.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, } } @@ -2159,6 +13228,554 @@ func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.Ope } } +func schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + Type: []string{"object"}, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this: type MyAwesomeAPIObject struct {\n runtime.TypeMeta `json:\",inline\"`\n ... // other fields\n} func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_Unknown(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Unknown allows api objects with unknown types to be passed-through. This can be used to deal with the API objects from a plug-in. Unknown objects still have functioning TypeMeta features-- kind, version, etc. metadata and field mutatation.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Raw": { + SchemaProps: spec.SchemaProps{ + Description: "Raw will hold the complete serialized object which couldn't be matched with a registered type. Most likely, nothing should be done with this except for passing it through the system.", + Type: []string{"string"}, + Format: "byte", + }, + }, + "ContentEncoding": { + SchemaProps: spec.SchemaProps{ + Description: "ContentEncoding is encoding used to encode 'Raw' data. Unspecified means no encoding.", + Type: []string{"string"}, + Format: "", + }, + }, + "ContentType": { + SchemaProps: spec.SchemaProps{ + Description: "ContentType is serialization method used to serialize 'Raw'. Unspecified means ContentTypeJSON.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"Raw", "ContentEncoding", "ContentType"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_AuthConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AuthConfig is our abstraction of the Registry authorization information for whatever docker client we happen to be based on", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "username": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "password": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "email": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "serverAddress": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "secretRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_CGroupLimits(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CGroupLimits holds limits used to constrain container resources.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "memoryLimitBytes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "cpuShares": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "cpuPeriod": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "cpuQuota": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "memorySwap": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int64", + }, + }, + "parent": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"memoryLimitBytes", "cpuShares", "cpuPeriod", "cpuQuota", "memorySwap", "parent"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_ContainerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ContainerConfig is the abstraction of the docker client provider (formerly go-dockerclient, now either engine-api or kube docker client) container.Config type that is leveraged by s2i or origin", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Labels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "Env": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"Labels", "Env"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_ContainerInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "builderImage": { + SchemaProps: spec.SchemaProps{ + Description: "BaseImage are the images this template will use.", + Type: []string{"string"}, + Format: "", + }, + }, + "runtimeImage": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "runtimeArtifacts": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec"), + }, + }, + }, + }, + }, + "buildVolumes": { + SchemaProps: spec.SchemaProps{ + Description: "BuildVolumes specifies a list of volumes to mount to container running the build.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_DockerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DockerConfig contains the configuration for a Docker connection.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "endPoint": { + SchemaProps: spec.SchemaProps{ + Description: "Endpoint is the docker network endpoint or socket", + Type: []string{"string"}, + Format: "", + }, + }, + "certFile": { + SchemaProps: spec.SchemaProps{ + Description: "CertFile is the certificate file path for a TLS connection", + Type: []string{"string"}, + Format: "", + }, + }, + "keyFile": { + SchemaProps: spec.SchemaProps{ + Description: "KeyFile is the key file path for a TLS connection", + Type: []string{"string"}, + Format: "", + }, + }, + "caFile": { + SchemaProps: spec.SchemaProps{ + Description: "CAFile is the certificate authority file path for a TLS connection", + Type: []string{"string"}, + Format: "", + }, + }, + "useTLS": { + SchemaProps: spec.SchemaProps{ + Description: "UseTLS indicates if TLS must be used", + Type: []string{"boolean"}, + Format: "", + }, + }, + "tlsVerify": { + SchemaProps: spec.SchemaProps{ + Description: "TLSVerify indicates if TLS peer must be verified", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"endPoint", "certFile", "keyFile", "caFile", "useTLS", "tlsVerify"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_DockerConfigEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "username": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "password": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "email": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "serverAddress": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"username", "password", "email"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_DockerConfigJson(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "auths": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfigEntry"), + }, + }, + }, + }, + }, + }, + Required: []string{"auths"}, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfigEntry"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_EnvironmentSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EnvironmentSpec specifies a single environment variable.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "value"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_Parameter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "key": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "optValues": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "required": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "defaultValue": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_ProxyConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ProxyConfig holds proxy configuration.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "httpProxy": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "httpsProxy": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iAutoScale(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "initReplicas": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "containers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"kind", "name"}, + }, + }, + } +} + func schema_pkg_apis_devops_v1alpha1_S2iBinary(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2318,3 +13935,1255 @@ func schema_pkg_apis_devops_v1alpha1_S2iBinaryStatus(ref common.ReferenceCallbac }, } } + +func schema_pkg_apis_devops_v1alpha1_S2iBuildResult(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "imageName": { + SchemaProps: spec.SchemaProps{ + Description: "ImageName is the name of artifact", + Type: []string{"string"}, + Format: "", + }, + }, + "imageSize": { + SchemaProps: spec.SchemaProps{ + Description: "The size in bytes of the image", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Description: "Image ID.", + Type: []string{"string"}, + Format: "", + }, + }, + "imageCreated": { + SchemaProps: spec.SchemaProps{ + Description: "Image created time.", + Type: []string{"string"}, + Format: "", + }, + }, + "imageRepoTags": { + SchemaProps: spec.SchemaProps{ + Description: "image tags.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "commandPull": { + SchemaProps: spec.SchemaProps{ + Description: "Command for pull image.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuildSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "sourceUrl": { + SchemaProps: spec.SchemaProps{ + Description: "SourceURL is url of the codes such as https://github.com/a/b.git", + Type: []string{"string"}, + Format: "", + }, + }, + "revisionId": { + SchemaProps: spec.SchemaProps{ + Description: "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", + Type: []string{"string"}, + Format: "", + }, + }, + "binaryName": { + SchemaProps: spec.SchemaProps{ + Description: "Binary file Name", + Type: []string{"string"}, + Format: "", + }, + }, + "binarySize": { + SchemaProps: spec.SchemaProps{ + Description: "Binary file Size", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "builderImage": { + SchemaProps: spec.SchemaProps{ + Description: "// BuilderImage describes which image is used for building the result images.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a result image description label. The default is no description.", + Type: []string{"string"}, + Format: "", + }, + }, + "commitID": { + SchemaProps: spec.SchemaProps{ + Description: "CommitID represents an arbitrary extended object reference in Git as SHA-1", + Type: []string{"string"}, + Format: "", + }, + }, + "committerName": { + SchemaProps: spec.SchemaProps{ + Description: "CommitterName contains the name of the committer", + Type: []string{"string"}, + Format: "", + }, + }, + "committerEmail": { + SchemaProps: spec.SchemaProps{ + Description: "CommitterEmail contains the e-mail of the committer", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilder(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilder is the Schema for the s2ibuilders API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderStatus"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderList contains a list of S2iBuilder", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilder"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilder"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderSpec defines the desired state of S2iBuilder", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "config": { + SchemaProps: spec.SchemaProps{ + Description: "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iConfig"), + }, + }, + "fromTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "FromTemplate define some inputs from user", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.UserDefineTemplate"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iConfig", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.UserDefineTemplate"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderStatus defines the observed state of S2iBuilder", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "runCount": { + SchemaProps: spec.SchemaProps{ + Description: "RunCount represent the sum of s2irun of this builder", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "lastRunState": { + SchemaProps: spec.SchemaProps{ + Description: "LastRunState return the state of the newest run of this builder", + Type: []string{"string"}, + Format: "", + }, + }, + "lastRunName": { + SchemaProps: spec.SchemaProps{ + Description: "LastRunState return the name of the newest run of this builder", + Type: []string{"string"}, + Format: "", + }, + }, + "lastRunStartTime": { + SchemaProps: spec.SchemaProps{ + Description: "LastRunStartTime return the startTime of the newest run of this builder", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + }, + Required: []string{"runCount"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderTemplate is the Schema for the s2ibuildertemplates API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderTemplateList contains a list of S2iBuilderTemplate", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplate"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuilderTemplate"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "defaultBaseImage": { + SchemaProps: spec.SchemaProps{ + Description: "DefaultBaseImage is the image that will be used by default", + Type: []string{"string"}, + Format: "", + }, + }, + "containerInfo": { + SchemaProps: spec.SchemaProps{ + Description: "Images are the images this template will use.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ContainerInfo"), + }, + }, + }, + }, + }, + "codeFramework": { + SchemaProps: spec.SchemaProps{ + Description: "CodeFramework means which language this template is designed for and which framework is using if has framework. Like Java, NodeJS etc", + Type: []string{"string"}, + Format: "", + }, + }, + "environment": { + SchemaProps: spec.SchemaProps{ + Description: "Parameters is a set of environment variables to be passed to the image.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.Parameter"), + }, + }, + }, + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version of template", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description illustrate the purpose of this template", + Type: []string{"string"}, + Format: "", + }, + }, + "iconPath": { + SchemaProps: spec.SchemaProps{ + Description: "IconPath is used for frontend display", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ContainerInfo", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.Parameter"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "displayName": { + SchemaProps: spec.SchemaProps{ + Description: "DisplayName is a result image display-name label. This defaults to the output image name.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "Description is a result image description label. The default is no description.", + Type: []string{"string"}, + Format: "", + }, + }, + "builderImage": { + SchemaProps: spec.SchemaProps{ + Description: "BuilderImage describes which image is used for building the result images.", + Type: []string{"string"}, + Format: "", + }, + }, + "builderImageVersion": { + SchemaProps: spec.SchemaProps{ + Description: "BuilderImageVersion provides optional version information about the builder image.", + Type: []string{"string"}, + Format: "", + }, + }, + "builderBaseImageVersion": { + SchemaProps: spec.SchemaProps{ + Description: "BuilderBaseImageVersion provides optional version information about the builder base image.", + Type: []string{"string"}, + Format: "", + }, + }, + "runtimeImage": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeImage specifies the image that will be a base for resulting image and will be used for running an application. By default, BuilderImage is used for building and running, but the latter may be overridden.", + Type: []string{"string"}, + Format: "", + }, + }, + "outputImageName": { + SchemaProps: spec.SchemaProps{ + Description: "OutputImageName is a result image name without tag, default is latest. tag will append to ImageName in the end", + Type: []string{"string"}, + Format: "", + }, + }, + "runtimeImagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeImagePullPolicy specifies when to pull a runtime image.", + Type: []string{"string"}, + Format: "", + }, + }, + "runtimeAuthentication": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeAuthentication holds the authentication information for pulling the runtime Docker images from private repositories.", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig"), + }, + }, + "runtimeArtifacts": { + SchemaProps: spec.SchemaProps{ + Description: "RuntimeArtifacts specifies a list of source/destination pairs that will be copied from builder to a runtime image. Source can be a file or directory. Destination must be a directory. Regardless whether it is an absolute or relative path, it will be placed into image's WORKDIR. Destination also can be empty or equals to \".\", in this case it just refers to a root of WORKDIR. In case it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files label on a RuntimeImage.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec"), + }, + }, + }, + }, + }, + "dockerConfig": { + SchemaProps: spec.SchemaProps{ + Description: "DockerConfig describes how to access host docker daemon.", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfig"), + }, + }, + "pullAuthentication": { + SchemaProps: spec.SchemaProps{ + Description: "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig"), + }, + }, + "pushAuthentication": { + SchemaProps: spec.SchemaProps{ + Description: "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig"), + }, + }, + "incrementalAuthentication": { + SchemaProps: spec.SchemaProps{ + Description: "IncrementalAuthentication holds the authentication information for pulling the previous image from private repositories", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig"), + }, + }, + "dockerNetworkMode": { + SchemaProps: spec.SchemaProps{ + Description: "DockerNetworkMode is used to set the docker network setting to --net=container: when the builder is invoked from a container.", + Type: []string{"string"}, + Format: "", + }, + }, + "preserveWorkingDir": { + SchemaProps: spec.SchemaProps{ + Description: "PreserveWorkingDir describes if working directory should be left after processing.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "imageName": { + SchemaProps: spec.SchemaProps{ + Description: "ImageName Contains the registry address and reponame, tag should set by field tag alone", + Type: []string{"string"}, + Format: "", + }, + }, + "tag": { + SchemaProps: spec.SchemaProps{ + Description: "Tag is a result image tag name.", + Type: []string{"string"}, + Format: "", + }, + }, + "builderPullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "BuilderPullPolicy specifies when to pull the builder image", + Type: []string{"string"}, + Format: "", + }, + }, + "previousImagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "PreviousImagePullPolicy specifies when to pull the previously build image when doing incremental build", + Type: []string{"string"}, + Format: "", + }, + }, + "incremental": { + SchemaProps: spec.SchemaProps{ + Description: "Incremental describes whether to try to perform incremental build.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "incrementalFromTag": { + SchemaProps: spec.SchemaProps{ + Description: "IncrementalFromTag sets an alternative image tag to look for existing artifacts. Tag is used by default if this is not set.", + Type: []string{"string"}, + Format: "", + }, + }, + "removePreviousImage": { + SchemaProps: spec.SchemaProps{ + Description: "RemovePreviousImage describes if previous image should be removed after successful build. This applies only to incremental builds.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "environment": { + SchemaProps: spec.SchemaProps{ + Description: "Environment is a map of environment variables to be passed to the image.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.EnvironmentSpec"), + }, + }, + }, + }, + }, + "labelNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "LabelNamespace provides the namespace under which the labels will be generated.", + Type: []string{"string"}, + Format: "", + }, + }, + "callbackUrl": { + SchemaProps: spec.SchemaProps{ + Description: "CallbackURL is a URL which is called upon successful build to inform about that fact.", + Type: []string{"string"}, + Format: "", + }, + }, + "scriptsUrl": { + SchemaProps: spec.SchemaProps{ + Description: "ScriptsURL is a URL describing where to fetch the S2I scripts from during build process. This url can be a reference within the builder image if the scheme is specified as image://", + Type: []string{"string"}, + Format: "", + }, + }, + "destination": { + SchemaProps: spec.SchemaProps{ + Description: "Destination specifies a location where the untar operation will place its artifacts.", + Type: []string{"string"}, + Format: "", + }, + }, + "workingDir": { + SchemaProps: spec.SchemaProps{ + Description: "WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.", + Type: []string{"string"}, + Format: "", + }, + }, + "workingSourceDir": { + SchemaProps: spec.SchemaProps{ + Description: "WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download that is later used as the root for ignore processing", + Type: []string{"string"}, + Format: "", + }, + }, + "layeredBuild": { + SchemaProps: spec.SchemaProps{ + Description: "LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "contextDir": { + SchemaProps: spec.SchemaProps{ + Description: "Specify a relative directory inside the application repository that should be used as a root directory for the application.", + Type: []string{"string"}, + Format: "", + }, + }, + "assembleUser": { + SchemaProps: spec.SchemaProps{ + Description: "AssembleUser specifies the user to run the assemble script in container", + Type: []string{"string"}, + Format: "", + }, + }, + "runImage": { + SchemaProps: spec.SchemaProps{ + Description: "RunImage will trigger a \"docker run ...\" invocation of the produced image so the user can see if it operates as he would expect", + Type: []string{"boolean"}, + Format: "", + }, + }, + "usage": { + SchemaProps: spec.SchemaProps{ + Description: "Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked", + Type: []string{"boolean"}, + Format: "", + }, + }, + "injections": { + SchemaProps: spec.SchemaProps{ + Description: "Injections specifies a list source/destination folders that are injected to the container that runs assemble. All files we inject will be truncated after the assemble script finishes.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec"), + }, + }, + }, + }, + }, + "cgroupLimits": { + SchemaProps: spec.SchemaProps{ + Description: "CGroupLimits describes the cgroups limits that will be applied to any containers run by s2i.", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.CGroupLimits"), + }, + }, + "dropCapabilities": { + SchemaProps: spec.SchemaProps{ + Description: "DropCapabilities contains a list of capabilities to drop when executing containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "scriptDownloadProxyConfig": { + SchemaProps: spec.SchemaProps{ + Description: "ScriptDownloadProxyConfig optionally specifies the http and https proxy to use when downloading scripts", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ProxyConfig"), + }, + }, + "excludeRegExp": { + SchemaProps: spec.SchemaProps{ + Description: "ExcludeRegExp contains a string representation of the regular expression desired for deciding which files to exclude from the tar stream", + Type: []string{"string"}, + Format: "", + }, + }, + "blockOnBuild": { + SchemaProps: spec.SchemaProps{ + Description: "BlockOnBuild prevents s2i from performing a docker build operation if one is necessary to execute ONBUILD commands, or to layer source code into the container for images that don't have a tar binary available, if the image contains ONBUILD commands that would be executed.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "hasOnBuild": { + SchemaProps: spec.SchemaProps{ + Description: "HasOnBuild will be set to true if the builder image contains ONBUILD instructions", + Type: []string{"boolean"}, + Format: "", + }, + }, + "buildVolumes": { + SchemaProps: spec.SchemaProps{ + Description: "BuildVolumes specifies a list of volumes to mount to container running the build.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Description: "Labels specify labels and their values to be applied to the resulting image. Label keys must have non-zero length. The labels defined here override generated labels in case they have the same name.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "securityOpt": { + SchemaProps: spec.SchemaProps{ + Description: "SecurityOpt are passed as options to the docker containers launched by s2i.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "keepSymlinks": { + SchemaProps: spec.SchemaProps{ + Description: "KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow symlinks and copy files by content.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "asDockerfile": { + SchemaProps: spec.SchemaProps{ + Description: "AsDockerfile indicates the path where the Dockerfile should be written instead of building a new image.", + Type: []string{"string"}, + Format: "", + }, + }, + "imageWorkDir": { + SchemaProps: spec.SchemaProps{ + Description: "ImageWorkDir is the default working directory for the builder image.", + Type: []string{"string"}, + Format: "", + }, + }, + "imageScriptsUrl": { + SchemaProps: spec.SchemaProps{ + Description: "ImageScriptsURL is the default location to find the assemble/run scripts for a builder image. This url can be a reference within the builder image if the scheme is specified as image://", + Type: []string{"string"}, + Format: "", + }, + }, + "addHost": { + SchemaProps: spec.SchemaProps{ + Description: "AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "export": { + SchemaProps: spec.SchemaProps{ + Description: "Export Push the result image to specify image registry in tag", + Type: []string{"boolean"}, + Format: "", + }, + }, + "sourceUrl": { + SchemaProps: spec.SchemaProps{ + Description: "SourceURL is url of the codes such as https://github.com/a/b.git", + Type: []string{"string"}, + Format: "", + }, + }, + "isBinaryURL": { + SchemaProps: spec.SchemaProps{ + Description: "IsBinaryURL explain the type of SourceURL. If it is IsBinaryURL, it will download the file directly without using git.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "gitSecretRef": { + SchemaProps: spec.SchemaProps{ + Description: "GitSecretRef is the BasicAuth Secret of Git Clone", + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + "revisionId": { + SchemaProps: spec.SchemaProps{ + Description: "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", + Type: []string{"string"}, + Format: "", + }, + }, + "taintKey": { + SchemaProps: spec.SchemaProps{ + Description: "The name of taint.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeAffinityKey": { + SchemaProps: spec.SchemaProps{ + Description: "The key of Node Affinity.", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeAffinityValues": { + SchemaProps: spec.SchemaProps{ + Description: "The values of Node Affinity.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "outputBuildResult": { + SchemaProps: spec.SchemaProps{ + Description: "Whether output build result to status.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "branchExpression": { + SchemaProps: spec.SchemaProps{ + Description: "Regular expressions, ignoring names that do not match the provided regular expression", + Type: []string{"string"}, + Format: "", + }, + }, + "secretCode": { + SchemaProps: spec.SchemaProps{ + Description: "SecretCode", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"imageName", "sourceUrl"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.LocalObjectReference", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.AuthConfig", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.CGroupLimits", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.DockerConfig", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.EnvironmentSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.ProxyConfig", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.VolumeSpec"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iRun(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iRun is the Schema for the s2iruns API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRunStatus"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iRunList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iRunList contains a list of S2iRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRun"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iRun"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iRunSpec defines the desired state of S2iRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "builderName": { + SchemaProps: spec.SchemaProps{ + Description: "BuilderName specify the name of s2ibuilder, required", + Type: []string{"string"}, + Format: "", + }, + }, + "backoffLimit": { + SchemaProps: spec.SchemaProps{ + Description: "BackoffLimit limits the restart count of each s2irun. Default is 0", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "secondsAfterFinished": { + SchemaProps: spec.SchemaProps{ + Description: "SecondsAfterFinished if is set and greater than zero, and the job created by s2irun become successful or failed , the job will be auto deleted after SecondsAfterFinished", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "newTag": { + SchemaProps: spec.SchemaProps{ + Description: "NewTag override the default tag in its s2ibuilder, image name cannot be changed.", + Type: []string{"string"}, + Format: "", + }, + }, + "newRevisionId": { + SchemaProps: spec.SchemaProps{ + Description: "NewRevisionId override the default NewRevisionId in its s2ibuilder.", + Type: []string{"string"}, + Format: "", + }, + }, + "newSourceURL": { + SchemaProps: spec.SchemaProps{ + Description: "NewSourceURL is used to download new binary artifacts", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"builderName"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha1_S2iRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "S2iRunStatus defines the observed state of S2iRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime represent when this run began", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "runState": { + SchemaProps: spec.SchemaProps{ + Description: "RunState indicates whether this job is done or failed", + Type: []string{"string"}, + Format: "", + }, + }, + "logURL": { + SchemaProps: spec.SchemaProps{ + Description: "LogURL is uesd for external log handler to let user know where is log located in", + Type: []string{"string"}, + Format: "", + }, + }, + "kubernetesJobName": { + SchemaProps: spec.SchemaProps{ + Description: "KubernetesJobName is the job name in k8s", + Type: []string{"string"}, + Format: "", + }, + }, + "s2iBuildResult": { + SchemaProps: spec.SchemaProps{ + Description: "S2i build result info.", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildResult"), + }, + }, + "s2iBuildSource": { + SchemaProps: spec.SchemaProps{ + Description: "S2i build source info.", + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildSource"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Time", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildResult", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.S2iBuildSource"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_UserDefineTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name specify a template to use, so many fields in Config can left empty", + Type: []string{"string"}, + Format: "", + }, + }, + "parameters": { + SchemaProps: spec.SchemaProps{ + Description: "Parameters must use with `template`, fill some parameters which template will use", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.Parameter"), + }, + }, + }, + }, + }, + "builderImage": { + SchemaProps: spec.SchemaProps{ + Description: "BaseImage specify which version of this template to use", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1.Parameter"}, + } +} + +func schema_pkg_apis_devops_v1alpha1_VolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "VolumeSpec represents a single volume mount point.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "source": { + SchemaProps: spec.SchemaProps{ + Description: "Source is a reference to the volume source.", + Type: []string{"string"}, + Format: "", + }, + }, + "destination": { + SchemaProps: spec.SchemaProps{ + Description: "Destination is the path to mount the volume to - absolute or relative.", + Type: []string{"string"}, + Format: "", + }, + }, + "keep": { + SchemaProps: spec.SchemaProps{ + Description: "Keep indicates if the mounted data should be kept in the final image.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/apis/devops/v1alpha1/s2ibinary_types.go b/pkg/apis/devops/v1alpha1/s2ibinary_types.go index f3b25783d..e2500656b 100644 --- a/pkg/apis/devops/v1alpha1/s2ibinary_types.go +++ b/pkg/apis/devops/v1alpha1/s2ibinary_types.go @@ -21,9 +21,9 @@ import ( ) const ( - ResourceKindS2iBinary = "S2iBinary" - ResourceSingularServicePolicy = "s2ibinary" - ResourcePluralServicePolicy = "s2ibinaries" + ResourceKindS2iBinary = "S2iBinary" + ResourceSingularS2iBinary = "s2ibinary" + ResourcePluralS2iBinary = "s2ibinaries" ) const ( diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_types.go b/pkg/apis/devops/v1alpha1/s2ibuilder_types.go similarity index 98% rename from vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_types.go rename to pkg/apis/devops/v1alpha1/s2ibuilder_types.go index 37fec96e9..c2250a690 100644 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_types.go +++ b/pkg/apis/devops/v1alpha1/s2ibuilder_types.go @@ -422,6 +422,12 @@ type S2iConfig struct { // Whether output build result to status. OutputBuildResult bool `json:"outputBuildResult,omitempty"` + + // Regular expressions, ignoring names that do not match the provided regular expression + BranchExpression string `json:"branchExpression,omitempty"` + + // SecretCode + SecretCode string `json:"secretCode,omitempty"` } type UserDefineTemplate struct { diff --git a/pkg/apis/devops/v1alpha1/s2ibuilder_types_test.go b/pkg/apis/devops/v1alpha1/s2ibuilder_types_test.go new file mode 100644 index 000000000..df3ef4a4a --- /dev/null +++ b/pkg/apis/devops/v1alpha1/s2ibuilder_types_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2019 The Kubesphere 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 v1alpha1 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStorageS2iBuilder(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created := &S2iBuilder{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &S2iBuilder{} + g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) +} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go b/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go similarity index 94% rename from vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go rename to pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go index 085f1b000..58a8bea37 100644 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go +++ b/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types.go @@ -36,6 +36,17 @@ type Parameter struct { Value string `json:"value,omitempty"` } +type CodeFramework string + +const ( + Ruby CodeFramework = "ruby" + Go CodeFramework = "go" + Java CodeFramework = "Java" + JavaTomcat CodeFramework = "JavaTomcat" + Nodejs CodeFramework = "Nodejs" + Python CodeFramework = "python" +) + func (p *Parameter) ToEnvonment() *EnvironmentSpec { var v string if p.Value == "" && p.DefaultValue != "" { diff --git a/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types_test.go b/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types_test.go new file mode 100644 index 000000000..60d3a76c3 --- /dev/null +++ b/pkg/apis/devops/v1alpha1/s2ibuildertemplate_types_test.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 The Kubesphere 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 v1alpha1 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStorageS2iBuilderTemplate(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + } + created := &S2iBuilderTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &S2iBuilderTemplate{} + g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) +} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2irun_types.go b/pkg/apis/devops/v1alpha1/s2irun_types.go similarity index 100% rename from vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2irun_types.go rename to pkg/apis/devops/v1alpha1/s2irun_types.go diff --git a/pkg/apis/devops/v1alpha1/s2irun_types_test.go b/pkg/apis/devops/v1alpha1/s2irun_types_test.go new file mode 100644 index 000000000..cb93ff172 --- /dev/null +++ b/pkg/apis/devops/v1alpha1/s2irun_types_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2019 The Kubesphere 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 v1alpha1 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStorageS2iRun(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created := &S2iRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &S2iRun{} + g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) + + g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) +} diff --git a/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go index 42481f051..c1c383403 100644 --- a/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go @@ -16,14 +16,250 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by deepcopy-gen. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1alpha1 import ( + "k8s.io/api/core/v1" 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 *AuthConfig) DeepCopyInto(out *AuthConfig) { + *out = *in + if in.SecretRef != nil { + in, out := &in.SecretRef, &out.SecretRef + *out = new(v1.LocalObjectReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. +func (in *AuthConfig) DeepCopy() *AuthConfig { + if in == nil { + return nil + } + out := new(AuthConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CGroupLimits) DeepCopyInto(out *CGroupLimits) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CGroupLimits. +func (in *CGroupLimits) DeepCopy() *CGroupLimits { + if in == nil { + return nil + } + out := new(CGroupLimits) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerConfig) DeepCopyInto(out *ContainerConfig) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerConfig. +func (in *ContainerConfig) DeepCopy() *ContainerConfig { + if in == nil { + return nil + } + out := new(ContainerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerInfo) DeepCopyInto(out *ContainerInfo) { + *out = *in + if in.RuntimeArtifacts != nil { + in, out := &in.RuntimeArtifacts, &out.RuntimeArtifacts + *out = make([]VolumeSpec, len(*in)) + copy(*out, *in) + } + if in.BuildVolumes != nil { + in, out := &in.BuildVolumes, &out.BuildVolumes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerInfo. +func (in *ContainerInfo) DeepCopy() *ContainerInfo { + if in == nil { + return nil + } + out := new(ContainerInfo) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DockerConfig) DeepCopyInto(out *DockerConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfig. +func (in *DockerConfig) DeepCopy() *DockerConfig { + if in == nil { + return nil + } + out := new(DockerConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DockerConfigEntry) DeepCopyInto(out *DockerConfigEntry) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigEntry. +func (in *DockerConfigEntry) DeepCopy() *DockerConfigEntry { + if in == nil { + return nil + } + out := new(DockerConfigEntry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DockerConfigJson) DeepCopyInto(out *DockerConfigJson) { + *out = *in + if in.Auths != nil { + in, out := &in.Auths, &out.Auths + *out = make(DockerConfigMap, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigJson. +func (in *DockerConfigJson) DeepCopy() *DockerConfigJson { + if in == nil { + return nil + } + out := new(DockerConfigJson) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in DockerConfigMap) DeepCopyInto(out *DockerConfigMap) { + { + in := &in + *out = make(DockerConfigMap, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigMap. +func (in DockerConfigMap) DeepCopy() DockerConfigMap { + if in == nil { + return nil + } + out := new(DockerConfigMap) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec. +func (in *EnvironmentSpec) DeepCopy() *EnvironmentSpec { + if in == nil { + return nil + } + out := new(EnvironmentSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Parameter) DeepCopyInto(out *Parameter) { + *out = *in + if in.OptValues != nil { + in, out := &in.OptValues, &out.OptValues + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. +func (in *Parameter) DeepCopy() *Parameter { + if in == nil { + return nil + } + out := new(Parameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig. +func (in *ProxyConfig) DeepCopy() *ProxyConfig { + if in == nil { + return nil + } + out := new(ProxyConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iAutoScale) DeepCopyInto(out *S2iAutoScale) { + *out = *in + if in.InitReplicas != nil { + in, out := &in.InitReplicas, &out.InitReplicas + *out = new(int32) + **out = **in + } + if in.Containers != nil { + in, out := &in.Containers, &out.Containers + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iAutoScale. +func (in *S2iAutoScale) DeepCopy() *S2iAutoScale { + if in == nil { + return nil + } + out := new(S2iAutoScale) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *S2iBinary) DeepCopyInto(out *S2iBinary) { *out = *in @@ -31,7 +267,6 @@ func (in *S2iBinary) DeepCopyInto(out *S2iBinary) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinary. @@ -56,7 +291,7 @@ func (in *S2iBinary) DeepCopyObject() runtime.Object { func (in *S2iBinaryList) DeepCopyInto(out *S2iBinaryList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]S2iBinary, len(*in)) @@ -64,7 +299,6 @@ func (in *S2iBinaryList) DeepCopyInto(out *S2iBinaryList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinaryList. @@ -92,7 +326,6 @@ func (in *S2iBinarySpec) DeepCopyInto(out *S2iBinarySpec) { in, out := &in.UploadTimeStamp, &out.UploadTimeStamp *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinarySpec. @@ -108,7 +341,6 @@ func (in *S2iBinarySpec) DeepCopy() *S2iBinarySpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *S2iBinaryStatus) DeepCopyInto(out *S2iBinaryStatus) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBinaryStatus. @@ -120,3 +352,495 @@ func (in *S2iBinaryStatus) DeepCopy() *S2iBinaryStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuildResult) DeepCopyInto(out *S2iBuildResult) { + *out = *in + if in.ImageRepoTags != nil { + in, out := &in.ImageRepoTags, &out.ImageRepoTags + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildResult. +func (in *S2iBuildResult) DeepCopy() *S2iBuildResult { + if in == nil { + return nil + } + out := new(S2iBuildResult) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuildSource) DeepCopyInto(out *S2iBuildSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildSource. +func (in *S2iBuildSource) DeepCopy() *S2iBuildSource { + if in == nil { + return nil + } + out := new(S2iBuildSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuilder) DeepCopyInto(out *S2iBuilder) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilder. +func (in *S2iBuilder) DeepCopy() *S2iBuilder { + if in == nil { + return nil + } + out := new(S2iBuilder) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iBuilder) 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 *S2iBuilderList) DeepCopyInto(out *S2iBuilderList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]S2iBuilder, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderList. +func (in *S2iBuilderList) DeepCopy() *S2iBuilderList { + if in == nil { + return nil + } + out := new(S2iBuilderList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iBuilderList) 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 *S2iBuilderSpec) DeepCopyInto(out *S2iBuilderSpec) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = new(S2iConfig) + (*in).DeepCopyInto(*out) + } + if in.FromTemplate != nil { + in, out := &in.FromTemplate, &out.FromTemplate + *out = new(UserDefineTemplate) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderSpec. +func (in *S2iBuilderSpec) DeepCopy() *S2iBuilderSpec { + if in == nil { + return nil + } + out := new(S2iBuilderSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuilderStatus) DeepCopyInto(out *S2iBuilderStatus) { + *out = *in + if in.LastRunName != nil { + in, out := &in.LastRunName, &out.LastRunName + *out = new(string) + **out = **in + } + if in.LastRunStartTime != nil { + in, out := &in.LastRunStartTime, &out.LastRunStartTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderStatus. +func (in *S2iBuilderStatus) DeepCopy() *S2iBuilderStatus { + if in == nil { + return nil + } + out := new(S2iBuilderStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuilderTemplate) DeepCopyInto(out *S2iBuilderTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplate. +func (in *S2iBuilderTemplate) DeepCopy() *S2iBuilderTemplate { + if in == nil { + return nil + } + out := new(S2iBuilderTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iBuilderTemplate) 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 *S2iBuilderTemplateList) DeepCopyInto(out *S2iBuilderTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]S2iBuilderTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateList. +func (in *S2iBuilderTemplateList) DeepCopy() *S2iBuilderTemplateList { + if in == nil { + return nil + } + out := new(S2iBuilderTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iBuilderTemplateList) 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 *S2iBuilderTemplateSpec) DeepCopyInto(out *S2iBuilderTemplateSpec) { + *out = *in + if in.ContainerInfo != nil { + in, out := &in.ContainerInfo, &out.ContainerInfo + *out = make([]ContainerInfo, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]Parameter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateSpec. +func (in *S2iBuilderTemplateSpec) DeepCopy() *S2iBuilderTemplateSpec { + if in == nil { + return nil + } + out := new(S2iBuilderTemplateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iBuilderTemplateStatus) DeepCopyInto(out *S2iBuilderTemplateStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateStatus. +func (in *S2iBuilderTemplateStatus) DeepCopy() *S2iBuilderTemplateStatus { + if in == nil { + return nil + } + out := new(S2iBuilderTemplateStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iConfig) DeepCopyInto(out *S2iConfig) { + *out = *in + if in.RuntimeAuthentication != nil { + in, out := &in.RuntimeAuthentication, &out.RuntimeAuthentication + *out = new(AuthConfig) + (*in).DeepCopyInto(*out) + } + if in.RuntimeArtifacts != nil { + in, out := &in.RuntimeArtifacts, &out.RuntimeArtifacts + *out = make([]VolumeSpec, len(*in)) + copy(*out, *in) + } + if in.DockerConfig != nil { + in, out := &in.DockerConfig, &out.DockerConfig + *out = new(DockerConfig) + **out = **in + } + if in.PullAuthentication != nil { + in, out := &in.PullAuthentication, &out.PullAuthentication + *out = new(AuthConfig) + (*in).DeepCopyInto(*out) + } + if in.PushAuthentication != nil { + in, out := &in.PushAuthentication, &out.PushAuthentication + *out = new(AuthConfig) + (*in).DeepCopyInto(*out) + } + if in.IncrementalAuthentication != nil { + in, out := &in.IncrementalAuthentication, &out.IncrementalAuthentication + *out = new(AuthConfig) + (*in).DeepCopyInto(*out) + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = make([]EnvironmentSpec, len(*in)) + copy(*out, *in) + } + if in.Injections != nil { + in, out := &in.Injections, &out.Injections + *out = make([]VolumeSpec, len(*in)) + copy(*out, *in) + } + if in.CGroupLimits != nil { + in, out := &in.CGroupLimits, &out.CGroupLimits + *out = new(CGroupLimits) + **out = **in + } + if in.DropCapabilities != nil { + in, out := &in.DropCapabilities, &out.DropCapabilities + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ScriptDownloadProxyConfig != nil { + in, out := &in.ScriptDownloadProxyConfig, &out.ScriptDownloadProxyConfig + *out = new(ProxyConfig) + **out = **in + } + if in.BuildVolumes != nil { + in, out := &in.BuildVolumes, &out.BuildVolumes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.SecurityOpt != nil { + in, out := &in.SecurityOpt, &out.SecurityOpt + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.AddHost != nil { + in, out := &in.AddHost, &out.AddHost + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.GitSecretRef != nil { + in, out := &in.GitSecretRef, &out.GitSecretRef + *out = new(v1.LocalObjectReference) + **out = **in + } + if in.NodeAffinityValues != nil { + in, out := &in.NodeAffinityValues, &out.NodeAffinityValues + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iConfig. +func (in *S2iConfig) DeepCopy() *S2iConfig { + if in == nil { + return nil + } + out := new(S2iConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iRun) DeepCopyInto(out *S2iRun) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRun. +func (in *S2iRun) DeepCopy() *S2iRun { + if in == nil { + return nil + } + out := new(S2iRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iRun) 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 *S2iRunList) DeepCopyInto(out *S2iRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]S2iRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunList. +func (in *S2iRunList) DeepCopy() *S2iRunList { + if in == nil { + return nil + } + out := new(S2iRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *S2iRunList) 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 *S2iRunSpec) DeepCopyInto(out *S2iRunSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunSpec. +func (in *S2iRunSpec) DeepCopy() *S2iRunSpec { + if in == nil { + return nil + } + out := new(S2iRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *S2iRunStatus) DeepCopyInto(out *S2iRunStatus) { + *out = *in + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + if in.S2iBuildResult != nil { + in, out := &in.S2iBuildResult, &out.S2iBuildResult + *out = new(S2iBuildResult) + (*in).DeepCopyInto(*out) + } + if in.S2iBuildSource != nil { + in, out := &in.S2iBuildSource, &out.S2iBuildSource + *out = new(S2iBuildSource) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunStatus. +func (in *S2iRunStatus) DeepCopy() *S2iRunStatus { + if in == nil { + return nil + } + out := new(S2iRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserDefineTemplate) DeepCopyInto(out *UserDefineTemplate) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]Parameter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserDefineTemplate. +func (in *UserDefineTemplate) DeepCopy() *UserDefineTemplate { + if in == nil { + return nil + } + out := new(UserDefineTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSpec. +func (in *VolumeSpec) DeepCopy() *VolumeSpec { + if in == nil { + return nil + } + out := new(VolumeSpec) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/devops/v1alpha3/credential_types.go b/pkg/apis/devops/v1alpha3/credential_types.go new file mode 100644 index 000000000..707fe1323 --- /dev/null +++ b/pkg/apis/devops/v1alpha3/credential_types.go @@ -0,0 +1,56 @@ +package v1alpha3 + +import v1 "k8s.io/api/core/v1" + +/** +We use a special type of secret as a credential for DevOps. +This file will not contain CRD, but the credential type constants and their fields. +*/ +const ( + CredentialFinalizerName = "credential.finalizers.kubesphere.io" + DevOpsCredentialPrefix = "credential.devops.kubesphere.io/" + // SecretTypeBasicAuth contains data needed for basic authentication. + // + // Required at least one of fields: + // - Secret.Data["username"] - username used for authentication + // - Secret.Data["password"] - password or token needed for authentication + SecretTypeBasicAuth v1.SecretType = DevOpsCredentialPrefix + "basic-auth" + // BasicAuthUsernameKey is the key of the username for SecretTypeBasicAuth secrets + BasicAuthUsernameKey = "username" + // BasicAuthPasswordKey is the key of the password or token for SecretTypeBasicAuth secrets + BasicAuthPasswordKey = "password" + + // SecretTypeSSHAuth contains data needed for ssh authentication. + // + // Required at least one of fields: + // - Secret.Data["username"] - username used for authentication + // - Secret.Data["passphrase"] - passphrase needed for authentication + // - Secret.Data["privatekey"] - privatekey needed for authentication + SecretTypeSSHAuth v1.SecretType = DevOpsCredentialPrefix + "ssh-auth" + // SSHAuthUsernameKey is the key of the username for SecretTypeSSHAuth secrets + SSHAuthUsernameKey = "username" + // SSHAuthPrivateKey is the key of the passphrase for SecretTypeSSHAuth secrets + SSHAuthPassphraseKey = "passphrase" + // SSHAuthPrivateKey is the key of the privatekey for SecretTypeSSHAuth secrets + SSHAuthPrivateKey = "privatekey" + + // SecretTypeSecretText contains data. + // + // Required at least one of fields: + // - Secret.Data["secret"] - secret + SecretTypeSecretText v1.SecretType = DevOpsCredentialPrefix + "secret-text" + // SecretTextSecretKey is the key of the secret for SecretTypeSecretText secrets + SecretTextSecretKey = "secret" + + // SecretTypeKubeConfig contains data. + // + // Required at least one of fields: + // - Secret.Data["secret"] - secret + SecretTypeKubeConfig v1.SecretType = DevOpsCredentialPrefix + "kubeconfig" + // KubeConfigSecretKey is the key of the secret for SecretTypeKubeConfig secrets + KubeConfigSecretKey = "secret" + // CredentialAutoSyncAnnoKey is used to indicate whether the secret is automatically synchronized to devops. + // In the old version, the credential is stored in jenkins and cannot be obtained. + // This field is set to ensure that the secret is not overwritten by a nil value. + CredentialAutoSyncAnnoKey = DevOpsCredentialPrefix + "autosync" +) diff --git a/pkg/apis/devops/v1alpha3/devopsproject_types.go b/pkg/apis/devops/v1alpha3/devopsproject_types.go new file mode 100644 index 000000000..3a856251f --- /dev/null +++ b/pkg/apis/devops/v1alpha3/devopsproject_types.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +const DevOpsProjectFinalizerName = "devopsproject.finalizers.kubesphere.io" + +const ( + ResourceKindDevOpsProject = "DevOpsProject" + ResourceSingularDevOpsProject = "devopsproject" + ResourcePluralDevOpsProject = "devopsprojects" +) + +// DevOpsProjectSpec defines the desired state of DevOpsProject +type DevOpsProjectSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + +} + +// DevOpsProjectStatus defines the observed state of DevOpsProject +type DevOpsProjectStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + AdminNamespace string `json:"adminNamespace,omitempty"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// DevOpsProject is the Schema for the devopsprojects API +// +kubebuilder:resource:categories="devops",scope="Cluster" +// +k8s:openapi-gen=true +type DevOpsProject struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DevOpsProjectSpec `json:"spec,omitempty"` + Status DevOpsProjectStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// DevOpsProjectList contains a list of DevOpsProject +type DevOpsProjectList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []DevOpsProject `json:"items"` +} + +func init() { + SchemeBuilder.Register(&DevOpsProject{}, &DevOpsProjectList{}) +} diff --git a/pkg/apis/devops/v1alpha3/devopsproject_types_test.go b/pkg/apis/devops/v1alpha3/devopsproject_types_test.go new file mode 100644 index 000000000..d8a20e81b --- /dev/null +++ b/pkg/apis/devops/v1alpha3/devopsproject_types_test.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStorageDevOpsProject(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + } + created := &DevOpsProject{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &DevOpsProject{} + g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed()) + g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed()) +} diff --git a/pkg/apis/devops/v1alpha3/doc.go b/pkg/apis/devops/v1alpha3/doc.go new file mode 100644 index 000000000..b526ff90e --- /dev/null +++ b/pkg/apis/devops/v1alpha3/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 contains API Schema definitions for the devops v1alpha3 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/devops +// +k8s:defaulter-gen=TypeMeta +// +groupName=devops.kubesphere.io +package v1alpha3 diff --git a/pkg/apis/devops/v1alpha3/openapi_generated.go b/pkg/apis/devops/v1alpha3/openapi_generated.go new file mode 100644 index 000000000..e7189ecf9 --- /dev/null +++ b/pkg/apis/devops/v1alpha3/openapi_generated.go @@ -0,0 +1,3114 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The KubeSphere 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 openapi-gen. DO NOT EDIT. + +// This file was autogenerated by openapi-gen. Do not edit it manually! + +package v1alpha3 + +import ( + spec "github.com/go-openapi/spec" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + common "k8s.io/kube-openapi/pkg/common" +) + +func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { + return map[string]common.OpenAPIDefinition{ + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), + "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), + "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.BitbucketServerSource": schema_pkg_apis_devops_v1alpha3_BitbucketServerSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProject": schema_pkg_apis_devops_v1alpha3_DevOpsProject(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectList": schema_pkg_apis_devops_v1alpha3_DevOpsProjectList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectSpec": schema_pkg_apis_devops_v1alpha3_DevOpsProjectSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectStatus": schema_pkg_apis_devops_v1alpha3_DevOpsProjectStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscarderProperty": schema_pkg_apis_devops_v1alpha3_DiscarderProperty(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscoverPRFromForks": schema_pkg_apis_devops_v1alpha3_DiscoverPRFromForks(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption": schema_pkg_apis_devops_v1alpha3_GitCloneOption(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitSource": schema_pkg_apis_devops_v1alpha3_GitSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GithubSource": schema_pkg_apis_devops_v1alpha3_GithubSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchJobTrigger": schema_pkg_apis_devops_v1alpha3_MultiBranchJobTrigger(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchPipeline": schema_pkg_apis_devops_v1alpha3_MultiBranchPipeline(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.NoScmPipeline": schema_pkg_apis_devops_v1alpha3_NoScmPipeline(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Parameter": schema_pkg_apis_devops_v1alpha3_Parameter(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Pipeline": schema_pkg_apis_devops_v1alpha3_Pipeline(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineList": schema_pkg_apis_devops_v1alpha3_PipelineList(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineSpec": schema_pkg_apis_devops_v1alpha3_PipelineSpec(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineStatus": schema_pkg_apis_devops_v1alpha3_PipelineStatus(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.RemoteTrigger": schema_pkg_apis_devops_v1alpha3_RemoteTrigger(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SingleSvnSource": schema_pkg_apis_devops_v1alpha3_SingleSvnSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SvnSource": schema_pkg_apis_devops_v1alpha3_SvnSource(ref), + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.TimerTrigger": schema_pkg_apis_devops_v1alpha3_TimerTrigger(ref), + } +} + +func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIGroup contains the name, the supported versions, and the preferred version of a group.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the name of the group.", + Type: []string{"string"}, + Format: "", + }, + }, + "versions": { + SchemaProps: spec.SchemaProps{ + Description: "versions are the versions supported in this group.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + }, + }, + }, + }, + }, + "preferredVersion": { + SchemaProps: spec.SchemaProps{ + Description: "preferredVersion is the version preferred by the API server, which probably is the storage version.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), + }, + }, + "serverAddressByClientCIDRs": { + SchemaProps: spec.SchemaProps{ + Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + }, + }, + }, + }, + }, + }, + Required: []string{"name", "versions"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery", "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + } +} + +func schema_pkg_apis_meta_v1_APIGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "groups": { + SchemaProps: spec.SchemaProps{ + Description: "groups is a list of APIGroup.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"), + }, + }, + }, + }, + }, + }, + Required: []string{"groups"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"}, + } +} + +func schema_pkg_apis_meta_v1_APIResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIResource specifies the name of a resource and whether it is namespaced.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is the plural name of the resource.", + Type: []string{"string"}, + Format: "", + }, + }, + "singularName": { + SchemaProps: spec.SchemaProps{ + Description: "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", + Type: []string{"string"}, + Format: "", + }, + }, + "namespaced": { + SchemaProps: spec.SchemaProps{ + Description: "namespaced indicates if a resource is namespaced or not.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + Type: []string{"string"}, + Format: "", + }, + }, + "verbs": { + SchemaProps: spec.SchemaProps{ + Description: "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "shortNames": { + SchemaProps: spec.SchemaProps{ + Description: "shortNames is a list of suggested short names of the resource.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "categories": { + SchemaProps: spec.SchemaProps{ + Description: "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "storageVersionHash": { + SchemaProps: spec.SchemaProps{ + Description: "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "singularName", "namespaced", "kind", "verbs"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_APIResourceList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "groupVersion": { + SchemaProps: spec.SchemaProps{ + Description: "groupVersion is the group and version this APIResourceList is for.", + Type: []string{"string"}, + Format: "", + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "resources contains the name of the resources and if they are namespaced.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"), + }, + }, + }, + }, + }, + }, + Required: []string{"groupVersion", "resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"}, + } +} + +func schema_pkg_apis_meta_v1_APIVersions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "versions": { + SchemaProps: spec.SchemaProps{ + Description: "versions are the api versions that are available.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "serverAddressByClientCIDRs": { + SchemaProps: spec.SchemaProps{ + Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), + }, + }, + }, + }, + }, + }, + Required: []string{"versions", "serverAddressByClientCIDRs"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, + } +} + +func schema_pkg_apis_meta_v1_CreateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CreateOptions may be provided when creating an API object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DeleteOptions may be provided when deleting an API object.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "gracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "preconditions": { + SchemaProps: spec.SchemaProps{ + Description: "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"), + }, + }, + "orphanDependents": { + SchemaProps: spec.SchemaProps{ + Description: "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "propagationPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"}, + } +} + +func schema_pkg_apis_meta_v1_Duration(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Duration is a wrapper around time.Duration which supports correct marshaling to YAML and JSON. In particular, it marshals into strings, which can be used as map keys in json.", + Type: v1.Duration{}.OpenAPISchemaType(), + Format: v1.Duration{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ExportOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ExportOptions is the query options to the standard REST get call. Deprecated. Planned for removal in 1.18.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "export": { + SchemaProps: spec.SchemaProps{ + Description: "Should this value be exported. Export strips fields that a user can not specify. Deprecated. Planned for removal in 1.18.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "exact": { + SchemaProps: spec.SchemaProps{ + Description: "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'. Deprecated. Planned for removal in 1.18.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"export", "exact"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_FieldsV1(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GetOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GetOptions is the standard query options to the standard REST get call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "kind"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersion(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersion contains the \"group\" and the \"version\", which uniquely identifies the API.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "groupVersion": { + SchemaProps: spec.SchemaProps{ + Description: "groupVersion specifies the API group and version in the form \"group/version\"", + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"groupVersion", "version"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version", "kind"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_GroupVersionResource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "group": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "resource": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"group", "version", "resource"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_InternalEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "InternalEvent makes watch.Event versioned", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "Type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Object": { + SchemaProps: spec.SchemaProps{ + Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Bookmark: the object (instance of a type being watched) where\n only ResourceVersion field is set. On successful restart of watch from a\n bookmark resourceVersion, client is guaranteed to not get repeat event\n nor miss any events.\n * If Type is Error: *api.Status is recommended; other types may make sense\n depending on context.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.Object"), + }, + }, + }, + Required: []string{"Type", "Object"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.Object"}, + } +} + +func schema_pkg_apis_meta_v1_LabelSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "matchLabels": { + SchemaProps: spec.SchemaProps{ + Description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "matchExpressions": { + SchemaProps: spec.SchemaProps{ + Description: "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"}, + } +} + +func schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "key": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "key", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "key is the label key that the selector applies to.", + Type: []string{"string"}, + Format: "", + }, + }, + "operator": { + SchemaProps: spec.SchemaProps{ + Description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + Type: []string{"string"}, + Format: "", + }, + }, + "values": { + SchemaProps: spec.SchemaProps{ + Description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"key", "operator"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "List holds a list of objects, which may not be known by the server.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "List of objects", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_pkg_apis_meta_v1_ListMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "selfLink": { + SchemaProps: spec.SchemaProps{ + Description: "selfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Description: "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", + Type: []string{"string"}, + Format: "", + }, + }, + "remainingItemCount": { + SchemaProps: spec.SchemaProps{ + Description: "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ListOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ListOptions is the query options to a standard REST list call.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "labelSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldSelector": { + SchemaProps: spec.SchemaProps{ + Description: "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + Type: []string{"string"}, + Format: "", + }, + }, + "watch": { + SchemaProps: spec.SchemaProps{ + Description: "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "allowWatchBookmarks": { + SchemaProps: spec.SchemaProps{ + Description: "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeoutSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "limit": { + SchemaProps: spec.SchemaProps{ + Description: "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "continue": { + SchemaProps: spec.SchemaProps{ + Description: "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "manager": { + SchemaProps: spec.SchemaProps{ + Description: "Manager is an identifier of the workflow managing these fields.", + Type: []string{"string"}, + Format: "", + }, + }, + "operation": { + SchemaProps: spec.SchemaProps{ + Description: "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + Type: []string{"string"}, + Format: "", + }, + }, + "time": { + SchemaProps: spec.SchemaProps{ + Description: "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "fieldsType": { + SchemaProps: spec.SchemaProps{ + Description: "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + Type: []string{"string"}, + Format: "", + }, + }, + "fieldsV1": { + SchemaProps: spec.SchemaProps{ + Description: "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_meta_v1_MicroTime(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "MicroTime is version of Time with microsecond level precision.", + Type: v1.MicroTime{}.OpenAPISchemaType(), + Format: v1.MicroTime{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "generateName": { + SchemaProps: spec.SchemaProps{ + Description: "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Description: "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces", + Type: []string{"string"}, + Format: "", + }, + }, + "selfLink": { + SchemaProps: spec.SchemaProps{ + Description: "SelfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", + Type: []string{"string"}, + Format: "", + }, + }, + "generation": { + SchemaProps: spec.SchemaProps{ + Description: "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "creationTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "deletionTimestamp": { + SchemaProps: spec.SchemaProps{ + Description: "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "deletionGracePeriodSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "labels": { + SchemaProps: spec.SchemaProps{ + Description: "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "ownerReferences": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), + }, + }, + }, + }, + }, + "finalizers": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "clusterName": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.", + Type: []string{"string"}, + Format: "", + }, + }, + "managedFields": { + SchemaProps: spec.SchemaProps{ + Description: "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry", "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_meta_v1_OwnerReference(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "API version of the referent.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "controller": { + SchemaProps: spec.SchemaProps{ + Description: "If true, this reference points to the managing controller.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "blockOwnerDeletion": { + SchemaProps: spec.SchemaProps{ + Description: "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + Required: []string{"apiVersion", "kind", "name", "uid"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_PartialObjectMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PartialObjectMetadata is a generic representation of any object with ObjectMeta. It allows clients to get access to a particular ObjectMeta schema without knowing the details of the version.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PartialObjectMetadataList contains a list of objects containing only their metadata", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Description: "items contains each of the included items.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"}, + } +} + +func schema_pkg_apis_meta_v1_Patch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_PatchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "force": { + SchemaProps: spec.SchemaProps{ + Description: "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Preconditions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the target UID.", + Type: []string{"string"}, + Format: "", + }, + }, + "resourceVersion": { + SchemaProps: spec.SchemaProps{ + Description: "Specifies the target ResourceVersion", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_RootPaths(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RootPaths lists the paths available at root. For example: \"/healthz\", \"/apis\".", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "paths": { + SchemaProps: spec.SchemaProps{ + Description: "paths are the paths available at root.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + Required: []string{"paths"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "clientCIDR": { + SchemaProps: spec.SchemaProps{ + Description: "The CIDR with which clients can match their IP to figure out the server address that they should use.", + Type: []string{"string"}, + Format: "", + }, + }, + "serverAddress": { + SchemaProps: spec.SchemaProps{ + Description: "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"clientCIDR", "serverAddress"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Status is a return value for calls that don't return other objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the status of this operation.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", + Type: []string{"string"}, + Format: "", + }, + }, + "details": { + SchemaProps: spec.SchemaProps{ + Description: "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"), + }, + }, + "code": { + SchemaProps: spec.SchemaProps{ + Description: "Suggested HTTP return code for this status, 0 if not set.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"}, + } +} + +func schema_pkg_apis_meta_v1_StatusCause(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + Type: []string{"string"}, + Format: "", + }, + }, + "field": { + SchemaProps: spec.SchemaProps{ + Description: "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_StatusDetails(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + Type: []string{"string"}, + Format: "", + }, + }, + "group": { + SchemaProps: spec.SchemaProps{ + Description: "The group attribute of the resource associated with the status StatusReason.", + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "uid": { + SchemaProps: spec.SchemaProps{ + Description: "UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids", + Type: []string{"string"}, + Format: "", + }, + }, + "causes": { + SchemaProps: spec.SchemaProps{ + Description: "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"), + }, + }, + }, + }, + }, + "retryAfterSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"}, + } +} + +func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Table is a tabular representation of a set of API resources. The server transforms the object into a set of preferred columns for quickly reviewing the objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "columnDefinitions": { + SchemaProps: spec.SchemaProps{ + Description: "columnDefinitions describes each column in the returned items array. The number of cells per row will always match the number of column definitions.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition"), + }, + }, + }, + }, + }, + "rows": { + SchemaProps: spec.SchemaProps{ + Description: "rows is the list of items in the table.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"), + }, + }, + }, + }, + }, + }, + Required: []string{"columnDefinitions", "rows"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition", "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"}, + } +} + +func schema_pkg_apis_meta_v1_TableColumnDefinition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableColumnDefinition contains information about a column returned in the Table.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "name is a human readable name for the column.", + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Description: "type is an OpenAPI type definition for this column, such as number, integer, string, or array. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + Type: []string{"string"}, + Format: "", + }, + }, + "format": { + SchemaProps: spec.SchemaProps{ + Description: "format is an optional OpenAPI type modifier for this column. A format modifies the type and imposes additional rules, like date or time formatting for a string. The 'name' format is applied to the primary identifier column which has type 'string' to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Description: "description is a human readable description of this column.", + Type: []string{"string"}, + Format: "", + }, + }, + "priority": { + SchemaProps: spec.SchemaProps{ + Description: "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a higher priority.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"name", "type", "format", "description", "priority"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TableOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableOptions are used when a Table is requested by the caller.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "includeObject": { + SchemaProps: spec.SchemaProps{ + Description: "includeObject decides whether to include each object along with its columnar information. Specifying \"None\" will return no object, specifying \"Object\" will return the full object contents, and specifying \"Metadata\" (the default) will return the object's metadata in the PartialObjectMetadata kind in version v1beta1 of the meta.k8s.io API group.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableRow is an individual row in a table.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "cells": { + SchemaProps: spec.SchemaProps{ + Description: "cells will be as wide as the column definitions array and may contain strings, numbers (float64 or int64), booleans, simple maps, lists, or null. See the type field of the column definition for a more detailed description.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Format: "", + }, + }, + }, + }, + }, + "conditions": { + SchemaProps: spec.SchemaProps{ + Description: "conditions describe additional status of a row that are relevant for a human user. These conditions apply to the row, not to the object, and will be specific to table output. The only defined condition type is 'Completed', for a row that indicates a resource that has run to completion and can be given less visual priority.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition"), + }, + }, + }, + }, + }, + "object": { + SchemaProps: spec.SchemaProps{ + Description: "This field contains the requested additional information about each object based on the includeObject policy when requesting the Table. If \"None\", this field is empty, if \"Object\" this will be the default serialization of the object for the current API version, and if \"Metadata\" (the default) will contain the object metadata. Check the returned kind and apiVersion of the object before parsing. The media type of the object will always match the enclosing list - if this as a JSON table, these will be JSON encoded objects.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"cells"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_pkg_apis_meta_v1_TableRowCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TableRowCondition allows a row to be marked with additional information.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "Type of row condition. The only defined value is 'Completed' indicating that the object this row represents has reached a completed state and may be given less visual priority than other rows. Clients are not required to honor any conditions but should be consistent where possible about handling the conditions.", + Type: []string{"string"}, + Format: "", + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the condition, one of True, False, Unknown.", + Type: []string{"string"}, + Format: "", + }, + }, + "reason": { + SchemaProps: spec.SchemaProps{ + Description: "(brief) machine readable reason for the condition's last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + "message": { + SchemaProps: spec.SchemaProps{ + Description: "Human readable message indicating details about last transition.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"type", "status"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Time(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + Type: v1.Time{}.OpenAPISchemaType(), + Format: v1.Time{}.OpenAPISchemaFormat(), + }, + }, + } +} + +func schema_pkg_apis_meta_v1_Timestamp(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Timestamp is a struct that is equivalent to Time, but intended for protobuf marshalling/unmarshalling. It is generated into a serialization that matches Time. Do not use in Go structs.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "seconds": { + SchemaProps: spec.SchemaProps{ + Description: "Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "nanos": { + SchemaProps: spec.SchemaProps{ + Description: "Non-negative fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values that count forward in time. Must be from 0 to 999,999,999 inclusive. This field may be limited in precision depending on context.", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + Required: []string{"seconds", "nanos"}, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypeMeta describes an individual object in an API response or request with strings representing the type of the object and its API schema version. Structures that are versioned or persisted should inline TypeMeta.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_UpdateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "dryRun": { + SchemaProps: spec.SchemaProps{ + Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "fieldManager": { + SchemaProps: spec.SchemaProps{ + Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Event represents a single event to a watched resource.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "object": { + SchemaProps: spec.SchemaProps{ + Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context.", + Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), + }, + }, + }, + Required: []string{"type", "object"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/runtime.RawExtension"}, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", + Type: []string{"object"}, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this: type MyAwesomeAPIObject struct {\n runtime.TypeMeta `json:\",inline\"`\n ... // other fields\n} func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_k8sio_apimachinery_pkg_runtime_Unknown(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Unknown allows api objects with unknown types to be passed-through. This can be used to deal with the API objects from a plug-in. Unknown objects still have functioning TypeMeta features-- kind, version, etc. metadata and field mutatation.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "Raw": { + SchemaProps: spec.SchemaProps{ + Description: "Raw will hold the complete serialized object which couldn't be matched with a registered type. Most likely, nothing should be done with this except for passing it through the system.", + Type: []string{"string"}, + Format: "byte", + }, + }, + "ContentEncoding": { + SchemaProps: spec.SchemaProps{ + Description: "ContentEncoding is encoding used to encode 'Raw' data. Unspecified means no encoding.", + Type: []string{"string"}, + Format: "", + }, + }, + "ContentType": { + SchemaProps: spec.SchemaProps{ + Description: "ContentType is serialization method used to serialize 'Raw'. Unspecified means ContentTypeJSON.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"Raw", "ContentEncoding", "ContentType"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_BitbucketServerSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scm_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "owner": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "repo": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "credential_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "api_uri": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "discover_branches": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "discover_pr_from_origin": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "discover_pr_from_forks": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscoverPRFromForks"), + }, + }, + "git_clone_option": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"), + }, + }, + "regex_filter": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscoverPRFromForks", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_DevOpsProject(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DevOpsProject is the Schema for the devopsprojects API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProjectStatus"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_DevOpsProjectList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DevOpsProjectList contains a list of DevOpsProject", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProject"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DevOpsProject"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_DevOpsProjectSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DevOpsProjectSpec defines the desired state of DevOpsProject", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_DevOpsProjectStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "DevOpsProjectStatus defines the observed state of DevOpsProject", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "adminNamespace": { + SchemaProps: spec.SchemaProps{ + Description: "INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run \"make\" to regenerate code after modifying this file", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_DiscarderProperty(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "days_to_keep": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "num_to_keep": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_DiscoverPRFromForks(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "strategy": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "trust": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_GitCloneOption(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "shallow": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "depth": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_GitSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scm_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "url": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "credential_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "discover_branches": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "git_clone_option": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"), + }, + }, + "regex_filter": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_GithubSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scm_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "owner": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "repo": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "credential_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "api_uri": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "discover_branches": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "discover_pr_from_origin": { + SchemaProps: spec.SchemaProps{ + Type: []string{"integer"}, + Format: "int32", + }, + }, + "discover_pr_from_forks": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscoverPRFromForks"), + }, + }, + "git_clone_option": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"), + }, + }, + "regex_filter": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscoverPRFromForks", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitCloneOption"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_MultiBranchJobTrigger(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "create_action_job_to_trigger": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "delete_action_job_to_trigger": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_MultiBranchPipeline(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "descriptio": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "discarder": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscarderProperty"), + }, + }, + "timer_trigger": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.TimerTrigger"), + }, + }, + "source_type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "git_source": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitSource"), + }, + }, + "github_source": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GithubSource"), + }, + }, + "svn_source": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SvnSource"), + }, + }, + "single_svn_source": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SingleSvnSource"), + }, + }, + "bitbucket_server_source": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.BitbucketServerSource"), + }, + }, + "script_path": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "multibranch_job_trigger": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchJobTrigger"), + }, + }, + }, + Required: []string{"name", "source_type", "script_path"}, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.BitbucketServerSource", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscarderProperty", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GitSource", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.GithubSource", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchJobTrigger", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SingleSvnSource", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.SvnSource", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.TimerTrigger"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_NoScmPipeline(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "descriptio": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "discarder": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscarderProperty"), + }, + }, + "parameters": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Parameter"), + }, + }, + }, + }, + }, + "disable_concurrent": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, + "timer_trigger": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.TimerTrigger"), + }, + }, + "remote_trigger": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.RemoteTrigger"), + }, + }, + "jenkinsfile": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.DiscarderProperty", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Parameter", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.RemoteTrigger", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.TimerTrigger"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_Parameter(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "default_value": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "type": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "type"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_Pipeline(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "Pipeline is the Schema for the pipelines API", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineSpec", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.PipelineStatus"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_PipelineList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineList contains a list of Pipeline", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Pipeline"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.Pipeline"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_PipelineSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineSpec defines the desired state of Pipeline", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "type": { + SchemaProps: spec.SchemaProps{ + Description: "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", + Type: []string{"string"}, + Format: "", + }, + }, + "pipeline": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.NoScmPipeline"), + }, + }, + "multi_branch_pipeline": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchPipeline"), + }, + }, + }, + Required: []string{"type"}, + }, + }, + Dependencies: []string{ + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.MultiBranchPipeline", "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3.NoScmPipeline"}, + } +} + +func schema_pkg_apis_devops_v1alpha3_PipelineStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PipelineStatus defines the observed state of Pipeline", + Type: []string{"object"}, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_RemoteTrigger(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "token": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_SingleSvnSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scm_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "remote": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "credential_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_SvnSource(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "scm_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "remote": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "credential_id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "includes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "excludes": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_devops_v1alpha3_TimerTrigger(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "cron": { + SchemaProps: spec.SchemaProps{ + Description: "user in no scm job", + Type: []string{"string"}, + Format: "", + }, + }, + "interval": { + SchemaProps: spec.SchemaProps{ + Description: "use in multi-branch job", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} diff --git a/pkg/apis/devops/v1alpha3/pipeline_types.go b/pkg/apis/devops/v1alpha3/pipeline_types.go new file mode 100644 index 000000000..34ea5161c --- /dev/null +++ b/pkg/apis/devops/v1alpha3/pipeline_types.go @@ -0,0 +1,192 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +const PipelineFinalizerName = "pipeline.finalizers.kubesphere.io" + +const ( + ResourceKindPipeline = "Pipeline" + ResourceSingularPipeline = "pipeline" + ResourcePluralPipeline = "pipelines" +) + +// PipelineSpec defines the desired state of Pipeline +type PipelineSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + Type string `json:"type" description:"type of devops pipeline, in scm or no scm"` + Pipeline *NoScmPipeline `json:"pipeline,omitempty" description:"no scm pipeline structs"` + MultiBranchPipeline *MultiBranchPipeline `json:"multi_branch_pipeline,omitempty" description:"in scm pipeline structs"` +} + +// PipelineStatus defines the observed state of Pipeline +type PipelineStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Pipeline is the Schema for the pipelines API +// +k8s:openapi-gen=true +type Pipeline struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PipelineSpec `json:"spec,omitempty"` + Status PipelineStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PipelineList contains a list of Pipeline +type PipelineList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Pipeline `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Pipeline{}, &PipelineList{}) +} + +const ( + NoScmPipelineType = "pipeline" + MultiBranchPipelineType = "multi-branch-pipeline" +) + +type NoScmPipeline struct { + Name string `json:"name" description:"name of pipeline"` + Description string `json:"descriptio,omitempty" description:"description of pipeline"` + Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"` + Parameters []Parameter `json:"parameters,omitempty" description:"Parameters define of pipeline,user could pass param when run pipeline"` + DisableConcurrent bool `json:"disable_concurrent,omitempty" mapstructure:"disable_concurrent" description:"Whether to prohibit the pipeline from running in parallel"` + TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"` + RemoteTrigger *RemoteTrigger `json:"remote_trigger,omitempty" mapstructure:"remote_trigger" description:"Remote api define to trigger pipeline run"` + Jenkinsfile string `json:"jenkinsfile,omitempty" description:"Jenkinsfile's content'"` +} + +type MultiBranchPipeline struct { + Name string `json:"name" description:"name of pipeline"` + Description string `json:"descriptio,omitempty" description:"description of pipeline"` + Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"` + TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"` + SourceType string `json:"source_type" description:"type of scm, such as github/git/svn"` + GitSource *GitSource `json:"git_source,omitempty" description:"git scm define"` + GitHubSource *GithubSource `json:"github_source,omitempty" description:"github scm define"` + SvnSource *SvnSource `json:"svn_source,omitempty" description:"multi branch svn scm define"` + SingleSvnSource *SingleSvnSource `json:"single_svn_source,omitempty" description:"single branch svn scm define"` + BitbucketServerSource *BitbucketServerSource `json:"bitbucket_server_source,omitempty" description:"bitbucket server scm defile"` + ScriptPath string `json:"script_path" mapstructure:"script_path" description:"script path in scm"` + MultiBranchJobTrigger *MultiBranchJobTrigger `json:"multibranch_job_trigger,omitempty" mapstructure:"multibranch_job_trigger" description:"Pipeline tasks that need to be triggered when branch creation/deletion"` +} + +type GitSource struct { + ScmId string `json:"scm_id,omitempty" description:"uid of scm"` + Url string `json:"url,omitempty" mapstructure:"url" description:"url of git source"` + CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access git source"` + DiscoverBranches bool `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Whether to discover a branch"` + CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` + RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` +} + +type GithubSource struct { + ScmId string `json:"scm_id,omitempty" description:"uid of scm"` + Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"` + Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"` + CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"` + ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"` + DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"` + DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"` + DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"` + CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` + RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` +} + +type MultiBranchJobTrigger struct { + CreateActionJobsToTrigger string `json:"create_action_job_to_trigger,omitempty" description:"pipeline name to trigger"` + DeleteActionJobsToTrigger string `json:"delete_action_job_to_trigger,omitempty" description:"pipeline name to trigger"` +} + +type BitbucketServerSource struct { + ScmId string `json:"scm_id,omitempty" description:"uid of scm"` + Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"` + Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"` + CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"` + ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"` + DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"` + DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"` + DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"` + CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` + RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` +} + +type GitCloneOption struct { + Shallow bool `json:"shallow,omitempty" mapstructure:"shallow" description:"Whether to use git shallow clone"` + Timeout int `json:"timeout,omitempty" mapstructure:"timeout" description:"git clone timeout mins"` + Depth int `json:"depth,omitempty" mapstructure:"depth" description:"git clone depth"` +} + +type SvnSource struct { + ScmId string `json:"scm_id,omitempty" description:"uid of scm"` + Remote string `json:"remote,omitempty" description:"remote address url"` + CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"` + Includes string `json:"includes,omitempty" description:"branches to run pipeline"` + Excludes string `json:"excludes,omitempty" description:"branches do not run pipeline"` +} +type SingleSvnSource struct { + ScmId string `json:"scm_id,omitempty" description:"uid of scm"` + Remote string `json:"remote,omitempty" description:"remote address url"` + CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"` +} + +type DiscoverPRFromForks struct { + Strategy int `json:"strategy,omitempty" mapstructure:"strategy" description:"github discover strategy"` + Trust int `json:"trust,omitempty" mapstructure:"trust" description:"trust user type"` +} + +type DiscarderProperty struct { + DaysToKeep string `json:"days_to_keep,omitempty" mapstructure:"days_to_keep" description:"days to keep pipeline"` + NumToKeep string `json:"num_to_keep,omitempty" mapstructure:"num_to_keep" description:"nums to keep pipeline"` +} + +type Parameter struct { + Name string `json:"name" description:"name of param"` + DefaultValue string `json:"default_value,omitempty" mapstructure:"default_value" description:"default value of param"` + Type string `json:"type" description:"type of param"` + Description string `json:"description,omitempty" description:"description of pipeline"` +} + +type TimerTrigger struct { + // user in no scm job + Cron string `json:"cron,omitempty" description:"jenkins cron script"` + + // use in multi-branch job + Interval string `json:"interval,omitempty" description:"interval ms"` +} + +type RemoteTrigger struct { + Token string `json:"token,omitempty" description:"remote trigger token"` +} diff --git a/pkg/apis/devops/v1alpha3/pipeline_types_test.go b/pkg/apis/devops/v1alpha3/pipeline_types_test.go new file mode 100644 index 000000000..f8c435fe6 --- /dev/null +++ b/pkg/apis/devops/v1alpha3/pipeline_types_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStoragePipeline(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + Namespace: "default", + } + created := &Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: "default", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &Pipeline{} + g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed()) + g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed()) +} diff --git a/pkg/apis/devops/v1alpha3/register.go b/pkg/apis/devops/v1alpha3/register.go new file mode 100644 index 000000000..51adad3c1 --- /dev/null +++ b/pkg/apis/devops/v1alpha3/register.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha3 contains API Schema definitions for the devops v1alpha3 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/devops +// +k8s:defaulter-gen=TypeMeta +// +groupName=devops.kubesphere.io +package v1alpha3 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "devops.kubesphere.io", Version: "v1alpha3"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme is required by pkg/client/... + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/apis/devops/v1alpha3/v1alpha3_suite_test.go b/pkg/apis/devops/v1alpha3/v1alpha3_suite_test.go new file mode 100644 index 000000000..00c341a74 --- /dev/null +++ b/pkg/apis/devops/v1alpha3/v1alpha3_suite_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var cfg *rest.Config +var c client.Client + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} diff --git a/pkg/apis/devops/v1alpha3/zz_generated.deepcopy.go b/pkg/apis/devops/v1alpha3/zz_generated.deepcopy.go new file mode 100644 index 000000000..3f7dd8c4f --- /dev/null +++ b/pkg/apis/devops/v1alpha3/zz_generated.deepcopy.go @@ -0,0 +1,508 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The KubeSphere 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 controller-gen. DO NOT EDIT. + +package v1alpha3 + +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 *BitbucketServerSource) DeepCopyInto(out *BitbucketServerSource) { + *out = *in + if in.DiscoverPRFromForks != nil { + in, out := &in.DiscoverPRFromForks, &out.DiscoverPRFromForks + *out = new(DiscoverPRFromForks) + **out = **in + } + if in.CloneOption != nil { + in, out := &in.CloneOption, &out.CloneOption + *out = new(GitCloneOption) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitbucketServerSource. +func (in *BitbucketServerSource) DeepCopy() *BitbucketServerSource { + if in == nil { + return nil + } + out := new(BitbucketServerSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevOpsProject) DeepCopyInto(out *DevOpsProject) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevOpsProject. +func (in *DevOpsProject) DeepCopy() *DevOpsProject { + if in == nil { + return nil + } + out := new(DevOpsProject) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevOpsProject) 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 *DevOpsProjectList) DeepCopyInto(out *DevOpsProjectList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]DevOpsProject, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevOpsProjectList. +func (in *DevOpsProjectList) DeepCopy() *DevOpsProjectList { + if in == nil { + return nil + } + out := new(DevOpsProjectList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *DevOpsProjectList) 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 *DevOpsProjectSpec) DeepCopyInto(out *DevOpsProjectSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevOpsProjectSpec. +func (in *DevOpsProjectSpec) DeepCopy() *DevOpsProjectSpec { + if in == nil { + return nil + } + out := new(DevOpsProjectSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DevOpsProjectStatus) DeepCopyInto(out *DevOpsProjectStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevOpsProjectStatus. +func (in *DevOpsProjectStatus) DeepCopy() *DevOpsProjectStatus { + if in == nil { + return nil + } + out := new(DevOpsProjectStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscarderProperty) DeepCopyInto(out *DiscarderProperty) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscarderProperty. +func (in *DiscarderProperty) DeepCopy() *DiscarderProperty { + if in == nil { + return nil + } + out := new(DiscarderProperty) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DiscoverPRFromForks) DeepCopyInto(out *DiscoverPRFromForks) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoverPRFromForks. +func (in *DiscoverPRFromForks) DeepCopy() *DiscoverPRFromForks { + if in == nil { + return nil + } + out := new(DiscoverPRFromForks) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitCloneOption) DeepCopyInto(out *GitCloneOption) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitCloneOption. +func (in *GitCloneOption) DeepCopy() *GitCloneOption { + if in == nil { + return nil + } + out := new(GitCloneOption) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GitSource) DeepCopyInto(out *GitSource) { + *out = *in + if in.CloneOption != nil { + in, out := &in.CloneOption, &out.CloneOption + *out = new(GitCloneOption) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GitSource. +func (in *GitSource) DeepCopy() *GitSource { + if in == nil { + return nil + } + out := new(GitSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GithubSource) DeepCopyInto(out *GithubSource) { + *out = *in + if in.DiscoverPRFromForks != nil { + in, out := &in.DiscoverPRFromForks, &out.DiscoverPRFromForks + *out = new(DiscoverPRFromForks) + **out = **in + } + if in.CloneOption != nil { + in, out := &in.CloneOption, &out.CloneOption + *out = new(GitCloneOption) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubSource. +func (in *GithubSource) DeepCopy() *GithubSource { + if in == nil { + return nil + } + out := new(GithubSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MultiBranchJobTrigger) DeepCopyInto(out *MultiBranchJobTrigger) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiBranchJobTrigger. +func (in *MultiBranchJobTrigger) DeepCopy() *MultiBranchJobTrigger { + if in == nil { + return nil + } + out := new(MultiBranchJobTrigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MultiBranchPipeline) DeepCopyInto(out *MultiBranchPipeline) { + *out = *in + if in.Discarder != nil { + in, out := &in.Discarder, &out.Discarder + *out = new(DiscarderProperty) + **out = **in + } + if in.TimerTrigger != nil { + in, out := &in.TimerTrigger, &out.TimerTrigger + *out = new(TimerTrigger) + **out = **in + } + if in.GitSource != nil { + in, out := &in.GitSource, &out.GitSource + *out = new(GitSource) + (*in).DeepCopyInto(*out) + } + if in.GitHubSource != nil { + in, out := &in.GitHubSource, &out.GitHubSource + *out = new(GithubSource) + (*in).DeepCopyInto(*out) + } + if in.SvnSource != nil { + in, out := &in.SvnSource, &out.SvnSource + *out = new(SvnSource) + **out = **in + } + if in.SingleSvnSource != nil { + in, out := &in.SingleSvnSource, &out.SingleSvnSource + *out = new(SingleSvnSource) + **out = **in + } + if in.BitbucketServerSource != nil { + in, out := &in.BitbucketServerSource, &out.BitbucketServerSource + *out = new(BitbucketServerSource) + (*in).DeepCopyInto(*out) + } + if in.MultiBranchJobTrigger != nil { + in, out := &in.MultiBranchJobTrigger, &out.MultiBranchJobTrigger + *out = new(MultiBranchJobTrigger) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MultiBranchPipeline. +func (in *MultiBranchPipeline) DeepCopy() *MultiBranchPipeline { + if in == nil { + return nil + } + out := new(MultiBranchPipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NoScmPipeline) DeepCopyInto(out *NoScmPipeline) { + *out = *in + if in.Discarder != nil { + in, out := &in.Discarder, &out.Discarder + *out = new(DiscarderProperty) + **out = **in + } + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]Parameter, len(*in)) + copy(*out, *in) + } + if in.TimerTrigger != nil { + in, out := &in.TimerTrigger, &out.TimerTrigger + *out = new(TimerTrigger) + **out = **in + } + if in.RemoteTrigger != nil { + in, out := &in.RemoteTrigger, &out.RemoteTrigger + *out = new(RemoteTrigger) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NoScmPipeline. +func (in *NoScmPipeline) DeepCopy() *NoScmPipeline { + if in == nil { + return nil + } + out := new(NoScmPipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Parameter) DeepCopyInto(out *Parameter) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. +func (in *Parameter) DeepCopy() *Parameter { + if in == nil { + return nil + } + out := new(Parameter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Pipeline) DeepCopyInto(out *Pipeline) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pipeline. +func (in *Pipeline) DeepCopy() *Pipeline { + if in == nil { + return nil + } + out := new(Pipeline) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Pipeline) 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 *PipelineList) DeepCopyInto(out *PipelineList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Pipeline, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineList. +func (in *PipelineList) DeepCopy() *PipelineList { + if in == nil { + return nil + } + out := new(PipelineList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PipelineList) 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 *PipelineSpec) DeepCopyInto(out *PipelineSpec) { + *out = *in + if in.Pipeline != nil { + in, out := &in.Pipeline, &out.Pipeline + *out = new(NoScmPipeline) + (*in).DeepCopyInto(*out) + } + if in.MultiBranchPipeline != nil { + in, out := &in.MultiBranchPipeline, &out.MultiBranchPipeline + *out = new(MultiBranchPipeline) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineSpec. +func (in *PipelineSpec) DeepCopy() *PipelineSpec { + if in == nil { + return nil + } + out := new(PipelineSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PipelineStatus) DeepCopyInto(out *PipelineStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PipelineStatus. +func (in *PipelineStatus) DeepCopy() *PipelineStatus { + if in == nil { + return nil + } + out := new(PipelineStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteTrigger) DeepCopyInto(out *RemoteTrigger) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteTrigger. +func (in *RemoteTrigger) DeepCopy() *RemoteTrigger { + if in == nil { + return nil + } + out := new(RemoteTrigger) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SingleSvnSource) DeepCopyInto(out *SingleSvnSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SingleSvnSource. +func (in *SingleSvnSource) DeepCopy() *SingleSvnSource { + if in == nil { + return nil + } + out := new(SingleSvnSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SvnSource) DeepCopyInto(out *SvnSource) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SvnSource. +func (in *SvnSource) DeepCopy() *SvnSource { + if in == nil { + return nil + } + out := new(SvnSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TimerTrigger) DeepCopyInto(out *TimerTrigger) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimerTrigger. +func (in *TimerTrigger) DeepCopy() *TimerTrigger { + if in == nil { + return nil + } + out := new(TimerTrigger) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/kapis/iam/group.go b/pkg/apis/iam/group.go similarity index 100% rename from pkg/kapis/iam/group.go rename to pkg/apis/iam/group.go diff --git a/pkg/apis/iam/v1alpha2/doc.go b/pkg/apis/iam/v1alpha2/doc.go new file mode 100644 index 000000000..5ddef225c --- /dev/null +++ b/pkg/apis/iam/v1alpha2/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha2 contains API Schema definitions for the iam v1alpha2 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/iam +// +k8s:defaulter-gen=TypeMeta +// +groupName=iam.kubesphere.io +package v1alpha2 diff --git a/pkg/apis/iam/v1alpha2/register.go b/pkg/apis/iam/v1alpha2/register.go new file mode 100644 index 000000000..ddb23db84 --- /dev/null +++ b/pkg/apis/iam/v1alpha2/register.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha2 contains API Schema definitions for the iam v1alpha2 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=kubesphere.io/kubesphere/pkg/apis/iam +// +k8s:defaulter-gen=TypeMeta +// +groupName=iam.kubesphere.io +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "iam.kubesphere.io", Version: "v1alpha2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme is required by pkg/client/... + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource is required by pkg/client/listers/... +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// Adds the list of known types to the given scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &User{}, + &UserList{}, + &GlobalRole{}, + &GlobalRoleList{}, + &GlobalRoleBinding{}, + &GlobalRoleBindingList{}, + &WorkspaceRole{}, + &WorkspaceRoleList{}, + &WorkspaceRoleBinding{}, + &WorkspaceRoleBindingList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/iam/v1alpha2/types.go b/pkg/apis/iam/v1alpha2/types.go new file mode 100644 index 000000000..20267a7d2 --- /dev/null +++ b/pkg/apis/iam/v1alpha2/types.go @@ -0,0 +1,293 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha2 + +import ( + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ResourceKindUser = "User" + ResourcesSingularUser = "user" + ResourcesPluralUser = "users" + ResourceKindGlobalRoleBinding = "GlobalRoleBinding" + ResourcesSingularGlobalRoleBinding = "globalrolebinding" + ResourcesPluralGlobalRoleBinding = "globalrolebindings" + ResourceKindGlobalRole = "GlobalRole" + ResourcesSingularGlobalRole = "globalrole" + ResourcesPluralGlobalRole = "globalroles" + ResourceKindWorkspaceRoleBinding = "WorkspaceRoleBinding" + ResourcesSingularWorkspaceRoleBinding = "workspacerolebinding" + ResourcesPluralWorkspaceRoleBinding = "workspacerolebindings" + ResourceKindWorkspaceRole = "WorkspaceRole" + ResourcesSingularWorkspaceRole = "workspacerole" + ResourcesPluralWorkspaceRole = "workspaceroles" + ResourceKindClusterRole = "ClusterRole" + ResourcesSingularClusterRole = "clusterrole" + ResourcesPluralClusterRole = "clusterroles" + ResourceKindRole = "Role" + ResourcesSingularRole = "role" + ResourcesPluralRole = "roles" + RegoOverrideAnnotation = "iam.kubesphere.io/rego-override" + GlobalScope = "Global" + ClusterScope = "Cluster" + WorkspaceScope = "Workspace" + NamespaceScope = "Namespace" +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true + +// User is the Schema for the users API +// +kubebuilder:printcolumn:name="Email",type="string",JSONPath=".spec.email" +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.state" +// +kubebuilder:resource:categories="iam",scope="Cluster" +type User struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec UserSpec `json:"spec"` + // +optional + Status UserStatus `json:"status,omitempty"` +} + +type FinalizerName string + +// UserSpec defines the desired state of User +type UserSpec struct { + // Unique email address. + Email string `json:"email"` + // The preferred written or spoken language for the user. + // +optional + Lang string `json:"lang,omitempty"` + // Description of the user. + // +optional + Description string `json:"description,omitempty"` + // +optional + DisplayName string `json:"displayName,omitempty"` + // +optional + Groups []string `json:"groups,omitempty"` + // password will be encrypted by mutating admission webhook + EncryptedPassword string `json:"password"` + // Finalizers is an opaque list of values that must be empty to permanently remove object from storage. + // +optional + Finalizers []FinalizerName `json:"finalizers,omitempty"` +} + +type UserState string + +// These are the valid phases of a user. +const ( + // UserActive means the user is available. + UserActive UserState = "Active" + // UserDisabled means the user is disabled. + UserDisabled UserState = "Disabled" +) + +// UserStatus defines the observed state of User +type UserStatus struct { + // The user status + // +optional + State UserState `json:"state,omitempty"` + + // Represents the latest available observations of a namespace's current state. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions []UserCondition `json:"conditions,omitempty"` +} + +type UserCondition struct { + // Type of namespace controller condition. + Type UserConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status ConditionStatus `json:"status"` + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // +optional + Reason string `json:"reason,omitempty"` + // +optional + Message string `json:"message,omitempty"` +} + +type UserConditionType string + +// These are valid conditions of a user. +const ( + // UserLoginFailure contains information about user login. + LoginFailure UserConditionType = "LoginFailure" +) + +type ConditionStatus string + +// These are valid condition statuses. "ConditionTrue" means a resource is in the condition. +// "ConditionFalse" means a resource is not in the condition. "ConditionUnknown" means kubernetes +// can't decide if a resource is in the condition or not. In the future, we could add other +// intermediate conditions, e.g. ConditionDegraded. +const ( + ConditionTrue ConditionStatus = "True" + ConditionFalse ConditionStatus = "False" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// UserList contains a list of User +type UserList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []User `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// +kubebuilder:resource:categories="iam",scope="Cluster" +type GlobalRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + + // AggregationRule is an optional field that describes how to build the Rules for this GlobalRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` +} + +// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole +type AggregationRule struct { + // ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. + // If any of the selectors match, then the ClusterRole's permissions will be added + // +optional + RoleSelectors []metav1.LabelSelector `json:"roleSelectors,omitempty" protobuf:"bytes,1,rep,name=roleSelectors"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleList contains a list of GlobalRole +type GlobalRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalRole `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleBinding is the Schema for the globalrolebindings API +// +kubebuilder:resource:categories="iam",scope="Cluster" +type GlobalRoleBinding struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Subjects holds references to the objects the role applies to. + // +optional + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GlobalRoleBindingList contains a list of GlobalRoleBinding +type GlobalRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []GlobalRoleBinding `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" +// +kubebuilder:printcolumn:name="Alias",type="string",JSONPath=".metadata.labels.kubesphere\\.io/alias-name" +// +kubebuilder:resource:categories="iam",scope="Cluster" +type WorkspaceRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Rules holds all the PolicyRules for this ClusterRole + Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` + // AggregationRule is an optional field that describes how to build the Rules for this WorkspaceRole. + // If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be + // stomped by the controller. + AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleList contains a list of WorkspaceRole +type WorkspaceRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WorkspaceRole `json:"items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleBinding is the Schema for the workspacerolebindings API +// +kubebuilder:printcolumn:name="Workspace",type="string",JSONPath=".metadata.labels.kubesphere\\.io/workspace" +// +kubebuilder:resource:categories="iam",scope="Cluster" +type WorkspaceRoleBinding struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Subjects holds references to the objects the role applies to. + // +optional + Subjects []rbacv1.Subject `json:"subjects,omitempty" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef rbacv1.RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// WorkspaceRoleBindingList contains a list of WorkspaceRoleBinding +type WorkspaceRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WorkspaceRoleBinding `json:"items"` +} + +type UserDetail struct { + *User + GlobalRole *GlobalRole `json:"globalRole"` +} diff --git a/pkg/apis/iam/v1alpha2/types_test.go b/pkg/apis/iam/v1alpha2/types_test.go new file mode 100644 index 000000000..a47667ef7 --- /dev/null +++ b/pkg/apis/iam/v1alpha2/types_test.go @@ -0,0 +1,58 @@ +/* + * + * Copyright 2020 The KubeSphere 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 v1alpha2 + +import ( + "testing" + + "github.com/onsi/gomega" + "golang.org/x/net/context" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func TestStorageUser(t *testing.T) { + key := types.NamespacedName{ + Name: "foo", + } + created := &User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }} + g := gomega.NewGomegaWithT(t) + + // Test Create + fetched := &User{} + g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(created)) + + // Test Updating the Labels + updated := fetched.DeepCopy() + updated.Labels = map[string]string{"hello": "world"} + g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed()) + + g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) + g.Expect(fetched).To(gomega.Equal(updated)) + + // Test Delete + g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed()) + g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed()) +} diff --git a/pkg/apis/iam/v1alpha2/v1alpha2_suite_test.go b/pkg/apis/iam/v1alpha2/v1alpha2_suite_test.go new file mode 100644 index 000000000..17192bd5f --- /dev/null +++ b/pkg/apis/iam/v1alpha2/v1alpha2_suite_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The KubeSphere 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 v1alpha2 + +import ( + "log" + "os" + "path/filepath" + "testing" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var cfg *rest.Config +var c client.Client + +func TestMain(m *testing.M) { + t := &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, + } + + err := SchemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + log.Fatal(err) + } + + if cfg, err = t.Start(); err != nil { + log.Fatal(err) + } + + if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { + log.Fatal(err) + } + + code := m.Run() + t.Stop() + os.Exit(code) +} diff --git a/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 000000000..8b27972de --- /dev/null +++ b/pkg/apis/iam/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,460 @@ +// +build !ignore_autogenerated + +/* +Copyright 2019 The KubeSphere 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 controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AggregationRule) DeepCopyInto(out *AggregationRule) { + *out = *in + if in.RoleSelectors != nil { + in, out := &in.RoleSelectors, &out.RoleSelectors + *out = make([]metav1.LabelSelector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule. +func (in *AggregationRule) DeepCopy() *AggregationRule { + if in == nil { + return nil + } + out := new(AggregationRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlobalRole) DeepCopyInto(out *GlobalRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRole. +func (in *GlobalRole) DeepCopy() *GlobalRole { + if in == nil { + return nil + } + out := new(GlobalRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalRole) 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 *GlobalRoleBinding) DeepCopyInto(out *GlobalRoleBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]v1.Subject, len(*in)) + copy(*out, *in) + } + out.RoleRef = in.RoleRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBinding. +func (in *GlobalRoleBinding) DeepCopy() *GlobalRoleBinding { + if in == nil { + return nil + } + out := new(GlobalRoleBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalRoleBinding) 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 *GlobalRoleBindingList) DeepCopyInto(out *GlobalRoleBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GlobalRoleBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleBindingList. +func (in *GlobalRoleBindingList) DeepCopy() *GlobalRoleBindingList { + if in == nil { + return nil + } + out := new(GlobalRoleBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalRoleBindingList) 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 *GlobalRoleList) DeepCopyInto(out *GlobalRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GlobalRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalRoleList. +func (in *GlobalRoleList) DeepCopy() *GlobalRoleList { + if in == nil { + return nil + } + out := new(GlobalRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GlobalRoleList) 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 *User) DeepCopyInto(out *User) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new User. +func (in *User) DeepCopy() *User { + if in == nil { + return nil + } + out := new(User) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *User) 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 *UserCondition) DeepCopyInto(out *UserCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserCondition. +func (in *UserCondition) DeepCopy() *UserCondition { + if in == nil { + return nil + } + out := new(UserCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserDetail) DeepCopyInto(out *UserDetail) { + *out = *in + if in.User != nil { + in, out := &in.User, &out.User + *out = new(User) + (*in).DeepCopyInto(*out) + } + if in.GlobalRole != nil { + in, out := &in.GlobalRole, &out.GlobalRole + *out = new(GlobalRole) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserDetail. +func (in *UserDetail) DeepCopy() *UserDetail { + if in == nil { + return nil + } + out := new(UserDetail) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserList) DeepCopyInto(out *UserList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]User, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserList. +func (in *UserList) DeepCopy() *UserList { + if in == nil { + return nil + } + out := new(UserList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *UserList) 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 *UserSpec) DeepCopyInto(out *UserSpec) { + *out = *in + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Finalizers != nil { + in, out := &in.Finalizers, &out.Finalizers + *out = make([]FinalizerName, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserSpec. +func (in *UserSpec) DeepCopy() *UserSpec { + if in == nil { + return nil + } + out := new(UserSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UserStatus) DeepCopyInto(out *UserStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]UserCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserStatus. +func (in *UserStatus) DeepCopy() *UserStatus { + if in == nil { + return nil + } + out := new(UserStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceRole) DeepCopyInto(out *WorkspaceRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]v1.PolicyRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AggregationRule != nil { + in, out := &in.AggregationRule, &out.AggregationRule + *out = new(AggregationRule) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRole. +func (in *WorkspaceRole) DeepCopy() *WorkspaceRole { + if in == nil { + return nil + } + out := new(WorkspaceRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRole) 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 *WorkspaceRoleBinding) DeepCopyInto(out *WorkspaceRoleBinding) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Subjects != nil { + in, out := &in.Subjects, &out.Subjects + *out = make([]v1.Subject, len(*in)) + copy(*out, *in) + } + out.RoleRef = in.RoleRef +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBinding. +func (in *WorkspaceRoleBinding) DeepCopy() *WorkspaceRoleBinding { + if in == nil { + return nil + } + out := new(WorkspaceRoleBinding) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBinding) 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 *WorkspaceRoleBindingList) DeepCopyInto(out *WorkspaceRoleBindingList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRoleBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleBindingList. +func (in *WorkspaceRoleBindingList) DeepCopy() *WorkspaceRoleBindingList { + if in == nil { + return nil + } + out := new(WorkspaceRoleBindingList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleBindingList) 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 *WorkspaceRoleList) DeepCopyInto(out *WorkspaceRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WorkspaceRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceRoleList. +func (in *WorkspaceRoleList) DeepCopy() *WorkspaceRoleList { + if in == nil { + return nil + } + out := new(WorkspaceRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WorkspaceRoleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/apis/network/v1alpha1/common.go b/pkg/apis/network/v1alpha1/common.go deleted file mode 100644 index 50fa9f706..000000000 --- a/pkg/apis/network/v1alpha1/common.go +++ /dev/null @@ -1,170 +0,0 @@ -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring" -) - -// A Rule encapsulates a set of match criteria and an action. Both selector-based security Policy -// and security Profiles reference rules - separated out as a list of rules for both -// ingress and egress packet matching. -// -// Each positive match criteria has a negated version, prefixed with â€Notâ€. All the match -// criteria within a rule must be satisfied for a packet to match. A single rule can contain -// the positive and negative version of a match and both must be satisfied for the rule to match. -type Rule struct { - Action Action `json:"action" validate:"action"` - // IPVersion is an optional field that restricts the rule to only match a specific IP - // version. - IPVersion *int `json:"ipVersion,omitempty" validate:"omitempty,ipVersion"` - // Protocol is an optional field that restricts the rule to only apply to traffic of - // a specific IP protocol. Required if any of the EntityRules contain Ports - // (because ports only apply to certain protocols). - // - // Must be one of these string values: "TCP", "UDP", "ICMP", "ICMPv6", "SCTP", "UDPLite" - // or an integer in the range 1-255. - Protocol *corev1.Protocol `json:"protocol,omitempty" validate:"omitempty"` - // ICMP is an optional field that restricts the rule to apply to a specific type and - // code of ICMP traffic. This should only be specified if the Protocol field is set to - // "ICMP" or "ICMPv6". - ICMP *ICMPFields `json:"icmp,omitempty" validate:"omitempty"` - // NotProtocol is the negated version of the Protocol field. - NotProtocol *corev1.Protocol `json:"notProtocol,omitempty" validate:"omitempty"` - // NotICMP is the negated version of the ICMP field. - NotICMP *ICMPFields `json:"notICMP,omitempty" validate:"omitempty"` - // Source contains the match criteria that apply to source entity. - Source EntityRule `json:"source,omitempty" validate:"omitempty"` - // Destination contains the match criteria that apply to destination entity. - Destination EntityRule `json:"destination,omitempty" validate:"omitempty"` - - // HTTP contains match criteria that apply to HTTP requests. - HTTP *HTTPMatch `json:"http,omitempty" validate:"omitempty"` -} - -// HTTPPath specifies an HTTP path to match. It may be either of the form: -// exact: : which matches the path exactly or -// prefix: : which matches the path prefix -type HTTPPath struct { - Exact string `json:"exact,omitempty" validate:"omitempty"` - Prefix string `json:"prefix,omitempty" validate:"omitempty"` -} - -// HTTPMatch is an optional field that apply only to HTTP requests -// The Methods and Path fields are joined with AND -type HTTPMatch struct { - // Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed - // HTTP Methods (e.g. GET, PUT, etc.) - // Multiple methods are OR'd together. - Methods []string `json:"methods,omitempty" validate:"omitempty"` - // Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed - // HTTP Paths. - // Multiple paths are OR'd together. - // e.g: - // - exact: /foo - // - prefix: /bar - // NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it. - Paths []HTTPPath `json:"paths,omitempty" validate:"omitempty"` -} - -// ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type -type ICMPFields struct { - // Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request - // (i.e. pings). - Type *int `json:"type,omitempty" validate:"omitempty,gte=0,lte=254"` - // Match on a specific ICMP code. If specified, the Type value must also be specified. - // This is a technical limitation imposed by the kernel’s iptables firewall, which - // Calico uses to enforce the rule. - Code *int `json:"code,omitempty" validate:"omitempty,gte=0,lte=255"` -} - -// An EntityRule is a sub-component of a Rule comprising the match criteria specific -// to a particular entity (that is either the source or destination). -// -// A source EntityRule matches the source endpoint and originating traffic. -// A destination EntityRule matches the destination endpoint and terminating traffic. -type EntityRule struct { - // Nets is an optional field that restricts the rule to only apply to traffic that - // originates from (or terminates at) IP addresses in any of the given subnets. - Nets []string `json:"nets,omitempty" validate:"omitempty,dive,net"` - - // Selector is an optional field that contains a selector expression (see Policy for - // sample syntax). Only traffic that originates from (terminates at) endpoints matching - // the selector will be matched. - // - // Note that: in addition to the negated version of the Selector (see NotSelector below), the - // selector expression syntax itself supports negation. The two types of negation are subtly - // different. One negates the set of matched endpoints, the other negates the whole match: - // - // Selector = "!has(my_label)" matches packets that are from other Calico-controlled - // endpoints that do not have the label “my_labelâ€. - // - // NotSelector = "has(my_label)" matches packets that are not from Calico-controlled - // endpoints that do have the label “my_labelâ€. - // - // The effect is that the latter will accept packets from non-Calico sources whereas the - // former is limited to packets from Calico-controlled endpoints. - Selector string `json:"selector,omitempty" validate:"omitempty,selector"` - - // NamespaceSelector is an optional field that contains a selector expression. Only traffic - // that originates from (or terminates at) endpoints within the selected namespaces will be - // matched. When both NamespaceSelector and Selector are defined on the same rule, then only - // workload endpoints that are matched by both selectors will be selected by the rule. - // - // For NetworkPolicy, an empty NamespaceSelector implies that the Selector is limited to selecting - // only workload endpoints in the same namespace as the NetworkPolicy. - // - // For GlobalNetworkPolicy, an empty NamespaceSelector implies the Selector applies to workload - // endpoints across all namespaces. - NamespaceSelector string `json:"namespaceSelector,omitempty" validate:"omitempty,selector"` - - // Ports is an optional field that restricts the rule to only apply to traffic that has a - // source (destination) port that matches one of these ranges/values. This value is a - // list of integers or strings that represent ranges of ports. - // - // Since only some protocols have ports, if any ports are specified it requires the - // Protocol match in the Rule to be set to "TCP" or "UDP". - Ports []numorstring.Port `json:"ports,omitempty" validate:"omitempty,dive"` - - // NotNets is the negated version of the Nets field. - NotNets []string `json:"notNets,omitempty" validate:"omitempty,dive,net"` - - // NotSelector is the negated version of the Selector field. See Selector field for - // subtleties with negated selectors. - NotSelector string `json:"notSelector,omitempty" validate:"omitempty,selector"` - - // NotPorts is the negated version of the Ports field. - // Since only some protocols have ports, if any ports are specified it requires the - // Protocol match in the Rule to be set to "TCP" or "UDP". - NotPorts []numorstring.Port `json:"notPorts,omitempty" validate:"omitempty,dive"` - - // ServiceAccounts is an optional field that restricts the rule to only apply to traffic that originates from (or - // terminates at) a pod running as a matching service account. - ServiceAccounts *ServiceAccountMatch `json:"serviceAccounts,omitempty" validate:"omitempty"` -} - -type ServiceAccountMatch struct { - // Names is an optional field that restricts the rule to only apply to traffic that originates from (or terminates - // at) a pod running as a service account whose name is in the list. - Names []string `json:"names,omitempty" validate:"omitempty"` - - // Selector is an optional field that restricts the rule to only apply to traffic that originates from - // (or terminates at) a pod running as a service account that matches the given label selector. - // If both Names and Selector are specified then they are AND'ed. - Selector string `json:"selector,omitempty" validate:"omitempty,selector"` -} - -type Action string - -const ( - Allow Action = "Allow" - Deny = "Deny" - Log = "Log" - Pass = "Pass" -) - -type PolicyType string - -const ( - PolicyTypeIngress PolicyType = "Ingress" - PolicyTypeEgress PolicyType = "Egress" -) diff --git a/pkg/apis/network/v1alpha1/namespacenetworkpolicy_types.go b/pkg/apis/network/v1alpha1/namespacenetworkpolicy_types.go index d8ab00bb6..3e674e072 100644 --- a/pkg/apis/network/v1alpha1/namespacenetworkpolicy_types.go +++ b/pkg/apis/network/v1alpha1/namespacenetworkpolicy_types.go @@ -17,68 +17,114 @@ limitations under the License. package v1alpha1 import ( + k8snet "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// All types in this file is copy from calicoapi as we use calico to policy +const ( + ResourceKindNamespaceNetworkPolicy = "NamespaceNetworkPolicy" + ResourceSingularNamespaceNetworkPolicy = "namespacenetworkpolicy" + ResourcePluralNamespaceNetworkPolicy = "namespacenetworkpolicies" +) -// NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy +// NamespaceNetworkPolicySpec provides the specification of a NamespaceNetworkPolicy type NamespaceNetworkPolicySpec struct { - // Order is an optional field that specifies the order in which the policy is applied. - // Policies with higher "order" are applied after those with lower - // order. If the order is omitted, it may be considered to be "infinite" - i.e. the - // policy will be applied last. Policies with identical order will be applied in - // alphanumerical order based on the Policy "Name". - Order *int `json:"order,omitempty"` - // The ordered set of ingress rules. Each rule contains a set of packet match criteria and - // a corresponding action to apply. - Ingress []Rule `json:"ingress,omitempty" validate:"omitempty,dive"` - // The ordered set of egress rules. Each rule contains a set of packet match criteria and - // a corresponding action to apply. - Egress []Rule `json:"egress,omitempty" validate:"omitempty,dive"` - // The selector is an expression used to pick pick out the endpoints that the policy should - // be applied to. - // - // Selector expressions follow this syntax: - // - // label == "string_literal" -> comparison, e.g. my_label == "foo bar" - // label != "string_literal" -> not equal; also matches if label is not present - // label in { "a", "b", "c", ... } -> true if the value of label X is one of "a", "b", "c" - // label not in { "a", "b", "c", ... } -> true if the value of label X is not one of "a", "b", "c" - // has(label_name) -> True if that label is present - // ! expr -> negation of expr - // expr && expr -> Short-circuit and - // expr || expr -> Short-circuit or - // ( expr ) -> parens for grouping - // all() or the empty selector -> matches all endpoints. - // - // Label names are allowed to contain alphanumerics, -, _ and /. String literals are more permissive - // but they do not support escape characters. - // - // Examples (with made-up labels): - // - // type == "webserver" && deployment == "prod" - // type in {"frontend", "backend"} - // deployment != "dev" - // ! has(label_name) - Selector string `json:"selector" validate:"selector"` - // Types indicates whether this policy applies to ingress, or to egress, or to both. When - // not explicitly specified (and so the value on creation is empty or nil), Calico defaults - // Types according to what Ingress and Egress are present in the policy. The - // default is: - // - // - [ PolicyTypeIngress ], if there are no Egress rules (including the case where there are - // also no Ingress rules) - // - // - [ PolicyTypeEgress ], if there are Egress rules but no Ingress rules - // - // - [ PolicyTypeIngress, PolicyTypeEgress ], if there are both Ingress and Egress rules. - // - // When the policy is read back again, Types will always be one of these values, never empty - // or nil. - Types []PolicyType `json:"types,omitempty" validate:"omitempty,dive,policyType"` - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + // List of ingress rules to be applied to the selected pods. Traffic is allowed to + // a pod if there are no NetworkPolicies selecting the pod + // (and cluster policy otherwise allows the traffic), OR if the traffic source is + // the pod's local node, OR if the traffic matches at least one ingress rule + // across all of the NetworkPolicy objects whose podSelector matches the pod. If + // this field is empty then this NetworkPolicy does not allow any traffic (and serves + // solely to ensure that the pods it selects are isolated by default) + // +optional + Ingress []NetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,1,rep,name=ingress"` + + // List of egress rules to be applied to the selected pods. Outgoing traffic is + // allowed if there are no NetworkPolicies selecting the pod (and cluster policy + // otherwise allows the traffic), OR if the traffic matches at least one egress rule + // across all of the NetworkPolicy objects whose podSelector matches the pod. If + // this field is empty then this NetworkPolicy limits all outgoing traffic (and serves + // solely to ensure that the pods it selects are isolated by default). + // This field is beta-level in 1.8 + // +optional + Egress []NetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,2,rep,name=egress"` + + // List of rule types that the NetworkPolicy relates to. + // Valid options are "Ingress", "Egress", or "Ingress,Egress". + // If this field is not specified, it will default based on the existence of Ingress or Egress rules; + // policies that contain an Egress section are assumed to affect Egress, and all policies + // (whether or not they contain an Ingress section) are assumed to affect Ingress. + // If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ]. + // Likewise, if you want to write a policy that specifies that no egress is allowed, + // you must specify a policyTypes value that include "Egress" (since such a policy would not include + // an Egress section and would otherwise default to just [ "Ingress" ]). + // This field is beta-level in 1.8 + // +optional + PolicyTypes []k8snet.PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,3,rep,name=policyTypes,casttype=PolicyType"` +} + +// NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods +// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from. +type NetworkPolicyIngressRule struct { + // List of ports which should be made accessible on the pods selected for this + // rule. Each item in this list is combined using a logical OR. If this field is + // empty or missing, this rule matches all ports (traffic not restricted by port). + // If this field is present and contains at least one item, then this rule allows + // traffic only if the traffic matches at least one port in the list. + // +optional + Ports []k8snet.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"` + + // List of sources which should be able to access the pods selected for this rule. + // Items in this list are combined using a logical OR operation. If this field is + // empty or missing, this rule matches all sources (traffic not restricted by + // source). If this field is present and contains at least one item, this rule + // allows traffic only if the traffic matches at least one item in the from list. + // +optional + From []NetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"` +} + +// NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods +// matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. +// This type is beta-level in 1.8 +type NetworkPolicyEgressRule struct { + // List of destination ports for outgoing traffic. + // Each item in this list is combined using a logical OR. If this field is + // empty or missing, this rule matches all ports (traffic not restricted by port). + // If this field is present and contains at least one item, then this rule allows + // traffic only if the traffic matches at least one port in the list. + // +optional + Ports []k8snet.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"` + + // List of destinations for outgoing traffic of pods selected for this rule. + // Items in this list are combined using a logical OR operation. If this field is + // empty or missing, this rule matches all destinations (traffic not restricted by + // destination). If this field is present and contains at least one item, this rule + // allows traffic only if the traffic matches at least one item in the to list. + // +optional + To []NetworkPolicyPeer `json:"to,omitempty" protobuf:"bytes,2,rep,name=to"` +} + +type NamespaceSelector struct { + Name string `json:"name" protobuf:"bytes,1,name=name"` +} + +type ServiceSelector struct { + Name string `json:"name" protobuf:"bytes,1,name=name"` + Namespace string `json:"namespace" protobuf:"bytes,2,name=namespace"` +} + +// NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of +// fields are allowed +type NetworkPolicyPeer struct { + // +optional + NamespaceSelector *NamespaceSelector `json:"namespace,omitempty" protobuf:"bytes,1,opt,name=namespace"` + + // IPBlock defines policy on a particular IPBlock. If this field is set then + // neither of the other fields can be. + // +optional + IPBlock *k8snet.IPBlock `json:"ipBlock,omitempty" protobuf:"bytes,2,rep,name=ipBlock"` + + ServiceSelector *ServiceSelector `json:"service,omitempty" protobuf:"bytes,3,opt,name=service"` } // +genclient diff --git a/pkg/apis/network/v1alpha1/numorstring/asnumber.go b/pkg/apis/network/v1alpha1/numorstring/asnumber.go deleted file mode 100644 index 9ff706d62..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/asnumber.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2016 Tigera, 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 numorstring - -import ( - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" -) - -type ASNumber uint32 - -// ASNumberFromString creates an ASNumber struct from a string value. The -// string value may simply be a number or may be the ASN in dotted notation. -func ASNumberFromString(s string) (ASNumber, error) { - if num, err := strconv.ParseUint(s, 10, 32); err == nil { - return ASNumber(num), nil - } - - parts := strings.Split(s, ".") - if len(parts) != 2 { - msg := fmt.Sprintf("invalid AS Number format (%s)", s) - return 0, errors.New(msg) - } - - if num1, err := strconv.ParseUint(parts[0], 10, 16); err != nil { - msg := fmt.Sprintf("invalid AS Number format (%s)", s) - return 0, errors.New(msg) - } else if num2, err := strconv.ParseUint(parts[1], 10, 16); err != nil { - msg := fmt.Sprintf("invalid AS Number format (%s)", s) - return 0, errors.New(msg) - } else { - return ASNumber((num1 << 16) + num2), nil - } -} - -// UnmarshalJSON implements the json.Unmarshaller uinterface. -func (a *ASNumber) UnmarshalJSON(b []byte) error { - if err := json.Unmarshal(b, (*uint32)(a)); err == nil { - return nil - } else { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - - if v, err := ASNumberFromString(s); err != nil { - return err - } else { - *a = v - return nil - } - } -} - -// String returns the string value, or the Itoa of the uint value. -func (a ASNumber) String() string { - return strconv.FormatUint(uint64(a), 10) -} diff --git a/pkg/apis/network/v1alpha1/numorstring/doc.go b/pkg/apis/network/v1alpha1/numorstring/doc.go deleted file mode 100644 index f37ce6efc..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2016 Tigera, 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 numorstring implements a set of type definitions that in YAML or JSON -format may be represented by either a number or a string. -*/ -package numorstring diff --git a/pkg/apis/network/v1alpha1/numorstring/numorstring_suite_test.go b/pkg/apis/network/v1alpha1/numorstring/numorstring_suite_test.go deleted file mode 100644 index 68a29fd72..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/numorstring_suite_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016,2018 Tigera, 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 numorstring_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - - "testing" -) - -func TestNumorstring(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Numorstring Suite") -} diff --git a/pkg/apis/network/v1alpha1/numorstring/numorstring_test.go b/pkg/apis/network/v1alpha1/numorstring/numorstring_test.go deleted file mode 100644 index c12270db0..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/numorstring_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) 2016-2017 Tigera, 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 numorstring_test - -import ( - "encoding/json" - "fmt" - "reflect" - - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" - "github.com/projectcalico/libcalico-go/lib/numorstring" -) - -func init() { - - asNumberType := reflect.TypeOf(numorstring.ASNumber(0)) - protocolType := reflect.TypeOf(numorstring.Protocol{}) - portType := reflect.TypeOf(numorstring.Port{}) - - // Perform tests of JSON unmarshaling of the various field types. - DescribeTable("NumOrStringJSONUnmarshaling", - func(jtext string, typ reflect.Type, expected interface{}) { - // Create a new field type and invoke the unmarshaller interface - // directly (this covers a couple more error cases than calling - // through json.Unmarshal. - new := reflect.New(typ) - u := new.Interface().(json.Unmarshaler) - err := u.UnmarshalJSON([]byte(jtext)) - - if expected != nil { - Expect(err).To(BeNil(), - "expected json unmarshal to not error") - Expect(new.Elem().Interface()).To(Equal(expected), - "expected value not same as json unmarshalled value") - } else { - Expect(err).ToNot(BeNil(), - "expected json unmarshal to error") - } - }, - // ASNumber tests. - Entry("should accept 0 AS number as int", "0", asNumberType, numorstring.ASNumber(0)), - Entry("should accept 4294967295 AS number as int", "4294967295", asNumberType, numorstring.ASNumber(4294967295)), - Entry("should accept 0 AS number as string", "\"0\"", asNumberType, numorstring.ASNumber(0)), - Entry("should accept 4294967295 AS number as string", "\"4294967295\"", asNumberType, numorstring.ASNumber(4294967295)), - Entry("should accept 1.10 AS number as string", "\"1.10\"", asNumberType, numorstring.ASNumber(65546)), - Entry("should accept 00.00 AS number as string", "\"00.00\"", asNumberType, numorstring.ASNumber(0)), - Entry("should accept 00.01 AS number as string", "\"00.01\"", asNumberType, numorstring.ASNumber(1)), - Entry("should accept 65535.65535 AS number as string", "\"65535.65535\"", asNumberType, numorstring.ASNumber(4294967295)), - Entry("should reject 1.1.1 AS number as string", "\"1.1.1\"", asNumberType, nil), - Entry("should reject 65536.65535 AS number as string", "\"65536.65535\"", asNumberType, nil), - Entry("should reject 65535.65536 AS number as string", "\"65535.65536\"", asNumberType, nil), - Entry("should reject 0.-1 AS number as string", "\"0.-1\"", asNumberType, nil), - Entry("should reject -1 AS number as int", "-1", asNumberType, nil), - Entry("should reject 4294967296 AS number as int", "4294967296", asNumberType, nil), - - // Port tests. - Entry("should accept 0 port as int", "0", portType, numorstring.SinglePort(0)), - Entry("should accept 65535 port as int", "65535", portType, numorstring.SinglePort(65535)), - Entry("should accept 0:65535 port range as string", "\"0:65535\"", portType, portFromRange(0, 65535)), - Entry("should accept 1:10 port range as string", "\"1:10\"", portType, portFromRange(1, 10)), - Entry("should accept foo-bar as named port", "\"foo-bar\"", portType, numorstring.NamedPort("foo-bar")), - Entry("should reject -1 port as int", "-1", portType, nil), - Entry("should reject 65536 port as int", "65536", portType, nil), - Entry("should reject 0:65536 port range as string", "\"0:65536\"", portType, nil), - Entry("should reject -1:65535 port range as string", "\"-1:65535\"", portType, nil), - Entry("should reject 10:1 port range as string", "\"10:1\"", portType, nil), - Entry("should reject 1:2:3 port range as string", "\"1:2:3\"", portType, nil), - Entry("should reject bad named port string", "\"*\"", portType, nil), - Entry("should reject bad port string", "\"1:2", portType, nil), - - // Protocol tests. Invalid integer values will be stored as strings. - Entry("should accept 0 protocol as int", "0", protocolType, numorstring.ProtocolFromInt(0)), - Entry("should accept 255 protocol as int", "255", protocolType, numorstring.ProtocolFromInt(255)), - Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")), - Entry("should accept tcp protocol as string", "\"TCP\"", protocolType, numorstring.ProtocolFromString("TCP")), - Entry("should accept 0 protocol as string", "\"0\"", protocolType, numorstring.ProtocolFromInt(0)), - Entry("should accept 0 protocol as string", "\"255\"", protocolType, numorstring.ProtocolFromInt(255)), - Entry("should accept 256 protocol as string", "\"256\"", protocolType, numorstring.ProtocolFromString("256")), - Entry("should reject bad protocol string", "\"25", protocolType, nil), - ) - - // Perform tests of JSON marshaling of the various field types. - DescribeTable("NumOrStringJSONMarshaling", - func(field interface{}, jtext string) { - b, err := json.Marshal(field) - if jtext != "" { - Expect(err).To(BeNil(), - "expected json marshal to not error") - Expect(string(b)).To(Equal(jtext), - "expected json not same as marshalled value") - } else { - Expect(err).ToNot(BeNil(), - "expected json marshal to error") - } - }, - // ASNumber tests. - Entry("should marshal ASN of 0", numorstring.ASNumber(0), "0"), - Entry("should marshal ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"), - - // Port tests. - Entry("should marshal port of 0", numorstring.SinglePort(0), "0"), - Entry("should marshal port of 65535", portFromRange(65535, 65535), "65535"), - Entry("should marshal port of 10", portFromString("10"), "10"), - Entry("should marshal port range of 10:20", portFromRange(10, 20), "\"10:20\""), - Entry("should marshal port range of 20:30", portFromRange(20, 30), "\"20:30\""), - Entry("should marshal named port", numorstring.NamedPort("foobar"), `"foobar"`), - - // Protocol tests. - Entry("should marshal protocol of 0", numorstring.ProtocolFromInt(0), "0"), - Entry("should marshal protocol of udp", numorstring.ProtocolFromString("UDP"), "\"UDP\""), - ) - - // Perform tests of Stringer interface various field types. - DescribeTable("NumOrStringStringify", - func(field interface{}, s string) { - a := fmt.Sprint(field) - Expect(a).To(Equal(s), - "expected String() value to match") - }, - // ASNumber tests. - Entry("should stringify ASN of 0", numorstring.ASNumber(0), "0"), - Entry("should stringify ASN of 4294967295", numorstring.ASNumber(4294967295), "4294967295"), - - // Port tests. - Entry("should stringify port of 20", numorstring.SinglePort(20), "20"), - Entry("should stringify port range of 10:20", portFromRange(10, 20), "10:20"), - - // Protocol tests. - Entry("should stringify protocol of 0", numorstring.ProtocolFromInt(0), "0"), - Entry("should stringify protocol of udp", numorstring.ProtocolFromString("UDP"), "UDP"), - ) - - // Perform tests of Protocols supporting ports. - DescribeTable("NumOrStringProtocolsSupportingPorts", - func(protocol numorstring.Protocol, supportsPorts bool) { - Expect(protocol.SupportsPorts()).To(Equal(supportsPorts), - "expected protocol port support to match") - }, - Entry("protocol 6 supports ports", numorstring.ProtocolFromInt(6), true), - Entry("protocol 17 supports ports", numorstring.ProtocolFromInt(17), true), - Entry("protocol udp supports ports", numorstring.ProtocolFromString("UDP"), true), - Entry("protocol udp supports ports", numorstring.ProtocolFromString("TCP"), true), - Entry("protocol foo does not support ports", numorstring.ProtocolFromString("foo"), false), - Entry("protocol 2 does not support ports", numorstring.ProtocolFromInt(2), false), - ) - - // Perform tests of Protocols FromString method. - DescribeTable("NumOrStringProtocols FromString is not case sensitive", - func(input, expected string) { - Expect(numorstring.ProtocolFromString(input).StrVal).To(Equal(expected), - "expected parsed protocol to match") - }, - Entry("protocol udp -> UDP", "udp", "UDP"), - Entry("protocol tcp -> TCP", "tcp", "TCP"), - Entry("protocol updlite -> UDPLite", "udplite", "UDPLite"), - Entry("unknown protocol xxxXXX", "xxxXXX", "xxxXXX"), - ) - - // Perform tests of Protocols FromStringV1 method. - DescribeTable("NumOrStringProtocols FromStringV1 is lowercase", - func(input, expected string) { - Expect(numorstring.ProtocolFromStringV1(input).StrVal).To(Equal(expected), - "expected parsed protocol to match") - }, - Entry("protocol udp -> UDP", "UDP", "udp"), - Entry("protocol tcp -> TCP", "TCP", "tcp"), - Entry("protocol updlite -> UDPLite", "UDPLite", "udplite"), - Entry("unknown protocol xxxXXX", "xxxXXX", "xxxxxx"), - ) - - // Perform tests of Protocols ToV1 method. - DescribeTable("NumOrStringProtocols FromStringV1 is lowercase", - func(input, expected numorstring.Protocol) { - Expect(input.ToV1()).To(Equal(expected), - "expected parsed protocol to match") - }, - // Protocol tests. - Entry("protocol udp -> UDP", numorstring.ProtocolFromInt(2), numorstring.ProtocolFromInt(2)), - Entry("protocol tcp -> TCP", numorstring.ProtocolFromString("TCP"), numorstring.ProtocolFromStringV1("TCP")), - ) -} - -func portFromRange(minPort, maxPort uint16) numorstring.Port { - p, _ := numorstring.PortFromRange(minPort, maxPort) - return p -} - -func portFromString(s string) numorstring.Port { - p, _ := numorstring.PortFromString(s) - return p -} diff --git a/pkg/apis/network/v1alpha1/numorstring/port.go b/pkg/apis/network/v1alpha1/numorstring/port.go deleted file mode 100644 index 9d737ff83..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/port.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2016-2017 Tigera, 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 numorstring - -import ( - "encoding/json" - "errors" - "fmt" - "regexp" - "strconv" -) - -// Port represents either a range of numeric ports or a named port. -// -// - For a named port, set the PortName, leaving MinPort and MaxPort as 0. -// - For a port range, set MinPort and MaxPort to the (inclusive) port numbers. Set -// PortName to "". -// - For a single port, set MinPort = MaxPort and PortName = "". -type Port struct { - MinPort uint16 `json:"minPort,omitempty"` - MaxPort uint16 `json:"maxPort,omitempty"` - PortName string `validate:"omitempty,portName" json:"portName,omitempty"` -} - -// SinglePort creates a Port struct representing a single port. -func SinglePort(port uint16) Port { - return Port{MinPort: port, MaxPort: port} -} - -func NamedPort(name string) Port { - return Port{PortName: name} -} - -// PortFromRange creates a Port struct representing a range of ports. -func PortFromRange(minPort, maxPort uint16) (Port, error) { - port := Port{MinPort: minPort, MaxPort: maxPort} - if minPort > maxPort { - msg := fmt.Sprintf("minimum port number (%d) is greater than maximum port number (%d) in port range", minPort, maxPort) - return port, errors.New(msg) - } - return port, nil -} - -var ( - allDigits = regexp.MustCompile(`^\d+$`) - portRange = regexp.MustCompile(`^(\d+):(\d+)$`) - nameRegex = regexp.MustCompile("^[a-zA-Z0-9_.-]{1,128}$") -) - -// PortFromString creates a Port struct from its string representation. A port -// may either be single value "1234", a range of values "100:200" or a named port: "name". -func PortFromString(s string) (Port, error) { - if allDigits.MatchString(s) { - // Port is all digits, it should parse as a single port. - num, err := strconv.ParseUint(s, 10, 16) - if err != nil { - msg := fmt.Sprintf("invalid port format (%s)", s) - return Port{}, errors.New(msg) - } - return SinglePort(uint16(num)), nil - } - - if groups := portRange.FindStringSubmatch(s); len(groups) > 0 { - // Port matches :, it should parse as a range of ports. - if pmin, err := strconv.ParseUint(groups[1], 10, 16); err != nil { - msg := fmt.Sprintf("invalid minimum port number in range (%s)", s) - return Port{}, errors.New(msg) - } else if pmax, err := strconv.ParseUint(groups[2], 10, 16); err != nil { - msg := fmt.Sprintf("invalid maximum port number in range (%s)", s) - return Port{}, errors.New(msg) - } else { - return PortFromRange(uint16(pmin), uint16(pmax)) - } - } - - if !nameRegex.MatchString(s) { - msg := fmt.Sprintf("invalid name for named port (%s)", s) - return Port{}, errors.New(msg) - } - - return NamedPort(s), nil -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (p *Port) UnmarshalJSON(b []byte) error { - if b[0] == '"' { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - - if v, err := PortFromString(s); err != nil { - return err - } else { - *p = v - return nil - } - } - - // It's not a string, it must be a single int. - var i uint16 - if err := json.Unmarshal(b, &i); err != nil { - return err - } - v := SinglePort(i) - *p = v - return nil -} - -// MarshalJSON implements the json.Marshaller interface. -func (p Port) MarshalJSON() ([]byte, error) { - if p.PortName != "" { - return json.Marshal(p.PortName) - } else if p.MinPort == p.MaxPort { - return json.Marshal(p.MinPort) - } else { - return json.Marshal(p.String()) - } -} - -// String returns the string value. If the min and max port are the same -// this returns a single string representation of the port number, otherwise -// if returns a colon separated range of ports. -func (p Port) String() string { - if p.PortName != "" { - return p.PortName - } else if p.MinPort == p.MaxPort { - return strconv.FormatUint(uint64(p.MinPort), 10) - } else { - return fmt.Sprintf("%d:%d", p.MinPort, p.MaxPort) - } -} diff --git a/pkg/apis/network/v1alpha1/numorstring/protocol.go b/pkg/apis/network/v1alpha1/numorstring/protocol.go deleted file mode 100644 index d700a21c6..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/protocol.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) 2016 Tigera, 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 numorstring - -import "strings" - -const ( - ProtocolUDP = "UDP" - ProtocolTCP = "TCP" - ProtocolICMP = "ICMP" - ProtocolICMPv6 = "ICMPv6" - ProtocolSCTP = "SCTP" - ProtocolUDPLite = "UDPLite" - - ProtocolUDPV1 = "udp" - ProtocolTCPV1 = "tcp" -) - -var ( - allProtocolNames = []string{ - ProtocolUDP, - ProtocolTCP, - ProtocolICMP, - ProtocolICMPv6, - ProtocolSCTP, - ProtocolUDPLite, - } -) - -type Protocol Uint8OrString - -// ProtocolFromInt creates a Protocol struct from an integer value. -func ProtocolFromInt(p uint8) Protocol { - return Protocol( - Uint8OrString{Type: NumOrStringNum, NumVal: p}, - ) -} - -// ProtocolV3FromProtocolV1 creates a v3 Protocol from a v1 Protocol, -// while handling case conversion. -func ProtocolV3FromProtocolV1(p Protocol) Protocol { - if p.Type == NumOrStringNum { - return p - } - - for _, n := range allProtocolNames { - if strings.ToLower(n) == strings.ToLower(p.StrVal) { - return Protocol( - Uint8OrString{Type: NumOrStringString, StrVal: n}, - ) - } - } - - return p -} - -// ProtocolFromString creates a Protocol struct from a string value. -func ProtocolFromString(p string) Protocol { - for _, n := range allProtocolNames { - if strings.ToLower(n) == strings.ToLower(p) { - return Protocol( - Uint8OrString{Type: NumOrStringString, StrVal: n}, - ) - } - } - - // Unknown protocol - return the value unchanged. Validation should catch this. - return Protocol( - Uint8OrString{Type: NumOrStringString, StrVal: p}, - ) -} - -// ProtocolFromStringV1 creates a Protocol struct from a string value (for the v1 API) -func ProtocolFromStringV1(p string) Protocol { - return Protocol( - Uint8OrString{Type: NumOrStringString, StrVal: strings.ToLower(p)}, - ) -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (p *Protocol) UnmarshalJSON(b []byte) error { - return (*Uint8OrString)(p).UnmarshalJSON(b) -} - -// MarshalJSON implements the json.Marshaller interface. -func (p Protocol) MarshalJSON() ([]byte, error) { - return Uint8OrString(p).MarshalJSON() -} - -// String returns the string value, or the Itoa of the int value. -func (p Protocol) String() string { - return (Uint8OrString)(p).String() -} - -// String returns the string value, or the Itoa of the int value. -func (p Protocol) ToV1() Protocol { - if p.Type == NumOrStringNum { - return p - } - return ProtocolFromStringV1(p.StrVal) -} - -// NumValue returns the NumVal if type Int, or if -// it is a String, will attempt a conversion to int. -func (p Protocol) NumValue() (uint8, error) { - return (Uint8OrString)(p).NumValue() -} - -// SupportsProtocols returns whether this protocol supports ports. This returns true if -// the numerical or string verion of the protocol indicates TCP (6) or UDP (17). -func (p Protocol) SupportsPorts() bool { - num, err := p.NumValue() - if err == nil { - return num == 6 || num == 17 - } else { - switch p.StrVal { - case ProtocolTCP, ProtocolUDP, ProtocolTCPV1, ProtocolUDPV1: - return true - } - return false - } -} diff --git a/pkg/apis/network/v1alpha1/numorstring/type.go b/pkg/apis/network/v1alpha1/numorstring/type.go deleted file mode 100644 index ae50cba43..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/type.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2016 Tigera, 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 numorstring - -// Type represents the stored type of Int32OrString. -type NumOrStringType int - -const ( - NumOrStringNum NumOrStringType = iota // The structure holds a number. - NumOrStringString // The structure holds a string. -) diff --git a/pkg/apis/network/v1alpha1/numorstring/uint8orstring.go b/pkg/apis/network/v1alpha1/numorstring/uint8orstring.go deleted file mode 100644 index 626a904b6..000000000 --- a/pkg/apis/network/v1alpha1/numorstring/uint8orstring.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2016 Tigera, 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 numorstring - -import ( - "encoding/json" - "strconv" -) - -// UInt8OrString is a type that can hold an uint8 or a string. When used in -// JSON or YAML marshalling and unmarshalling, it produces or consumes the -// inner type. This allows you to have, for example, a JSON field that can -// accept a name or number. -type Uint8OrString struct { - Type NumOrStringType - NumVal uint8 - StrVal string -} - -// UnmarshalJSON implements the json.Unmarshaller interface. -func (i *Uint8OrString) UnmarshalJSON(b []byte) error { - if b[0] == '"' { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - - num, err := strconv.ParseUint(s, 10, 8) - if err == nil { - i.Type = NumOrStringNum - i.NumVal = uint8(num) - } else { - i.Type = NumOrStringString - i.StrVal = s - } - - return nil - } - i.Type = NumOrStringNum - return json.Unmarshal(b, &i.NumVal) -} - -// MarshalJSON implements the json.Marshaller interface. -func (i Uint8OrString) MarshalJSON() ([]byte, error) { - if num, err := i.NumValue(); err == nil { - return json.Marshal(num) - } else { - return json.Marshal(i.StrVal) - } -} - -// String returns the string value, or the Itoa of the int value. -func (i Uint8OrString) String() string { - if i.Type == NumOrStringString { - return i.StrVal - } - return strconv.FormatUint(uint64(i.NumVal), 10) -} - -// NumValue returns the NumVal if type Int, or if -// it is a String, will attempt a conversion to int. -func (i Uint8OrString) NumValue() (uint8, error) { - if i.Type == NumOrStringString { - num, err := strconv.ParseUint(i.StrVal, 10, 8) - return uint8(num), err - } - return i.NumVal, nil -} diff --git a/pkg/apis/network/v1alpha1/openapi_generated.go b/pkg/apis/network/v1alpha1/openapi_generated.go index 713dbeb10..0bbf69ce0 100644 --- a/pkg/apis/network/v1alpha1/openapi_generated.go +++ b/pkg/apis/network/v1alpha1/openapi_generated.go @@ -25,82 +25,76 @@ package v1alpha1 import ( spec "github.com/go-openapi/spec" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + intstr "k8s.io/apimachinery/pkg/util/intstr" common "k8s.io/kube-openapi/pkg/common" ) func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "k8s.io/api/networking/v1.IPBlock": schema_k8sio_api_networking_v1_IPBlock(ref), - "k8s.io/api/networking/v1.NetworkPolicy": schema_k8sio_api_networking_v1_NetworkPolicy(ref), - "k8s.io/api/networking/v1.NetworkPolicyEgressRule": schema_k8sio_api_networking_v1_NetworkPolicyEgressRule(ref), - "k8s.io/api/networking/v1.NetworkPolicyIngressRule": schema_k8sio_api_networking_v1_NetworkPolicyIngressRule(ref), - "k8s.io/api/networking/v1.NetworkPolicyList": schema_k8sio_api_networking_v1_NetworkPolicyList(ref), - "k8s.io/api/networking/v1.NetworkPolicyPeer": schema_k8sio_api_networking_v1_NetworkPolicyPeer(ref), - "k8s.io/api/networking/v1.NetworkPolicyPort": schema_k8sio_api_networking_v1_NetworkPolicyPort(ref), - "k8s.io/api/networking/v1.NetworkPolicySpec": schema_k8sio_api_networking_v1_NetworkPolicySpec(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule": schema_pkg_apis_network_v1alpha1_EntityRule(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch": schema_pkg_apis_network_v1alpha1_HTTPMatch(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath": schema_pkg_apis_network_v1alpha1_HTTPPath(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields": schema_pkg_apis_network_v1alpha1_ICMPFields(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicyList(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule": schema_pkg_apis_network_v1alpha1_Rule(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch": schema_pkg_apis_network_v1alpha1_ServiceAccountMatch(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicy(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyEgressRule(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyIngressRule(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyList(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyPeer(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref), - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus": schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyStatus(ref), + "k8s.io/api/networking/v1.IPBlock": schema_k8sio_api_networking_v1_IPBlock(ref), + "k8s.io/api/networking/v1.NetworkPolicy": schema_k8sio_api_networking_v1_NetworkPolicy(ref), + "k8s.io/api/networking/v1.NetworkPolicyEgressRule": schema_k8sio_api_networking_v1_NetworkPolicyEgressRule(ref), + "k8s.io/api/networking/v1.NetworkPolicyIngressRule": schema_k8sio_api_networking_v1_NetworkPolicyIngressRule(ref), + "k8s.io/api/networking/v1.NetworkPolicyList": schema_k8sio_api_networking_v1_NetworkPolicyList(ref), + "k8s.io/api/networking/v1.NetworkPolicyPeer": schema_k8sio_api_networking_v1_NetworkPolicyPeer(ref), + "k8s.io/api/networking/v1.NetworkPolicyPort": schema_k8sio_api_networking_v1_NetworkPolicyPort(ref), + "k8s.io/api/networking/v1.NetworkPolicySpec": schema_k8sio_api_networking_v1_NetworkPolicySpec(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), + "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), + "k8s.io/apimachinery/pkg/util/intstr.IntOrString": schema_apimachinery_pkg_util_intstr_IntOrString(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicyList(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector": schema_pkg_apis_network_v1alpha1_NamespaceSelector(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyEgressRule(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyIngressRule(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer": schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref), + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector": schema_pkg_apis_network_v1alpha1_ServiceSelector(ref), } } @@ -2509,191 +2503,13 @@ func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.Ope } } -func schema_pkg_apis_network_v1alpha1_EntityRule(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_apimachinery_pkg_util_intstr_IntOrString(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "An EntityRule is a sub-component of a Rule comprising the match criteria specific to a particular entity (that is either the source or destination).\n\nA source EntityRule matches the source endpoint and originating traffic. A destination EntityRule matches the destination endpoint and terminating traffic.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "nets": { - SchemaProps: spec.SchemaProps{ - Description: "Nets is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) IP addresses in any of the given subnets.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "Selector is an optional field that contains a selector expression (see Policy for sample syntax). Only traffic that originates from (terminates at) endpoints matching the selector will be matched.\n\nNote that: in addition to the negated version of the Selector (see NotSelector below), the selector expression syntax itself supports negation. The two types of negation are subtly different. One negates the set of matched endpoints, the other negates the whole match:\n\n\tSelector = \"!has(my_label)\" matches packets that are from other Calico-controlled\n\tendpoints that do not have the label “my_labelâ€.\n\n\tNotSelector = \"has(my_label)\" matches packets that are not from Calico-controlled\n\tendpoints that do have the label “my_labelâ€.\n\nThe effect is that the latter will accept packets from non-Calico sources whereas the former is limited to packets from Calico-controlled endpoints.", - Type: []string{"string"}, - Format: "", - }, - }, - "namespaceSelector": { - SchemaProps: spec.SchemaProps{ - Description: "NamespaceSelector is an optional field that contains a selector expression. Only traffic that originates from (or terminates at) endpoints within the selected namespaces will be matched. When both NamespaceSelector and Selector are defined on the same rule, then only workload endpoints that are matched by both selectors will be selected by the rule.\n\nFor NetworkPolicy, an empty NamespaceSelector implies that the Selector is limited to selecting only workload endpoints in the same namespace as the NetworkPolicy.\n\nFor GlobalNetworkPolicy, an empty NamespaceSelector implies the Selector applies to workload endpoints across all namespaces.", - Type: []string{"string"}, - Format: "", - }, - }, - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "Ports is an optional field that restricts the rule to only apply to traffic that has a source (destination) port that matches one of these ranges/values. This value is a list of integers or strings that represent ranges of ports.\n\nSince only some protocols have ports, if any ports are specified it requires the Protocol match in the Rule to be set to \"TCP\" or \"UDP\".", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"), - }, - }, - }, - }, - }, - "notNets": { - SchemaProps: spec.SchemaProps{ - Description: "NotNets is the negated version of the Nets field.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "notSelector": { - SchemaProps: spec.SchemaProps{ - Description: "NotSelector is the negated version of the Selector field. See Selector field for subtleties with negated selectors.", - Type: []string{"string"}, - Format: "", - }, - }, - "notPorts": { - SchemaProps: spec.SchemaProps{ - Description: "NotPorts is the negated version of the Ports field. Since only some protocols have ports, if any ports are specified it requires the Protocol match in the Rule to be set to \"TCP\" or \"UDP\".", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"), - }, - }, - }, - }, - }, - "serviceAccounts": { - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccounts is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a matching service account.", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceAccountMatch", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring.Port"}, - } -} - -func schema_pkg_apis_network_v1alpha1_HTTPMatch(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "HTTPMatch is an optional field that apply only to HTTP requests The Methods and Path fields are joined with AND", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "methods": { - SchemaProps: spec.SchemaProps{ - Description: "Methods is an optional field that restricts the rule to apply only to HTTP requests that use one of the listed HTTP Methods (e.g. GET, PUT, etc.) Multiple methods are OR'd together.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "paths": { - SchemaProps: spec.SchemaProps{ - Description: "Paths is an optional field that restricts the rule to apply to HTTP requests that use one of the listed HTTP Paths. Multiple paths are OR'd together. e.g: - exact: /foo - prefix: /bar NOTE: Each entry may ONLY specify either a `exact` or a `prefix` match. The validator will check for it.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPPath"}, - } -} - -func schema_pkg_apis_network_v1alpha1_HTTPPath(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "HTTPPath specifies an HTTP path to match. It may be either of the form: exact: : which matches the path exactly or prefix: : which matches the path prefix", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "exact": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "prefix": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_network_v1alpha1_ICMPFields(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ICMPFields defines structure for ICMP and NotICMP sub-struct for ICMP code and type", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Match on a specific ICMP type. For example a value of 8 refers to ICMP Echo Request (i.e. pings).", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "code": { - SchemaProps: spec.SchemaProps{ - Description: "Match on a specific ICMP code. If specified, the Type value must also be specified. This is a technical limitation imposed by the kernel’s iptables firewall, which Calico uses to enforce the rule.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, + Description: "IntOrString is a type that can hold an int32 or a string. When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type. This allows you to have, for example, a JSON field that can accept a name or number.", + Type: intstr.IntOrString{}.OpenAPISchemaType(), + Format: intstr.IntOrString{}.OpenAPISchemaFormat(), }, }, } @@ -2789,417 +2605,9 @@ func schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref common.Refe return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "NamespaceNetworkPolicySpec defines the desired state of NamespaceNetworkPolicy", + Description: "NamespaceNetworkPolicySpec provides the specification of a NamespaceNetworkPolicy", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "order": { - SchemaProps: spec.SchemaProps{ - Description: "Order is an optional field that specifies the order in which the policy is applied. Policies with higher \"order\" are applied after those with lower order. If the order is omitted, it may be considered to be \"infinite\" - i.e. the policy will be applied last. Policies with identical order will be applied in alphanumerical order based on the Policy \"Name\".", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "ingress": { - SchemaProps: spec.SchemaProps{ - Description: "The ordered set of ingress rules. Each rule contains a set of packet match criteria and a corresponding action to apply.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"), - }, - }, - }, - }, - }, - "egress": { - SchemaProps: spec.SchemaProps{ - Description: "The ordered set of egress rules. Each rule contains a set of packet match criteria and a corresponding action to apply.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"), - }, - }, - }, - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "The selector is an expression used to pick pick out the endpoints that the policy should be applied to.\n\nSelector expressions follow this syntax:\n\n\tlabel == \"string_literal\" -> comparison, e.g. my_label == \"foo bar\"\n\tlabel != \"string_literal\" -> not equal; also matches if label is not present\n\tlabel in { \"a\", \"b\", \"c\", ... } -> true if the value of label X is one of \"a\", \"b\", \"c\"\n\tlabel not in { \"a\", \"b\", \"c\", ... } -> true if the value of label X is not one of \"a\", \"b\", \"c\"\n\thas(label_name) -> True if that label is present\n\t! expr -> negation of expr\n\texpr && expr -> Short-circuit and\n\texpr || expr -> Short-circuit or\n\t( expr ) -> parens for grouping\n\tall() or the empty selector -> matches all endpoints.\n\nLabel names are allowed to contain alphanumerics, -, _ and /. String literals are more permissive but they do not support escape characters.\n\nExamples (with made-up labels):\n\n\ttype == \"webserver\" && deployment == \"prod\"\n\ttype in {\"frontend\", \"backend\"}\n\tdeployment != \"dev\"\n\t! has(label_name)", - Type: []string{"string"}, - Format: "", - }, - }, - "types": { - SchemaProps: spec.SchemaProps{ - Description: "Types indicates whether this policy applies to ingress, or to egress, or to both. When not explicitly specified (and so the value on creation is empty or nil), Calico defaults Types according to what Ingress and Egress are present in the policy. The default is:\n\n- [ PolicyTypeIngress ], if there are no Egress rules (including the case where there are\n also no Ingress rules)\n\n- [ PolicyTypeEgress ], if there are Egress rules but no Ingress rules\n\n- [ PolicyTypeIngress, PolicyTypeEgress ], if there are both Ingress and Egress rules.\n\nWhen the policy is read back again, Types will always be one of these values, never empty or nil.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"selector"}, - }, - }, - Dependencies: []string{ - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Rule"}, - } -} - -func schema_pkg_apis_network_v1alpha1_Rule(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A Rule encapsulates a set of match criteria and an action. Both selector-based security Policy and security Profiles reference rules - separated out as a list of rules for both ingress and egress packet matching.\n\nEach positive match criteria has a negated version, prefixed with â€Notâ€. All the match criteria within a rule must be satisfied for a packet to match. A single rule can contain the positive and negative version of a match and both must be satisfied for the rule to match.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "action": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "ipVersion": { - SchemaProps: spec.SchemaProps{ - Description: "IPVersion is an optional field that restricts the rule to only match a specific IP version.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "protocol": { - SchemaProps: spec.SchemaProps{ - Description: "Protocol is an optional field that restricts the rule to only apply to traffic of a specific IP protocol. Required if any of the EntityRules contain Ports (because ports only apply to certain protocols).\n\nMust be one of these string values: \"TCP\", \"UDP\", \"ICMP\", \"ICMPv6\", \"SCTP\", \"UDPLite\" or an integer in the range 1-255.", - Type: []string{"string"}, - Format: "", - }, - }, - "icmp": { - SchemaProps: spec.SchemaProps{ - Description: "ICMP is an optional field that restricts the rule to apply to a specific type and code of ICMP traffic. This should only be specified if the Protocol field is set to \"ICMP\" or \"ICMPv6\".", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"), - }, - }, - "notProtocol": { - SchemaProps: spec.SchemaProps{ - Description: "NotProtocol is the negated version of the Protocol field.", - Type: []string{"string"}, - Format: "", - }, - }, - "notICMP": { - SchemaProps: spec.SchemaProps{ - Description: "NotICMP is the negated version of the ICMP field.", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"), - }, - }, - "source": { - SchemaProps: spec.SchemaProps{ - Description: "Source contains the match criteria that apply to source entity.", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule"), - }, - }, - "destination": { - SchemaProps: spec.SchemaProps{ - Description: "Destination contains the match criteria that apply to destination entity.", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule"), - }, - }, - "http": { - SchemaProps: spec.SchemaProps{ - Description: "HTTP contains match criteria that apply to HTTP requests.", - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch"), - }, - }, - }, - Required: []string{"action"}, - }, - }, - Dependencies: []string{ - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.EntityRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.HTTPMatch", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ICMPFields"}, - } -} - -func schema_pkg_apis_network_v1alpha1_ServiceAccountMatch(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "names": { - SchemaProps: spec.SchemaProps{ - Description: "Names is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a service account whose name is in the list.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "Selector is an optional field that restricts the rule to only apply to traffic that originates from (or terminates at) a pod running as a service account that matches the given label selector. If both Names and Selector are specified then they are AND'ed.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicySpec", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyStatus"}, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyEgressRule(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and to.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"), - }, - }, - }, - }, - }, - "from": { - SchemaProps: spec.SchemaProps{ - Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"}, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyIngressRule(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and from.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"), - }, - }, - }, - }, - }, - "from": { - SchemaProps: spec.SchemaProps{ - Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least on item, this rule allows traffic only if the traffic matches at least one item in the from list.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyPeer"}, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicyList contains a list of WorkspaceNetworkPolicy", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicy"}, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyPeer(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed. It is same as 'NetworkPolicyPeer' in k8s but with an additional field 'WorkspaceSelector'", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "podSelector": { - SchemaProps: spec.SchemaProps{ - Description: "This is a label selector which selects Pods. This field follows standard label selector semantics; if present but empty, it selects all pods.\n\nIf NamespaceSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects the Pods matching PodSelector in the policy's own Namespace.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - "namespaceSelector": { - SchemaProps: spec.SchemaProps{ - Description: "Selects Namespaces using cluster-scoped labels. This field follows standard label selector semantics; if present but empty, it selects all namespaces.\n\nIf PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Pods matching PodSelector in the Namespaces selected by NamespaceSelector. Otherwise it selects all Pods in the Namespaces selected by NamespaceSelector.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - "ipBlock": { - SchemaProps: spec.SchemaProps{ - Description: "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.", - Ref: ref("k8s.io/api/networking/v1.IPBlock"), - }, - }, - "workspaceSelector": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/networking/v1.IPBlock", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, - } -} - -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "workspace": { - SchemaProps: spec.SchemaProps{ - Description: "Workspace specify the name of ws to apply this workspace network policy", - Type: []string{"string"}, - Format: "", - }, - }, - "policyTypes": { - SchemaProps: spec.SchemaProps{ - Description: "List of rule types that the WorkspaceNetworkPolicy relates to. Valid options are Ingress, Egress, or Ingress,Egress. If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]).", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, "ingress": { SchemaProps: spec.SchemaProps{ Description: "List of ingress rules to be applied to the selected pods. Traffic is allowed to a pod if there are no NetworkPolicies selecting the pod (and cluster policy otherwise allows the traffic), OR if the traffic source is the pod's local node, OR if the traffic matches at least one ingress rule across all of the NetworkPolicy objects whose podSelector matches the pod. If this field is empty then this NetworkPolicy does not allow any traffic (and serves solely to ensure that the pods it selects are isolated by default)", @@ -3207,7 +2615,7 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule"), + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule"), }, }, }, @@ -3220,7 +2628,21 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule"), + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule"), + }, + }, + }, + }, + }, + "policyTypes": { + SchemaProps: spec.SchemaProps{ + Description: "List of rule types that the NetworkPolicy relates to. Valid options are \"Ingress\", \"Egress\", or \"Ingress,Egress\". If this field is not specified, it will default based on the existence of Ingress or Egress rules; policies that contain an Egress section are assumed to affect Egress, and all policies (whether or not they contain an Ingress section) are assumed to affect Ingress. If you want to write an egress-only policy, you must explicitly specify policyTypes [ \"Egress\" ]. Likewise, if you want to write a policy that specifies that no egress is allowed, you must specify a policyTypes value that include \"Egress\" (since such a policy would not include an Egress section and would otherwise default to just [ \"Ingress\" ]). This field is beta-level in 1.8", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", }, }, }, @@ -3230,16 +2652,162 @@ func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicySpec(ref common.Refe }, }, Dependencies: []string{ - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyEgressRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceNetworkPolicyIngressRule"}, + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule"}, } } -func schema_pkg_apis_network_v1alpha1_WorkspaceNetworkPolicyStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_network_v1alpha1_NamespaceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "WorkspaceNetworkPolicyStatus defines the observed state of WorkspaceNetworkPolicy", - Type: []string{"object"}, + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name"}, + }, + }, + } +} + +func schema_pkg_apis_network_v1alpha1_NetworkPolicyEgressRule(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and to. This type is beta-level in 1.8", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "List of destination ports for outgoing traffic. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"), + }, + }, + }, + }, + }, + "to": { + SchemaProps: spec.SchemaProps{ + Description: "List of destinations for outgoing traffic of pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all destinations (traffic not restricted by destination). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the to list.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"}, + } +} + +func schema_pkg_apis_network_v1alpha1_NetworkPolicyIngressRule(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods matched by a NetworkPolicySpec's podSelector. The traffic must match both ports and from.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "ports": { + SchemaProps: spec.SchemaProps{ + Description: "List of ports which should be made accessible on the pods selected for this rule. Each item in this list is combined using a logical OR. If this field is empty or missing, this rule matches all ports (traffic not restricted by port). If this field is present and contains at least one item, then this rule allows traffic only if the traffic matches at least one port in the list.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/networking/v1.NetworkPolicyPort"), + }, + }, + }, + }, + }, + "from": { + SchemaProps: spec.SchemaProps{ + Description: "List of sources which should be able to access the pods selected for this rule. Items in this list are combined using a logical OR operation. If this field is empty or missing, this rule matches all sources (traffic not restricted by source). If this field is present and contains at least one item, this rule allows traffic only if the traffic matches at least one item in the from list.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/networking/v1.NetworkPolicyPort", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer"}, + } +} + +func schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of fields are allowed", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "namespace": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector"), + }, + }, + "ipBlock": { + SchemaProps: spec.SchemaProps{ + Description: "IPBlock defines policy on a particular IPBlock. If this field is set then neither of the other fields can be.", + Ref: ref("k8s.io/api/networking/v1.IPBlock"), + }, + }, + "service": { + SchemaProps: spec.SchemaProps{ + Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/networking/v1.IPBlock", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceSelector", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector"}, + } +} + +func schema_pkg_apis_network_v1alpha1_ServiceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "namespace": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "namespace"}, }, }, } diff --git a/pkg/apis/network/v1alpha1/v1alpha1_suite_test.go b/pkg/apis/network/v1alpha1/v1alpha1_suite_test.go index 96cd648d8..2206160be 100644 --- a/pkg/apis/network/v1alpha1/v1alpha1_suite_test.go +++ b/pkg/apis/network/v1alpha1/v1alpha1_suite_test.go @@ -33,7 +33,7 @@ var c client.Client func TestMain(m *testing.M) { t := &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "kustomize", "network", "crds")}, + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, } err := SchemeBuilder.AddToScheme(scheme.Scheme) diff --git a/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types.go b/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types.go deleted file mode 100644 index ba37f7df2..000000000 --- a/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 v1alpha1 - -import ( - k8snetworkv1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - ResourceKindWorkspaceNetworkPolicy = "WorkspaceNetworkPolicy" - ResourceSingularWorkspaceNetworkPolicy = "workspacenetworkpolicy" - ResourcePluralWorkspaceNetworkPolicy = "workspacenetworkpolicies" -) - -// WorkspaceNetworkPolicySpec defines the desired state of WorkspaceNetworkPolicy -type WorkspaceNetworkPolicySpec struct { - // Workspace specify the name of ws to apply this workspace network policy - Workspace string `json:"workspace,omitempty"` - // List of rule types that the WorkspaceNetworkPolicy relates to. - // Valid options are Ingress, Egress, or Ingress,Egress. - // If this field is not specified, it will default based on the existence of Ingress or Egress rules; - // policies that contain an Egress section are assumed to affect Egress, and all policies - // (whether or not they contain an Ingress section) are assumed to affect Ingress. - // If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ]. - // Likewise, if you want to write a policy that specifies that no egress is allowed, - // you must specify a policyTypes value that include "Egress" (since such a policy would not include - // an Egress section and would otherwise default to just [ "Ingress" ]). - // +optional - PolicyTypes []k8snetworkv1.PolicyType `json:"policyTypes,omitempty" protobuf:"bytes,4,rep,name=policyTypes,casttype=PolicyType"` - // List of ingress rules to be applied to the selected pods. Traffic is allowed to - // a pod if there are no NetworkPolicies selecting the pod - // (and cluster policy otherwise allows the traffic), OR if the traffic source is - // the pod's local node, OR if the traffic matches at least one ingress rule - // across all of the NetworkPolicy objects whose podSelector matches the pod. If - // this field is empty then this NetworkPolicy does not allow any traffic (and serves - // solely to ensure that the pods it selects are isolated by default) - // +optional - Ingress []WorkspaceNetworkPolicyIngressRule `json:"ingress,omitempty" protobuf:"bytes,2,rep,name=ingress"` - - // List of egress rules to be applied to the selected pods. Outgoing traffic is - // allowed if there are no NetworkPolicies selecting the pod (and cluster policy - // otherwise allows the traffic), OR if the traffic matches at least one egress rule - // across all of the NetworkPolicy objects whose podSelector matches the pod. If - // this field is empty then this NetworkPolicy limits all outgoing traffic (and serves - // solely to ensure that the pods it selects are isolated by default). - // This field is beta-level in 1.8 - // +optional - Egress []WorkspaceNetworkPolicyEgressRule `json:"egress,omitempty" protobuf:"bytes,3,rep,name=egress"` -} - -// WorkspaceNetworkPolicyStatus defines the observed state of WorkspaceNetworkPolicy -type WorkspaceNetworkPolicyStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// WorkspaceNetworkPolicy is a set of network policies applied to the scope to workspace -// +k8s:openapi-gen=true -// +kubebuilder:resource:categories="networking",scope="Cluster",shortName="wsnp" -type WorkspaceNetworkPolicy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec WorkspaceNetworkPolicySpec `json:"spec,omitempty"` - Status WorkspaceNetworkPolicyStatus `json:"status,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// WorkspaceNetworkPolicyList contains a list of WorkspaceNetworkPolicy -type WorkspaceNetworkPolicyList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []WorkspaceNetworkPolicy `json:"items"` -} - -// WorkspaceNetworkPolicyIngressRule describes a particular set of traffic that is allowed to the pods -// matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and from. -type WorkspaceNetworkPolicyIngressRule struct { - // List of ports which should be made accessible on the pods selected for this - // rule. Each item in this list is combined using a logical OR. If this field is - // empty or missing, this rule matches all ports (traffic not restricted by port). - // If this field is present and contains at least one item, then this rule allows - // traffic only if the traffic matches at least one port in the list. - // +optional - Ports []k8snetworkv1.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"` - - // List of sources which should be able to access the pods selected for this rule. - // Items in this list are combined using a logical OR operation. If this field is - // empty or missing, this rule matches all sources (traffic not restricted by - // source). If this field is present and contains at least on item, this rule - // allows traffic only if the traffic matches at least one item in the from list. - // +optional - From []WorkspaceNetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"` -} - -// WorkspaceNetworkPolicyPeer describes a peer to allow traffic from. Only certain combinations of -// fields are allowed. It is same as 'NetworkPolicyPeer' in k8s but with an additional field 'WorkspaceSelector' -type WorkspaceNetworkPolicyPeer struct { - k8snetworkv1.NetworkPolicyPeer `json:",inline"` - WorkspaceSelector *metav1.LabelSelector `json:"workspaceSelector,omitempty"` -} - -// WorkspaceNetworkPolicyEgressRule describes a particular set of traffic that is allowed out of pods -// matched by a WorkspaceNetworkPolicySpec's podSelector. The traffic must match both ports and to. -type WorkspaceNetworkPolicyEgressRule struct { - // List of ports which should be made accessible on the pods selected for this - // rule. Each item in this list is combined using a logical OR. If this field is - // empty or missing, this rule matches all ports (traffic not restricted by port). - // If this field is present and contains at least one item, then this rule allows - // traffic only if the traffic matches at least one port in the list. - // +optional - Ports []k8snetworkv1.NetworkPolicyPort `json:"ports,omitempty" protobuf:"bytes,1,rep,name=ports"` - - // List of sources which should be able to access the pods selected for this rule. - // Items in this list are combined using a logical OR operation. If this field is - // empty or missing, this rule matches all sources (traffic not restricted by - // source). If this field is present and contains at least on item, this rule - // allows traffic only if the traffic matches at least one item in the from list. - // +optional - To []WorkspaceNetworkPolicyPeer `json:"from,omitempty" protobuf:"bytes,2,rep,name=from"` -} - -func init() { - SchemeBuilder.Register(&WorkspaceNetworkPolicy{}, &WorkspaceNetworkPolicyList{}) -} diff --git a/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types_test.go b/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types_test.go deleted file mode 100644 index 134655158..000000000 --- a/pkg/apis/network/v1alpha1/workspacenetworkpolicy_types_test.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 v1alpha1 - -import ( - "testing" - - "github.com/onsi/gomega" - "golang.org/x/net/context" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" -) - -func TestStorageWorkspaceNetworkPolicy(t *testing.T) { - key := types.NamespacedName{ - Name: "foo", - } - created := &WorkspaceNetworkPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - }} - g := gomega.NewGomegaWithT(t) - - // Test Create - fetched := &WorkspaceNetworkPolicy{} - g.Expect(c.Create(context.TODO(), created)).To(gomega.Succeed()) - - g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) - g.Expect(fetched).To(gomega.Equal(created)) - - // Test Updating the Labels - updated := fetched.DeepCopy() - updated.Labels = map[string]string{"hello": "world"} - g.Expect(c.Update(context.TODO(), updated)).To(gomega.Succeed()) - - g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.Succeed()) - g.Expect(fetched).To(gomega.Equal(updated)) - - // Test Delete - g.Expect(c.Delete(context.TODO(), fetched)).To(gomega.Succeed()) - g.Expect(c.Get(context.TODO(), key, fetched)).ToNot(gomega.Succeed()) -} diff --git a/pkg/apis/network/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/network/v1alpha1/zz_generated.deepcopy.go index d2a563ca8..770ebc77f 100644 --- a/pkg/apis/network/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/network/v1alpha1/zz_generated.deepcopy.go @@ -21,122 +21,10 @@ limitations under the License. package v1alpha1 import ( - v1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/api/networking/v1" runtime "k8s.io/apimachinery/pkg/runtime" - numorstring "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1/numorstring" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EntityRule) DeepCopyInto(out *EntityRule) { - *out = *in - if in.Nets != nil { - in, out := &in.Nets, &out.Nets - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Ports != nil { - in, out := &in.Ports, &out.Ports - *out = make([]numorstring.Port, len(*in)) - copy(*out, *in) - } - if in.NotNets != nil { - in, out := &in.NotNets, &out.NotNets - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.NotPorts != nil { - in, out := &in.NotPorts, &out.NotPorts - *out = make([]numorstring.Port, len(*in)) - copy(*out, *in) - } - if in.ServiceAccounts != nil { - in, out := &in.ServiceAccounts, &out.ServiceAccounts - *out = new(ServiceAccountMatch) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EntityRule. -func (in *EntityRule) DeepCopy() *EntityRule { - if in == nil { - return nil - } - out := new(EntityRule) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPMatch) DeepCopyInto(out *HTTPMatch) { - *out = *in - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Paths != nil { - in, out := &in.Paths, &out.Paths - *out = make([]HTTPPath, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatch. -func (in *HTTPMatch) DeepCopy() *HTTPMatch { - if in == nil { - return nil - } - out := new(HTTPMatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPPath) DeepCopyInto(out *HTTPPath) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPath. -func (in *HTTPPath) DeepCopy() *HTTPPath { - if in == nil { - return nil - } - out := new(HTTPPath) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ICMPFields) DeepCopyInto(out *ICMPFields) { - *out = *in - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(int) - **out = **in - } - if in.Code != nil { - in, out := &in.Code, &out.Code - *out = new(int) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ICMPFields. -func (in *ICMPFields) DeepCopy() *ICMPFields { - if in == nil { - return nil - } - out := new(ICMPFields) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NamespaceNetworkPolicy) DeepCopyInto(out *NamespaceNetworkPolicy) { *out = *in @@ -168,7 +56,7 @@ func (in *NamespaceNetworkPolicy) DeepCopyObject() runtime.Object { func (in *NamespaceNetworkPolicyList) DeepCopyInto(out *NamespaceNetworkPolicyList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]NamespaceNetworkPolicy, len(*in)) @@ -200,28 +88,23 @@ func (in *NamespaceNetworkPolicyList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NamespaceNetworkPolicySpec) DeepCopyInto(out *NamespaceNetworkPolicySpec) { *out = *in - if in.Order != nil { - in, out := &in.Order, &out.Order - *out = new(int) - **out = **in - } if in.Ingress != nil { in, out := &in.Ingress, &out.Ingress - *out = make([]Rule, len(*in)) + *out = make([]NetworkPolicyIngressRule, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.Egress != nil { in, out := &in.Egress, &out.Egress - *out = make([]Rule, len(*in)) + *out = make([]NetworkPolicyEgressRule, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Types != nil { - in, out := &in.Types, &out.Types - *out = make([]PolicyType, len(*in)) + if in.PolicyTypes != nil { + in, out := &in.PolicyTypes, &out.PolicyTypes + *out = make([]v1.PolicyType, len(*in)) copy(*out, *in) } return @@ -238,115 +121,34 @@ func (in *NamespaceNetworkPolicySpec) DeepCopy() *NamespaceNetworkPolicySpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Rule) DeepCopyInto(out *Rule) { +func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { *out = *in - if in.IPVersion != nil { - in, out := &in.IPVersion, &out.IPVersion - *out = new(int) - **out = **in - } - if in.Protocol != nil { - in, out := &in.Protocol, &out.Protocol - *out = new(v1.Protocol) - **out = **in - } - if in.ICMP != nil { - in, out := &in.ICMP, &out.ICMP - *out = new(ICMPFields) - (*in).DeepCopyInto(*out) - } - if in.NotProtocol != nil { - in, out := &in.NotProtocol, &out.NotProtocol - *out = new(v1.Protocol) - **out = **in - } - if in.NotICMP != nil { - in, out := &in.NotICMP, &out.NotICMP - *out = new(ICMPFields) - (*in).DeepCopyInto(*out) - } - in.Source.DeepCopyInto(&out.Source) - in.Destination.DeepCopyInto(&out.Destination) - if in.HTTP != nil { - in, out := &in.HTTP, &out.HTTP - *out = new(HTTPMatch) - (*in).DeepCopyInto(*out) - } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule. -func (in *Rule) DeepCopy() *Rule { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. +func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { if in == nil { return nil } - out := new(Rule) + out := new(NamespaceSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceAccountMatch) DeepCopyInto(out *ServiceAccountMatch) { - *out = *in - if in.Names != nil { - in, out := &in.Names, &out.Names - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccountMatch. -func (in *ServiceAccountMatch) DeepCopy() *ServiceAccountMatch { - if in == nil { - return nil - } - out := new(ServiceAccountMatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceNetworkPolicy) DeepCopyInto(out *WorkspaceNetworkPolicy) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicy. -func (in *WorkspaceNetworkPolicy) DeepCopy() *WorkspaceNetworkPolicy { - if in == nil { - return nil - } - out := new(WorkspaceNetworkPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *WorkspaceNetworkPolicy) 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 *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPolicyEgressRule) { +func (in *NetworkPolicyEgressRule) DeepCopyInto(out *NetworkPolicyEgressRule) { *out = *in if in.Ports != nil { in, out := &in.Ports, &out.Ports - *out = make([]networkingv1.NetworkPolicyPort, len(*in)) + *out = make([]v1.NetworkPolicyPort, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.To != nil { in, out := &in.To, &out.To - *out = make([]WorkspaceNetworkPolicyPeer, len(*in)) + *out = make([]NetworkPolicyPeer, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -354,29 +156,29 @@ func (in *WorkspaceNetworkPolicyEgressRule) DeepCopyInto(out *WorkspaceNetworkPo return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyEgressRule. -func (in *WorkspaceNetworkPolicyEgressRule) DeepCopy() *WorkspaceNetworkPolicyEgressRule { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyEgressRule. +func (in *NetworkPolicyEgressRule) DeepCopy() *NetworkPolicyEgressRule { if in == nil { return nil } - out := new(WorkspaceNetworkPolicyEgressRule) + out := new(NetworkPolicyEgressRule) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkPolicyIngressRule) { +func (in *NetworkPolicyIngressRule) DeepCopyInto(out *NetworkPolicyIngressRule) { *out = *in if in.Ports != nil { in, out := &in.Ports, &out.Ports - *out = make([]networkingv1.NetworkPolicyPort, len(*in)) + *out = make([]v1.NetworkPolicyPort, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.From != nil { in, out := &in.From, &out.From - *out = make([]WorkspaceNetworkPolicyPeer, len(*in)) + *out = make([]NetworkPolicyPeer, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -384,118 +186,59 @@ func (in *WorkspaceNetworkPolicyIngressRule) DeepCopyInto(out *WorkspaceNetworkP return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyIngressRule. -func (in *WorkspaceNetworkPolicyIngressRule) DeepCopy() *WorkspaceNetworkPolicyIngressRule { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyIngressRule. +func (in *NetworkPolicyIngressRule) DeepCopy() *NetworkPolicyIngressRule { if in == nil { return nil } - out := new(WorkspaceNetworkPolicyIngressRule) + out := new(NetworkPolicyIngressRule) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceNetworkPolicyList) DeepCopyInto(out *WorkspaceNetworkPolicyList) { +func (in *NetworkPolicyPeer) DeepCopyInto(out *NetworkPolicyPeer) { *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]WorkspaceNetworkPolicy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(NamespaceSelector) + **out = **in } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyList. -func (in *WorkspaceNetworkPolicyList) DeepCopy() *WorkspaceNetworkPolicyList { - if in == nil { - return nil - } - out := new(WorkspaceNetworkPolicyList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *WorkspaceNetworkPolicyList) 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 *WorkspaceNetworkPolicyPeer) DeepCopyInto(out *WorkspaceNetworkPolicyPeer) { - *out = *in - in.NetworkPolicyPeer.DeepCopyInto(&out.NetworkPolicyPeer) - if in.WorkspaceSelector != nil { - in, out := &in.WorkspaceSelector, &out.WorkspaceSelector - *out = new(metav1.LabelSelector) + if in.IPBlock != nil { + in, out := &in.IPBlock, &out.IPBlock + *out = new(v1.IPBlock) (*in).DeepCopyInto(*out) } + if in.ServiceSelector != nil { + in, out := &in.ServiceSelector, &out.ServiceSelector + *out = new(ServiceSelector) + **out = **in + } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyPeer. -func (in *WorkspaceNetworkPolicyPeer) DeepCopy() *WorkspaceNetworkPolicyPeer { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicyPeer. +func (in *NetworkPolicyPeer) DeepCopy() *NetworkPolicyPeer { if in == nil { return nil } - out := new(WorkspaceNetworkPolicyPeer) + out := new(NetworkPolicyPeer) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceNetworkPolicySpec) DeepCopyInto(out *WorkspaceNetworkPolicySpec) { - *out = *in - if in.PolicyTypes != nil { - in, out := &in.PolicyTypes, &out.PolicyTypes - *out = make([]networkingv1.PolicyType, len(*in)) - copy(*out, *in) - } - if in.Ingress != nil { - in, out := &in.Ingress, &out.Ingress - *out = make([]WorkspaceNetworkPolicyIngressRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Egress != nil { - in, out := &in.Egress, &out.Egress - *out = make([]WorkspaceNetworkPolicyEgressRule, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicySpec. -func (in *WorkspaceNetworkPolicySpec) DeepCopy() *WorkspaceNetworkPolicySpec { - if in == nil { - return nil - } - out := new(WorkspaceNetworkPolicySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceNetworkPolicyStatus) DeepCopyInto(out *WorkspaceNetworkPolicyStatus) { +func (in *ServiceSelector) DeepCopyInto(out *ServiceSelector) { *out = *in return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceNetworkPolicyStatus. -func (in *WorkspaceNetworkPolicyStatus) DeepCopy() *WorkspaceNetworkPolicyStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSelector. +func (in *ServiceSelector) DeepCopy() *ServiceSelector { if in == nil { return nil } - out := new(WorkspaceNetworkPolicyStatus) + out := new(ServiceSelector) in.DeepCopyInto(out) return out } diff --git a/pkg/apis/rbac/v1/evaluation_helpers.go b/pkg/apis/rbac/v1/evaluation_helpers.go new file mode 100644 index 000000000..3707760bf --- /dev/null +++ b/pkg/apis/rbac/v1/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/apis/servicemesh/v1alpha2/openapi_generated.go b/pkg/apis/servicemesh/v1alpha2/openapi_generated.go index 7c459e159..55788b1a8 100644 --- a/pkg/apis/servicemesh/v1alpha2/openapi_generated.go +++ b/pkg/apis/servicemesh/v1alpha2/openapi_generated.go @@ -2173,7 +2173,7 @@ func schema_pkg_apis_servicemesh_v1alpha2_DestinationRuleSpecTemplate(ref common SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ - "ObjectMeta": { + "metadata": { SchemaProps: spec.SchemaProps{ Description: "Metadata of the virtual services created from this template", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), @@ -2244,40 +2244,40 @@ func schema_pkg_apis_servicemesh_v1alpha2_ServicePolicyCondition(ref common.Refe Description: "StrategyCondition describes current state of a strategy.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "Type": { + "type": { SchemaProps: spec.SchemaProps{ Description: "Type of strategy condition, Complete or Failed.", Type: []string{"string"}, Format: "", }, }, - "Status": { + "status": { SchemaProps: spec.SchemaProps{ Description: "Status of the condition, one of True, False, Unknown", Type: []string{"string"}, Format: "", }, }, - "LastProbeTime": { + "lastProbeTime": { SchemaProps: spec.SchemaProps{ Description: "Last time the condition was checked.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "LastTransitionTime": { + "lastTransitionTime": { SchemaProps: spec.SchemaProps{ Description: "Last time the condition transit from one status to another", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "Reason": { + "reason": { SchemaProps: spec.SchemaProps{ Description: "reason for the condition's last transition", Type: []string{"string"}, Format: "", }, }, - "Message": { + "message": { SchemaProps: spec.SchemaProps{ Description: "Human readable message indicating details about last transition.", Type: []string{"string"}, @@ -2285,7 +2285,6 @@ func schema_pkg_apis_servicemesh_v1alpha2_ServicePolicyCondition(ref common.Refe }, }, }, - Required: []string{"Type", "Status", "Reason", "Message"}, }, }, Dependencies: []string{ @@ -2374,7 +2373,7 @@ func schema_pkg_apis_servicemesh_v1alpha2_ServicePolicyStatus(ref common.Referen Description: "ServicePolicyStatus defines the observed state of ServicePolicy", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "Conditions": { + "conditions": { SchemaProps: spec.SchemaProps{ Description: "The latest available observations of an object's current state.", Type: []string{"array"}, @@ -2387,13 +2386,13 @@ func schema_pkg_apis_servicemesh_v1alpha2_ServicePolicyStatus(ref common.Referen }, }, }, - "StartTime": { + "startTime": { SchemaProps: spec.SchemaProps{ Description: "Represents time when the strategy was acknowledged by the controller. It is represented in RFC3339 form and is in UTC.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "CompletionTime": { + "completionTime": { SchemaProps: spec.SchemaProps{ Description: "Represents time when the strategy was completed. It is represented in RFC3339 form and is in UTC.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), @@ -2458,40 +2457,40 @@ func schema_pkg_apis_servicemesh_v1alpha2_StrategyCondition(ref common.Reference Description: "StrategyCondition describes current state of a strategy.", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "Type": { + "type": { SchemaProps: spec.SchemaProps{ Description: "Type of strategy condition, Complete or Failed.", Type: []string{"string"}, Format: "", }, }, - "Status": { + "status": { SchemaProps: spec.SchemaProps{ Description: "Status of the condition, one of True, False, Unknown", Type: []string{"string"}, Format: "", }, }, - "LastProbeTime": { + "lastProbeTime": { SchemaProps: spec.SchemaProps{ Description: "Last time the condition was checked.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "LastTransitionTime": { + "lastTransitionTime": { SchemaProps: spec.SchemaProps{ Description: "Last time the condition transit from one status to another", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "Reason": { + "reason": { SchemaProps: spec.SchemaProps{ Description: "reason for the condition's last transition", Type: []string{"string"}, Format: "", }, }, - "Message": { + "message": { SchemaProps: spec.SchemaProps{ Description: "Human readable message indicating details about last transition.", Type: []string{"string"}, @@ -2499,7 +2498,6 @@ func schema_pkg_apis_servicemesh_v1alpha2_StrategyCondition(ref common.Reference }, }, }, - Required: []string{"Type", "Status", "Reason", "Message"}, }, }, Dependencies: []string{ @@ -2616,7 +2614,7 @@ func schema_pkg_apis_servicemesh_v1alpha2_StrategyStatus(ref common.ReferenceCal Description: "StrategyStatus defines the observed state of Strategy", Type: []string{"object"}, Properties: map[string]spec.Schema{ - "Conditions": { + "conditions": { SchemaProps: spec.SchemaProps{ Description: "The latest available observations of an object's current state.", Type: []string{"array"}, @@ -2629,13 +2627,13 @@ func schema_pkg_apis_servicemesh_v1alpha2_StrategyStatus(ref common.ReferenceCal }, }, }, - "StartTime": { + "startTime": { SchemaProps: spec.SchemaProps{ Description: "Represents time when the strategy was acknowledged by the controller. It is represented in RFC3339 form and is in UTC.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, - "CompletionTime": { + "completionTime": { SchemaProps: spec.SchemaProps{ Description: "Represents time when the strategy was completed. It is represented in RFC3339 form and is in UTC.", Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), diff --git a/pkg/apis/servicemesh/v1alpha2/servicepolicy_types.go b/pkg/apis/servicemesh/v1alpha2/servicepolicy_types.go index b961c0a8d..26c018ea5 100644 --- a/pkg/apis/servicemesh/v1alpha2/servicepolicy_types.go +++ b/pkg/apis/servicemesh/v1alpha2/servicepolicy_types.go @@ -47,7 +47,7 @@ type DestinationRuleSpecTemplate struct { // Metadata of the virtual services created from this template // +optional - metav1.ObjectMeta + metav1.ObjectMeta `json:"metadata,omitempty"` // Spec indicates the behavior of a destination rule. // +optional @@ -68,42 +68,42 @@ const ( // StrategyCondition describes current state of a strategy. type ServicePolicyCondition struct { // Type of strategy condition, Complete or Failed. - Type ServicePolicyConditionType + Type ServicePolicyConditionType `json:"type,omitempty"` // Status of the condition, one of True, False, Unknown - Status apiextensions.ConditionStatus + Status apiextensions.ConditionStatus `json:"status,omitempty"` // Last time the condition was checked. // +optional - LastProbeTime metav1.Time + LastProbeTime metav1.Time `json:"lastProbeTime,omitempty"` // Last time the condition transit from one status to another // +optional - LastTransitionTime metav1.Time + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` // reason for the condition's last transition - Reason string + Reason string `json:"reason,omitempty"` // Human readable message indicating details about last transition. // +optinal - Message string + Message string `json:"message,omitempty"` } // ServicePolicyStatus defines the observed state of ServicePolicy type ServicePolicyStatus struct { // The latest available observations of an object's current state. // +optional - Conditions []ServicePolicyCondition + Conditions []ServicePolicyCondition `json:"conditions,omitempty"` // Represents time when the strategy was acknowledged by the controller. // It is represented in RFC3339 form and is in UTC. // +optional - StartTime *metav1.Time + StartTime *metav1.Time `json:"startTime,omitempty"` // Represents time when the strategy was completed. // It is represented in RFC3339 form and is in UTC. // +optional - CompletionTime *metav1.Time + CompletionTime *metav1.Time `json:"completionTime,omitempty"` } // +genclient diff --git a/pkg/apis/servicemesh/v1alpha2/strategy_types.go b/pkg/apis/servicemesh/v1alpha2/strategy_types.go index 5954e4b28..c86b459d9 100644 --- a/pkg/apis/servicemesh/v1alpha2/strategy_types.go +++ b/pkg/apis/servicemesh/v1alpha2/strategy_types.go @@ -103,17 +103,17 @@ type StrategyStatus struct { // The latest available observations of an object's current state. // +optional - Conditions []StrategyCondition + Conditions []StrategyCondition `json:"conditions,omitempty"` // Represents time when the strategy was acknowledged by the controller. // It is represented in RFC3339 form and is in UTC. // +optional - StartTime *metav1.Time + StartTime *metav1.Time `json:"startTime,omitempty"` // Represents time when the strategy was completed. // It is represented in RFC3339 form and is in UTC. // +optional - CompletionTime *metav1.Time + CompletionTime *metav1.Time `json:"completionTime,omitempty"` } type StrategyConditionType string @@ -130,25 +130,25 @@ const ( // StrategyCondition describes current state of a strategy. type StrategyCondition struct { // Type of strategy condition, Complete or Failed. - Type StrategyConditionType + Type StrategyConditionType `json:"type,omitempty"` // Status of the condition, one of True, False, Unknown - Status apiextensions.ConditionStatus + Status apiextensions.ConditionStatus `json:"status,omitempty"` // Last time the condition was checked. // +optional - LastProbeTime metav1.Time + LastProbeTime metav1.Time `json:"lastProbeTime,omitempty"` // Last time the condition transit from one status to another // +optional - LastTransitionTime metav1.Time + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` // reason for the condition's last transition - Reason string + Reason string `json:"reason,omitempty"` // Human readable message indicating details about last transition. // +optinal - Message string + Message string `json:"message,omitempty"` } // +genclient diff --git a/pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go index fb56a0ea7..c5a810aa5 100644 --- a/pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/servicemesh/v1alpha2/zz_generated.deepcopy.go @@ -16,12 +16,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by deepcopy-gen. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1alpha2 import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -30,7 +30,6 @@ func (in *DestinationRuleSpecTemplate) DeepCopyInto(out *DestinationRuleSpecTemp *out = *in in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationRuleSpecTemplate. @@ -50,7 +49,6 @@ func (in *ServicePolicy) DeepCopyInto(out *ServicePolicy) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicy. @@ -76,7 +74,6 @@ func (in *ServicePolicyCondition) DeepCopyInto(out *ServicePolicyCondition) { *out = *in in.LastProbeTime.DeepCopyInto(&out.LastProbeTime) in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyCondition. @@ -93,7 +90,7 @@ func (in *ServicePolicyCondition) DeepCopy() *ServicePolicyCondition { func (in *ServicePolicyList) DeepCopyInto(out *ServicePolicyList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]ServicePolicy, len(*in)) @@ -101,7 +98,6 @@ func (in *ServicePolicyList) DeepCopyInto(out *ServicePolicyList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyList. @@ -131,7 +127,6 @@ func (in *ServicePolicySpec) DeepCopyInto(out *ServicePolicySpec) { (*in).DeepCopyInto(*out) } in.Template.DeepCopyInto(&out.Template) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicySpec. @@ -162,7 +157,6 @@ func (in *ServicePolicyStatus) DeepCopyInto(out *ServicePolicyStatus) { in, out := &in.CompletionTime, &out.CompletionTime *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePolicyStatus. @@ -182,7 +176,6 @@ func (in *Strategy) DeepCopyInto(out *Strategy) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Strategy. @@ -208,7 +201,6 @@ func (in *StrategyCondition) DeepCopyInto(out *StrategyCondition) { *out = *in in.LastProbeTime.DeepCopyInto(&out.LastProbeTime) in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyCondition. @@ -225,7 +217,7 @@ func (in *StrategyCondition) DeepCopy() *StrategyCondition { func (in *StrategyList) DeepCopyInto(out *StrategyList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]Strategy, len(*in)) @@ -233,7 +225,6 @@ func (in *StrategyList) DeepCopyInto(out *StrategyList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyList. @@ -263,7 +254,6 @@ func (in *StrategySpec) DeepCopyInto(out *StrategySpec) { (*in).DeepCopyInto(*out) } in.Template.DeepCopyInto(&out.Template) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategySpec. @@ -294,7 +284,6 @@ func (in *StrategyStatus) DeepCopyInto(out *StrategyStatus) { in, out := &in.CompletionTime, &out.CompletionTime *out = (*in).DeepCopy() } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StrategyStatus. @@ -312,7 +301,6 @@ func (in *VirtualServiceTemplateSpec) DeepCopyInto(out *VirtualServiceTemplateSp *out = *in in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceTemplateSpec. diff --git a/pkg/apis/tenant/v1alpha1/openapi_generated.go b/pkg/apis/tenant/v1alpha1/openapi_generated.go index cb6046263..b78398b71 100644 --- a/pkg/apis/tenant/v1alpha1/openapi_generated.go +++ b/pkg/apis/tenant/v1alpha1/openapi_generated.go @@ -2263,6 +2263,12 @@ func schema_pkg_apis_tenant_v1alpha1_WorkspaceSpec(ref common.ReferenceCallback) Format: "", }, }, + "networkIsolation": { + SchemaProps: spec.SchemaProps{ + Type: []string{"boolean"}, + Format: "", + }, + }, }, }, }, diff --git a/pkg/apis/tenant/v1alpha1/workspace_types.go b/pkg/apis/tenant/v1alpha1/workspace_types.go index 876e87d5e..e08247e45 100644 --- a/pkg/apis/tenant/v1alpha1/workspace_types.go +++ b/pkg/apis/tenant/v1alpha1/workspace_types.go @@ -26,6 +26,7 @@ const ( ResourceKindWorkspace = "Workspace" ResourceSingularWorkspace = "workspace" ResourcePluralWorkspace = "workspaces" + WorkspaceLabel = "kubesphere.io/workspace" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -33,7 +34,8 @@ const ( // WorkspaceSpec defines the desired state of Workspace type WorkspaceSpec struct { - Manager string `json:"manager,omitempty"` + Manager string `json:"manager,omitempty"` + NetworkIsolation bool `json:"networkIsolation,omitempty"` } // WorkspaceStatus defines the observed state of Workspace diff --git a/pkg/apis/tenant/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/tenant/v1alpha1/zz_generated.deepcopy.go index 2bb7f7349..a9319670d 100644 --- a/pkg/apis/tenant/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/tenant/v1alpha1/zz_generated.deepcopy.go @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Code generated by deepcopy-gen. DO NOT EDIT. +// Code generated by controller-gen. DO NOT EDIT. package v1alpha1 @@ -31,7 +31,6 @@ func (in *Workspace) DeepCopyInto(out *Workspace) { in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec out.Status = in.Status - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Workspace. @@ -56,7 +55,7 @@ func (in *Workspace) DeepCopyObject() runtime.Object { func (in *WorkspaceList) DeepCopyInto(out *WorkspaceList) { *out = *in out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items *out = make([]Workspace, len(*in)) @@ -64,7 +63,6 @@ func (in *WorkspaceList) DeepCopyInto(out *WorkspaceList) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceList. @@ -88,7 +86,6 @@ func (in *WorkspaceList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceSpec) DeepCopyInto(out *WorkspaceSpec) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceSpec. @@ -104,7 +101,6 @@ func (in *WorkspaceSpec) DeepCopy() *WorkspaceSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceStatus) DeepCopyInto(out *WorkspaceStatus) { *out = *in - return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStatus. diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go new file mode 100644 index 000000000..bb5e2d840 --- /dev/null +++ b/pkg/apiserver/apiserver.go @@ -0,0 +1,435 @@ +package apiserver + +import ( + "bytes" + "context" + "fmt" + "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/runtime/schema" + urlruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/sets" + unionauth "k8s.io/apiserver/pkg/authentication/request/union" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/klog" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/request/bearertoken" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/path" + unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union" + apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" + "kubesphere.io/kubesphere/pkg/apiserver/dispatch" + "kubesphere.io/kubesphere/pkg/apiserver/filters" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/informers" + configv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/config/v1alpha2" + devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" + iamapi "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" + loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" + monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" + networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" + "kubesphere.io/kubesphere/pkg/kapis/oauth" + openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" + operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" + resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3" + servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2" + tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" + terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/models/iam/im" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/ldap" + "kubesphere.io/kubesphere/pkg/simple/client/logging" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + "net" + "net/http" + rt "runtime" + "strings" + "time" +) + +const ( + // ApiRootPath defines the root path of all KubeSphere apis. + ApiRootPath = "/kapis" + + // MimeMergePatchJson is the mime header used in merge request + MimeMergePatchJson = "application/merge-patch+json" + + // + MimeJsonPatchJson = "application/json-patch+json" +) + +type APIServer struct { + + // number of kubesphere apiserver + ServerCount int + + // + Server *http.Server + + Config *apiserverconfig.Config + + // webservice container, where all webservice defines + container *restful.Container + + // kubeClient is a collection of all kubernetes(include CRDs) objects clientset + KubernetesClient k8s.Client + + // informerFactory is a collection of all kubernetes(include CRDs) objects informers, + // mainly for fast query + InformerFactory informers.InformerFactory + + // cache is used for short lived objects, like session + CacheClient cache.Interface + + // monitoring client set + MonitoringClient monitoring.Interface + + // + OpenpitrixClient openpitrix.Client + + // + LoggingClient logging.Interface + + // + DevopsClient devops.Interface + + // + S3Client s3.Interface + + // + LdapClient ldap.Interface + + SonarClient sonarqube.SonarInterface +} + +func (s *APIServer) PrepareRun() error { + + s.container = restful.NewContainer() + s.container.Filter(logRequestAndResponse) + s.container.Router(restful.CurlyRouter{}) + s.container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) { + logStackOnRecover(panicReason, httpWriter) + }) + + s.installKubeSphereAPIs() + + for _, ws := range s.container.RegisteredWebServices() { + klog.V(2).Infof("%s", ws.RootPath()) + } + + s.Server.Handler = s.container + + s.buildHandlerChain() + + return nil +} + +func (s *APIServer) installKubeSphereAPIs() { + urlruntime.Must(configv1alpha2.AddToContainer(s.container, s.Config)) + urlruntime.Must(resourcev1alpha3.AddToContainer(s.container, s.InformerFactory)) + urlruntime.Must(loggingv1alpha2.AddToContainer(s.container, s.KubernetesClient, s.LoggingClient)) + urlruntime.Must(monitoringv1alpha3.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.MonitoringClient)) + urlruntime.Must(openpitrixv1.AddToContainer(s.container, s.InformerFactory, s.OpenpitrixClient)) + urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost)) + urlruntime.Must(operationsv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes())) + urlruntime.Must(resourcesv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.InformerFactory)) + urlruntime.Must(tenantv1alpha2.AddToContainer(s.container, s.InformerFactory)) + urlruntime.Must(terminalv1alpha2.AddToContainer(s.container, s.KubernetesClient.Kubernetes(), s.KubernetesClient.Config())) + urlruntime.Must(iamapi.AddToContainer(s.container, im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory), + am.NewAMOperator(s.InformerFactory), + s.Config.AuthenticationOptions)) + urlruntime.Must(oauth.AddToContainer(s.container, token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient), s.Config.AuthenticationOptions)) + urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) + urlruntime.Must(devopsv1alpha2.AddToContainer(s.container, s.InformerFactory.KubeSphereSharedInformerFactory(), s.DevopsClient, s.SonarClient, s.KubernetesClient.KubeSphere(), s.S3Client)) +} + +func (s *APIServer) Run(stopCh <-chan struct{}) (err error) { + + err = s.waitForResourceSync(stopCh) + if err != nil { + return err + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go func() { + <-stopCh + _ = s.Server.Shutdown(ctx) + }() + + klog.V(0).Infof("Start listening on %s", s.Server.Addr) + if s.Server.TLSConfig != nil { + err = s.Server.ListenAndServeTLS("", "") + } else { + err = s.Server.ListenAndServe() + } + + return err +} + +func (s *APIServer) buildHandlerChain() { + requestInfoResolver := &request.RequestInfoFactory{ + APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), + GrouplessAPIPrefixes: sets.NewString("api", "kapi"), + GlobalResources: []schema.GroupResource{ + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralUser}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRole}, + {Group: iamv1alpha2.SchemeGroupVersion.Group, Resource: iamv1alpha2.ResourcesPluralGlobalRoleBinding}, + {Group: tenantv1alpha1.SchemeGroupVersion.Group, Resource: tenantv1alpha1.ResourcePluralWorkspace}, + {Group: clusterv1alpha1.SchemeGroupVersion.Group, Resource: clusterv1alpha1.ResourcesPluralCluster}, + }, + } + + handler := s.Server.Handler + handler = filters.WithKubeAPIServer(handler, s.KubernetesClient.Config(), &errorResponder{}) + + if s.Config.MultiClusterOptions.Enable { + clusterDispatcher := dispatch.NewClusterDispatch(s.InformerFactory.KubeSphereSharedInformerFactory().Cluster().V1alpha1().Clusters().Lister()) + handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher) + } + + var authorizers authorizer.Authorizer + + switch s.Config.AuthorizationOptions.Mode { + case authorizationoptions.AlwaysAllow: + authorizers = authorizerfactory.NewAlwaysAllowAuthorizer() + case authorizationoptions.AlwaysDeny: + authorizers = authorizerfactory.NewAlwaysDenyAuthorizer() + default: + fallthrough + case authorizationoptions.RBAC: + excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*"} + pathAuthorizer, _ := path.NewAuthorizer(excludedPaths) + authorizers = unionauthorizer.New(pathAuthorizer, authorizerfactory.NewOPAAuthorizer(am.NewAMOperator(s.InformerFactory)), authorizerfactory.NewRBACAuthorizer(am.NewAMOperator(s.InformerFactory))) + } + + handler = filters.WithAuthorization(handler, authorizers) + + // authenticators are unordered + authn := unionauth.New(anonymous.NewAuthenticator(), + basictoken.New(basic.NewBasicAuthenticator(im.NewOperator(s.KubernetesClient.KubeSphere(), s.InformerFactory))), + bearertoken.New(jwttoken.NewTokenAuthenticator(token.NewJwtTokenIssuer(token.DefaultIssuerName, s.Config.AuthenticationOptions, s.CacheClient)))) + handler = filters.WithAuthentication(handler, authn) + handler = filters.WithRequestInfo(handler, requestInfoResolver) + s.Server.Handler = handler +} + +func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error { + klog.V(0).Info("Start cache objects") + + discoveryClient := s.KubernetesClient.Kubernetes().Discovery() + _, apiResourcesList, err := discoveryClient.ServerGroupsAndResources() + if err != nil { + return err + } + + isResourceExists := func(resource schema.GroupVersionResource) bool { + for _, apiResource := range apiResourcesList { + if apiResource.GroupVersion == resource.GroupVersion().String() { + for _, rsc := range apiResource.APIResources { + if rsc.Name == resource.Resource { + return true + } + } + } + } + return false + } + + // resources we have to create informer first + k8sGVRs := []schema.GroupVersionResource{ + {Group: "", Version: "v1", Resource: "namespaces"}, + {Group: "", Version: "v1", Resource: "nodes"}, + {Group: "", Version: "v1", Resource: "resourcequotas"}, + {Group: "", Version: "v1", Resource: "pods"}, + {Group: "", Version: "v1", Resource: "services"}, + {Group: "", Version: "v1", Resource: "persistentvolumeclaims"}, + {Group: "", Version: "v1", Resource: "secrets"}, + {Group: "", Version: "v1", Resource: "configmaps"}, + + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterrolebindings"}, + + {Group: "apps", Version: "v1", Resource: "deployments"}, + {Group: "apps", Version: "v1", Resource: "daemonsets"}, + {Group: "apps", Version: "v1", Resource: "replicasets"}, + {Group: "apps", Version: "v1", Resource: "statefulsets"}, + {Group: "apps", Version: "v1", Resource: "controllerrevisions"}, + + {Group: "storage.k8s.io", Version: "v1", Resource: "storageclasses"}, + + {Group: "batch", Version: "v1", Resource: "jobs"}, + {Group: "batch", Version: "v1beta1", Resource: "cronjobs"}, + + {Group: "extensions", Version: "v1beta1", Resource: "ingresses"}, + + {Group: "autoscaling", Version: "v2beta2", Resource: "horizontalpodautoscalers"}, + } + + for _, gvr := range k8sGVRs { + if !isResourceExists(gvr) { + klog.Warningf("resource %s not exists in the cluster", gvr) + } else { + _, err := s.InformerFactory.KubernetesSharedInformerFactory().ForResource(gvr) + if err != nil { + klog.Errorf("cannot create informer for %s", gvr) + return err + } + } + } + + s.InformerFactory.KubernetesSharedInformerFactory().Start(stopCh) + s.InformerFactory.KubernetesSharedInformerFactory().WaitForCacheSync(stopCh) + + ksInformerFactory := s.InformerFactory.KubeSphereSharedInformerFactory() + + ksGVRs := []schema.GroupVersionResource{ + {Group: "tenant.kubesphere.io", Version: "v1alpha1", Resource: "workspaces"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "users"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"}, + {Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"}, + {Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"}, + } + + devopsGVRs := []schema.GroupVersionResource{ + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibinaries"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuildertemplates"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2iruns"}, + {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"}, + } + + servicemeshGVRs := []schema.GroupVersionResource{ + {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "strategies"}, + {Group: "servicemesh.kubesphere.io", Version: "v1alpha2", Resource: "servicepolicies"}, + } + + // skip caching devops resources if devops not enabled + if s.DevopsClient != nil { + ksGVRs = append(ksGVRs, devopsGVRs...) + } + + // skip caching servicemesh resources if servicemesh not enabled + if s.KubernetesClient.Istio() != nil { + ksGVRs = append(ksGVRs, servicemeshGVRs...) + } + + for _, gvr := range ksGVRs { + if !isResourceExists(gvr) { + klog.Warningf("resource %s not exists in the cluster", gvr) + } else { + _, err := ksInformerFactory.ForResource(gvr) + if err != nil { + return err + } + } + } + + ksInformerFactory.Start(stopCh) + ksInformerFactory.WaitForCacheSync(stopCh) + + appInformerFactory := s.InformerFactory.ApplicationSharedInformerFactory() + + appGVRs := []schema.GroupVersionResource{ + {Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}, + } + + for _, gvr := range appGVRs { + if !isResourceExists(gvr) { + klog.Warningf("resource %s not exists in the cluster", gvr) + } else { + _, err = appInformerFactory.ForResource(gvr) + if err != nil { + return err + } + } + } + + appInformerFactory.Start(stopCh) + appInformerFactory.WaitForCacheSync(stopCh) + + klog.V(0).Info("Finished caching objects") + + return nil + +} + +func logStackOnRecover(panicReason interface{}, w http.ResponseWriter) { + var buffer bytes.Buffer + buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) + for i := 2; ; i += 1 { + _, file, line, ok := rt.Caller(i) + if !ok { + break + } + buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line)) + } + klog.Errorln(buffer.String()) + + headers := http.Header{} + if ct := w.Header().Get("Content-Type"); len(ct) > 0 { + headers.Set("Accept", ct) + } + + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("Internal server error")) +} + +func logRequestAndResponse(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { + start := time.Now() + chain.ProcessFilter(req, resp) + klog.V(4).Infof("%s - \"%s %s %s\" %d %d %dms", + getRequestIP(req), + req.Request.Method, + req.Request.RequestURI, + req.Request.Proto, + resp.StatusCode(), + resp.ContentLength(), + time.Since(start)/time.Millisecond, + ) +} + +func getRequestIP(req *restful.Request) string { + address := strings.Trim(req.Request.Header.Get("X-Real-Ip"), " ") + if address != "" { + return address + } + + address = strings.Trim(req.Request.Header.Get("X-Forwarded-For"), " ") + if address != "" { + return address + } + + address, _, err := net.SplitHostPort(req.Request.RemoteAddr) + if err != nil { + return req.Request.RemoteAddr + } + + return address +} + +type errorResponder struct{} + +func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) { + klog.Error(err) + responsewriters.InternalError(w, req, err) +} diff --git a/pkg/apiserver/authentication/authenticators/basic/basic.go b/pkg/apiserver/authentication/authenticators/basic/basic.go new file mode 100644 index 000000000..434de152c --- /dev/null +++ b/pkg/apiserver/authentication/authenticators/basic/basic.go @@ -0,0 +1,58 @@ +/* + * + * Copyright 2020 The KubeSphere 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 basic + +import ( + "context" + "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/authentication/user" + "kubesphere.io/kubesphere/pkg/models/iam/im" +) + +// TokenAuthenticator implements kubernetes token authenticate interface with our custom logic. +// TokenAuthenticator will retrieve user info from cache by given token. If empty or invalid token +// was given, authenticator will still give passed response at the condition user will be user.Anonymous +// and group from user.AllUnauthenticated. This helps requests be passed along the handler chain, +// because some resources are public accessible. +type basicAuthenticator struct { + im im.IdentityManagementInterface +} + +func NewBasicAuthenticator(im im.IdentityManagementInterface) authenticator.Password { + return &basicAuthenticator{ + im: im, + } +} + +func (t *basicAuthenticator) AuthenticatePassword(ctx context.Context, username, password string) (*authenticator.Response, bool, error) { + + providedUser, err := t.im.Authenticate(username, password) + + if err != nil { + return nil, false, err + } + + return &authenticator.Response{ + User: &user.DefaultInfo{ + Name: providedUser.GetName(), + UID: string(providedUser.GetUID()), + Groups: []string{user.AllAuthenticated}, + }, + }, true, nil +} diff --git a/pkg/apiserver/authentication/authenticators/jwttoken/jwt_token.go b/pkg/apiserver/authentication/authenticators/jwttoken/jwt_token.go new file mode 100644 index 000000000..a8bea8f18 --- /dev/null +++ b/pkg/apiserver/authentication/authenticators/jwttoken/jwt_token.go @@ -0,0 +1,38 @@ +package jwttoken + +import ( + "context" + "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/authentication/user" + token2 "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" +) + +// TokenAuthenticator implements kubernetes token authenticate interface with our custom logic. +// TokenAuthenticator will retrieve user info from cache by given token. If empty or invalid token +// was given, authenticator will still give passed response at the condition user will be user.Anonymous +// and group from user.AllUnauthenticated. This helps requests be passed along the handler chain, +// because some resources are public accessible. +type tokenAuthenticator struct { + jwtTokenIssuer token2.Issuer +} + +func NewTokenAuthenticator(issuer token2.Issuer) authenticator.Token { + return &tokenAuthenticator{ + jwtTokenIssuer: issuer, + } +} + +func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) { + providedUser, err := t.jwtTokenIssuer.Verify(token) + if err != nil { + return nil, false, err + } + + return &authenticator.Response{ + User: &user.DefaultInfo{ + Name: providedUser.GetName(), + UID: providedUser.GetUID(), + Groups: []string{user.AllAuthenticated}, + }, + }, true, nil +} diff --git a/pkg/apiserver/authentication/identityprovider/identity_provider.go b/pkg/apiserver/authentication/identityprovider/identity_provider.go new file mode 100644 index 000000000..1a5a4c262 --- /dev/null +++ b/pkg/apiserver/authentication/identityprovider/identity_provider.go @@ -0,0 +1,63 @@ +/* + * + * Copyright 2020 The KubeSphere 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 identityprovider + +import ( + "errors" + "k8s.io/apiserver/pkg/authentication/user" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" +) + +var ( + ErrorIdentityProviderNotFound = errors.New("the identity provider was not found") + ErrorAlreadyRegistered = errors.New("the identity provider was not found") + oauthProviderCodecs = map[string]OAuthProviderCodec{} +) + +type OAuthProvider interface { + IdentityExchange(code string) (user.Info, error) +} + +type OAuthProviderCodec interface { + Type() string + Decode(options *oauth.DynamicOptions) (OAuthProvider, error) + Encode(provider OAuthProvider) (*oauth.DynamicOptions, error) +} + +func ResolveOAuthProvider(providerType string, options *oauth.DynamicOptions) (OAuthProvider, error) { + if codec, ok := oauthProviderCodecs[providerType]; ok { + return codec.Decode(options) + } + return nil, ErrorIdentityProviderNotFound +} + +func ResolveOAuthOptions(providerType string, provider OAuthProvider) (*oauth.DynamicOptions, error) { + if codec, ok := oauthProviderCodecs[providerType]; ok { + return codec.Encode(provider) + } + return nil, ErrorIdentityProviderNotFound +} + +func RegisterOAuthProviderCodec(codec OAuthProviderCodec) error { + if _, ok := oauthProviderCodecs[codec.Type()]; ok { + return ErrorAlreadyRegistered + } + oauthProviderCodecs[codec.Type()] = codec + return nil +} diff --git a/pkg/apiserver/authentication/oauth/oauth_options.go b/pkg/apiserver/authentication/oauth/oauth_options.go new file mode 100644 index 000000000..bb964dc74 --- /dev/null +++ b/pkg/apiserver/authentication/oauth/oauth_options.go @@ -0,0 +1,235 @@ +/* + * + * Copyright 2020 The KubeSphere 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 oauth + +import ( + "errors" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "net/url" + "time" +) + +type GrantHandlerType string +type MappingMethod string +type IdentityProviderType string + +const ( + // GrantHandlerAuto auto-approves client authorization grant requests + GrantHandlerAuto GrantHandlerType = "auto" + // GrantHandlerPrompt prompts the user to approve new client authorization grant requests + GrantHandlerPrompt GrantHandlerType = "prompt" + // GrantHandlerDeny auto-denies client authorization grant requests + GrantHandlerDeny GrantHandlerType = "deny" + // MappingMethodAuto The default value.The user will automatically create and mapping when login successful. + // Fails if a user with that user name is already mapped to another identity. + MappingMethodAuto MappingMethod = "auto" + // MappingMethodLookup Looks up an existing identity, user identity mapping, and user, but does not automatically + // provision users or identities. Using this method requires you to manually provision users. + MappingMethodLookup MappingMethod = "lookup" + // MappingMethodMixed A user entity can be mapped with multiple identifyProvider. + MappingMethodMixed MappingMethod = "mixed" +) + +var ( + ErrorClientNotFound = errors.New("the OAuth client was not found") + ErrorRedirectURLNotAllowed = errors.New("redirect URL is not allowed") +) + +type Options struct { + // LDAPPasswordIdentityProvider provider is used by default. + IdentityProviders []IdentityProviderOptions `json:"identityProviders,omitempty" yaml:"identityProviders,omitempty"` + + // Register additional OAuth clients. + Clients []Client `json:"clients,omitempty" yaml:"clients,omitempty"` + + // AccessTokenMaxAgeSeconds control the lifetime of access tokens. The default lifetime is 24 hours. + // 0 means no expiration. + AccessTokenMaxAge time.Duration `json:"accessTokenMaxAge" yaml:"accessTokenMaxAge"` + + // Inactivity timeout for tokens + // The value represents the maximum amount of time that can occur between + // consecutive uses of the token. Tokens become invalid if they are not + // used within this temporal window. The user will need to acquire a new + // token to regain access once a token times out. + // This value needs to be set only if the default set in configuration is + // not appropriate for this client. Valid values are: + // - 0: Tokens for this client never time out + // - X: Tokens time out if there is no activity + // The current minimum allowed value for X is 5 minutes + AccessTokenInactivityTimeout time.Duration `json:"accessTokenInactivityTimeout" yaml:"accessTokenInactivityTimeout"` +} + +type DynamicOptions map[string]interface{} + +type IdentityProviderOptions struct { + // The provider name. + Name string `json:"name" yaml:"name"` + + // Defines how new identities are mapped to users when they login. Allowed values are: + // - auto: The default value.The user will automatically create and mapping when login successful. + // Fails if a user with that user name is already mapped to another identity. + // - lookup: Looks up an existing identity, user identity mapping, and user, but does not automatically + // provision users or identities. Using this method requires you to manually provision users. + // - mixed: A user entity can be mapped with multiple identifyProvider. + MappingMethod MappingMethod `json:"mappingMethod" yaml:"mappingMethod"` + + // When true, unauthenticated token requests from web clients (like the web console) + // are redirected to a login page (with WWW-Authenticate challenge header) backed by this provider. + LoginRedirect bool `json:"loginRedirect" yaml:"loginRedirect"` + + // The type of identify provider + Type string `json:"type" yaml:"type"` + + // The options of identify provider + Provider *DynamicOptions `json:"provider,omitempty" yaml:"provider,omitempty"` +} + +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string `json:"access_token"` + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string `json:"token_type,omitempty"` + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string `json:"refresh_token,omitempty"` + + // ExpiresIn is the optional expiration second of the access token. + ExpiresIn int `json:"expires_in,omitempty"` +} + +type Client struct { + // The name of the OAuth client is used as the client_id parameter when making requests to /oauth/authorize + // and /oauth/token. + Name string + + // Secret is the unique secret associated with a client + Secret string `json:"-" yaml:"secret,omitempty"` + + // RespondWithChallenges indicates whether the client wants authentication needed responses made + // in the form of challenges instead of redirects + RespondWithChallenges bool `json:"respondWithChallenges,omitempty" yaml:"respondWithChallenges,omitempty"` + + // RedirectURIs is the valid redirection URIs associated with a client + RedirectURIs []string `json:"redirectURIs,omitempty" yaml:"redirectURIs,omitempty"` + + // GrantMethod determines how to handle grants for this client. If no method is provided, the + // cluster default grant handling method will be used. Valid grant handling methods are: + // - auto: always approves grant requests, useful for trusted clients + // - prompt: prompts the end user for approval of grant requests, useful for third-party clients + // - deny: always denies grant requests, useful for black-listed clients + GrantMethod GrantHandlerType `json:"grantMethod,omitempty" yaml:"grantMethod,omitempty"` + + // ScopeRestrictions describes which scopes this client can request. Each requested scope + // is checked against each restriction. If any restriction matches, then the scope is allowed. + // If no restriction matches, then the scope is denied. + ScopeRestrictions []string `json:"scopeRestrictions,omitempty" yaml:"scopeRestrictions,omitempty"` + + // AccessTokenMaxAge overrides the default access token max age for tokens granted to this client. + AccessTokenMaxAge *time.Duration `json:"accessTokenMaxAge,omitempty" yaml:"accessTokenMaxAge,omitempty"` + + // AccessTokenInactivityTimeout overrides the default token + // inactivity timeout for tokens granted to this client. + AccessTokenInactivityTimeout *time.Duration `json:"accessTokenInactivityTimeout,omitempty" yaml:"accessTokenInactivityTimeout,omitempty"` +} + +var ( + // Allow any redirect URI if the redirectURI is defined in request + AllowAllRedirectURI = "*" + DefaultTokenMaxAge = time.Second * 86400 + DefaultAccessTokenInactivityTimeout = time.Duration(0) + DefaultClients = []Client{{ + Name: "default", + RespondWithChallenges: true, + RedirectURIs: []string{AllowAllRedirectURI}, + GrantMethod: GrantHandlerAuto, + ScopeRestrictions: []string{"full"}, + AccessTokenMaxAge: &DefaultTokenMaxAge, + AccessTokenInactivityTimeout: &DefaultAccessTokenInactivityTimeout, + }} +) + +func (o *Options) OAuthClient(name string) (Client, error) { + for _, found := range o.Clients { + if found.Name == name { + return found, nil + } + } + for _, defaultClient := range DefaultClients { + if defaultClient.Name == name { + return defaultClient, nil + } + } + return Client{}, ErrorClientNotFound +} +func (o *Options) IdentityProviderOptions(name string) (IdentityProviderOptions, error) { + for _, found := range o.IdentityProviders { + if found.Name == name { + return found, nil + } + } + return IdentityProviderOptions{}, ErrorClientNotFound +} + +func (c Client) anyRedirectAbleURI() []string { + uris := make([]string, 0) + for _, uri := range c.RedirectURIs { + _, err := url.Parse(uri) + if err == nil { + uris = append(uris, uri) + } + } + return uris +} + +func (c Client) ResolveRedirectURL(expectURL string) (string, error) { + // RedirectURIs is empty + if len(c.RedirectURIs) == 0 { + return "", ErrorRedirectURLNotAllowed + } + allowAllRedirectURI := sliceutil.HasString(c.RedirectURIs, AllowAllRedirectURI) + redirectAbleURIs := c.anyRedirectAbleURI() + + if expectURL == "" { + // Need to specify at least one RedirectURI + if len(redirectAbleURIs) > 0 { + return redirectAbleURIs[0], nil + } else { + return "", ErrorRedirectURLNotAllowed + } + } + if allowAllRedirectURI || sliceutil.HasString(redirectAbleURIs, expectURL) { + return expectURL, nil + } + + return "", ErrorRedirectURLNotAllowed +} + +func NewOptions() *Options { + return &Options{ + IdentityProviders: make([]IdentityProviderOptions, 0), + Clients: make([]Client, 0), + AccessTokenMaxAge: time.Hour * 24, + AccessTokenInactivityTimeout: 0, + } +} diff --git a/pkg/apiserver/authentication/oauth/oauth_options_test.go b/pkg/apiserver/authentication/oauth/oauth_options_test.go new file mode 100644 index 000000000..c7af42075 --- /dev/null +++ b/pkg/apiserver/authentication/oauth/oauth_options_test.go @@ -0,0 +1,104 @@ +/* + * + * Copyright 2020 The KubeSphere 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 oauth + +import ( + "github.com/google/go-cmp/cmp" + "testing" + "time" +) + +func TestDefaultAuthOptions(t *testing.T) { + oneDay := time.Second * 86400 + zero := time.Duration(0) + expect := Client{ + Name: "default", + RespondWithChallenges: true, + RedirectURIs: []string{AllowAllRedirectURI}, + GrantMethod: GrantHandlerAuto, + ScopeRestrictions: []string{"full"}, + AccessTokenMaxAge: &oneDay, + AccessTokenInactivityTimeout: &zero, + } + + options := NewOptions() + client, err := options.OAuthClient("default") + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(expect, client); len(diff) != 0 { + t.Errorf("%T differ (-got, +expected), %s", expect, diff) + } +} + +func TestClientResolveRedirectURL(t *testing.T) { + + options := NewOptions() + defaultClient, err := options.OAuthClient("default") + if err != nil { + t.Fatal(err) + } + tests := []struct { + Name string + client Client + expectError error + expectURL string + }{ + { + Name: "default client test", + client: defaultClient, + expectError: nil, + expectURL: "https://localhost:8080/auth/cb", + }, + { + Name: "custom client test", + client: Client{ + Name: "default", + RespondWithChallenges: true, + RedirectURIs: []string{"https://foo.bar.com/oauth/cb"}, + GrantMethod: GrantHandlerAuto, + ScopeRestrictions: []string{"full"}, + }, + expectError: ErrorRedirectURLNotAllowed, + expectURL: "https://foo.bar.com/oauth/err", + }, + { + Name: "custom client test", + client: Client{ + Name: "default", + RespondWithChallenges: true, + RedirectURIs: []string{AllowAllRedirectURI, "https://foo.bar.com/oauth/cb"}, + GrantMethod: GrantHandlerAuto, + ScopeRestrictions: []string{"full"}, + }, + expectError: nil, + expectURL: "https://foo.bar.com/oauth/err2", + }, + } + + for _, test := range tests { + redirectURL, err := test.client.ResolveRedirectURL(test.expectURL) + if err != test.expectError { + t.Errorf("expected error: %s, got: %s", test.expectError, err) + } + if test.expectError == nil && test.expectURL != redirectURL { + t.Errorf("expected redirect url: %s, got: %s", test.expectURL, redirectURL) + } + } +} diff --git a/pkg/apiserver/authentication/options/authenticate_options.go b/pkg/apiserver/authentication/options/authenticate_options.go new file mode 100644 index 000000000..789c56286 --- /dev/null +++ b/pkg/apiserver/authentication/options/authenticate_options.go @@ -0,0 +1,73 @@ +/* + * + * Copyright 2020 The KubeSphere 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 options + +import ( + "fmt" + "github.com/spf13/pflag" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" + "time" +) + +type AuthenticationOptions struct { + // authenticate rate limit will + AuthenticateRateLimiterMaxTries int `json:"authenticateRateLimiterMaxTries" yaml:"authenticateRateLimiterMaxTries"` + AuthenticateRateLimiterDuration time.Duration `json:"authenticationRateLimiterDuration" yaml:"authenticationRateLimiterDuration"` + + // maximum retries when authenticate failed + MaxAuthenticateRetries int `json:"maxAuthenticateRetries" yaml:"maxAuthenticateRetries"` + + // allow multiple users login at the same time + MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"` + + // secret to signed jwt token + JwtSecret string `json:"-" yaml:"jwtSecret"` + + OAuthOptions *oauth.Options `json:"oauthOptions" yaml:"oauthOptions"` +} + +func NewAuthenticateOptions() *AuthenticationOptions { + return &AuthenticationOptions{ + AuthenticateRateLimiterMaxTries: 5, + AuthenticateRateLimiterDuration: time.Minute * 30, + MaxAuthenticateRetries: 0, + OAuthOptions: oauth.NewOptions(), + MultipleLogin: false, + JwtSecret: "", + } +} + +func (options *AuthenticationOptions) Validate() []error { + var errs []error + + if len(options.JwtSecret) == 0 { + errs = append(errs, fmt.Errorf("jwt secret is empty")) + } + + return errs +} + +func (options *AuthenticationOptions) AddFlags(fs *pflag.FlagSet, s *AuthenticationOptions) { + fs.IntVar(&options.AuthenticateRateLimiterMaxTries, "authenticate-rate-limiter-max-retries", s.AuthenticateRateLimiterMaxTries, "") + fs.DurationVar(&options.AuthenticateRateLimiterDuration, "authenticate-rate-limiter-duration", s.AuthenticateRateLimiterDuration, "") + fs.IntVar(&options.MaxAuthenticateRetries, "authenticate-max-retries", s.MaxAuthenticateRetries, "") + fs.BoolVar(&options.MultipleLogin, "multiple-login", s.MultipleLogin, "Allow multiple login with the same account, disable means only one user can login at the same time.") + fs.StringVar(&options.JwtSecret, "jwt-secret", s.JwtSecret, "Secret to sign jwt token, must not be empty.") + fs.DurationVar(&options.OAuthOptions.AccessTokenMaxAge, "access-token-max-age", s.OAuthOptions.AccessTokenMaxAge, "AccessTokenMaxAgeSeconds control the lifetime of access tokens, 0 means no expiration.") +} diff --git a/pkg/apiserver/authentication/request/anonymous/anonymous.go b/pkg/apiserver/authentication/request/anonymous/anonymous.go new file mode 100644 index 000000000..9ff1115a2 --- /dev/null +++ b/pkg/apiserver/authentication/request/anonymous/anonymous.go @@ -0,0 +1,46 @@ +/* + * + * Copyright 2020 The KubeSphere 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 anonymous + +import ( + "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/authentication/user" + "net/http" + "strings" +) + +type Authenticator struct{} + +func NewAuthenticator() authenticator.Request { + return &Authenticator{} +} + +func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) { + auth := strings.TrimSpace(req.Header.Get("Authorization")) + if auth == "" { + return &authenticator.Response{ + User: &user.DefaultInfo{ + Name: user.Anonymous, + UID: "", + Groups: []string{user.AllUnauthenticated}, + }, + }, true, nil + } + return nil, false, nil +} diff --git a/pkg/apiserver/authentication/request/basictoken/basic_token.go b/pkg/apiserver/authentication/request/basictoken/basic_token.go new file mode 100644 index 000000000..ebb75f6a5 --- /dev/null +++ b/pkg/apiserver/authentication/request/basictoken/basic_token.go @@ -0,0 +1,56 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package basictoken + +import ( + "errors" + "k8s.io/apiserver/pkg/authentication/authenticator" + "net/http" +) + +type Authenticator struct { + auth authenticator.Password +} + +func New(auth authenticator.Password) *Authenticator { + return &Authenticator{auth} +} + +var invalidToken = errors.New("invalid basic token") + +func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) { + + username, password, ok := req.BasicAuth() + + if !ok { + return nil, false, nil + } + + resp, ok, err := a.auth.AuthenticatePassword(req.Context(), username, password) + // if we authenticated successfully, go ahead and remove the bearer token so that no one + // is ever tempted to use it inside of the API server + if ok { + req.Header.Del("Authorization") + } + + // If the token authenticator didn't error, provide a default error + if !ok && err == nil { + err = invalidToken + } + + return resp, ok, err +} diff --git a/pkg/apiserver/authentication/request/bearertoken/bearertoken.go b/pkg/apiserver/authentication/request/bearertoken/bearertoken.go new file mode 100644 index 000000000..e637fb17a --- /dev/null +++ b/pkg/apiserver/authentication/request/bearertoken/bearertoken.go @@ -0,0 +1,62 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bearertoken + +import ( + "errors" + "net/http" + "strings" + + "k8s.io/apiserver/pkg/authentication/authenticator" +) + +type Authenticator struct { + auth authenticator.Token +} + +func New(auth authenticator.Token) *Authenticator { + return &Authenticator{auth} +} + +var invalidToken = errors.New("invalid bearer token") + +func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) { + auth := strings.TrimSpace(req.Header.Get("Authorization")) + if auth == "" { + return nil, false, nil + } + parts := strings.Split(auth, " ") + if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { + return nil, false, nil + } + + token := parts[1] + + // Empty bearer tokens aren't valid + if len(token) == 0 { + return nil, false, nil + } + + resp, ok, err := a.auth.AuthenticateToken(req.Context(), token) + + // If the token authenticator didn't error, provide a default error + if !ok && err == nil { + err = invalidToken + } + + return resp, ok, err +} diff --git a/pkg/apiserver/authentication/request/bearertoken/bearertoken_test.go b/pkg/apiserver/authentication/request/bearertoken/bearertoken_test.go new file mode 100644 index 000000000..14125ccc9 --- /dev/null +++ b/pkg/apiserver/authentication/request/bearertoken/bearertoken_test.go @@ -0,0 +1,192 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bearertoken + +import ( + "context" + "errors" + "net/http" + "reflect" + "testing" + + "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/authentication/user" +) + +func TestAuthenticateRequest(t *testing.T) { + auth := New(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { + if token != "token" { + t.Errorf("unexpected token: %s", token) + } + return &authenticator.Response{User: &user.DefaultInfo{Name: "user"}}, true, nil + })) + resp, ok, err := auth.AuthenticateRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer token"}}, + }) + if !ok || resp == nil || err != nil { + t.Errorf("expected valid user") + } +} + +func TestAuthenticateRequestTokenInvalid(t *testing.T) { + auth := New(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { + return nil, false, nil + })) + resp, ok, err := auth.AuthenticateRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer token"}}, + }) + if ok || resp != nil { + t.Errorf("expected not authenticated user") + } + if err != invalidToken { + t.Errorf("expected invalidToken error, got %v", err) + } +} + +func TestAuthenticateRequestTokenInvalidCustomError(t *testing.T) { + customError := errors.New("custom") + auth := New(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { + return nil, false, customError + })) + resp, ok, err := auth.AuthenticateRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer token"}}, + }) + if ok || resp != nil { + t.Errorf("expected not authenticated user") + } + if err != customError { + t.Errorf("expected custom error, got %v", err) + } +} + +func TestAuthenticateRequestTokenError(t *testing.T) { + auth := New(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { + return nil, false, errors.New("error") + })) + resp, ok, err := auth.AuthenticateRequest(&http.Request{ + Header: http.Header{"Authorization": []string{"Bearer token"}}, + }) + if ok || resp != nil || err == nil { + t.Errorf("expected error") + } +} + +func TestAuthenticateRequestBadValue(t *testing.T) { + testCases := []struct { + Req *http.Request + }{ + {Req: &http.Request{}}, + {Req: &http.Request{Header: http.Header{"Authorization": []string{"Bearer"}}}}, + {Req: &http.Request{Header: http.Header{"Authorization": []string{"bear token"}}}}, + {Req: &http.Request{Header: http.Header{"Authorization": []string{"Bearer: token"}}}}, + } + for i, testCase := range testCases { + auth := New(authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) { + t.Errorf("authentication should not have been called") + return nil, false, nil + })) + user, ok, err := auth.AuthenticateRequest(testCase.Req) + if ok || user != nil || err != nil { + t.Errorf("%d: expected not authenticated (no token)", i) + } + } +} + +func TestBearerToken(t *testing.T) { + tests := map[string]struct { + AuthorizationHeaders []string + TokenAuth authenticator.Token + + ExpectedUserName string + ExpectedOK bool + ExpectedErr bool + ExpectedAuthorizationHeaders []string + }{ + "no header": { + AuthorizationHeaders: nil, + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: false, + ExpectedAuthorizationHeaders: nil, + }, + "empty header": { + AuthorizationHeaders: []string{""}, + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: false, + ExpectedAuthorizationHeaders: []string{""}, + }, + "non-bearer header": { + AuthorizationHeaders: []string{"Basic 123"}, + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: false, + ExpectedAuthorizationHeaders: []string{"Basic 123"}, + }, + "empty bearer token": { + AuthorizationHeaders: []string{"Bearer "}, + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: false, + ExpectedAuthorizationHeaders: []string{"Bearer "}, + }, + "invalid bearer token": { + AuthorizationHeaders: []string{"Bearer 123"}, + TokenAuth: authenticator.TokenFunc(func(ctx context.Context, t string) (*authenticator.Response, bool, error) { return nil, false, nil }), + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: true, + ExpectedAuthorizationHeaders: []string{"Bearer 123"}, + }, + "error bearer token": { + AuthorizationHeaders: []string{"Bearer 123"}, + TokenAuth: authenticator.TokenFunc(func(ctx context.Context, t string) (*authenticator.Response, bool, error) { + return nil, false, errors.New("error") + }), + ExpectedUserName: "", + ExpectedOK: false, + ExpectedErr: true, + ExpectedAuthorizationHeaders: []string{"Bearer 123"}, + }, + } + + for k, tc := range tests { + req, _ := http.NewRequest("GET", "/", nil) + for _, h := range tc.AuthorizationHeaders { + req.Header.Add("Authorization", h) + } + + bearerAuth := New(tc.TokenAuth) + resp, ok, err := bearerAuth.AuthenticateRequest(req) + if tc.ExpectedErr != (err != nil) { + t.Errorf("%s: Expected err=%v, got %v", k, tc.ExpectedErr, err) + continue + } + if ok != tc.ExpectedOK { + t.Errorf("%s: Expected ok=%v, got %v", k, tc.ExpectedOK, ok) + continue + } + if ok && resp.User.GetName() != tc.ExpectedUserName { + t.Errorf("%s: Expected username=%v, got %v", k, tc.ExpectedUserName, resp.User.GetName()) + continue + } + if !reflect.DeepEqual(req.Header["Authorization"], tc.ExpectedAuthorizationHeaders) { + t.Errorf("%s: Expected headers=%#v, got %#v", k, tc.ExpectedAuthorizationHeaders, req.Header["Authorization"]) + continue + } + } +} diff --git a/pkg/apiserver/authentication/token/issuer.go b/pkg/apiserver/authentication/token/issuer.go new file mode 100644 index 000000000..1661592ca --- /dev/null +++ b/pkg/apiserver/authentication/token/issuer.go @@ -0,0 +1,33 @@ +/* + * + * Copyright 2020 The KubeSphere 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 token + +import "time" + +// Issuer issues token to user, tokens are required to perform mutating requests to resources +type Issuer interface { + // IssueTo issues a token a User, return error if issuing process failed + IssueTo(user User, expiresIn time.Duration) (string, error) + + // Verify verifies a token, and return a User if it's a valid token, otherwise return error + Verify(string) (User, error) + + // Revoke a token, + Revoke(token string) error +} diff --git a/pkg/apiserver/authentication/token/jwt.go b/pkg/apiserver/authentication/token/jwt.go new file mode 100644 index 000000000..cae79371d --- /dev/null +++ b/pkg/apiserver/authentication/token/jwt.go @@ -0,0 +1,143 @@ +/* + * + * Copyright 2020 The KubeSphere 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 token + +import ( + "fmt" + "github.com/dgrijalva/jwt-go" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/klog" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "time" +) + +const DefaultIssuerName = "kubesphere" + +var ( + errInvalidToken = errors.New("invalid token") + errTokenExpired = errors.New("expired token") +) + +type Claims struct { + Username string `json:"username"` + UID string `json:"uid"` + // Currently, we are not using any field in jwt.StandardClaims + jwt.StandardClaims +} + +type jwtTokenIssuer struct { + name string + options *authoptions.AuthenticationOptions + cache cache.Interface + keyFunc jwt.Keyfunc +} + +func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) { + if len(tokenString) == 0 { + return nil, errInvalidToken + } + + clm := &Claims{} + + _, err := jwt.ParseWithClaims(tokenString, clm, s.keyFunc) + + if err != nil { + return nil, err + } + + // 0 means no expiration. + // validate token cache + if s.options.OAuthOptions.AccessTokenMaxAge > 0 { + _, err = s.cache.Get(tokenCacheKey(tokenString)) + + if err != nil { + if err == cache.ErrNoSuchKey { + return nil, errTokenExpired + } + return nil, err + } + } + + return &user.DefaultInfo{Name: clm.Username, UID: clm.UID}, nil +} + +func (s *jwtTokenIssuer) IssueTo(user User, expiresIn time.Duration) (string, error) { + clm := &Claims{ + Username: user.GetName(), + UID: user.GetUID(), + StandardClaims: jwt.StandardClaims{ + IssuedAt: time.Now().Unix(), + Issuer: s.name, + NotBefore: time.Now().Unix(), + }, + } + + if expiresIn > 0 { + clm.ExpiresAt = clm.IssuedAt + int64(expiresIn.Seconds()) + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, clm) + + tokenString, err := token.SignedString([]byte(s.options.JwtSecret)) + + if err != nil { + klog.Error(err) + return "", err + } + + // 0 means no expiration. + // validate token cache + if s.options.OAuthOptions.AccessTokenMaxAge > 0 { + err = s.cache.Set(tokenCacheKey(tokenString), tokenString, s.options.OAuthOptions.AccessTokenMaxAge) + if err != nil { + klog.Error(err) + return "", err + } + } + + return tokenString, nil +} + +func (s *jwtTokenIssuer) Revoke(token string) error { + if s.options.OAuthOptions.AccessTokenMaxAge > 0 { + return s.cache.Del(tokenCacheKey(token)) + } + return nil +} + +func NewJwtTokenIssuer(issuerName string, options *authoptions.AuthenticationOptions, cache cache.Interface) Issuer { + return &jwtTokenIssuer{ + name: issuerName, + options: options, + cache: cache, + keyFunc: func(token *jwt.Token) (i interface{}, err error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { + return []byte(options.JwtSecret), nil + } else { + return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"]) + } + }, + } +} + +func tokenCacheKey(token string) string { + return fmt.Sprintf("kubesphere:tokens:%s", token) +} diff --git a/pkg/apiserver/authentication/token/jwt_test.go b/pkg/apiserver/authentication/token/jwt_test.go new file mode 100644 index 000000000..c326cfa36 --- /dev/null +++ b/pkg/apiserver/authentication/token/jwt_test.go @@ -0,0 +1,109 @@ +/* + * + * Copyright 2020 The KubeSphere 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 token + +import ( + "github.com/google/go-cmp/cmp" + "k8s.io/apiserver/pkg/authentication/user" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "testing" +) + +func TestJwtTokenIssuer(t *testing.T) { + options := authoptions.NewAuthenticateOptions() + options.JwtSecret = "kubesphere" + issuer := NewJwtTokenIssuer(DefaultIssuerName, options, cache.NewSimpleCache()) + + testCases := []struct { + description string + name string + uid string + email string + }{ + { + name: "admin", + uid: "b8be6edd-2c92-4535-9b2a-df6326474458", + }, + { + name: "bar", + uid: "b8be6edd-2c92-4535-9b2a-df6326474452", + }, + } + + for _, testCase := range testCases { + user := &user.DefaultInfo{ + Name: testCase.name, + UID: testCase.uid, + } + + t.Run(testCase.description, func(t *testing.T) { + token, err := issuer.IssueTo(user, 0) + if err != nil { + t.Fatal(err) + } + + got, err := issuer.Verify(token) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(user, got); len(diff) != 0 { + t.Errorf("%T differ (-got, +expected), %s", user, diff) + } + }) + } +} + +func TestTokenVerifyWithoutCacheValidate(t *testing.T) { + options := authoptions.NewAuthenticateOptions() + + // do not set token cache and disable token cache validate, + options.OAuthOptions = &oauth.Options{AccessTokenMaxAge: 0} + options.JwtSecret = "kubesphere" + issuer := NewJwtTokenIssuer(DefaultIssuerName, options, nil) + + client, err := options.OAuthOptions.OAuthClient("default") + + if err != nil { + t.Fatal(err) + } + + user := &user.DefaultInfo{ + Name: "admin", + UID: "admin", + } + + tokenString, err := issuer.IssueTo(user, *client.AccessTokenMaxAge) + + if err != nil { + t.Fatal(err) + } + + got, err := issuer.Verify(tokenString) + + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, user); diff != "" { + t.Error("token validate failed") + } +} diff --git a/pkg/apiserver/authentication/token/user.go b/pkg/apiserver/authentication/token/user.go new file mode 100644 index 000000000..ccae5b6ee --- /dev/null +++ b/pkg/apiserver/authentication/token/user.go @@ -0,0 +1,27 @@ +/* + * + * Copyright 2020 The KubeSphere 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 token + +type User interface { + // Name + GetName() string + + // UID + GetUID() string +} diff --git a/pkg/apiserver/authorization/authorizer/interfaces.go b/pkg/apiserver/authorization/authorizer/interfaces.go new file mode 100644 index 000000000..af67af2b5 --- /dev/null +++ b/pkg/apiserver/authorization/authorizer/interfaces.go @@ -0,0 +1,190 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authorizer + +import ( + "net/http" + + "k8s.io/apiserver/pkg/authentication/user" +) + +// Attributes is an interface used by an Authorizer to get information about a request +// that is used to make an authorization decision. +type Attributes interface { + // GetUser returns the user.Info object to authorize + GetUser() user.Info + + // GetVerb returns the kube verb associated with API requests (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy), + // or the lowercased HTTP verb associated with non-API requests (this includes get, put, post, patch, and delete) + GetVerb() string + + // When IsReadOnly() == true, the request has no side effects, other than + // caching, logging, and other incidentals. + IsReadOnly() bool + + // Indicates whether or not the request should be handled by kubernetes or kubesphere + IsKubernetesRequest() bool + + // The cluster of the object, if a request is for a REST object. + GetCluster() string + + // The workspace of the object, if a request is for a REST object. + GetWorkspace() string + + // The namespace of the object, if a request is for a REST object. + GetNamespace() string + + // The kind of object, if a request is for a REST object. + GetResource() string + + // GetSubresource returns the subresource being requested, if present + GetSubresource() string + + // GetName returns the name of the object as parsed off the request. This will not be present for all request types, but + // will be present for: get, update, delete + GetName() string + + // The group of the resource, if a request is for a REST object. + GetAPIGroup() string + + // GetAPIVersion returns the version of the group requested, if a request is for a REST object. + GetAPIVersion() string + + // IsResourceRequest returns true for requests to API resources, like /api/v1/nodes, + // and false for non-resource endpoints like /api, /healthz + IsResourceRequest() bool + + // GetResourceScope returns the scope of the resource requested, if a request is for a REST object. + GetResourceScope() string + + // GetPath returns the path of the request + GetPath() string +} + +// Authorizer makes an authorization decision based on information gained by making +// zero or more calls to methods of the Attributes interface. It returns nil when an action is +// authorized, otherwise it returns an error. +type Authorizer interface { + Authorize(a Attributes) (authorized Decision, reason string, err error) +} + +type AuthorizerFunc func(a Attributes) (Decision, string, error) + +func (f AuthorizerFunc) Authorize(a Attributes) (Decision, string, error) { + return f(a) +} + +// RuleResolver provides a mechanism for resolving the list of rules that apply to a given user within a namespace. +type RuleResolver interface { + // RulesFor get the list of cluster wide rules, the list of rules in the specific namespace, incomplete status and errors. + RulesFor(user user.Info, namespace string) ([]ResourceRuleInfo, []NonResourceRuleInfo, bool, error) +} + +// RequestAttributesGetter provides a function that extracts Attributes from an http.Request +type RequestAttributesGetter interface { + GetRequestAttributes(user.Info, *http.Request) Attributes +} + +// AttributesRecord implements Attributes interface. +type AttributesRecord struct { + User user.Info + Verb string + Cluster string + Workspace string + Namespace string + APIGroup string + APIVersion string + Resource string + Subresource string + Name string + KubernetesRequest bool + ResourceRequest bool + Path string + ResourceScope string +} + +func (a AttributesRecord) GetUser() user.Info { + return a.User +} + +func (a AttributesRecord) GetVerb() string { + return a.Verb +} + +func (a AttributesRecord) IsReadOnly() bool { + return a.Verb == "get" || a.Verb == "list" || a.Verb == "watch" +} + +func (a AttributesRecord) GetCluster() string { + return a.Cluster +} + +func (a AttributesRecord) GetWorkspace() string { + return a.Workspace +} + +func (a AttributesRecord) GetNamespace() string { + return a.Namespace +} + +func (a AttributesRecord) GetResource() string { + return a.Resource +} + +func (a AttributesRecord) GetSubresource() string { + return a.Subresource +} + +func (a AttributesRecord) GetName() string { + return a.Name +} + +func (a AttributesRecord) GetAPIGroup() string { + return a.APIGroup +} + +func (a AttributesRecord) GetAPIVersion() string { + return a.APIVersion +} + +func (a AttributesRecord) IsResourceRequest() bool { + return a.ResourceRequest +} + +func (a AttributesRecord) IsKubernetesRequest() bool { + return a.KubernetesRequest +} + +func (a AttributesRecord) GetPath() string { + return a.Path +} + +func (a AttributesRecord) GetResourceScope() string { + return a.ResourceScope +} + +type Decision int + +const ( + // DecisionDeny means that an authorizer decided to deny the action. + DecisionDeny Decision = iota + // DecisionAllow means that an authorizer decided to allow the action. + DecisionAllow + // DecisionNoOpionion means that an authorizer has no opinion on whether + // to allow or deny an action. + DecisionNoOpinion +) diff --git a/pkg/apiserver/authorization/authorizer/rule.go b/pkg/apiserver/authorization/authorizer/rule.go new file mode 100644 index 000000000..8f7d9d9ef --- /dev/null +++ b/pkg/apiserver/authorization/authorizer/rule.go @@ -0,0 +1,73 @@ +/* +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 authorizer + +type ResourceRuleInfo interface { + // GetVerbs returns a list of kubernetes resource API verbs. + GetVerbs() []string + // GetAPIGroups return the names of the APIGroup that contains the resources. + GetAPIGroups() []string + // GetResources return a list of resources the rule applies to. + GetResources() []string + // GetResourceNames return a white list of names that the rule applies to. + GetResourceNames() []string +} + +// DefaultResourceRuleInfo holds information that describes a rule for the resource +type DefaultResourceRuleInfo struct { + Verbs []string + APIGroups []string + Resources []string + ResourceNames []string +} + +func (i *DefaultResourceRuleInfo) GetVerbs() []string { + return i.Verbs +} + +func (i *DefaultResourceRuleInfo) GetAPIGroups() []string { + return i.APIGroups +} + +func (i *DefaultResourceRuleInfo) GetResources() []string { + return i.Resources +} + +func (i *DefaultResourceRuleInfo) GetResourceNames() []string { + return i.ResourceNames +} + +type NonResourceRuleInfo interface { + // GetVerbs returns a list of kubernetes resource API verbs. + GetVerbs() []string + // GetNonResourceURLs return a set of partial urls that a user should have access to. + GetNonResourceURLs() []string +} + +// DefaultNonResourceRuleInfo holds information that describes a rule for the non-resource +type DefaultNonResourceRuleInfo struct { + Verbs []string + NonResourceURLs []string +} + +func (i *DefaultNonResourceRuleInfo) GetVerbs() []string { + return i.Verbs +} + +func (i *DefaultNonResourceRuleInfo) GetNonResourceURLs() []string { + return i.NonResourceURLs +} diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin.go b/pkg/apiserver/authorization/authorizerfactory/builtin.go new file mode 100644 index 000000000..6d2f4d650 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin.go @@ -0,0 +1,49 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" +) + +// alwaysAllowAuthorizer is an implementation of authorizer.Attributes +// which always says yes to an authorization request. +// It is useful in tests and when using kubernetes in an open manner. +type alwaysAllowAuthorizer struct{} + +func (alwaysAllowAuthorizer) Authorize(authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + return authorizer.DecisionAllow, "", nil +} + +func NewAlwaysAllowAuthorizer() *alwaysAllowAuthorizer { + return new(alwaysAllowAuthorizer) +} + +// alwaysDenyAuthorizer is an implementation of authorizer.Attributes +// which always says no to an authorization request. +// It is useful in unit tests to force an operation to be forbidden. +type alwaysDenyAuthorizer struct{} + +func (alwaysDenyAuthorizer) Authorize(a authorizer.Attributes) (decision authorizer.Decision, reason string, err error) { + return authorizer.DecisionNoOpinion, "Everything is forbidden.", nil +} + +func NewAlwaysDenyAuthorizer() *alwaysDenyAuthorizer { + return new(alwaysDenyAuthorizer) +} diff --git a/pkg/apiserver/authorization/authorizerfactory/builtin_test.go b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go new file mode 100644 index 000000000..67cd5595e --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/builtin_test.go @@ -0,0 +1,38 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "testing" +) + +func TestNewAlwaysAllowAuthorizer(t *testing.T) { + aaa := NewAlwaysAllowAuthorizer() + if decision, _, _ := aaa.Authorize(nil); decision != authorizer.DecisionAllow { + t.Errorf("AlwaysAllowAuthorizer.Authorize did not authorize successfully.") + } +} + +func TestNewAlwaysDenyAuthorizer(t *testing.T) { + ada := NewAlwaysDenyAuthorizer() + if decision, _, _ := ada.Authorize(nil); decision == authorizer.DecisionAllow { + t.Errorf("AlwaysDenyAuthorizer.Authorize returned nil instead of error.") + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/opa.go b/pkg/apiserver/authorization/authorizerfactory/opa.go new file mode 100644 index 000000000..07b407d3a --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/opa.go @@ -0,0 +1,175 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "context" + "github.com/open-policy-agent/opa/rego" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/models/iam/am" +) + +type opaAuthorizer struct { + am am.AccessManagementInterface +} + +const ( + defaultRegoQuery = "data.authz.allow" +) + +// Make decision by request attributes +func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + // Make decisions based on the authorization policy of different levels of roles + // Error returned when an internal error occurs + // Reason must be returned when access is denied + globalRole, err := o.am.GetGlobalRoleOfUser(attr.GetUser().GetName()) + + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err + } + + // check global policy rules + if authorized, reason, err = o.makeDecision(globalRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, nil + } + + // it's global resource, permission denied + if attr.GetResourceScope() == iamv1alpha2.GlobalScope { + return authorizer.DecisionNoOpinion, "", nil + } + + if attr.GetResourceScope() == iamv1alpha2.WorkspaceScope { + workspaceRole, err := o.am.GetWorkspaceRoleOfUser(attr.GetUser().GetName(), attr.GetWorkspace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err + } + + // check workspace role policy rules + if authorized, reason, err := o.makeDecision(workspaceRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + return authorizer.DecisionNoOpinion, "", nil + } + + if attr.GetResourceScope() == iamv1alpha2.NamespaceScope { + role, err := o.am.GetNamespaceRoleOfUser(attr.GetUser().GetName(), attr.GetNamespace()) + if err != nil { + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + return authorizer.DecisionNoOpinion, "", err + } + // check namespace role policy rules + if authorized, reason, err := o.makeDecision(role, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, err + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + return authorizer.DecisionNoOpinion, "", nil + } + + clusterRole, err := o.am.GetClusterRoleOfUser(attr.GetUser().GetName(), attr.GetCluster()) + + if errors.IsNotFound(err) { + return authorizer.DecisionNoOpinion, "", nil + } + + if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + // check cluster role policy rules + if authorized, reason, err := o.makeDecision(clusterRole, attr); authorized == authorizer.DecisionAllow { + return authorized, reason, nil + } else if err != nil { + return authorizer.DecisionNoOpinion, "", err + } + + return authorizer.DecisionNoOpinion, "", nil +} + +// Make decision base on role +func (o *opaAuthorizer) makeDecision(role interface{}, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { + + regoPolicy := "" + + // override + if globalRole, ok := role.(*iamv1alpha2.GlobalRole); ok { + if overrideRego, ok := globalRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if workspaceRole, ok := role.(*iamv1alpha2.WorkspaceRole); ok { + if overrideRego, ok := workspaceRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if clusterRole, ok := role.(*rbacv1.ClusterRole); ok { + if overrideRego, ok := clusterRole.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } else if role, ok := role.(*rbacv1.Role); ok { + if overrideRego, ok := role.Annotations[iamv1alpha2.RegoOverrideAnnotation]; ok { + regoPolicy = overrideRego + } + } + + if regoPolicy == "" { + return authorizer.DecisionNoOpinion, "", nil + } + + // Call the rego.New function to create an object that can be prepared or evaluated + // After constructing a new rego.Rego object you can call PrepareForEval() to obtain an executable query + query, err := rego.New(rego.Query(defaultRegoQuery), rego.Module("authz.rego", regoPolicy)).PrepareForEval(context.Background()) + + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + // The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly. + results, err := query.Eval(context.Background(), rego.EvalInput(a)) + + if err != nil { + klog.Errorf("syntax error:%s,refer: %s+v", err, role) + return authorizer.DecisionNoOpinion, "", err + } + + if len(results) > 0 && results[0].Expressions[0].Value == true { + return authorizer.DecisionAllow, "", nil + } + + return authorizer.DecisionNoOpinion, "", nil +} + +func NewOPAAuthorizer(am am.AccessManagementInterface) *opaAuthorizer { + return &opaAuthorizer{am: am} +} diff --git a/pkg/apiserver/authorization/authorizerfactory/opa_test.go b/pkg/apiserver/authorization/authorizerfactory/opa_test.go new file mode 100644 index 000000000..c7206df99 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/opa_test.go @@ -0,0 +1,252 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "fmt" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" + fakek8s "k8s.io/client-go/kubernetes/fake" + iamvealpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + factory "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "testing" +) + +func TestGlobalRole(t *testing.T) { + + operator, err := prepare() + + if err != nil { + t.Fatal(err) + } + + opa := NewOPAAuthorizer(operator) + + tests := []struct { + name string + request authorizer.AttributesRecord + expectedDecision authorizer.Decision + }{ + { + name: "admin can list nodes", + request: authorizer.AttributesRecord{ + User: &user.DefaultInfo{ + Name: "admin", + UID: "0", + Groups: []string{"admin"}, + Extra: nil, + }, + Verb: "list", + APIVersion: "v1", + Resource: "nodes", + KubernetesRequest: true, + ResourceRequest: true, + Path: "/api/v1/nodes", + }, + expectedDecision: authorizer.DecisionAllow, + }, + { + name: "anonymous can not list nodes", + request: authorizer.AttributesRecord{ + User: &user.DefaultInfo{ + Name: user.Anonymous, + UID: "0", + Groups: []string{"admin"}, + Extra: nil, + }, + Verb: "list", + APIVersion: "v1", + Resource: "nodes", + KubernetesRequest: true, + ResourceRequest: true, + Path: "/api/v1/nodes", + }, + expectedDecision: authorizer.DecisionNoOpinion, + }, { + name: "tom can list nodes in cluster1", + request: authorizer.AttributesRecord{ + User: &user.DefaultInfo{ + Name: "tom", + }, + Verb: "list", + Cluster: "cluster1", + APIVersion: "v1", + Resource: "nodes", + KubernetesRequest: true, + ResourceRequest: true, + Path: "/api/v1/clusters/cluster1/nodes", + }, + expectedDecision: authorizer.DecisionAllow, + }, + { + name: "tom can not list nodes in cluster2", + request: authorizer.AttributesRecord{ + User: &user.DefaultInfo{ + Name: "tom", + }, + Verb: "list", + Cluster: "cluster2", + APIVersion: "v1", + Resource: "nodes", + KubernetesRequest: true, + ResourceRequest: true, + Path: "/api/v1/clusters/cluster2/nodes", + }, + expectedDecision: authorizer.DecisionNoOpinion, + }, + } + + for _, test := range tests { + decision, _, err := opa.Authorize(test.request) + if err != nil { + t.Errorf("test failed: %s, %v", test.name, err) + } + if decision != test.expectedDecision { + t.Errorf("%s: expected decision %v, actual %+v", test.name, test.expectedDecision, decision) + } + } +} + +func prepare() (am.AccessManagementInterface, error) { + globalRoles := []*iamvealpha2.GlobalRole{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = true"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: "package authz\ndefault allow = false"}, + }, + }, { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRole, + APIVersion: iamvealpha2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + Annotations: map[string]string{iamvealpha2.RegoOverrideAnnotation: `package authz +default allow = false +allow { + resources_in_cluster1 +} +resources_in_cluster1 { + input.Cluster == "cluster1" +}`}, + }, + }, + } + + roleBindings := []*iamvealpha2.GlobalRoleBinding{ + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "global-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "admin", + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "anonymous", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "anonymous", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: user.Anonymous, + }, + }, + }, + { + TypeMeta: metav1.TypeMeta{ + Kind: iamvealpha2.ResourceKindGlobalRoleBinding, + APIVersion: iamvealpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster1-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Kind: iamvealpha2.ResourceKindGlobalRole, + Name: "cluster1-admin", + }, + Subjects: []rbacv1.Subject{ + { + Kind: iamvealpha2.ResourceKindUser, + APIGroup: iamvealpha2.SchemeGroupVersion.String(), + Name: "tom", + }, + }, + }, + } + + ksClient := fake.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + factory := factory.NewInformerFactories(k8sClient, ksClient, nil, nil) + for _, role := range globalRoles { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, fmt.Errorf("add role:%s", err) + } + } + + for _, roleBinding := range roleBindings { + err := factory.KubeSphereSharedInformerFactory().Iam().V1alpha2().GlobalRoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, fmt.Errorf("add role binding:%s", err) + } + } + + operator := am.NewAMOperator(factory) + + return operator, nil +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac.go b/pkg/apiserver/authorization/authorizerfactory/rbac.go new file mode 100644 index 000000000..d54199879 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac.go @@ -0,0 +1,384 @@ +/* + * + * Copyright 2020 The KubeSphere 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 authorizerfactory + +import ( + "bytes" + "fmt" + "k8s.io/apiserver/pkg/authentication/serviceaccount" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + + "k8s.io/klog" + + rbacv1 "k8s.io/api/rbac/v1" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/user" + rbacv1helpers "kubesphere.io/kubesphere/pkg/apis/rbac/v1" +) + +type RBACAuthorizer struct { + am am.AccessManagementInterface +} + +// authorizingVisitor short-circuits once allowed, and collects any resolution errors encountered +type authorizingVisitor struct { + requestAttributes authorizer.Attributes + + allowed bool + reason string + errors []error +} + +func (v *authorizingVisitor) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil && ruleAllows(v.requestAttributes, rule) { + v.allowed = true + v.reason = fmt.Sprintf("RBAC: allowed by %s", source.String()) + return false + } + if err != nil { + v.errors = append(v.errors, err) + } + return true +} + +type ruleAccumulator struct { + rules []rbacv1.PolicyRule + errors []error +} + +func (r *ruleAccumulator) visit(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool { + if rule != nil { + r.rules = append(r.rules, *rule) + } + if err != nil { + r.errors = append(r.errors, err) + } + return true +} + +func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) { + ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes} + + r.visitRulesFor(requestAttributes, ruleCheckingVisitor.visit) + + if ruleCheckingVisitor.allowed { + return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil + } + + // Build a detailed log of the denial. + // Make the whole block conditional so we don't do a lot of string-building we won't use. + if klog.V(4) { + var operation string + if requestAttributes.IsResourceRequest() { + b := &bytes.Buffer{} + b.WriteString(`"`) + b.WriteString(requestAttributes.GetVerb()) + b.WriteString(`" resource "`) + b.WriteString(requestAttributes.GetResource()) + if len(requestAttributes.GetAPIGroup()) > 0 { + b.WriteString(`.`) + b.WriteString(requestAttributes.GetAPIGroup()) + } + if len(requestAttributes.GetSubresource()) > 0 { + b.WriteString(`/`) + b.WriteString(requestAttributes.GetSubresource()) + } + b.WriteString(`"`) + if len(requestAttributes.GetName()) > 0 { + b.WriteString(` named "`) + b.WriteString(requestAttributes.GetName()) + b.WriteString(`"`) + } + operation = b.String() + } else { + operation = fmt.Sprintf("%q nonResourceURL %q", requestAttributes.GetVerb(), requestAttributes.GetPath()) + } + + var scope string + if ns := requestAttributes.GetNamespace(); len(ns) > 0 { + scope = fmt.Sprintf("in namespace %q", ns) + } else if ws := requestAttributes.GetWorkspace(); len(ws) > 0 { + scope = fmt.Sprintf("in workspace %q", ws) + } else if cluster := requestAttributes.GetWorkspace(); len(cluster) > 0 { + scope = fmt.Sprintf("in cluster %q", cluster) + } else { + scope = "global-wide" + } + + klog.Infof("RBAC: no rules authorize user %q with groups %q to %s %s", requestAttributes.GetUser().GetName(), requestAttributes.GetUser().GetGroups(), operation, scope) + } + + reason := "" + if len(ruleCheckingVisitor.errors) > 0 { + reason = fmt.Sprintf("RBAC: %v", utilerrors.NewAggregate(ruleCheckingVisitor.errors)) + } + return authorizer.DecisionNoOpinion, reason, nil +} + +func NewRBACAuthorizer(am am.AccessManagementInterface) *RBACAuthorizer { + return &RBACAuthorizer{am: am} +} + +func ruleAllows(requestAttributes authorizer.Attributes, rule *rbacv1.PolicyRule) bool { + if requestAttributes.IsResourceRequest() { + combinedResource := requestAttributes.GetResource() + if len(requestAttributes.GetSubresource()) > 0 { + combinedResource = requestAttributes.GetResource() + "/" + requestAttributes.GetSubresource() + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.APIGroupMatches(rule, requestAttributes.GetAPIGroup()) && + rbacv1helpers.ResourceMatches(rule, combinedResource, requestAttributes.GetSubresource()) && + rbacv1helpers.ResourceNameMatches(rule, requestAttributes.GetName()) + } + + return rbacv1helpers.VerbMatches(rule, requestAttributes.GetVerb()) && + rbacv1helpers.NonResourceURLMatches(rule, requestAttributes.GetPath()) +} + +func (r *RBACAuthorizer) rulesFor(requestAttributes authorizer.Attributes) ([]rbacv1.PolicyRule, error) { + visitor := &ruleAccumulator{} + r.visitRulesFor(requestAttributes, visitor.visit) + return visitor.rules, utilerrors.NewAggregate(visitor.errors) +} + +func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes, visitor func(source fmt.Stringer, rule *rbacv1.PolicyRule, err error) bool) { + if globalRoleBindings, err := r.am.ListGlobalRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &globalRoleBindingDescriber{} + for _, globalRoleBinding := range globalRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), globalRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(globalRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = globalRoleBinding + sourceDescriber.subject = &globalRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.WorkspaceScope { + if workspaceRoleBindings, err := r.am.ListWorkspaceRoleBindings("", requestAttributes.GetWorkspace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &workspaceRoleBindingDescriber{} + for _, workspaceRoleBinding := range workspaceRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), workspaceRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(workspaceRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = workspaceRoleBinding + sourceDescriber.subject = &workspaceRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if requestAttributes.GetResourceScope() == iamv1alpha2.NamespaceScope { + if roleBindings, err := r.am.ListRoleBindings("", requestAttributes.GetNamespace()); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &roleBindingDescriber{} + for _, roleBinding := range roleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, requestAttributes.GetNamespace()) + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, requestAttributes.GetNamespace()) + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = roleBinding + sourceDescriber.subject = &roleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } + } + + if clusterRoleBindings, err := r.am.ListClusterRoleBindings(""); err != nil { + if !visitor(nil, nil, err) { + return + } + } else { + sourceDescriber := &clusterRoleBindingDescriber{} + for _, clusterRoleBinding := range clusterRoleBindings { + subjectIndex, applies := appliesTo(requestAttributes.GetUser(), clusterRoleBinding.Subjects, "") + if !applies { + continue + } + rules, err := r.am.GetRoleReferenceRules(clusterRoleBinding.RoleRef, "") + if err != nil { + if !visitor(nil, nil, err) { + return + } + continue + } + sourceDescriber.binding = clusterRoleBinding + sourceDescriber.subject = &clusterRoleBinding.Subjects[subjectIndex] + for i := range rules { + if !visitor(sourceDescriber, &rules[i], nil) { + return + } + } + } + } +} + +// appliesTo returns whether any of the bindingSubjects applies to the specified subject, +// and if true, the index of the first subject that applies +func appliesTo(user user.Info, bindingSubjects []rbacv1.Subject, namespace string) (int, bool) { + for i, bindingSubject := range bindingSubjects { + if appliesToUser(user, bindingSubject, namespace) { + return i, true + } + } + return 0, false +} + +func appliesToUser(user user.Info, subject rbacv1.Subject, namespace string) bool { + switch subject.Kind { + case rbacv1.UserKind: + return user.GetName() == subject.Name + + case rbacv1.GroupKind: + return sliceutil.HasString(user.GetGroups(), subject.Name) + + case rbacv1.ServiceAccountKind: + // default the namespace to namespace we're working in if its available. This allows rolebindings that reference + // SAs in th local namespace to avoid having to qualify them. + saNamespace := namespace + if len(subject.Namespace) > 0 { + saNamespace = subject.Namespace + } + if len(saNamespace) == 0 { + return false + } + // use a more efficient comparison for RBAC checking + return serviceaccount.MatchesUsername(saNamespace, subject.Name, user.GetName()) + default: + return false + } +} + +type globalRoleBindingDescriber struct { + binding *iamv1alpha2.GlobalRoleBinding + subject *rbacv1.Subject +} + +func (d *globalRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type clusterRoleBindingDescriber struct { + binding *rbacv1.ClusterRoleBinding + subject *rbacv1.Subject +} + +func (d *clusterRoleBindingDescriber) String() string { + return fmt.Sprintf("ClusterRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type workspaceRoleBindingDescriber struct { + binding *iamv1alpha2.WorkspaceRoleBinding + subject *rbacv1.Subject +} + +func (d *workspaceRoleBindingDescriber) String() string { + return fmt.Sprintf("GlobalRoleBinding %q of %s %q to %s", + d.binding.Name, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, ""), + ) +} + +type roleBindingDescriber struct { + binding *rbacv1.RoleBinding + subject *rbacv1.Subject +} + +func (d *roleBindingDescriber) String() string { + return fmt.Sprintf("RoleBinding %q of %s %q to %s", + d.binding.Name+"/"+d.binding.Namespace, + d.binding.RoleRef.Kind, + d.binding.RoleRef.Name, + describeSubject(d.subject, d.binding.Namespace), + ) +} + +func describeSubject(s *rbacv1.Subject, bindingNamespace string) string { + switch s.Kind { + case rbacv1.ServiceAccountKind: + if len(s.Namespace) > 0 { + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+s.Namespace) + } + return fmt.Sprintf("%s %q", s.Kind, s.Name+"/"+bindingNamespace) + default: + return fmt.Sprintf("%s %q", s.Kind, s.Name) + } +} diff --git a/pkg/apiserver/authorization/authorizerfactory/rbac_test.go b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go new file mode 100644 index 000000000..cf2ff6570 --- /dev/null +++ b/pkg/apiserver/authorization/authorizerfactory/rbac_test.go @@ -0,0 +1,389 @@ +/* +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 authorizerfactory + +import ( + "errors" + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + "hash/fnv" + "io" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + fakek8s "k8s.io/client-go/kubernetes/fake" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "sort" + "testing" + + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" +) + +// StaticRoles is a rule resolver that resolves from lists of role objects. +type StaticRoles struct { + roles []*rbacv1.Role + roleBindings []*rbacv1.RoleBinding + clusterRoles []*rbacv1.ClusterRole + clusterRoleBindings []*rbacv1.ClusterRoleBinding +} + +func (r *StaticRoles) GetRole(namespace, name string) (*rbacv1.Role, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when getting role") + } + for _, role := range r.roles { + if role.Namespace == namespace && role.Name == name { + return role, nil + } + } + return nil, errors.New("role not found") +} + +func (r *StaticRoles) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { + for _, clusterRole := range r.clusterRoles { + if clusterRole.Name == name { + return clusterRole, nil + } + } + return nil, errors.New("clusterrole not found") +} + +func (r *StaticRoles) ListRoleBindings(namespace string) ([]*rbacv1.RoleBinding, error) { + if len(namespace) == 0 { + return nil, errors.New("must provide namespace when listing role bindings") + } + + roleBindingList := []*rbacv1.RoleBinding{} + for _, roleBinding := range r.roleBindings { + if roleBinding.Namespace != namespace { + continue + } + // TODO(ericchiang): need to implement label selectors? + roleBindingList = append(roleBindingList, roleBinding) + } + return roleBindingList, nil +} + +func (r *StaticRoles) ListClusterRoleBindings() ([]*rbacv1.ClusterRoleBinding, error) { + return r.clusterRoleBindings, nil +} + +// compute a hash of a policy rule so we can sort in a deterministic order +func hashOf(p rbacv1.PolicyRule) string { + hash := fnv.New32() + writeStrings := func(slis ...[]string) { + for _, sli := range slis { + for _, s := range sli { + io.WriteString(hash, s) + } + } + } + writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs) + return string(hash.Sum(nil)) +} + +// byHash sorts a set of policy rules by a hash of its fields +type byHash []rbacv1.PolicyRule + +func (b byHash) Len() int { return len(b) } +func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) } +func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] } + +func TestRBACAuthorizer(t *testing.T) { + ruleReadPods := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"pods"}, + } + ruleReadServices := rbacv1.PolicyRule{ + Verbs: []string{"GET", "WATCH"}, + APIGroups: []string{"v1"}, + Resources: []string{"services"}, + } + ruleWriteNodes := rbacv1.PolicyRule{ + Verbs: []string{"PUT", "CREATE", "UPDATE"}, + APIGroups: []string{"v1"}, + Resources: []string{"nodes"}, + } + ruleAdmin := rbacv1.PolicyRule{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + } + + staticRoles1 := StaticRoles{ + roles: []*rbacv1.Role{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1", Name: "readthings"}, + Rules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + }, + clusterRoles: []*rbacv1.ClusterRole{ + { + ObjectMeta: metav1.ObjectMeta{Name: "cluster-admin"}, + Rules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "write-nodes"}, + Rules: []rbacv1.PolicyRule{ruleWriteNodes}, + }, + }, + roleBindings: []*rbacv1.RoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "namespace1"}, + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + {Kind: rbacv1.GroupKind, Name: "group1"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: "readthings"}, + }, + }, + clusterRoleBindings: []*rbacv1.ClusterRoleBinding{ + { + Subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "admin"}, + {Kind: rbacv1.GroupKind, Name: "admin"}, + }, + RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "cluster-admin"}, + }, + }, + } + + tests := []struct { + StaticRoles + + // For a given context, what are the rules that apply? + user user.Info + namespace string + effectiveRules []rbacv1.PolicyRule + }{ + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace1", + effectiveRules: []rbacv1.PolicyRule{ruleReadPods, ruleReadServices}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "namespace2", + effectiveRules: nil, + }, + { + StaticRoles: staticRoles1, + // Same as above but without a namespace. Only cluster rules should apply. + user: &user.DefaultInfo{Name: "foobar", Groups: []string{"admin"}}, + effectiveRules: []rbacv1.PolicyRule{ruleAdmin}, + }, + { + StaticRoles: staticRoles1, + user: &user.DefaultInfo{}, + effectiveRules: nil, + }, + } + + for i, tc := range tests { + ruleResolver, err := newMockRBACAuthorizer(&tc.StaticRoles) + + if err != nil { + t.Fatal(err) + } + + scope := iamv1alpha2.ClusterScope + + if tc.namespace != "" { + scope = iamv1alpha2.NamespaceScope + } + + rules, err := ruleResolver.rulesFor(authorizer.AttributesRecord{ + User: tc.user, + Namespace: tc.namespace, + ResourceScope: scope, + }) + + if err != nil { + t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err) + continue + } + + // Sort for deep equals + sort.Sort(byHash(rules)) + sort.Sort(byHash(tc.effectiveRules)) + + if diff := cmp.Diff(rules, tc.effectiveRules); diff != "" { + t.Errorf("case %d: %s", i, diff) + } + } +} + +func newMockRBACAuthorizer(staticRoles *StaticRoles) (*RBACAuthorizer, error) { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, role := range staticRoles.roles { + err := k8sInformerFactory.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + if err != nil { + return nil, err + } + } + + for _, roleBinding := range staticRoles.roleBindings { + err := k8sInformerFactory.Rbac().V1().RoleBindings().Informer().GetIndexer().Add(roleBinding) + if err != nil { + return nil, err + } + } + + for _, clusterRole := range staticRoles.clusterRoles { + err := k8sInformerFactory.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(clusterRole) + if err != nil { + return nil, err + } + } + + for _, clusterRoleBinding := range staticRoles.clusterRoleBindings { + err := k8sInformerFactory.Rbac().V1().ClusterRoleBindings().Informer().GetIndexer().Add(clusterRoleBinding) + if err != nil { + return nil, err + } + } + return NewRBACAuthorizer(am.NewAMOperator(fakeInformerFactory)), nil +} + +func TestAppliesTo(t *testing.T) { + tests := []struct { + subjects []rbacv1.Subject + user user.Info + namespace string + appliesTo bool + index int + testCase string + }{ + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 0, + testCase: "single subject that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that matches username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.UserKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam"}, + appliesTo: false, + testCase: "multiple subjects, none that match username", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + }, + user: &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}, + namespace: "namespace1", + appliesTo: true, + index: 1, + testCase: "multiple subjects, one that match group, should ignore namespace", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "barfoo"}, + {Kind: rbacv1.GroupKind, Name: "foobar"}, + {Kind: rbacv1.ServiceAccountKind, Namespace: "kube-system", Name: "default"}, + }, + user: &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}, + namespace: "default", + appliesTo: true, + index: 2, + testCase: "multiple subjects with a service account that matches", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.UserKind, Name: "*"}, + }, + user: &user.DefaultInfo{Name: "foobar"}, + namespace: "default", + appliesTo: false, + testCase: "* user subject name doesn't match all users", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "foobar", Groups: []string{user.AllAuthenticated}}, + namespace: "default", + appliesTo: true, + index: 0, + testCase: "binding to all authenticated and unauthenticated subjects matches authenticated user", + }, + { + subjects: []rbacv1.Subject{ + {Kind: rbacv1.GroupKind, Name: user.AllAuthenticated}, + {Kind: rbacv1.GroupKind, Name: user.AllUnauthenticated}, + }, + user: &user.DefaultInfo{Name: "system:anonymous", Groups: []string{user.AllUnauthenticated}}, + namespace: "default", + appliesTo: true, + index: 1, + testCase: "binding to all authenticated and unauthenticated subjects matches anonymous user", + }, + } + + for _, tc := range tests { + gotIndex, got := appliesTo(tc.user, tc.subjects, tc.namespace) + if got != tc.appliesTo { + t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got) + } + if gotIndex != tc.index { + t.Errorf("case %q want index %d, got %d", tc.testCase, tc.index, gotIndex) + } + } +} diff --git a/pkg/apiserver/authorization/options/authorization_options.go b/pkg/apiserver/authorization/options/authorization_options.go new file mode 100644 index 000000000..0a2a5e8f2 --- /dev/null +++ b/pkg/apiserver/authorization/options/authorization_options.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2020 The KubeSphere 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 options + +import ( + "fmt" + "github.com/spf13/pflag" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" +) + +type AuthorizationOptions struct { + Mode string `json:"mode" yaml:"mode"` +} + +func NewAuthorizationOptions() *AuthorizationOptions { + return &AuthorizationOptions{Mode: RBAC} +} + +var ( + AlwaysDeny = "AlwaysDeny" + AlwaysAllow = "AlwaysAllow" + RBAC = "RBAC" +) + +func (o *AuthorizationOptions) AddFlags(fs *pflag.FlagSet, s *AuthorizationOptions) { + fs.StringVar(&o.Mode, "authorization", s.Mode, "Authorization setting, allowed values: AlwaysDeny, AlwaysAllow, RBAC.") +} + +func (o AuthorizationOptions) Validate() []error { + errs := make([]error, 0) + if !sliceutil.HasString([]string{AlwaysAllow, AlwaysDeny, RBAC}, o.Mode) { + err := fmt.Errorf("authorization mode %s not support", o.Mode) + klog.Error(err) + errs = append(errs, err) + } + return errs +} diff --git a/pkg/apiserver/authorization/path/doc.go b/pkg/apiserver/authorization/path/doc.go new file mode 100644 index 000000000..743d945b4 --- /dev/null +++ b/pkg/apiserver/authorization/path/doc.go @@ -0,0 +1,18 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package path contains an authorizer that allows certain paths and path prefixes. +package path diff --git a/pkg/apiserver/authorization/path/path.go b/pkg/apiserver/authorization/path/path.go new file mode 100644 index 000000000..435cca198 --- /dev/null +++ b/pkg/apiserver/authorization/path/path.go @@ -0,0 +1,63 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package path + +import ( + "fmt" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" +) + +// NewAuthorizer returns an authorizer which accepts a given set of paths. +// Each path is either a fully matching path or it ends in * in case a prefix match is done. A leading / is optional. +func NewAuthorizer(alwaysAllowPaths []string) (authorizer.Authorizer, error) { + var prefixes []string + paths := sets.NewString() + for _, p := range alwaysAllowPaths { + p = strings.TrimPrefix(p, "/") + if len(p) == 0 { + // matches "/" + paths.Insert(p) + continue + } + if strings.ContainsRune(p[:len(p)-1], '*') { + return nil, fmt.Errorf("only trailing * allowed in %q", p) + } + if strings.HasSuffix(p, "*") { + prefixes = append(prefixes, p[:len(p)-1]) + } else { + paths.Insert(p) + } + } + + return authorizer.AuthorizerFunc(func(a authorizer.Attributes) (authorizer.Decision, string, error) { + pth := strings.TrimPrefix(a.GetPath(), "/") + if paths.Has(pth) { + return authorizer.DecisionAllow, "", nil + } + + for _, prefix := range prefixes { + if strings.HasPrefix(pth, prefix) { + return authorizer.DecisionAllow, "", nil + } + } + + return authorizer.DecisionNoOpinion, "", nil + }), nil +} diff --git a/pkg/apiserver/authorization/path/path_test.go b/pkg/apiserver/authorization/path/path_test.go new file mode 100644 index 000000000..73c4eb2f6 --- /dev/null +++ b/pkg/apiserver/authorization/path/path_test.go @@ -0,0 +1,76 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package path + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "testing" +) + +func TestNewAuthorizer(t *testing.T) { + tests := []struct { + name string + excludedPaths []string + allowed, denied, noOpinion []string + wantErr bool + }{ + {"inner star", []string{"/foo*bar"}, nil, nil, nil, true}, + {"double star", []string{"/foo**"}, nil, nil, nil, true}, + {"empty", nil, nil, nil, []string{"/"}, false}, + {"slash", []string{"/"}, []string{"/"}, nil, []string{"/foo", "//"}, false}, + {"foo", []string{"/foo"}, []string{"/foo", "foo"}, nil, []string{"/", "", "/bar", "/foo/", "/fooooo", "//foo"}, false}, + {"foo slash", []string{"/foo/"}, []string{"/foo/"}, nil, []string{"/", "", "/bar", "/foo", "/fooooo"}, false}, + {"foo slash star", []string{"/foo/*"}, []string{"/foo/", "/foo/bar/bla"}, nil, []string{"/", "", "/foo", "/bar", "/fooooo"}, false}, + {"foo bar", []string{"/foo", "/bar"}, []string{"/foo", "/bar"}, nil, []string{"/", "", "/foo/", "/bar/", "/fooooo"}, false}, + {"foo star", []string{"/foo*"}, []string{"/foo", "/foooo"}, nil, []string{"/", "", "/fo", "/bar"}, false}, + {"star", []string{"/*"}, []string{"/", "", "/foo", "/foooo"}, nil, nil, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a, err := NewAuthorizer(tt.excludedPaths) + if err != nil && !tt.wantErr { + t.Fatalf("unexpected error: %v", err) + } + if err == nil && tt.wantErr { + t.Fatalf("expected error, didn't get any") + } + if err != nil { + return + } + + for _, cases := range []struct { + paths []string + want authorizer.Decision + }{ + {tt.allowed, authorizer.DecisionAllow}, + {tt.denied, authorizer.DecisionDeny}, + {tt.noOpinion, authorizer.DecisionNoOpinion}, + } { + for _, pth := range cases.paths { + info := authorizer.AttributesRecord{ + Path: pth, + } + if got, _, err := a.Authorize(info); err != nil { + t.Errorf("NewAuthorizer(%v).Authorize(%q) return unexpected error: %v", tt.excludedPaths, pth, err) + } else if got != cases.want { + t.Errorf("NewAuthorizer(%v).Authorize(%q) = %v, want %v", tt.excludedPaths, pth, got, cases.want) + } + } + } + }) + } +} diff --git a/pkg/apiserver/authorization/union/union.go b/pkg/apiserver/authorization/union/union.go new file mode 100644 index 000000000..1e3dfac97 --- /dev/null +++ b/pkg/apiserver/authorization/union/union.go @@ -0,0 +1,105 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package union implements an authorizer that combines multiple subauthorizer. +// The union authorizer iterates over each subauthorizer and returns the first +// decision that is either an Allow decision or a Deny decision. If a +// subauthorizer returns a NoOpinion, then the union authorizer moves onto the +// next authorizer or, if the subauthorizer was the last authorizer, returns +// NoOpinion as the aggregate decision. I.e. union authorizer creates an +// aggregate decision and supports short-circuit allows and denies from +// subauthorizers. +package union + +import ( + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "strings" + + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apiserver/pkg/authentication/user" +) + +// unionAuthzHandler authorizer against a chain of authorizer.Authorizer +type unionAuthzHandler []authorizer.Authorizer + +// New returns an authorizer that authorizes against a chain of authorizer.Authorizer objects +func New(authorizationHandlers ...authorizer.Authorizer) authorizer.Authorizer { + return unionAuthzHandler(authorizationHandlers) +} + +// Authorizes against a chain of authorizer.Authorizer objects and returns nil if successful and returns error if unsuccessful +func (authzHandler unionAuthzHandler) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) { + var ( + errlist []error + reasonlist []string + ) + + for _, currAuthzHandler := range authzHandler { + decision, reason, err := currAuthzHandler.Authorize(a) + + if err != nil { + errlist = append(errlist, err) + } + if len(reason) != 0 { + reasonlist = append(reasonlist, reason) + } + switch decision { + case authorizer.DecisionAllow, authorizer.DecisionDeny: + return decision, reason, err + case authorizer.DecisionNoOpinion: + // continue to the next authorizer + } + } + + return authorizer.DecisionNoOpinion, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist) +} + +// unionAuthzRulesHandler authorizer against a chain of authorizer.RuleResolver +type unionAuthzRulesHandler []authorizer.RuleResolver + +// NewRuleResolvers returns an authorizer that authorizes against a chain of authorizer.Authorizer objects +func NewRuleResolvers(authorizationHandlers ...authorizer.RuleResolver) authorizer.RuleResolver { + return unionAuthzRulesHandler(authorizationHandlers) +} + +// RulesFor against a chain of authorizer.RuleResolver objects and returns nil if successful and returns error if unsuccessful +func (authzHandler unionAuthzRulesHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) { + var ( + errList []error + resourceRulesList []authorizer.ResourceRuleInfo + nonResourceRulesList []authorizer.NonResourceRuleInfo + ) + incompleteStatus := false + + for _, currAuthzHandler := range authzHandler { + resourceRules, nonResourceRules, incomplete, err := currAuthzHandler.RulesFor(user, namespace) + + if incomplete == true { + incompleteStatus = true + } + if err != nil { + errList = append(errList, err) + } + if len(resourceRules) > 0 { + resourceRulesList = append(resourceRulesList, resourceRules...) + } + if len(nonResourceRules) > 0 { + nonResourceRulesList = append(nonResourceRulesList, nonResourceRules...) + } + } + + return resourceRulesList, nonResourceRulesList, incompleteStatus, utilerrors.NewAggregate(errList) +} diff --git a/pkg/apiserver/authorization/union/union_test.go b/pkg/apiserver/authorization/union/union_test.go new file mode 100644 index 000000000..592629eeb --- /dev/null +++ b/pkg/apiserver/authorization/union/union_test.go @@ -0,0 +1,265 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package union + +import ( + "errors" + "fmt" + "k8s.io/apiserver/pkg/authentication/user" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "reflect" + "testing" +) + +type mockAuthzHandler struct { + decision authorizer.Decision + err error +} + +func (mock *mockAuthzHandler) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) { + return mock.decision, "", mock.err +} + +func TestAuthorizationSecondPasses(t *testing.T) { + handler1 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion} + handler2 := &mockAuthzHandler{decision: authorizer.DecisionAllow} + authzHandler := New(handler1, handler2) + + authorized, _, _ := authzHandler.Authorize(nil) + if authorized != authorizer.DecisionAllow { + t.Errorf("Unexpected authorization failure") + } +} + +func TestAuthorizationFirstPasses(t *testing.T) { + handler1 := &mockAuthzHandler{decision: authorizer.DecisionAllow} + handler2 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion} + authzHandler := New(handler1, handler2) + + authorized, _, _ := authzHandler.Authorize(nil) + if authorized != authorizer.DecisionAllow { + t.Errorf("Unexpected authorization failure") + } +} + +func TestAuthorizationNonePasses(t *testing.T) { + handler1 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion} + handler2 := &mockAuthzHandler{decision: authorizer.DecisionNoOpinion} + authzHandler := New(handler1, handler2) + + authorized, _, _ := authzHandler.Authorize(nil) + if authorized == authorizer.DecisionAllow { + t.Errorf("Expected failed authorization") + } +} + +func TestAuthorizationError(t *testing.T) { + handler1 := &mockAuthzHandler{err: fmt.Errorf("foo")} + handler2 := &mockAuthzHandler{err: fmt.Errorf("foo")} + authzHandler := New(handler1, handler2) + + _, _, err := authzHandler.Authorize(nil) + if err == nil { + t.Errorf("Expected error: %v", err) + } +} + +type mockAuthzRuleHandler struct { + resourceRules []authorizer.ResourceRuleInfo + nonResourceRules []authorizer.NonResourceRuleInfo + err error +} + +func (mock *mockAuthzRuleHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) { + if mock.err != nil { + return []authorizer.ResourceRuleInfo{}, []authorizer.NonResourceRuleInfo{}, false, mock.err + } + return mock.resourceRules, mock.nonResourceRules, false, nil +} + +func TestAuthorizationResourceRules(t *testing.T) { + handler1 := &mockAuthzRuleHandler{ + resourceRules: []authorizer.ResourceRuleInfo{ + &authorizer.DefaultResourceRuleInfo{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"bindings"}, + }, + &authorizer.DefaultResourceRuleInfo{ + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + }, + } + handler2 := &mockAuthzRuleHandler{ + resourceRules: []authorizer.ResourceRuleInfo{ + &authorizer.DefaultResourceRuleInfo{ + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"events"}, + }, + &authorizer.DefaultResourceRuleInfo{ + Verbs: []string{"get"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + ResourceNames: []string{"foo"}, + }, + }, + } + + expected := []authorizer.DefaultResourceRuleInfo{ + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"bindings"}, + }, + { + Verbs: []string{"get", "list", "watch"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"events"}, + }, + { + Verbs: []string{"get"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + ResourceNames: []string{"foo"}, + }, + } + + authzRulesHandler := NewRuleResolvers(handler1, handler2) + + rules, _, _, _ := authzRulesHandler.RulesFor(nil, "") + actual := getResourceRules(rules) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual) + } +} + +func TestAuthorizationNonResourceRules(t *testing.T) { + handler1 := &mockAuthzRuleHandler{ + nonResourceRules: []authorizer.NonResourceRuleInfo{ + &authorizer.DefaultNonResourceRuleInfo{ + Verbs: []string{"get"}, + NonResourceURLs: []string{"/api"}, + }, + }, + } + + handler2 := &mockAuthzRuleHandler{ + nonResourceRules: []authorizer.NonResourceRuleInfo{ + &authorizer.DefaultNonResourceRuleInfo{ + Verbs: []string{"get"}, + NonResourceURLs: []string{"/api/*"}, + }, + }, + } + + expected := []authorizer.DefaultNonResourceRuleInfo{ + { + Verbs: []string{"get"}, + NonResourceURLs: []string{"/api"}, + }, + { + Verbs: []string{"get"}, + NonResourceURLs: []string{"/api/*"}, + }, + } + + authzRulesHandler := NewRuleResolvers(handler1, handler2) + + _, rules, _, _ := authzRulesHandler.RulesFor(nil, "") + actual := getNonResourceRules(rules) + if !reflect.DeepEqual(expected, actual) { + t.Errorf("Expected: \n%#v\n but actual: \n%#v\n", expected, actual) + } +} + +func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizer.DefaultResourceRuleInfo { + rules := make([]authorizer.DefaultResourceRuleInfo, len(infos)) + for i, info := range infos { + rules[i] = authorizer.DefaultResourceRuleInfo{ + Verbs: info.GetVerbs(), + APIGroups: info.GetAPIGroups(), + Resources: info.GetResources(), + ResourceNames: info.GetResourceNames(), + } + } + return rules +} + +func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.DefaultNonResourceRuleInfo { + rules := make([]authorizer.DefaultNonResourceRuleInfo, len(infos)) + for i, info := range infos { + rules[i] = authorizer.DefaultNonResourceRuleInfo{ + Verbs: info.GetVerbs(), + NonResourceURLs: info.GetNonResourceURLs(), + } + } + return rules +} + +func TestAuthorizationUnequivocalDeny(t *testing.T) { + cs := []struct { + authorizers []authorizer.Authorizer + decision authorizer.Decision + }{ + { + authorizers: []authorizer.Authorizer{}, + decision: authorizer.DecisionNoOpinion, + }, + { + authorizers: []authorizer.Authorizer{ + &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}, + &mockAuthzHandler{decision: authorizer.DecisionAllow}, + &mockAuthzHandler{decision: authorizer.DecisionDeny}, + }, + decision: authorizer.DecisionAllow, + }, + { + authorizers: []authorizer.Authorizer{ + &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}, + &mockAuthzHandler{decision: authorizer.DecisionDeny}, + &mockAuthzHandler{decision: authorizer.DecisionAllow}, + }, + decision: authorizer.DecisionDeny, + }, + { + authorizers: []authorizer.Authorizer{ + &mockAuthzHandler{decision: authorizer.DecisionNoOpinion}, + &mockAuthzHandler{decision: authorizer.DecisionDeny, err: errors.New("webhook failed closed")}, + &mockAuthzHandler{decision: authorizer.DecisionAllow}, + }, + decision: authorizer.DecisionDeny, + }, + } + for i, c := range cs { + t.Run(fmt.Sprintf("case %v", i), func(t *testing.T) { + authzHandler := New(c.authorizers...) + + decision, _, _ := authzHandler.Authorize(nil) + if decision != c.decision { + t.Errorf("Unexpected authorization failure: %v, expected: %v", decision, c.decision) + } + }) + } +} diff --git a/pkg/apiserver/components/components.go b/pkg/apiserver/components/components.go deleted file mode 100644 index be8b692da..000000000 --- a/pkg/apiserver/components/components.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 components - -import ( - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/models/components" - "kubesphere.io/kubesphere/pkg/server/errors" - "net/http" -) - -func GetSystemHealthStatus(request *restful.Request, response *restful.Response) { - result, err := components.GetSystemHealthStatus() - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(result) -} - -// get a specific component status -func GetComponentStatus(request *restful.Request, response *restful.Response) { - component := request.PathParameter("component") - - result, err := components.GetComponentStatus(component) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(result) -} - -// get all componentsHandler -func GetComponents(request *restful.Request, response *restful.Response) { - - result, err := components.GetAllComponentsStatus() - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(result) -} diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go new file mode 100644 index 000000000..af159a7fd --- /dev/null +++ b/pkg/apiserver/config/config.go @@ -0,0 +1,216 @@ +package config + +import ( + "fmt" + "github.com/spf13/viper" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" + "kubesphere.io/kubesphere/pkg/simple/client/alerting" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/ldap" + "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/multicluster" + "kubesphere.io/kubesphere/pkg/simple/client/network" + "kubesphere.io/kubesphere/pkg/simple/client/notification" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + "kubesphere.io/kubesphere/pkg/simple/client/servicemesh" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + "reflect" + "strings" +) + +// Package config saves configuration for running KubeSphere components +// +// Config can be configured from command line flags and configuration file. +// Command line flags hold higher priority than configuration file. But if +// component Endpoint/Host/APIServer was left empty, all of that component +// command line flags will be ignored, use configuration file instead. +// For example, we have configuration file +// +// mysql: +// host: mysql.kubesphere-system.svc +// username: root +// password: password +// +// At the same time, have command line flags like following: +// +// --mysql-host mysql.openpitrix-system.svc --mysql-username king --mysql-password 1234 +// +// We will use `king:1234@mysql.openpitrix-system.svc` from command line flags rather +// than `root:password@mysql.kubesphere-system.svc` from configuration file, +// cause command line has higher priority. But if command line flags like following: +// +// --mysql-username root --mysql-password password +// +// we will `root:password@mysql.kubesphere-system.svc` as input, cause +// mysql-host is missing in command line flags, all other mysql command line flags +// will be ignored. + +const ( + // DefaultConfigurationName is the default name of configuration + defaultConfigurationName = "kubesphere" + + // DefaultConfigurationPath the default location of the configuration file + defaultConfigurationPath = "/etc/kubesphere" +) + +// Config defines everything needed for apiserver to deal with external services +type Config struct { + DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` + SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` + KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` + ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` + NetworkOptions *network.Options `json:"network,omitempty" yaml:"network,omitempty" mapstructure:"network"` + LdapOptions *ldap.Options `json:"-,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` + RedisOptions *cache.Options `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` + S3Options *s3.Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` + OpenPitrixOptions *openpitrix.Options `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` + MonitoringOptions *prometheus.Options `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` + LoggingOptions *elasticsearch.Options `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` + AuthenticationOptions *authoptions.AuthenticationOptions `json:"authentication,omitempty" yaml:"authentication,omitempty" mapstructure:"authentication"` + AuthorizationOptions *authorizationoptions.AuthorizationOptions `json:"authorization,omitempty" yaml:"authorization,omitempty" mapstructure:"authorization"` + MultiClusterOptions *multicluster.Options `json:"multicluster,omitempty" yaml:"multicluster,omitempty" mapstructure:"multicluster"` + // Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere, + // we can add these options to kubesphere command lines + AlertingOptions *alerting.Options `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"` + NotificationOptions *notification.Options `json:"notification,omitempty" yaml:"notification,omitempty" mapstructure:"notification"` +} + +// newConfig creates a default non-empty Config +func New() *Config { + return &Config{ + DevopsOptions: jenkins.NewDevopsOptions(), + SonarQubeOptions: sonarqube.NewSonarQubeOptions(), + KubernetesOptions: k8s.NewKubernetesOptions(), + ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), + NetworkOptions: network.NewNetworkOptions(), + LdapOptions: ldap.NewOptions(), + RedisOptions: cache.NewRedisOptions(), + S3Options: s3.NewS3Options(), + OpenPitrixOptions: openpitrix.NewOptions(), + MonitoringOptions: prometheus.NewPrometheusOptions(), + AlertingOptions: alerting.NewAlertingOptions(), + NotificationOptions: notification.NewNotificationOptions(), + LoggingOptions: elasticsearch.NewElasticSearchOptions(), + AuthenticationOptions: authoptions.NewAuthenticateOptions(), + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), + MultiClusterOptions: multicluster.NewOptions(), + } +} + +// TryLoadFromDisk loads configuration from default location after server startup +// return nil error if configuration file not exists +func TryLoadFromDisk() (*Config, error) { + viper.SetConfigName(defaultConfigurationName) + viper.AddConfigPath(defaultConfigurationPath) + + // Load from current working directory, only used for debugging + viper.AddConfigPath(".") + + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + return nil, err + } else { + return nil, fmt.Errorf("error parsing configuration file %s", err) + } + } + + conf := New() + + if err := viper.Unmarshal(conf); err != nil { + return nil, err + } + + return conf, nil +} + +// convertToMap simply converts config to map[string]bool +// to hide sensitive information +func (conf *Config) ToMap() map[string]bool { + conf.stripEmptyOptions() + result := make(map[string]bool, 0) + + if conf == nil { + return result + } + + c := reflect.Indirect(reflect.ValueOf(conf)) + + for i := 0; i < c.NumField(); i++ { + name := strings.Split(c.Type().Field(i).Tag.Get("json"), ",")[0] + if strings.HasPrefix(name, "-") { + continue + } + + if c.Field(i).IsNil() { + result[name] = false + } else { + result[name] = true + } + } + + return result +} + +// Remove invalid options before serializing to json or yaml +func (conf *Config) stripEmptyOptions() { + + if conf.RedisOptions != nil && conf.RedisOptions.Host == "" { + conf.RedisOptions = nil + } + + if conf.DevopsOptions != nil && conf.DevopsOptions.Host == "" { + conf.DevopsOptions = nil + } + + if conf.MonitoringOptions != nil && conf.MonitoringOptions.Endpoint == "" && + conf.MonitoringOptions.SecondaryEndpoint == "" { + conf.MonitoringOptions = nil + } + + if conf.SonarQubeOptions != nil && conf.SonarQubeOptions.Host == "" { + conf.SonarQubeOptions = nil + } + + if conf.LdapOptions != nil && conf.LdapOptions.Host == "" { + conf.LdapOptions = nil + } + + if conf.OpenPitrixOptions != nil && conf.OpenPitrixOptions.IsEmpty() { + conf.OpenPitrixOptions = nil + } + + if conf.NetworkOptions != nil && conf.NetworkOptions.WeaveScopeHost == "" { + conf.NetworkOptions = nil + } + + if conf.ServiceMeshOptions != nil && conf.ServiceMeshOptions.IstioPilotHost == "" && + conf.ServiceMeshOptions.ServicemeshPrometheusHost == "" && + conf.ServiceMeshOptions.JaegerQueryHost == "" { + conf.ServiceMeshOptions = nil + } + + if conf.S3Options != nil && conf.S3Options.Endpoint == "" { + conf.S3Options = nil + } + + if conf.AlertingOptions != nil && conf.AlertingOptions.Endpoint == "" { + conf.AlertingOptions = nil + } + + if conf.LoggingOptions != nil && conf.LoggingOptions.Host == "" { + conf.LoggingOptions = nil + } + + if conf.NotificationOptions != nil && conf.NotificationOptions.Endpoint == "" { + conf.NotificationOptions = nil + } + + if conf.MultiClusterOptions != nil && !conf.MultiClusterOptions.Enable { + conf.MultiClusterOptions = nil + } +} diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go new file mode 100644 index 000000000..5bbc1f028 --- /dev/null +++ b/pkg/apiserver/config/config_test.go @@ -0,0 +1,171 @@ +package config + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "gopkg.in/yaml.v2" + "io/ioutil" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options" + "kubesphere.io/kubesphere/pkg/simple/client/alerting" + "kubesphere.io/kubesphere/pkg/simple/client/cache" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/ldap" + "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus" + "kubesphere.io/kubesphere/pkg/simple/client/multicluster" + "kubesphere.io/kubesphere/pkg/simple/client/network" + "kubesphere.io/kubesphere/pkg/simple/client/notification" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + "kubesphere.io/kubesphere/pkg/simple/client/servicemesh" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + "os" + "testing" + "time" +) + +func newTestConfig() (*Config, error) { + + var conf = &Config{ + DevopsOptions: &jenkins.Options{ + Host: "http://ks-devops.kubesphere-devops-system.svc", + Username: "jenkins", + Password: "kubesphere", + MaxConnections: 10, + }, + SonarQubeOptions: &sonarqube.Options{ + Host: "http://sonarqube.kubesphere-devops-system.svc", + Token: "ABCDEFG", + }, + KubernetesOptions: &k8s.KubernetesOptions{ + KubeConfig: "/Users/zry/.kube/config", + Master: "https://127.0.0.1:6443", + QPS: 1e6, + Burst: 1e6, + }, + ServiceMeshOptions: &servicemesh.Options{ + IstioPilotHost: "http://istio-pilot.istio-system.svc:9090", + JaegerQueryHost: "http://jaeger-query.istio-system.svc:80", + ServicemeshPrometheusHost: "http://prometheus-k8s.kubesphere-monitoring-system.svc", + }, + LdapOptions: &ldap.Options{ + Host: "http://openldap.kubesphere-system.svc", + ManagerDN: "cn=admin,dc=example,dc=org", + ManagerPassword: "P@88w0rd", + UserSearchBase: "ou=Users,dc=example,dc=org", + GroupSearchBase: "ou=Groups,dc=example,dc=org", + }, + RedisOptions: &cache.Options{ + Host: "localhost", + Port: 6379, + Password: "P@88w0rd", + DB: 0, + }, + S3Options: &s3.Options{ + Endpoint: "http://minio.openpitrix-system.svc", + Region: "", + DisableSSL: false, + ForcePathStyle: false, + AccessKeyID: "ABCDEFGHIJKLMN", + SecretAccessKey: "OPQRSTUVWXYZ", + SessionToken: "abcdefghijklmn", + Bucket: "ssss", + }, + OpenPitrixOptions: &openpitrix.Options{ + RuntimeManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9103", + ClusterManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9104", + RepoManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9101", + AppManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9102", + CategoryManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9113", + AttachmentManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9122", + }, + NetworkOptions: &network.Options{ + WeaveScopeHost: "weave-scope-app.weave.svc", + }, + MonitoringOptions: &prometheus.Options{ + Endpoint: "http://prometheus.kubesphere-monitoring-system.svc", + SecondaryEndpoint: "http://prometheus.kubesphere-monitoring-system.svc", + }, + LoggingOptions: &elasticsearch.Options{ + Host: "http://elasticsearch-logging.kubesphere-logging-system.svc:9200", + IndexPrefix: "elk", + Version: "6", + }, + AlertingOptions: &alerting.Options{ + Endpoint: "http://alerting.kubesphere-alerting-system.svc:9200", + }, + NotificationOptions: ¬ification.Options{ + Endpoint: "http://notification.kubesphere-alerting-system.svc:9200", + }, + AuthorizationOptions: authorizationoptions.NewAuthorizationOptions(), + AuthenticationOptions: &authoptions.AuthenticationOptions{ + AuthenticateRateLimiterMaxTries: 5, + AuthenticateRateLimiterDuration: 30 * time.Minute, + MaxAuthenticateRetries: 6, + JwtSecret: "xxxxxx", + MultipleLogin: false, + OAuthOptions: &oauth.Options{ + IdentityProviders: []oauth.IdentityProviderOptions{}, + Clients: []oauth.Client{{ + Name: "kubesphere-console-client", + Secret: "xxxxxx-xxxxxx-xxxxxx", + RespondWithChallenges: true, + RedirectURIs: []string{"http://ks-console.kubesphere-system.svc/oauth/token/implicit"}, + GrantMethod: oauth.GrantHandlerAuto, + AccessTokenInactivityTimeout: nil, + }}, + AccessTokenMaxAge: time.Hour * 24, + AccessTokenInactivityTimeout: 0, + }, + }, + MultiClusterOptions: &multicluster.Options{ + Enable: false, + }, + } + return conf, nil +} + +func saveTestConfig(t *testing.T, conf *Config) { + content, err := yaml.Marshal(conf) + if err != nil { + t.Fatalf("error marshal config. %v", err) + } + err = ioutil.WriteFile(fmt.Sprintf("%s.yaml", defaultConfigurationName), content, 0640) + if err != nil { + t.Fatalf("error write configuration file, %v", err) + } +} + +func cleanTestConfig(t *testing.T) { + file := fmt.Sprintf("%s.yaml", defaultConfigurationName) + if _, err := os.Stat(file); os.IsNotExist(err) { + t.Log("file not exists, skipping") + return + } + + err := os.Remove(file) + if err != nil { + t.Fatalf("remove %s file failed", file) + } + +} + +func TestGet(t *testing.T) { + conf, err := newTestConfig() + if err != nil { + t.Fatal(err) + } + saveTestConfig(t, conf) + defer cleanTestConfig(t) + + conf2, err := TryLoadFromDisk() + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(conf, conf2); diff != "" { + t.Fatal(diff) + } +} diff --git a/pkg/apiserver/devops/devops.go b/pkg/apiserver/devops/devops.go deleted file mode 100644 index a4277c5e3..000000000 --- a/pkg/apiserver/devops/devops.go +++ /dev/null @@ -1,647 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 devops - -import ( - "encoding/json" - "github.com/emicklei/go-restful" - log "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/devops" - "net/http" - "strings" -) - -const jenkinsHeaderPre = "X-" - -func GetPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.GetPipeline(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func SearchPipelines(req *restful.Request, resp *restful.Response) { - res, err := devops.SearchPipelines(req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func SearchPipelineRuns(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.SearchPipelineRuns(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetBranchPipelineRun(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetPipelineRunNodesbyBranch(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetBranchStepLog(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - - res, header, err := devops.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) - - if err != nil { - parseErr(err, resp) - return - } - for k, v := range header { - if strings.HasPrefix(k, jenkinsHeaderPre) { - resp.AddHeader(k, v[0]) - } - } - resp.Write(res) -} - -func GetStepLog(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - - res, header, err := devops.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - for k, v := range header { - if strings.HasPrefix(k, jenkinsHeaderPre) { - resp.AddHeader(k, v[0]) - } - } - resp.Write(res) -} - -func GetSCMServers(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - - res, err := devops.GetSCMServers(scmId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func CreateSCMServers(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - - res, err := devops.CreateSCMServers(scmId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func Validate(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - - res, err := devops.Validate(scmId, req.Request) - if err != nil { - log.Error(err) - if jErr, ok := err.(*devops.JkError); ok { - if jErr.Code != http.StatusUnauthorized { - resp.WriteError(jErr.Code, err) - } else { - resp.WriteHeader(http.StatusPreconditionRequired) - } - } else { - resp.WriteError(http.StatusInternalServerError, err) - } - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetSCMOrg(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - - res, err := devops.GetSCMOrg(scmId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetOrgRepo(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - organizationId := req.PathParameter("organization") - - res, err := devops.GetOrgRepo(scmId, organizationId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func StopBranchPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.StopBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func StopPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.StopPipeline(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func ReplayBranchPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func ReplayPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.ReplayPipeline(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetBranchRunLog(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.GetBranchRunLog(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func GetRunLog(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetRunLog(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func GetBranchArtifacts(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.GetBranchArtifacts(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetArtifacts(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetArtifacts(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetPipeBranch(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.GetPipeBranch(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func SubmitBranchInputStep(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - - res, err := devops.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func SubmitInputStep(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - - res, err := devops.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func GetConsoleLog(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.GetConsoleLog(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func ScanBranch(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.ScanBranch(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Write(res) -} - -func RunBranchPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - - res, err := devops.RunBranchPipeline(projectName, pipelineName, branchName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func RunPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - res, err := devops.RunPipeline(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetCrumb(req *restful.Request, resp *restful.Response) { - res, err := devops.GetCrumb(req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func CheckScriptCompile(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - - resBody, err := devops.CheckScriptCompile(projectName, pipelineName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - // Jenkins will return different struct according to different results. - var resJson = new(devops.CheckScript) - if ok := json.Unmarshal(resBody, &resJson); ok != nil { - var resJson []interface{} - err := json.Unmarshal(resBody, &resJson) - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - resp.WriteAsJson(resJson[0]) - return - - } - - resp.WriteAsJson(resJson) -} - -func CheckCron(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - - res, err := devops.CheckCron(projectName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.WriteAsJson(res) -} - -func GetPipelineRun(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetPipelineRun(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetBranchPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - - res, err := devops.GetBranchPipeline(projectName, pipelineName, branchName, req.Request) - if err != nil { - parseErr(err, resp) - return - } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetPipelineRunNodes(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetPipelineRunNodes(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetBranchNodeSteps(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - - res, err := devops.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetNodeSteps(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - - res, err := devops.GetNodeSteps(projectName, pipelineName, runId, nodeId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func ToJenkinsfile(req *restful.Request, resp *restful.Response) { - res, err := devops.ToJenkinsfile(req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func ToJson(req *restful.Request, resp *restful.Response) { - res, err := devops.ToJson(req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) -} - -func GetNotifyCommit(req *restful.Request, resp *restful.Response) { - res, err := devops.GetNotifyCommit(req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Write(res) -} - -func PostNotifyCommit(req *restful.Request, resp *restful.Response) { - res, err := devops.GetNotifyCommit(req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Write(res) -} -func GithubWebhook(req *restful.Request, resp *restful.Response) { - res, err := devops.GithubWebhook(req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.Write(res) -} - -func GetBranchNodesDetail(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - - res, err := devops.GetBranchNodesDetail(projectName, pipelineName, branchName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.WriteAsJson(res) -} - -func GetNodesDetail(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetNodesDetail(projectName, pipelineName, runId, req.Request) - if err != nil { - parseErr(err, resp) - return - } - resp.WriteAsJson(res) -} - -func parseErr(err error, resp *restful.Response) { - log.Error(err) - if jErr, ok := err.(*devops.JkError); ok { - resp.WriteError(jErr.Code, err) - } else { - resp.WriteError(http.StatusInternalServerError, err) - } - return -} diff --git a/pkg/apiserver/devops/member.go b/pkg/apiserver/devops/member.go deleted file mode 100644 index d168e833d..000000000 --- a/pkg/apiserver/devops/member.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "fmt" - "github.com/asaskevich/govalidator" - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" - "net/http" -) - -func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - orderBy := request.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(request) - limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam)) - conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam)) - - project, err := devops.GetProjectMembers(projectId, conditions, orderBy, reverse, limit, offset) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - member := request.PathParameter("member") - - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - project, err := devops.GetProjectMember(projectId, member) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - member := &devops.DevOpsProjectMembership{} - err := request.ReadEntity(&member) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if govalidator.IsNull(member.Username) { - err := fmt.Errorf("error need username") - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if !reflectutils.In(member.Role, devops.AllRoleSlice) { - err := fmt.Errorf("err role [%s] not in [%s]", member.Role, - devops.AllRoleSlice) - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - project, err := devops.AddProjectMember(projectId, username, member) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - member := &devops.DevOpsProjectMembership{} - err := request.ReadEntity(&member) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - member.Username = request.PathParameter("member") - if govalidator.IsNull(member.Username) { - err := fmt.Errorf("error need username") - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if username == member.Username { - err := fmt.Errorf("you can not change your role") - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if !reflectutils.In(member.Role, devops.AllRoleSlice) { - err := fmt.Errorf("err role [%s] not in [%s]", member.Role, - devops.AllRoleSlice) - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - project, err := devops.UpdateProjectMember(projectId, username, member) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func DeleteDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - member := request.PathParameter("member") - - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - username, err = devops.DeleteProjectMember(projectId, member) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(struct { - Username string `json:"username"` - }{Username: username}) - return -} diff --git a/pkg/apiserver/devops/project.go b/pkg/apiserver/devops/project.go deleted file mode 100644 index 5589dac6f..000000000 --- a/pkg/apiserver/devops/project.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/server/errors" - "net/http" -) - -func GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - project, err := devops.GetProject(projectId) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func UpdateProjectHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - var project *v1alpha2.DevOpsProject - err := request.ReadEntity(&project) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - project.ProjectId = projectId - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - project, err = devops.UpdateProject(project) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) - return -} - -func GetDevOpsProjectDefaultRoles(request *restful.Request, resp *restful.Response) { - resp.WriteAsJson(devops.DefaultRoles) - return -} diff --git a/pkg/apiserver/devops/project_credential.go b/pkg/apiserver/devops/project_credential.go deleted file mode 100644 index 1ad032676..000000000 --- a/pkg/apiserver/devops/project_credential.go +++ /dev/null @@ -1,165 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/server/errors" - "net/http" -) - -func CreateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - var credential *devops.JenkinsCredential - err := request.ReadEntity(&credential) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - credentialId, err := devops.CreateProjectCredential(projectId, username, credential) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: credentialId}) - return -} - -func UpdateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - credentialId := request.PathParameter("credential") - var credential *devops.JenkinsCredential - err := request.ReadEntity(&credential) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - credentialId, err = devops.UpdateProjectCredential(projectId, credentialId, credential) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: credentialId}) - return -} - -func DeleteDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - credentialId := request.PathParameter("credential") - var credential *devops.JenkinsCredential - err := request.ReadEntity(&credential) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - credentialId, err = devops.DeleteProjectCredential(projectId, credentialId, credential) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: credentialId}) - return -} - -func GetDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - credentialId := request.PathParameter("credential") - getContent := request.QueryParameter("content") - domain := request.QueryParameter("domain") - - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - response, err := devops.GetProjectCredential(projectId, credentialId, domain, getContent) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(response) - return -} - -func GetDevOpsProjectCredentialsHandler(request *restful.Request, resp *restful.Response) { - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - domain := request.QueryParameter("domain") - - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - jenkinsCredentials, err := devops.GetProjectCredentials(projectId, domain) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(jenkinsCredentials) - return -} diff --git a/pkg/apiserver/devops/project_pipeline.go b/pkg/apiserver/devops/project_pipeline.go deleted file mode 100644 index fe62c69e4..000000000 --- a/pkg/apiserver/devops/project_pipeline.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/server/errors" - "net/http" -) - -func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - var pipeline *devops.ProjectPipeline - err := request.ReadEntity(&pipeline) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - pipelineName, err := devops.CreateProjectPipeline(projectId, pipeline) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: pipelineName}) - return -} - -func DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - pipelineId := request.PathParameter("pipeline") - - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - pipelineName, err := devops.DeleteProjectPipeline(projectId, pipelineId) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: pipelineName}) - return -} - -func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - pipelineId := request.PathParameter("pipeline") - var pipeline *devops.ProjectPipeline - err := request.ReadEntity(&pipeline) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - pipelineName, err := devops.UpdateProjectPipeline(projectId, pipelineId, pipeline) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(struct { - Name string `json:"name"` - }{Name: pipelineName}) - return -} - -func GetDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { - - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - pipelineId := request.PathParameter("pipeline") - - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - pipeline, err := devops.GetProjectPipeline(projectId, pipelineId) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(pipeline) - return -} - -func GetPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) { - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - pipelineId := request.PathParameter("pipeline") - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - sonarStatus, err := devops.GetPipelineSonar(projectId, pipelineId) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(sonarStatus) -} - -func GetMultiBranchesPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) { - projectId := request.PathParameter("devops") - username := request.HeaderParameter(constants.UserNameHeader) - pipelineId := request.PathParameter("pipeline") - branchId := request.PathParameter("branch") - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) - return - } - sonarStatus, err := devops.GetMultiBranchPipelineSonar(projectId, pipelineId, branchId) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(sonarStatus) -} diff --git a/pkg/apiserver/devops/s2ibinary.go b/pkg/apiserver/devops/s2ibinary.go deleted file mode 100644 index bf4eeee5c..000000000 --- a/pkg/apiserver/devops/s2ibinary.go +++ /dev/null @@ -1,87 +0,0 @@ -package devops - -import ( - "code.cloudfoundry.org/bytefmt" - "fmt" - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/utils/hashutil" - "net/http" -) - -func UploadS2iBinary(req *restful.Request, resp *restful.Response) { - ns := req.PathParameter("namespace") - name := req.PathParameter("s2ibinary") - - err := req.Request.ParseMultipartForm(bytefmt.MEGABYTE * 20) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if len(req.Request.MultipartForm.File) == 0 { - err := restful.NewError(http.StatusBadRequest, "could not get file from form") - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - if len(req.Request.MultipartForm.File["s2ibinary"]) == 0 { - err := restful.NewError(http.StatusBadRequest, "could not get file from form") - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - if len(req.Request.MultipartForm.File["s2ibinary"]) > 1 { - err := restful.NewError(http.StatusBadRequest, "s2ibinary should only have one file") - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - defer req.Request.MultipartForm.RemoveAll() - file, err := req.Request.MultipartForm.File["s2ibinary"][0].Open() - if err != nil { - klog.Error(err) - errors.ParseSvcErr(err, resp) - return - } - filemd5, err := hashutil.GetMD5(file) - if err != nil { - klog.Error(err) - errors.ParseSvcErr(err, resp) - return - } - md5, ok := req.Request.MultipartForm.Value["md5"] - if ok && len(req.Request.MultipartForm.Value["md5"]) > 0 { - if md5[0] != filemd5 { - err := restful.NewError(http.StatusBadRequest, fmt.Sprintf("md5 not match, origin: %+v, calculate: %+v", md5[0], filemd5)) - klog.Error(err) - errors.ParseSvcErr(err, resp) - return - } - } - - s2ibin, err := devops.UploadS2iBinary(ns, name, filemd5, req.Request.MultipartForm.File["s2ibinary"][0]) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(s2ibin) - -} - -func DownloadS2iBinary(req *restful.Request, resp *restful.Response) { - ns := req.PathParameter("namespace") - name := req.PathParameter("s2ibinary") - fileName := req.PathParameter("file") - url, err := devops.DownloadS2iBinary(ns, name, fileName) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - http.Redirect(resp.ResponseWriter, req.Request, url, http.StatusFound) - return -} diff --git a/pkg/apiserver/dispatch/dispatch.go b/pkg/apiserver/dispatch/dispatch.go new file mode 100644 index 000000000..b90bbd476 --- /dev/null +++ b/pkg/apiserver/dispatch/dispatch.go @@ -0,0 +1,101 @@ +package dispatch + +import ( + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/util/proxy" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/klog" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1" + "net/http" + "net/url" + "strings" +) + +// Dispatcher defines how to forward request to designated cluster based on cluster name +type Dispatcher interface { + Dispatch(w http.ResponseWriter, req *http.Request, handler http.Handler) +} + +type clusterDispatch struct { + clusterLister v1alpha1.ClusterLister +} + +func NewClusterDispatch(clusterLister v1alpha1.ClusterLister) Dispatcher { + return &clusterDispatch{ + clusterLister: clusterLister, + } +} + +func (c *clusterDispatch) Dispatch(w http.ResponseWriter, req *http.Request, handler http.Handler) { + + info, _ := request.RequestInfoFrom(req.Context()) + + if len(info.Cluster) == 0 { + klog.Warningf("Request with empty cluster, %v", req.URL) + http.Error(w, fmt.Sprintf("Bad request, empty cluster"), http.StatusBadRequest) + return + } + + cluster, err := c.clusterLister.Get(info.Cluster) + if err != nil { + if errors.IsNotFound(err) { + http.Error(w, fmt.Sprintf("cluster %s not found", info.Cluster), http.StatusNotFound) + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return + } + + // request cluster is host cluster, no need go through agent + if isClusterHostCluster(cluster) { + req.URL.Path = strings.Replace(req.URL.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1) + handler.ServeHTTP(w, req) + return + } + + if !isClusterReady(cluster) { + http.Error(w, fmt.Sprintf("cluster agent is not ready"), http.StatusInternalServerError) + return + } + + endpoint, err := url.Parse(cluster.Spec.Connection.KubeSphereAPIEndpoint) + if err != nil { + klog.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + u := *req.URL + u.Host = endpoint.Host + u.Path = strings.Replace(u.Path, fmt.Sprintf("/clusters/%s", info.Cluster), "", 1) + + httpProxy := proxy.NewUpgradeAwareHandler(&u, http.DefaultTransport, true, false, c) + httpProxy.ServeHTTP(w, req) +} + +func (c *clusterDispatch) Error(w http.ResponseWriter, req *http.Request, err error) { + responsewriters.InternalError(w, req, err) +} + +func isClusterReady(cluster *clusterv1alpha1.Cluster) bool { + for _, condition := range cluster.Status.Conditions { + if condition.Type == clusterv1alpha1.ClusterReady && condition.Status == corev1.ConditionTrue { + return true + } + } + + return false +} + +func isClusterHostCluster(cluster *clusterv1alpha1.Cluster) bool { + for key, value := range cluster.Annotations { + if key == clusterv1alpha1.IsHostCluster && value == "true" { + return true + } + } + + return false +} diff --git a/pkg/apiserver/filters/authentication.go b/pkg/apiserver/filters/authentication.go new file mode 100644 index 000000000..09e38b0de --- /dev/null +++ b/pkg/apiserver/filters/authentication.go @@ -0,0 +1,48 @@ +package filters + +import ( + "errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apiserver/pkg/authentication/authenticator" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +// WithAuthentication installs authentication handler to handler chain. +func WithAuthentication(handler http.Handler, auth authenticator.Request) http.Handler { + if auth == nil { + klog.Warningf("Authentication is disabled") + return handler + } + + s := serializer.NewCodecFactory(runtime.NewScheme()).WithoutConversion() + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + + resp, ok, err := auth.AuthenticateRequest(req) + if err != nil || !ok { + if err != nil { + klog.Errorf("Unable to authenticate the request due to error: %v", err) + } + + ctx := req.Context() + requestInfo, found := request.RequestInfoFrom(ctx) + if !found { + responsewriters.InternalError(w, req, errors.New("no RequestInfo found in the context")) + return + } + + gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} + responsewriters.ErrorNegotiated(apierrors.NewUnauthorized("Unauthorized"), s, gv, w, req) + return + } + + req = req.WithContext(request.WithUser(req.Context(), resp.User)) + handler.ServeHTTP(w, req) + }) +} diff --git a/pkg/apiserver/filters/authorization.go b/pkg/apiserver/filters/authorization.go new file mode 100644 index 000000000..588ce7bef --- /dev/null +++ b/pkg/apiserver/filters/authorization.go @@ -0,0 +1,78 @@ +package filters + +import ( + "context" + "errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +// WithAuthorization passes all authorized requests on to handler, and returns forbidden error otherwise. +func WithAuthorization(handler http.Handler, authorizers authorizer.Authorizer) http.Handler { + if authorizers == nil { + klog.Warningf("Authorization is disabled") + return handler + } + + defaultSerializer := serializer.NewCodecFactory(runtime.NewScheme()).WithoutConversion() + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := req.Context() + + attributes, err := getAuthorizerAttributes(ctx) + if err != nil { + responsewriters.InternalError(w, req, err) + } + + authorized, reason, err := authorizers.Authorize(attributes) + if authorized == authorizer.DecisionAllow { + handler.ServeHTTP(w, req) + return + } + + if err != nil { + responsewriters.InternalError(w, req, err) + return + } + + klog.V(4).Infof("Forbidden: %#v, Reason: %q", req.RequestURI, reason) + responsewriters.Forbidden(ctx, attributes, w, req, reason, defaultSerializer) + }) +} + +func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) { + attribs := authorizer.AttributesRecord{} + + user, ok := request.UserFrom(ctx) + if ok { + attribs.User = user + } + + requestInfo, found := request.RequestInfoFrom(ctx) + if !found { + return nil, errors.New("no RequestInfo found in the context") + } + + // Start with common attributes that apply to resource and non-resource requests + attribs.ResourceScope = requestInfo.ResourceScope + attribs.ResourceRequest = requestInfo.IsResourceRequest + attribs.Path = requestInfo.Path + attribs.Verb = requestInfo.Verb + attribs.Cluster = requestInfo.Cluster + attribs.Workspace = requestInfo.Workspace + attribs.KubernetesRequest = requestInfo.IsKubernetesRequest + + attribs.APIGroup = requestInfo.APIGroup + attribs.APIVersion = requestInfo.APIVersion + attribs.Resource = requestInfo.Resource + attribs.Subresource = requestInfo.Subresource + attribs.Namespace = requestInfo.Namespace + attribs.Name = requestInfo.Name + + return &attribs, nil +} diff --git a/pkg/apiserver/filters/dispatch.go b/pkg/apiserver/filters/dispatch.go new file mode 100644 index 000000000..842336b4f --- /dev/null +++ b/pkg/apiserver/filters/dispatch.go @@ -0,0 +1,32 @@ +package filters + +import ( + "fmt" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/dispatch" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +// Multiple cluster dispatcher forward request to desired cluster based on request cluster name +// which included in request path clusters/{cluster} +func WithMultipleClusterDispatcher(handler http.Handler, dispatch dispatch.Dispatcher) http.Handler { + if dispatch == nil { + klog.V(4).Infof("Multiple cluster dispatcher is disabled") + return handler + } + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + info, ok := request.RequestInfoFrom(req.Context()) + if !ok { + responsewriters.InternalError(w, req, fmt.Errorf("")) + return + } + + if info.Cluster == "" { + handler.ServeHTTP(w, req) + } else { + dispatch.Dispatch(w, req, handler) + } + }) +} diff --git a/pkg/apiserver/filters/kubeapiserver.go b/pkg/apiserver/filters/kubeapiserver.go new file mode 100644 index 000000000..ba972085b --- /dev/null +++ b/pkg/apiserver/filters/kubeapiserver.go @@ -0,0 +1,46 @@ +package filters + +import ( + "k8s.io/apimachinery/pkg/util/proxy" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "k8s.io/client-go/rest" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/server/errors" + "net/http" + "net/url" +) + +// WithKubeAPIServer proxy request to kubernetes service if requests path starts with /api +func WithKubeAPIServer(handler http.Handler, config *rest.Config, failed proxy.ErrorResponder) http.Handler { + kubernetes, _ := url.Parse(config.Host) + defaultTransport, err := rest.TransportFor(config) + if err != nil { + klog.Errorf("Unable to create transport from rest.Config: %v", err) + return handler + } + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + info, ok := request.RequestInfoFrom(req.Context()) + if !ok { + err := errors.New("Unable to retrieve request info from request") + klog.Error(err) + responsewriters.InternalError(w, req, err) + } + + if info.IsKubernetesRequest { + s := *req.URL + s.Host = kubernetes.Host + s.Scheme = kubernetes.Scheme + + // Do not cover k8s client authorization header + req.Header.Del("Authorization") + + httpProxy := proxy.NewUpgradeAwareHandler(&s, defaultTransport, true, false, failed) + httpProxy.ServeHTTP(w, req) + return + } + + handler.ServeHTTP(w, req) + }) +} diff --git a/pkg/apiserver/filters/requestinfo.go b/pkg/apiserver/filters/requestinfo.go new file mode 100644 index 000000000..c1d605b36 --- /dev/null +++ b/pkg/apiserver/filters/requestinfo.go @@ -0,0 +1,22 @@ +package filters + +import ( + "fmt" + "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx := req.Context() + info, err := resolver.NewRequestInfo(req) + if err != nil { + responsewriters.InternalError(w, req, fmt.Errorf("failed to crate RequestInfo: %v", err)) + return + } + + req = req.WithContext(request.WithRequestInfo(ctx, info)) + handler.ServeHTTP(w, req) + }) +} diff --git a/pkg/apiserver/git/git.go b/pkg/apiserver/git/git.go deleted file mode 100644 index 6f7ae3148..000000000 --- a/pkg/apiserver/git/git.go +++ /dev/null @@ -1,30 +0,0 @@ -package git - -import ( - "net/http" - - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/models/git" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func GitReadVerify(request *restful.Request, response *restful.Response) { - - authInfo := git.AuthInfo{} - - err := request.ReadEntity(&authInfo) - ns := request.PathParameter("namespace") - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - err = git.GitReadVerify(ns, authInfo) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(errors.None) -} diff --git a/pkg/apiserver/iam/am.go b/pkg/apiserver/iam/am.go deleted file mode 100644 index 21cc996a6..000000000 --- a/pkg/apiserver/iam/am.go +++ /dev/null @@ -1,200 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "github.com/emicklei/go-restful" - "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "net/http" - "sort" - - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/iam/policy" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -type RoleList struct { - ClusterRoles []*v1.ClusterRole `json:"clusterRole" description:"cluster role list"` - Roles []*v1.Role `json:"roles" description:"role list"` -} - -func ListRoleUsers(req *restful.Request, resp *restful.Response) { - roleName := req.PathParameter("role") - namespace := req.PathParameter("namespace") - - users, err := iam.RoleUsers(namespace, roleName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(users) -} - -func ListClusterRoles(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListClusterRoles(conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) - -} - -func ListRoles(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListRoles(namespace, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) - -} - -// List users by namespace -func ListNamespaceUsers(req *restful.Request, resp *restful.Response) { - - namespace := req.PathParameter("namespace") - - users, err := iam.NamespaceUsers(namespace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - // sort by time by default - sort.Slice(users, func(i, j int) bool { - return users[i].RoleBindTime.After(*users[j].RoleBindTime) - }) - - resp.WriteAsJson(users) -} - -func ListUserRoles(req *restful.Request, resp *restful.Response) { - - username := req.PathParameter("user") - - roles, err := iam.GetUserRoles("", username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - _, clusterRoles, err := iam.GetUserClusterRoles(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - roleList := RoleList{} - roleList.Roles = roles - roleList.ClusterRoles = clusterRoles - - resp.WriteAsJson(roleList) -} - -func RulesMapping(req *restful.Request, resp *restful.Response) { - rules := policy.RoleRuleMapping - resp.WriteAsJson(rules) -} - -func ClusterRulesMapping(req *restful.Request, resp *restful.Response) { - rules := policy.ClusterRoleRuleMapping - resp.WriteAsJson(rules) -} - -func ListClusterRoleRules(req *restful.Request, resp *restful.Response) { - clusterRoleName := req.PathParameter("clusterrole") - rules, err := iam.GetClusterRoleSimpleRules(clusterRoleName) - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - resp.WriteAsJson(rules) -} - -func ListClusterRoleUsers(req *restful.Request, resp *restful.Response) { - clusterRoleName := req.PathParameter("clusterrole") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListClusterRoleUsers(clusterRoleName, conditions, orderBy, reverse, limit, offset) - - if err != nil { - if k8serr.IsNotFound(err) { - resp.WriteError(http.StatusNotFound, err) - } else { - resp.WriteError(http.StatusInternalServerError, err) - } - return - } - - resp.WriteAsJson(result) -} - -func ListRoleRules(req *restful.Request, resp *restful.Response) { - namespaceName := req.PathParameter("namespace") - roleName := req.PathParameter("role") - - rules, err := iam.GetRoleSimpleRules(namespaceName, roleName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(rules) -} diff --git a/pkg/apiserver/iam/auth.go b/pkg/apiserver/iam/auth.go deleted file mode 100644 index 07892b8aa..000000000 --- a/pkg/apiserver/iam/auth.go +++ /dev/null @@ -1,191 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "fmt" - "github.com/dgrijalva/jwt-go" - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/utils/iputil" - "kubesphere.io/kubesphere/pkg/utils/jwtutil" - "net/http" -) - -type Spec struct { - Token string `json:"token" description:"access token"` -} - -type Status struct { - Authenticated bool `json:"authenticated" description:"is authenticated"` - User map[string]interface{} `json:"user,omitempty" description:"user info"` -} - -type TokenReview struct { - APIVersion string `json:"apiVersion" description:"Kubernetes API version"` - Kind string `json:"kind" description:"kind of the API object"` - Spec *Spec `json:"spec,omitempty"` - Status *Status `json:"status,omitempty" description:"token review status"` -} - -type LoginRequest struct { - Username string `json:"username" description:"username"` - Password string `json:"password" description:"password"` -} - -type OAuthRequest struct { - GrantType string `json:"grant_type"` - Username string `json:"username,omitempty" description:"username"` - Password string `json:"password,omitempty" description:"password"` - RefreshToken string `json:"refresh_token,omitempty"` -} - -const ( - KindTokenReview = "TokenReview" -) - -func Login(req *restful.Request, resp *restful.Response) { - var loginRequest LoginRequest - - err := req.ReadEntity(&loginRequest) - - if err != nil || loginRequest.Username == "" || loginRequest.Password == "" { - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.New("incorrect username or password")) - return - } - - ip := iputil.RemoteIp(req.Request) - - token, err := iam.Login(loginRequest.Username, loginRequest.Password, ip) - - if err != nil { - if serviceError, ok := err.(restful.ServiceError); ok { - resp.WriteHeaderAndEntity(serviceError.Code, errors.New(serviceError.Message)) - return - } - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err)) - return - } - - resp.WriteAsJson(token) -} - -func OAuth(req *restful.Request, resp *restful.Response) { - - authRequest := &OAuthRequest{} - - err := req.ReadEntity(authRequest) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - var result *models.AuthGrantResponse - switch authRequest.GrantType { - case "refresh_token": - result, err = iam.RefreshToken(authRequest.RefreshToken) - case "password": - ip := iputil.RemoteIp(req.Request) - result, err = iam.PasswordCredentialGrant(authRequest.Username, authRequest.Password, ip) - default: - resp.Header().Set("WWW-Authenticate", "grant_type is not supported") - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(fmt.Errorf("grant_type is not supported"))) - return - } - - if err != nil { - resp.Header().Set("WWW-Authenticate", err.Error()) - resp.WriteHeaderAndEntity(http.StatusUnauthorized, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) - -} - -// k8s token review -func TokenReviewHandler(req *restful.Request, resp *restful.Response) { - var tokenReview TokenReview - - err := req.ReadEntity(&tokenReview) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if tokenReview.Spec == nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("token must not be null")) - return - } - - uToken := tokenReview.Spec.Token - - token, err := jwtutil.ValidateToken(uToken) - - if err != nil { - klog.Errorln("token review failed", uToken, err) - failed := TokenReview{APIVersion: tokenReview.APIVersion, - Kind: KindTokenReview, - Status: &Status{ - Authenticated: false, - }, - } - resp.WriteAsJson(failed) - return - } - - claims := token.Claims.(jwt.MapClaims) - - username, ok := claims["username"].(string) - - if !ok { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New("username not found")) - return - } - - user, err := iam.GetUserInfo(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - groups, err := iam.GetUserGroups(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - user.Groups = groups - - success := TokenReview{APIVersion: tokenReview.APIVersion, - Kind: KindTokenReview, - Status: &Status{ - Authenticated: true, - User: map[string]interface{}{"username": user.Username, "uid": user.Username, "groups": user.Groups}, - }, - } - - resp.WriteAsJson(success) - return -} diff --git a/pkg/apiserver/iam/groups.go b/pkg/apiserver/iam/groups.go deleted file mode 100644 index 42b2d3080..000000000 --- a/pkg/apiserver/iam/groups.go +++ /dev/null @@ -1,201 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "fmt" - "net/http" - "regexp" - "strings" - - "github.com/emicklei/go-restful" - "github.com/go-ldap/ldap" - - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func CreateGroup(req *restful.Request, resp *restful.Response) { - var group models.Group - - err := req.ReadEntity(&group) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if !regexp.MustCompile("[a-z0-9]([-a-z0-9]*[a-z0-9])?").MatchString(group.Name) { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.New(fmt.Sprintf("incalid group name %s", group))) - return - } - - created, err := iam.CreateGroup(&group) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - resp.WriteAsJson(created) -} - -func DeleteGroup(req *restful.Request, resp *restful.Response) { - path := req.PathParameter("group") - - if path == "" { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("group path must not be null"))) - return - } - - err := iam.DeleteGroup(path) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) - -} - -func UpdateGroup(req *restful.Request, resp *restful.Response) { - groupPathInPath := req.PathParameter("group") - - var group models.Group - - req.ReadEntity(&group) - - if groupPathInPath != group.Path { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("the path of group (%s) does not match the path on the URL (%s)", group.Path, groupPathInPath))) - return - } - - edited, err := iam.UpdateGroup(&group) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(edited) - -} - -func DescribeGroup(req *restful.Request, resp *restful.Response) { - - path := req.PathParameter("group") - - group, err := iam.DescribeGroup(path) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - resp.WriteAsJson(group) - -} - -func ListGroupUsers(req *restful.Request, resp *restful.Response) { - - path := req.PathParameter("group") - - group, err := iam.DescribeGroup(path) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - users := make([]*models.User, 0) - - modify := false - - for i := 0; i < len(group.Members); i++ { - name := group.Members[i] - user, err := iam.GetUserInfo(name) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - group.Members = append(group.Members[:i], group.Members[i+1:]...) - i-- - modify = true - continue - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - } - - users = append(users, user) - } - - if modify { - go iam.UpdateGroup(group) - } - - resp.WriteAsJson(users) - -} - -func ListGroups(req *restful.Request, resp *restful.Response) { - - array := req.QueryParameter("path") - - if array == "" { - groups, err := iam.ChildList("") - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(groups) - } else { - paths := strings.Split(array, ",") - - groups := make([]*models.Group, 0) - - for _, v := range paths { - path := strings.TrimSpace(v) - group, err := iam.DescribeGroup(path) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - groups = append(groups, group) - } - - resp.WriteAsJson(groups) - } - -} diff --git a/pkg/apiserver/iam/im.go b/pkg/apiserver/iam/im.go deleted file mode 100644 index 2957d0e21..000000000 --- a/pkg/apiserver/iam/im.go +++ /dev/null @@ -1,309 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/server/params" - "net/http" - "net/mail" - "strings" - - "github.com/emicklei/go-restful" - "github.com/go-ldap/ldap" - rbacv1 "k8s.io/api/rbac/v1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func CreateUser(req *restful.Request, resp *restful.Response) { - var user models.User - - err := req.ReadEntity(&user) - - if err != nil { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if user.Username == "" { - err = fmt.Errorf("invalid username: %s", user.Username) - klog.Info(err, user.Username) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - // Parses a single RFC 5322 address, e.g. "Barry Gibbs " - if _, err = mail.ParseAddress(user.Email); err != nil { - err = fmt.Errorf("invalid email: %s", user.Email) - klog.Info(err, user.Email) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if len(user.Password) < 6 { - err = fmt.Errorf("invalid password") - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - created, err := iam.CreateUser(&user) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) - return - } - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(created) -} - -func DeleteUser(req *restful.Request, resp *restful.Response) { - username := req.PathParameter("user") - - operator := req.HeaderParameter(constants.UserNameHeader) - - if operator == username { - err := fmt.Errorf("cannot delete yourself") - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - err := iam.DeleteUser(username) - - if err != nil { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} - -func UpdateUser(req *restful.Request, resp *restful.Response) { - - usernameInPath := req.PathParameter("user") - usernameInHeader := req.HeaderParameter(constants.UserNameHeader) - var user models.User - - err := req.ReadEntity(&user) - - if err != nil { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if usernameInPath != user.Username { - err = fmt.Errorf("the name of user (%s) does not match the name on the URL (%s)", user.Username, usernameInPath) - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if _, err = mail.ParseAddress(user.Email); err != nil { - err = fmt.Errorf("invalid email: %s", user.Email) - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if user.Password != "" && len(user.Password) < 6 { - err = fmt.Errorf("invalid password") - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - // change password by self - if usernameInHeader == user.Username && user.Password != "" { - isUserManager, err := isUserManager(usernameInHeader) - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - if !isUserManager { - _, err = iam.Login(usernameInHeader, user.CurrentPassword, "") - } - if err != nil { - err = fmt.Errorf("incorrect current password") - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - } - - if usernameInHeader == user.Username { - // change cluster role by self is not permitted - user.ClusterRole = "" - } - - result, err := iam.UpdateUser(&user) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) - return - } - - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func isUserManager(username string) (bool, error) { - rules, err := iam.GetUserClusterRules(username) - if err != nil { - return false, err - } - if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"update"}, Resources: []string{"users"}, APIGroups: []string{"iam.kubesphere.io"}}) { - return true, nil - } - return false, nil -} - -func UserLoginLogs(req *restful.Request, resp *restful.Response) { - username := req.PathParameter("user") - logs, err := iam.LoginLog(username) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - result := make([]map[string]string, 0) - - for _, v := range logs { - item := strings.Split(v, ",") - time := item[0] - var ip string - if len(item) > 1 { - ip = item[1] - } - result = append(result, map[string]string{"login_time": time, "login_ip": ip}) - } - - resp.WriteAsJson(result) -} - -func DescribeUser(req *restful.Request, resp *restful.Response) { - - username := req.PathParameter("user") - - user, err := iam.DescribeUser(username) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - clusterRole, err := iam.GetUserClusterRole(username) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - user.ClusterRole = clusterRole.Name - - clusterRules, err := iam.GetUserClusterSimpleRules(username) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - result := struct { - *models.User - ClusterRules []models.SimpleRule `json:"cluster_rules"` - }{ - User: user, - ClusterRules: clusterRules, - } - - resp.WriteAsJson(result) -} - -func Precheck(req *restful.Request, resp *restful.Response) { - - check := req.QueryParameter("check") - - exist, err := iam.UserCreateCheck(check) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(map[string]bool{"exist": exist}) -} - -func ListUsers(req *restful.Request, resp *restful.Response) { - - if check := req.QueryParameter("check"); check != "" { - Precheck(req, resp) - return - } - - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(req) - - if err != nil { - klog.Info(err) - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - users, err := iam.ListUsers(conditions, orderBy, reverse, limit, offset) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(users) -} diff --git a/pkg/apiserver/iam/types.go b/pkg/apiserver/iam/types.go deleted file mode 100644 index 6c25b043f..000000000 --- a/pkg/apiserver/iam/types.go +++ /dev/null @@ -1,18 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam diff --git a/pkg/apiserver/iam/workspaces.go b/pkg/apiserver/iam/workspaces.go deleted file mode 100644 index 7bbdecb3f..000000000 --- a/pkg/apiserver/iam/workspaces.go +++ /dev/null @@ -1,166 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "github.com/emicklei/go-restful" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/workspaces" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "net/http" -) - -func ListWorkspaceRoles(req *restful.Request, resp *restful.Response) { - - workspace := req.PathParameter("workspace") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListWorkspaceRoles(workspace, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result.Items) -} - -func ListWorkspaceRoleRules(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - role := req.PathParameter("role") - - rules := iam.GetWorkspaceRoleSimpleRules(workspace, role) - - resp.WriteAsJson(rules) -} - -func DescribeWorkspaceRole(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - roleName := req.PathParameter("role") - - role, err := iam.GetWorkspaceRole(workspace, roleName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(role) -} - -func DescribeWorkspaceUser(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - - workspaceRole, err := iam.GetUserWorkspaceRole(workspace, username) - - if err != nil { - if k8serr.IsNotFound(err) { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - - return - } - - user, err := iam.GetUserInfo(username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - user.WorkspaceRole = workspaceRole.Annotations[constants.DisplayNameAnnotationKey] - - resp.WriteAsJson(user) -} - -func ListDevopsRoleRules(req *restful.Request, resp *restful.Response) { - role := req.PathParameter("role") - - rules := iam.GetDevopsRoleSimpleRules(role) - - resp.WriteAsJson(rules) -} - -func InviteUser(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - var user models.User - err := req.ReadEntity(&user) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = workspaces.InviteUser(workspace, &user) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} - -func RemoveUser(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - - err := workspaces.RemoveUser(workspace, username) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} - -func ListWorkspaceUsers(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := iam.ListWorkspaceUsers(workspace, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} diff --git a/pkg/apiserver/logging/logging.go b/pkg/apiserver/logging/logging.go deleted file mode 100644 index 39a73e608..000000000 --- a/pkg/apiserver/logging/logging.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 logging - -import ( - "bytes" - "fmt" - "github.com/emicklei/go-restful" - "io" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/models/log" - "kubesphere.io/kubesphere/pkg/server/errors" - cs "kubesphere.io/kubesphere/pkg/simple/client" - fb "kubesphere.io/kubesphere/pkg/simple/client/fluentbit" - "kubesphere.io/kubesphere/pkg/utils/stringutils" - "net/http" - "strconv" - "strings" - "time" -) - -func LoggingQueryCluster(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelCluster, request) - if param.Operation == v1alpha2.OperationExport { - logExport(param, request, response) - } else { - logQuery(param, response) - } -} - -func LoggingQueryWorkspace(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelWorkspace, request) - logQuery(param, response) -} - -func LoggingQueryNamespace(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelNamespace, request) - logQuery(param, response) -} - -func LoggingQueryWorkload(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelWorkload, request) - logQuery(param, response) -} - -func LoggingQueryPod(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelPod, request) - logQuery(param, response) -} - -func LoggingQueryContainer(request *restful.Request, response *restful.Response) { - param := parseRequest(log.QueryLevelContainer, request) - if param.Operation == v1alpha2.OperationExport { - logExport(param, request, response) - } else { - logQuery(param, response) - } -} - -func LoggingQueryFluentbitOutputs(request *restful.Request, response *restful.Response) { - res := log.FluentbitOutputsQuery() - if res.Status != http.StatusOK { - response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) - return - } - response.WriteAsJson(res) -} - -func LoggingInsertFluentbitOutput(request *restful.Request, response *restful.Response) { - var output fb.OutputPlugin - var res *log.FluentbitOutputsResult - - err := request.ReadEntity(&output) - if err != nil { - klog.Errorln(err) - response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - res = log.FluentbitOutputInsert(output) - if res.Status != http.StatusOK { - response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) - return - } - - response.WriteAsJson(res) -} - -func LoggingUpdateFluentbitOutput(request *restful.Request, response *restful.Response) { - var output fb.OutputPlugin - - id := request.PathParameter("output") - - err := request.ReadEntity(&output) - if err != nil { - klog.Errorln(err) - response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - res := log.FluentbitOutputUpdate(output, id) - - if res.Status != http.StatusOK { - response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) - return - } - - response.WriteAsJson(res) -} - -func LoggingDeleteFluentbitOutput(request *restful.Request, response *restful.Response) { - var res *log.FluentbitOutputsResult - - id := request.PathParameter("output") - res = log.FluentbitOutputDelete(id) - - if res.Status != http.StatusOK { - response.WriteHeaderAndEntity(res.Status, errors.New(res.Error)) - return - } - - response.WriteAsJson(res) -} - -func logQuery(param v1alpha2.QueryParameters, response *restful.Response) { - es, err := cs.ClientSets().ElasticSearch() - if err != nil { - response.WriteHeaderAndEntity(http.StatusServiceUnavailable, errors.Wrap(err)) - return - } - - res, err := es.Query(param) - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(res) -} - -func logExport(param v1alpha2.QueryParameters, request *restful.Request, response *restful.Response) { - es, err := cs.ClientSets().ElasticSearch() - if err != nil { - response.WriteHeaderAndEntity(http.StatusServiceUnavailable, errors.Wrap(err)) - return - } - - response.Header().Set(restful.HEADER_ContentType, "text/plain") - response.Header().Set("Content-Disposition", "attachment") - - // keep search context alive for 1m - param.ScrollTimeout = time.Minute - // export 1000 records in every iteration - param.Size = 1000 - // from is not allowed in a scroll context - param.From = 0 - - var scrollId string - // limit to retrieve max 100k records - for i := 0; i < 100; i++ { - var res *v1alpha2.QueryResult - if scrollId == "" { - res, err = es.Query(param) - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - } else { - res, err = es.Scroll(scrollId) - if err != nil { - break - } - } - - if res.Read == nil || len(res.Read.Records) == 0 { - break - } - output := new(bytes.Buffer) - for _, r := range res.Read.Records { - output.WriteString(fmt.Sprintf(`%s`, stringutils.StripAnsi(r.Log))) - } - _, err = io.Copy(response, output) - if err != nil { - klog.Error(err) - break - } - - scrollId = res.Read.ScrollID - - select { - case <-request.Request.Context().Done(): - break - default: - } - } - - if scrollId != "" { - es.ClearScroll(scrollId) - } -} - -func parseRequest(level log.LogQueryLevel, request *restful.Request) v1alpha2.QueryParameters { - var param v1alpha2.QueryParameters - - switch level { - case log.QueryLevelCluster: - var namespaces []string - param.NamespaceNotFound, namespaces = log.MatchNamespace(stringutils.Split(request.QueryParameter("namespaces"), ","), - stringutils.Split(strings.ToLower(request.QueryParameter("namespace_query")), ","), - stringutils.Split(request.QueryParameter("workspaces"), ","), - stringutils.Split(strings.ToLower(request.QueryParameter("workspace_query")), ",")) - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.WorkloadFilter = stringutils.Split(request.QueryParameter("workloads"), ",") - param.WorkloadQuery = stringutils.Split(request.QueryParameter("workload_query"), ",") - param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") - param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") - param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") - param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") - case log.QueryLevelWorkspace: - var namespaces []string - param.NamespaceNotFound, namespaces = log.MatchNamespace(stringutils.Split(request.QueryParameter("namespaces"), ","), - stringutils.Split(strings.ToLower(request.QueryParameter("namespace_query")), ","), - stringutils.Split(request.PathParameter("workspace"), ","), nil) - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.WorkloadFilter = stringutils.Split(request.QueryParameter("workloads"), ",") - param.WorkloadQuery = stringutils.Split(request.QueryParameter("workload_query"), ",") - param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") - param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") - param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") - param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") - case log.QueryLevelNamespace: - namespaces := []string{request.PathParameter("namespace")} - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.WorkloadFilter = stringutils.Split(request.QueryParameter("workloads"), ",") - param.WorkloadQuery = stringutils.Split(request.QueryParameter("workload_query"), ",") - param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") - param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") - param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") - param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") - case log.QueryLevelWorkload: - namespaces := []string{request.PathParameter("namespace")} - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.WorkloadFilter = []string{request.PathParameter("workload")} - param.PodFilter = stringutils.Split(request.QueryParameter("pods"), ",") - param.PodQuery = stringutils.Split(request.QueryParameter("pod_query"), ",") - param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") - param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") - case log.QueryLevelPod: - namespaces := []string{request.PathParameter("namespace")} - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.PodFilter = []string{request.PathParameter("pod")} - param.ContainerFilter = stringutils.Split(request.QueryParameter("containers"), ",") - param.ContainerQuery = stringutils.Split(request.QueryParameter("container_query"), ",") - case log.QueryLevelContainer: - namespaces := []string{request.PathParameter("namespace")} - param.NamespaceWithCreationTime = log.MakeNamespaceCreationTimeMap(namespaces) - param.PodFilter = []string{request.PathParameter("pod")} - param.ContainerFilter = []string{request.PathParameter("container")} - } - - param.LogQuery = stringutils.Split(request.QueryParameter("log_query"), ",") - param.Interval = request.QueryParameter("interval") - param.StartTime = request.QueryParameter("start_time") - param.EndTime = request.QueryParameter("end_time") - param.Sort = request.QueryParameter("sort") - switch request.QueryParameter("operation") { - case "statistics": - param.Operation = v1alpha2.OperationStatistics - case "histogram": - param.Operation = v1alpha2.OperationHistogram - case "export": - param.Operation = v1alpha2.OperationExport - default: - param.Operation = v1alpha2.OperationQuery - } - - var err error - param.From, err = strconv.ParseInt(request.QueryParameter("from"), 10, 64) - if err != nil { - param.From = 0 - } - - param.Size, err = strconv.ParseInt(request.QueryParameter("size"), 10, 64) - if err != nil { - param.Size = 10 - } - - return param -} diff --git a/pkg/apiserver/monitoring/monitoring.go b/pkg/apiserver/monitoring/monitoring.go deleted file mode 100644 index 446156fe8..000000000 --- a/pkg/apiserver/monitoring/monitoring.go +++ /dev/null @@ -1,203 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 monitoring - -import ( - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models/metrics" - "net/url" - "strconv" - "strings" -) - -func MonitorCluster(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - - // TODO: expose kubesphere iam and devops statistics in prometheus format - var res *metrics.Response - if r.Type == "statistics" { - res = metrics.GetClusterStatistics() - } else { - res = metrics.GetClusterMetrics(r) - } - - response.WriteAsJson(res) -} - -func MonitorNode(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetNodeMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorWorkspace(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - - // TODO: expose kubesphere iam and devops statistics in prometheus format - var res *metrics.Response - if r.Type == "statistics" && r.WorkspaceName != "" { - res = metrics.GetWorkspaceStatistics(r.WorkspaceName) - } else { - res = metrics.GetWorkspaceMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - } - - response.WriteAsJson(res) -} - -func MonitorNamespace(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetNamespaceMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorWorkload(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetWorkloadMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorPod(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetPodMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorContainer(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetContainerMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorPVC(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetPVCMetrics(r) - res, metricsNum := res.SortBy(r.SortMetric, r.SortType) - res = res.Page(r.PageNum, r.LimitNum, metricsNum) - response.WriteAsJson(res) -} - -func MonitorComponent(request *restful.Request, response *restful.Response) { - r := ParseRequestParams(request) - res := metrics.GetComponentMetrics(r) - response.WriteAsJson(res) -} - -func ParseRequestParams(request *restful.Request) metrics.RequestParams { - var requestParams metrics.RequestParams - - queryTime := strings.Trim(request.QueryParameter("time"), " ") - start := strings.Trim(request.QueryParameter("start"), " ") - end := strings.Trim(request.QueryParameter("end"), " ") - step := strings.Trim(request.QueryParameter("step"), " ") - sortMetric := strings.Trim(request.QueryParameter("sort_metric"), " ") - sortType := strings.Trim(request.QueryParameter("sort_type"), " ") - pageNum := strings.Trim(request.QueryParameter("page"), " ") - limitNum := strings.Trim(request.QueryParameter("limit"), " ") - tp := strings.Trim(request.QueryParameter("type"), " ") - metricsFilter := strings.Trim(request.QueryParameter("metrics_filter"), " ") - resourcesFilter := strings.Trim(request.QueryParameter("resources_filter"), " ") - nodeName := strings.Trim(request.PathParameter("node"), " ") - workspaceName := strings.Trim(request.PathParameter("workspace"), " ") - namespaceName := strings.Trim(request.PathParameter("namespace"), " ") - workloadKind := strings.Trim(request.PathParameter("kind"), " ") - workloadName := strings.Trim(request.PathParameter("workload"), " ") - podName := strings.Trim(request.PathParameter("pod"), " ") - containerName := strings.Trim(request.PathParameter("container"), " ") - pvcName := strings.Trim(request.PathParameter("pvc"), " ") - storageClassName := strings.Trim(request.PathParameter("storageclass"), " ") - componentName := strings.Trim(request.PathParameter("component"), " ") - - requestParams = metrics.RequestParams{ - SortMetric: sortMetric, - SortType: sortType, - PageNum: pageNum, - LimitNum: limitNum, - Type: tp, - MetricsFilter: metricsFilter, - ResourcesFilter: resourcesFilter, - NodeName: nodeName, - WorkspaceName: workspaceName, - NamespaceName: namespaceName, - WorkloadKind: workloadKind, - WorkloadName: workloadName, - PodName: podName, - ContainerName: containerName, - PVCName: pvcName, - StorageClassName: storageClassName, - ComponentName: componentName, - } - - if metricsFilter == "" { - requestParams.MetricsFilter = ".*" - } - if resourcesFilter == "" { - requestParams.ResourcesFilter = ".*" - } - - v := url.Values{} - - if start != "" && end != "" { // range query - - // metrics from a deleted namespace should be hidden - // therefore, for range query, if range query start time is less than the namespace creation time, set it to creation time - // it is the same with query at a fixed time point - if namespaceName != "" { - nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - ns, err := nsLister.Get(namespaceName) - if err == nil { - creationTime := ns.CreationTimestamp.Time.Unix() - queryStart, err := strconv.ParseInt(start, 10, 64) - if err == nil && queryStart < creationTime { - start = strconv.FormatInt(creationTime, 10) - } - } - } - - v.Set("start", start) - v.Set("end", end) - - if step == "" { - v.Set("step", metrics.DefaultQueryStep) - } else { - v.Set("step", step) - } - requestParams.QueryParams = v - requestParams.QueryType = metrics.RangeQuery - - return requestParams - } else if queryTime != "" { // query - v.Set("time", queryTime) - } - - requestParams.QueryParams = v - requestParams.QueryType = metrics.Query - return requestParams -} diff --git a/pkg/apiserver/openpitrix/applications.go b/pkg/apiserver/openpitrix/applications.go deleted file mode 100644 index f536cf85f..000000000 --- a/pkg/apiserver/openpitrix/applications.go +++ /dev/null @@ -1,280 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "fmt" - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/api/core/v1" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/openpitrix" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func ListApplications(req *restful.Request, resp *restful.Response) { - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - namespaceName := req.PathParameter("namespace") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(req) - - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if namespaceName != "" { - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId == "" { - resp.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) - return - } else { - conditions.Match["runtime_id"] = runtimeId - } - } - - result, err := openpitrix.ListApplications(conditions, limit, offset, orderBy, reverse) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func DescribeApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - resp.WriteEntity(app) - return -} - -func CreateApplication(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - var createClusterRequest openpitrix.CreateClusterRequest - err := req.ReadEntity(&createClusterRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - createClusterRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - err = openpitrix.CreateApplication(namespace, createClusterRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteEntity(errors.None) -} - -func ModifyApplication(req *restful.Request, resp *restful.Response) { - var modifyClusterAttributesRequest openpitrix.ModifyClusterAttributesRequest - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - err := req.ReadEntity(&modifyClusterAttributesRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - err = openpitrix.PatchApplication(&modifyClusterAttributesRequest) - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteApplication(req *restful.Request, resp *restful.Response) { - clusterId := req.PathParameter("application") - namespaceName := req.PathParameter("namespace") - app, err := openpitrix.DescribeApplication(namespaceName, clusterId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespace, err := resources.GetResource("", resources.Namespaces, namespaceName) - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - var runtimeId string - - if ns, ok := namespace.(*v1.Namespace); ok { - runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] - } - - if runtimeId != app.Cluster.RuntimeId { - err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) - klog.V(4).Info(err) - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - err = openpitrix.DeleteApplication(clusterId) - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} diff --git a/pkg/apiserver/openpitrix/apps.go b/pkg/apiserver/openpitrix/apps.go deleted file mode 100644 index 1603b15d4..000000000 --- a/pkg/apiserver/openpitrix/apps.go +++ /dev/null @@ -1,547 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/openpitrix" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" - "strings" -) - -func GetAppVersionPackage(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - versionId := req.PathParameter("version") - - result, err := openpitrix.GetAppVersionPackage(appId, versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DoAppAction(req *restful.Request, resp *restful.Response) { - var doActionRequest openpitrix.ActionRequest - err := req.ReadEntity(&doActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - appId := req.PathParameter("app") - - err = openpitrix.DoAppAction(appId, &doActionRequest) - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DoAppVersionAction(req *restful.Request, resp *restful.Response) { - var doActionRequest openpitrix.ActionRequest - err := req.ReadEntity(&doActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - doActionRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - versionId := req.PathParameter("version") - - err = openpitrix.DoAppVersionAction(versionId, &doActionRequest) - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func GetAppVersionFiles(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - getAppVersionFilesRequest := &openpitrix.GetAppVersionFilesRequest{} - if f := req.QueryParameter("files"); f != "" { - getAppVersionFilesRequest.Files = strings.Split(f, ",") - } - - result, err := openpitrix.GetAppVersionFiles(versionId, getAppVersionFilesRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListAppVersionAudits(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - appId := req.PathParameter("app") - versionId := req.PathParameter("version") - if orderBy == "" { - orderBy = "status_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - conditions.Match["app"] = appId - if versionId != "" { - conditions.Match["version"] = versionId - } - - result, err := openpitrix.ListAppVersionAudits(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListReviews(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "status_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.ListAppVersionReviews(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListAppVersions(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - appId := req.PathParameter("app") - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - conditions.Match["app"] = appId - - result, err := openpitrix.ListAppVersions(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if version, ok := item.(*openpitrix.AppVersion); ok { - statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": version.AppId, "version_id": version.VersionId}}, 0, 0, "", false) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - version.ClusterTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} - -func ListApps(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.ListApps(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if app, ok := item.(*openpitrix.App); ok { - status := "active|used|enabled|stopped|pending|creating|upgrading|updating|rollbacking|stopping|starting|recovering|resizing|scaling|deleting" - statisticsResult, err := openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": app.AppId, "status": status}}, 0, 0, "", false) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - app.ClusterTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} - -func ModifyApp(req *restful.Request, resp *restful.Response) { - - var patchAppRequest openpitrix.ModifyAppRequest - err := req.ReadEntity(&patchAppRequest) - appId := req.PathParameter("app") - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = openpitrix.PatchApp(appId, &patchAppRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeApp(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - - result, err := openpitrix.DescribeApp(appId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DeleteApp(req *restful.Request, resp *restful.Response) { - appId := req.PathParameter("app") - - err := openpitrix.DeleteApp(appId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func CreateApp(req *restful.Request, resp *restful.Response) { - createAppRequest := &openpitrix.CreateAppRequest{} - err := req.ReadEntity(createAppRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - createAppRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validatePackageRequest := &openpitrix.ValidatePackageRequest{ - VersionPackage: createAppRequest.VersionPackage, - VersionType: createAppRequest.VersionType, - } - result, err = openpitrix.ValidatePackage(validatePackageRequest) - } else { - result, err = openpitrix.CreateApp(createAppRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func CreateAppVersion(req *restful.Request, resp *restful.Response) { - var createAppVersionRequest openpitrix.CreateAppVersionRequest - err := req.ReadEntity(&createAppVersionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - // override app id - createAppVersionRequest.AppId = req.PathParameter("app") - createAppVersionRequest.Username = req.HeaderParameter(constants.UserNameHeader) - - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validatePackageRequest := &openpitrix.ValidatePackageRequest{ - VersionPackage: createAppVersionRequest.Package, - VersionType: createAppVersionRequest.Type, - } - result, err = openpitrix.ValidatePackage(validatePackageRequest) - } else { - result, err = openpitrix.CreateAppVersion(&createAppVersionRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ModifyAppVersion(req *restful.Request, resp *restful.Response) { - - var patchAppVersionRequest openpitrix.ModifyAppVersionRequest - err := req.ReadEntity(&patchAppVersionRequest) - versionId := req.PathParameter("version") - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = openpitrix.PatchAppVersion(versionId, &patchAppVersionRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteAppVersion(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - - err := openpitrix.DeleteAppVersion(versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeAppVersion(req *restful.Request, resp *restful.Response) { - versionId := req.PathParameter("version") - - result, err := openpitrix.DescribeAppVersion(versionId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/attachments.go b/pkg/apiserver/openpitrix/attachments.go deleted file mode 100644 index 87093119b..000000000 --- a/pkg/apiserver/openpitrix/attachments.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "kubesphere.io/kubesphere/pkg/models/openpitrix" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func DescribeAttachment(req *restful.Request, resp *restful.Response) { - attachmentId := req.PathParameter("attachment") - fileName := req.QueryParameter("filename") - result, err := openpitrix.DescribeAttachment(attachmentId) - // file raw - if fileName != "" { - data := result.AttachmentContent[fileName] - resp.Write(data) - resp.Header().Set("Content-Type", "text/plain") - return - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/categories.go b/pkg/apiserver/openpitrix/categories.go deleted file mode 100644 index 2723f900e..000000000 --- a/pkg/apiserver/openpitrix/categories.go +++ /dev/null @@ -1,171 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/openpitrix" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" -) - -func CreateCategory(req *restful.Request, resp *restful.Response) { - createCategoryRequest := &openpitrix.CreateCategoryRequest{} - err := req.ReadEntity(createCategoryRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.CreateCategory(createCategoryRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func DeleteCategory(req *restful.Request, resp *restful.Response) { - categoryId := req.PathParameter("category") - - err := openpitrix.DeleteCategory(categoryId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} -func ModifyCategory(req *restful.Request, resp *restful.Response) { - var modifyCategoryRequest openpitrix.ModifyCategoryRequest - categoryId := req.PathParameter("category") - err := req.ReadEntity(&modifyCategoryRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = openpitrix.PatchCategory(categoryId, &modifyCategoryRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} -func DescribeCategory(req *restful.Request, resp *restful.Response) { - categoryId := req.PathParameter("category") - - result, err := openpitrix.DescribeCategory(categoryId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func ListCategories(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - statistics, _ := strconv.ParseBool(req.QueryParameter("statistics")) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.ListCategories(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - if statistics { - for _, item := range result.Items { - if category, ok := item.(*openpitrix.Category); ok { - statisticsResult, err := openpitrix.ListApps(¶ms.Conditions{Match: map[string]string{"category_id": category.CategoryID, "status": openpitrix.StatusActive, "repo": openpitrix.BuiltinRepoId}}, "", false, 0, 0) - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - category.AppTotal = &statisticsResult.TotalCount - } - } - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/openpitrix/repos.go b/pkg/apiserver/openpitrix/repos.go deleted file mode 100644 index 38c1704d8..000000000 --- a/pkg/apiserver/openpitrix/repos.go +++ /dev/null @@ -1,222 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "github.com/emicklei/go-restful" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/openpitrix" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strconv" -) - -func CreateRepo(req *restful.Request, resp *restful.Response) { - createRepoRequest := &openpitrix.CreateRepoRequest{} - err := req.ReadEntity(createRepoRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - validate, _ := strconv.ParseBool(req.QueryParameter("validate")) - - var result interface{} - - if validate { - validateRepoRequest := &openpitrix.ValidateRepoRequest{ - Type: createRepoRequest.Type, - Url: createRepoRequest.URL, - Credential: createRepoRequest.Credential, - } - result, err = openpitrix.ValidateRepo(validateRepoRequest) - } else { - result, err = openpitrix.CreateRepo(createRepoRequest) - } - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func DoRepoAction(req *restful.Request, resp *restful.Response) { - repoActionRequest := &openpitrix.RepoActionRequest{} - repoId := req.PathParameter("repo") - err := req.ReadEntity(repoActionRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = openpitrix.DoRepoAction(repoId, repoActionRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DeleteRepo(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - - err := openpitrix.DeleteRepo(repoId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func ModifyRepo(req *restful.Request, resp *restful.Response) { - var updateRepoRequest openpitrix.ModifyRepoRequest - repoId := req.PathParameter("repo") - err := req.ReadEntity(&updateRepoRequest) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - err = openpitrix.PatchRepo(repoId, &updateRepoRequest) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.InvalidArgument { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(errors.None) -} - -func DescribeRepo(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - - result, err := openpitrix.DescribeRepo(repoId) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - if status.Code(err) == codes.NotFound { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - return - } - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} -func ListRepos(req *restful.Request, resp *restful.Response) { - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - if orderBy == "" { - orderBy = "create_time" - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.ListRepos(conditions, orderBy, reverse, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} - -func ListRepoEvents(req *restful.Request, resp *restful.Response) { - repoId := req.PathParameter("repo") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := openpitrix.ListRepoEvents(repoId, conditions, limit, offset) - - if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled { - resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err)) - return - } - - if err != nil { - klog.Errorln(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteEntity(result) -} diff --git a/pkg/apiserver/operations/job.go b/pkg/apiserver/operations/job.go deleted file mode 100644 index db10b3455..000000000 --- a/pkg/apiserver/operations/job.go +++ /dev/null @@ -1,57 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 operations - -import ( - k8serr "k8s.io/apimachinery/pkg/api/errors" - "kubesphere.io/kubesphere/pkg/models/workloads" - "kubesphere.io/kubesphere/pkg/server/errors" - "net/http" - - "github.com/emicklei/go-restful" - - "fmt" -) - -func RerunJob(req *restful.Request, resp *restful.Response) { - var err error - - job := req.PathParameter("job") - namespace := req.PathParameter("namespace") - action := req.QueryParameter("action") - resourceVersion := req.QueryParameter("resourceVersion") - - switch action { - case "rerun": - err = workloads.JobReRun(namespace, job, resourceVersion) - default: - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid operation %s", action))) - return - } - if err != nil { - if k8serr.IsConflict(err) { - resp.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) - return - } - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} diff --git a/pkg/apiserver/operations/node.go b/pkg/apiserver/operations/node.go deleted file mode 100644 index b70bf67f4..000000000 --- a/pkg/apiserver/operations/node.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 operations - -import ( - "github.com/emicklei/go-restful" - "net/http" - - "kubesphere.io/kubesphere/pkg/models/nodes" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func DrainNode(request *restful.Request, response *restful.Response) { - - nodeName := request.PathParameter("node") - - err := nodes.DrainNode(nodeName) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(errors.None) -} diff --git a/pkg/apiserver/query/field.go b/pkg/apiserver/query/field.go new file mode 100644 index 000000000..3824f8868 --- /dev/null +++ b/pkg/apiserver/query/field.go @@ -0,0 +1,39 @@ +package query + +type Field string +type Value string + +const ( + FieldName = "name" + FieldUID = "uid" + FieldCreationTimeStamp = "creationTimestamp" + FieldCreateTime = "createTime" + FieldLastUpdateTimestamp = "lastUpdateTimestamp" + FieldUpdateTime = "updateTime" + FieldLabel = "label" + FieldAnnotation = "annotation" + FieldNamespace = "namespace" + FieldStatus = "status" + FieldOwnerReference = "ownerReference" + FieldOwnerKind = "ownerKind" +) + +var SortableFields = []Field{ + FieldCreationTimeStamp, + FieldCreateTime, + FieldUpdateTime, + FieldLastUpdateTimestamp, + FieldName, +} + +// Field contains all the query field that can be compared +var ComparableFields = []Field{ + FieldName, + FieldUID, + FieldLabel, + FieldAnnotation, + FieldNamespace, + FieldStatus, + FieldOwnerReference, + FieldOwnerKind, +} diff --git a/pkg/apiserver/query/types.go b/pkg/apiserver/query/types.go new file mode 100644 index 000000000..2ad1ca36e --- /dev/null +++ b/pkg/apiserver/query/types.go @@ -0,0 +1,142 @@ +package query + +import ( + "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "strconv" +) + +const ( + ParameterName = "name" + ParameterLabelSelector = "labelSelector" + ParameterFieldSelector = "fieldSelector" + ParameterPage = "page" + ParameterLimit = "limit" + ParameterOrderBy = "sortBy" + ParameterAscending = "ascending" +) + +// Query represents api search terms +type Query struct { + Pagination *Pagination + + // sort result in which field, default to FieldCreationTimeStamp + SortBy Field + + // sort result in ascending or descending order, default to descending + Ascending bool + + // + Filters map[Field]Value + + LabelSelector string +} + +type Pagination struct { + // items per page + Limit int + + // offset + Offset int +} + +var NoPagination = newPagination(-1, 0) + +// make sure that pagination is valid +func newPagination(limit int, offset int) *Pagination { + return &Pagination{ + Limit: limit, + Offset: offset, + } +} + +func (q *Query) Selector() labels.Selector { + if selector, err := labels.Parse(q.LabelSelector); err != nil { + return labels.Everything() + } else { + return selector + } +} + +func (p *Pagination) GetValidPagination(total int) (startIndex, endIndex int) { + + // no pagination + if p.Limit == NoPagination.Limit { + return 0, total + } + + // out of range + if p.Limit < 0 || p.Offset < 0 || p.Offset > total { + return 0, 0 + } + + startIndex = p.Offset + endIndex = startIndex + p.Limit + + if endIndex > total { + endIndex = total + } + + return startIndex, endIndex +} + +func New() *Query { + return &Query{ + Pagination: NoPagination, + SortBy: "", + Ascending: false, + Filters: map[Field]Value{}, + } +} + +type Filter struct { + Field Field + Value Value +} + +func ParseQueryParameter(request *restful.Request) *Query { + query := New() + + limit, err := strconv.Atoi(request.QueryParameter(ParameterLimit)) + // equivalent to undefined, use the default value + if err != nil { + limit = -1 + } + page, err := strconv.Atoi(request.QueryParameter(ParameterPage)) + // equivalent to undefined, use the default value + if err != nil { + page = 1 + } + + query.Pagination = newPagination(limit, (page-1)*limit) + + query.SortBy = Field(defaultString(request.QueryParameter(ParameterOrderBy), FieldCreationTimeStamp)) + + ascending, err := strconv.ParseBool(defaultString(request.QueryParameter(ParameterAscending), "false")) + if err != nil { + query.Ascending = false + } else { + query.Ascending = ascending + } + + query.LabelSelector = request.QueryParameter(ParameterLabelSelector) + + for key, values := range request.Request.URL.Query() { + if !sliceutil.HasString([]string{ParameterPage, ParameterLimit, ParameterOrderBy, ParameterAscending, ParameterLabelSelector}, key) { + // support multiple query condition + for _, value := range values { + query.Filters[Field(key)] = Value(value) + } + } + } + + return query +} + +func defaultString(value, defaultValue string) string { + if len(value) == 0 { + return defaultValue + } + return value +} diff --git a/pkg/apiserver/query/types_test.go b/pkg/apiserver/query/types_test.go new file mode 100644 index 000000000..d4de9cb09 --- /dev/null +++ b/pkg/apiserver/query/types_test.go @@ -0,0 +1,64 @@ +package query + +import ( + "fmt" + "github.com/emicklei/go-restful" + "github.com/google/go-cmp/cmp" + "net/http" + "testing" +) + +func TestParseQueryParameter(t *testing.T) { + tests := []struct { + description string + queryString string + expected *Query + }{ + { + "test normal case", + "label=app.kubernetes.io/name=book&name=foo&status=Running&page=1&limit=10&ascending=true", + &Query{ + Pagination: newPagination(10, 0), + SortBy: FieldCreationTimeStamp, + Ascending: true, + Filters: map[Field]Value{ + FieldLabel: Value("app.kubernetes.io/name=book"), + FieldName: Value("foo"), + FieldStatus: Value("Running"), + }, + }, + }, + { + "test bad case", + "xxxx=xxxx&dsfsw=xxxx&page=abc&limit=add&ascending=ssss", + &Query{ + Pagination: NoPagination, + SortBy: FieldCreationTimeStamp, + Ascending: false, + Filters: map[Field]Value{ + Field("xxxx"): Value("xxxx"), + Field("dsfsw"): Value("xxxx"), + }, + }, + }, + } + + for _, test := range tests { + req, err := http.NewRequest("GET", fmt.Sprintf("http://localhost?%s", test.queryString), nil) + if err != nil { + t.Fatal(err) + } + + request := restful.NewRequest(req) + + t.Run(test.description, func(t *testing.T) { + got := ParseQueryParameter(request) + + if diff := cmp.Diff(got, test.expected); diff != "" { + + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + return + } + }) + } +} diff --git a/pkg/apiserver/quotas/quotas.go b/pkg/apiserver/quotas/quotas.go deleted file mode 100644 index 3d9376ef4..000000000 --- a/pkg/apiserver/quotas/quotas.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 quotas - -import ( - "github.com/emicklei/go-restful" - "net/http" - - "kubesphere.io/kubesphere/pkg/server/errors" - - "kubesphere.io/kubesphere/pkg/models/quotas" -) - -func GetNamespaceQuotas(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - quota, err := quotas.GetNamespaceQuotas(namespace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(quota) -} - -func GetClusterQuotas(req *restful.Request, resp *restful.Response) { - quota, err := quotas.GetClusterQuotas() - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(quota) -} diff --git a/pkg/apiserver/registries/registries.go b/pkg/apiserver/registries/registries.go deleted file mode 100644 index 4a8ec0138..000000000 --- a/pkg/apiserver/registries/registries.go +++ /dev/null @@ -1,149 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 registries - -import ( - "github.com/emicklei/go-restful" - "net/http" - "strings" - - "kubesphere.io/kubesphere/pkg/models/registries" - "kubesphere.io/kubesphere/pkg/server/errors" - - k8serror "k8s.io/apimachinery/pkg/api/errors" - log "k8s.io/klog" -) - -func RegistryVerify(request *restful.Request, response *restful.Response) { - - authInfo := registries.AuthInfo{} - - err := request.ReadEntity(&authInfo) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - err = registries.RegistryVerify(authInfo) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(errors.None) -} - -func RegistryImageBlob(request *restful.Request, response *restful.Response) { - imageName := request.QueryParameter("image") - namespace := request.QueryParameter("namespace") - secretName := request.QueryParameter("secret") - - // get entry - entry, err := registries.GetEntryBySecret(namespace, secretName) - if err != nil { - log.Errorf("%+v", err) - if k8serror.IsNotFound(err) { - log.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response) - return - } - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) - return - } - - // default use ssl - checkSSl := func(serverAddress string) bool { - if strings.HasPrefix(serverAddress, "http://") { - return false - } else { - return true - } - } - - if strings.HasPrefix(imageName, "http") { - dockerurl, err := registries.ParseDockerURL(imageName) - if err != nil { - log.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response) - return - } - imageName = dockerurl.StringWithoutScheme() - } - - // parse image - image, err := registries.ParseImage(imageName) - if err != nil { - log.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), response) - return - } - - useSSL := checkSSl(entry.ServerAddress) - - // Create the registry client. - r, err := registries.CreateRegistryClient(entry.Username, entry.Password, image.Domain, useSSL) - if err != nil { - log.Errorf("%+v", err) - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) - return - } - - digestUrl := r.GetDigestUrl(image) - - // Get token. - token, err := r.Token(digestUrl) - if err != nil { - log.Errorf("%+v", err) - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) - return - } - - // Get digest. - imageManifest, err := r.ImageManifest(image, token) - if err != nil { - if serviceError, ok := err.(restful.ServiceError); ok { - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: serviceError.Message}) - return - } - log.Errorf("%+v", err) - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) - return - } - image.Digest = imageManifest.ManifestConfig.Digest - - // Get blob. - imageBlob, err := r.ImageBlob(image, token) - if err != nil { - log.Errorf("%+v", err) - response.WriteAsJson(®istries.ImageDetails{Status: registries.StatusFailed, Message: err.Error()}) - return - } - - imageDetails := ®istries.ImageDetails{ - Status: registries.StatusSuccess, - ImageManifest: imageManifest, - ImageBlob: imageBlob, - ImageTag: image.Tag, - Registry: image.Domain, - } - - response.WriteAsJson(imageDetails) -} diff --git a/pkg/apiserver/request/context.go b/pkg/apiserver/request/context.go new file mode 100644 index 000000000..fe3ae38ed --- /dev/null +++ b/pkg/apiserver/request/context.go @@ -0,0 +1,96 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package request + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/apis/audit" + "k8s.io/apiserver/pkg/authentication/user" +) + +// The key type is unexported to prevent collisions +type key int + +const ( + // namespaceKey is the context key for the request namespace. + namespaceKey key = iota + + // userKey is the context key for the request user. + userKey + + // auditKey is the context key for the audit event. + auditKey + + // audiencesKey is the context key for request audiences. + audiencesKey +) + +// NewContext instantiates a base context object for request flows. +func NewContext() context.Context { + return context.TODO() +} + +// NewDefaultContext instantiates a base context object for request flows in the default namespace +func NewDefaultContext() context.Context { + return WithNamespace(NewContext(), metav1.NamespaceDefault) +} + +// WithValue returns a copy of parent in which the value associated with key is val. +func WithValue(parent context.Context, key interface{}, val interface{}) context.Context { + return context.WithValue(parent, key, val) +} + +// WithNamespace returns a copy of parent in which the namespace value is set +func WithNamespace(parent context.Context, namespace string) context.Context { + return WithValue(parent, namespaceKey, namespace) +} + +// NamespaceFrom returns the value of the namespace key on the ctx +func NamespaceFrom(ctx context.Context) (string, bool) { + namespace, ok := ctx.Value(namespaceKey).(string) + return namespace, ok +} + +// NamespaceValue returns the value of the namespace key on the ctx, or the empty string if none +func NamespaceValue(ctx context.Context) string { + namespace, _ := NamespaceFrom(ctx) + return namespace +} + +// WithUser returns a copy of parent in which the user value is set +func WithUser(parent context.Context, user user.Info) context.Context { + return WithValue(parent, userKey, user) +} + +// UserFrom returns the value of the user key on the ctx +func UserFrom(ctx context.Context) (user.Info, bool) { + user, ok := ctx.Value(userKey).(user.Info) + return user, ok +} + +// WithAuditEvent returns set audit event struct. +func WithAuditEvent(parent context.Context, ev *audit.Event) context.Context { + return WithValue(parent, auditKey, ev) +} + +// AuditEventFrom returns the audit event struct on the ctx +func AuditEventFrom(ctx context.Context) *audit.Event { + ev, _ := ctx.Value(auditKey).(*audit.Event) + return ev +} diff --git a/pkg/apiserver/request/context_test.go b/pkg/apiserver/request/context_test.go new file mode 100644 index 000000000..72b3124b4 --- /dev/null +++ b/pkg/apiserver/request/context_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package request + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" +) + +// TestNamespaceContext validates that a namespace can be get/set on a context object +func TestNamespaceContext(t *testing.T) { + ctx := NewDefaultContext() + result, ok := NamespaceFrom(ctx) + if !ok { + t.Fatalf("Error getting namespace") + } + if metav1.NamespaceDefault != result { + t.Fatalf("Expected: %s, Actual: %s", metav1.NamespaceDefault, result) + } + + ctx = NewContext() + result, ok = NamespaceFrom(ctx) + if ok { + t.Fatalf("Should not be ok because there is no namespace on the context") + } +} + +//TestUserContext validates that a userinfo can be get/set on a context object +func TestUserContext(t *testing.T) { + ctx := NewContext() + _, ok := UserFrom(ctx) + if ok { + t.Fatalf("Should not be ok because there is no user.Info on the context") + } + ctx = WithUser( + ctx, + &user.DefaultInfo{ + Name: "bob", + UID: "123", + Groups: []string{"group1"}, + Extra: map[string][]string{"foo": {"bar"}}, + }, + ) + + result, ok := UserFrom(ctx) + if !ok { + t.Fatalf("Error getting user info") + } + + expectedName := "bob" + if result.GetName() != expectedName { + t.Fatalf("Get user name error, Expected: %s, Actual: %s", expectedName, result.GetName()) + } + + expectedUID := "123" + if result.GetUID() != expectedUID { + t.Fatalf("Get UID error, Expected: %s, Actual: %s", expectedUID, result.GetName()) + } + + expectedGroup := "group1" + actualGroup := result.GetGroups() + if len(actualGroup) != 1 { + t.Fatalf("Get user group number error, Expected: 1, Actual: %d", len(actualGroup)) + } else if actualGroup[0] != expectedGroup { + t.Fatalf("Get user group error, Expected: %s, Actual: %s", expectedGroup, actualGroup[0]) + } + + expectedExtraKey := "foo" + expectedExtraValue := "bar" + actualExtra := result.GetExtra() + if len(actualExtra[expectedExtraKey]) != 1 { + t.Fatalf("Get user extra map number error, Expected: 1, Actual: %d", len(actualExtra[expectedExtraKey])) + } else if actualExtra[expectedExtraKey][0] != expectedExtraValue { + t.Fatalf("Get user extra map value error, Expected: %s, Actual: %s", expectedExtraValue, actualExtra[expectedExtraKey]) + } + +} diff --git a/pkg/apiserver/request/requestinfo.go b/pkg/apiserver/request/requestinfo.go new file mode 100644 index 000000000..c7a9baf12 --- /dev/null +++ b/pkg/apiserver/request/requestinfo.go @@ -0,0 +1,292 @@ +package request + +import ( + "context" + "fmt" + "k8s.io/apimachinery/pkg/api/validation/path" + metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "net/http" + "strings" + + k8srequest "k8s.io/apiserver/pkg/endpoints/request" +) + +type RequestInfoResolver interface { + NewRequestInfo(req *http.Request) (*RequestInfo, error) +} + +// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal +// CRUDdy GET/POST/PUT/DELETE actions on REST objects. +// master's Mux. +var specialVerbs = sets.NewString("proxy", "watch") + +// specialVerbsNoSubresources contains root verbs which do not allow subresources +var specialVerbsNoSubresources = sets.NewString("proxy") + +// namespaceSubresources contains subresources of namespace +// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource +var namespaceSubresources = sets.NewString("status", "finalize") + +var kubernetesAPIPrefixes = sets.NewString("api", "apis") + +// RequestInfo holds information parsed from the http.Request, +// extended from k8s.io/apiserver/pkg/endpoints/request/requestinfo.go +type RequestInfo struct { + *k8srequest.RequestInfo + + // IsKubernetesRequest indicates whether or not the request should be handled by kubernetes or kubesphere + IsKubernetesRequest bool + + // Workspace of requested resource, for non-workspaced resources, this may be empty + Workspace string + + // Cluster of requested resource, this is empty in single-cluster environment + Cluster string + + // Scope of requested resource. + ResourceScope string +} + +type RequestInfoFactory struct { + APIPrefixes sets.String + GrouplessAPIPrefixes sets.String + GlobalResources []schema.GroupResource +} + +// NewRequestInfo returns the information from the http request. If error is not nil, RequestInfo holds the information as best it is known before the failure +// It handles both resource and non-resource requests and fills in all the pertinent information for each. +// Valid Inputs: +// +// /apis/{api-group}/{version}/namespaces +// /api/{version}/namespaces +// /api/{version}/namespaces/{namespace} +// /api/{version}/namespaces/{namespace}/{resource} +// /api/{version}/namespaces/{namespace}/{resource}/{resourceName} +// /api/{version}/{resource} +// /api/{version}/{resource}/{resourceName} +// +// Special verbs without subresources: +// /api/{version}/proxy/{resource}/{resourceName} +// /api/{version}/proxy/namespaces/{namespace}/{resource}/{resourceName} +// +// Special verbs with subresources: +// /api/{version}/watch/{resource} +// /api/{version}/watch/namespaces/{namespace}/{resource} +// +// /kapis/{api-group}/{version}/workspaces/{workspace}/{resource}/{resourceName} +// / +// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource} +// /kapis/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName} +// With workspaces: +// /kapis/clusters/{cluster}/{api-group}/{version}/namespaces/{namespace}/{resource} +// /kapis/clusters/{cluster}/{api-group}/{version}/namespaces/{namespace}/{resource}/{resourceName} +// +func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, error) { + + requestInfo := RequestInfo{ + IsKubernetesRequest: false, + RequestInfo: &k8srequest.RequestInfo{ + Path: req.URL.Path, + Verb: req.Method, + }, + Workspace: api.WorkspaceNone, + Cluster: api.ClusterNone, + } + + defer func() { + if kubernetesAPIPrefixes.Has(requestInfo.APIPrefix) { + requestInfo.IsKubernetesRequest = true + } + }() + + currentParts := splitPath(req.URL.Path) + if len(currentParts) < 3 { + return &requestInfo, nil + } + + if !r.APIPrefixes.Has(currentParts[0]) { + // return a non-resource request + return &requestInfo, nil + } + requestInfo.APIPrefix = currentParts[0] + currentParts = currentParts[1:] + + // URL forms: /clusters/{cluster}/* + if currentParts[0] == "clusters" { + if len(currentParts) > 1 { + requestInfo.Cluster = currentParts[1] + } + if len(currentParts) > 2 { + currentParts = currentParts[2:] + } + } + + if !r.GrouplessAPIPrefixes.Has(requestInfo.APIPrefix) { + // one part (APIPrefix) has already been consumed, so this is actually "do we have four parts?" + if len(currentParts) < 3 { + // return a non-resource request + return &requestInfo, nil + } + + requestInfo.APIGroup = currentParts[0] + currentParts = currentParts[1:] + } + + requestInfo.IsResourceRequest = true + requestInfo.APIVersion = currentParts[0] + currentParts = currentParts[1:] + + if specialVerbs.Has(currentParts[0]) { + if len(currentParts) < 2 { + return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url: %v", req.URL) + } + + requestInfo.Verb = currentParts[0] + currentParts = currentParts[1:] + } else { + switch req.Method { + case "POST": + requestInfo.Verb = "create" + case "GET", "HEAD": + requestInfo.Verb = "get" + case "PUT": + requestInfo.Verb = "update" + case "PATCH": + requestInfo.Verb = "patch" + case "DELETE": + requestInfo.Verb = "delete" + default: + requestInfo.Verb = "" + } + } + + // URL forms: /workspaces/{workspace}/* + if currentParts[0] == "workspaces" { + if len(currentParts) > 1 { + requestInfo.Workspace = currentParts[1] + } + if len(currentParts) > 2 { + currentParts = currentParts[2:] + } + } + + // URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind + if currentParts[0] == "namespaces" { + if len(currentParts) > 1 { + requestInfo.Namespace = currentParts[1] + + // if there is another step after the namespace name and it is not a known namespace subresource + // move currentParts to include it as a resource in its own right + if len(currentParts) > 2 && !namespaceSubresources.Has(currentParts[2]) { + currentParts = currentParts[2:] + } + } + } else { + requestInfo.Namespace = metav1.NamespaceNone + } + + // parsing successful, so we now know the proper value for .Parts + requestInfo.Parts = currentParts + + requestInfo.ResourceScope = r.resolveResourceScope(requestInfo) + + // parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret + switch { + case len(requestInfo.Parts) >= 3 && !specialVerbsNoSubresources.Has(requestInfo.Verb): + requestInfo.Subresource = requestInfo.Parts[2] + fallthrough + case len(requestInfo.Parts) >= 2: + requestInfo.Name = requestInfo.Parts[1] + fallthrough + case len(requestInfo.Parts) >= 1: + requestInfo.Resource = requestInfo.Parts[0] + } + + // if there's no name on the request and we thought it was a get before, then the actual verb is a list or a watch + if len(requestInfo.Name) == 0 && requestInfo.Verb == "get" { + opts := metainternalversion.ListOptions{} + if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), metav1.SchemeGroupVersion, &opts); err != nil { + // An error in parsing request will result in default to "list" and not setting "name" field. + klog.Errorf("Couldn't parse request %#v: %v", req.URL.Query(), err) + // Reset opts to not rely on partial results from parsing. + // However, if watch is set, let's report it. + opts = metainternalversion.ListOptions{} + if values := req.URL.Query()["watch"]; len(values) > 0 { + switch strings.ToLower(values[0]) { + case "false", "0": + default: + opts.Watch = true + } + } + } + + if opts.Watch { + requestInfo.Verb = "watch" + } else { + requestInfo.Verb = "list" + } + + if opts.FieldSelector != nil { + if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok { + if len(path.IsValidPathSegmentName(name)) == 0 { + requestInfo.Name = name + } + } + } + } + // if there's no name on the request and we thought it was a delete before, then the actual verb is deletecollection + if len(requestInfo.Name) == 0 && requestInfo.Verb == "delete" { + requestInfo.Verb = "deletecollection" + } + + return &requestInfo, nil +} + +type requestInfoKeyType int + +// requestInfoKey is the RequestInfo key for the context. It's of private type here. Because +// keys are interfaces and interfaces are equal when the type and the value is equal, this +// does not conflict with the keys defined in pkg/api. +const requestInfoKey requestInfoKeyType = iota + +func WithRequestInfo(parent context.Context, info *RequestInfo) context.Context { + return k8srequest.WithValue(parent, requestInfoKey, info) +} + +func RequestInfoFrom(ctx context.Context) (*RequestInfo, bool) { + info, ok := ctx.Value(requestInfoKey).(*RequestInfo) + return info, ok +} + +// splitPath returns the segments for a URL path. +func splitPath(path string) []string { + path = strings.Trim(path, "/") + if path == "" { + return []string{} + } + return strings.Split(path, "/") +} + +func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string { + for _, globalResource := range r.GlobalResources { + if globalResource.Group == request.APIGroup && + globalResource.Resource == request.Resource { + return iamv1alpha2.GlobalScope + } + } + if request.Namespace != "" { + return iamv1alpha2.NamespaceScope + } + + if request.Workspace != "" { + return iamv1alpha2.WorkspaceScope + } + + return iamv1alpha2.ClusterScope +} diff --git a/pkg/apiserver/request/requestinfo_test.go b/pkg/apiserver/request/requestinfo_test.go new file mode 100644 index 000000000..c7d63a134 --- /dev/null +++ b/pkg/apiserver/request/requestinfo_test.go @@ -0,0 +1,247 @@ +/* + * + * Copyright 2020 The KubeSphere 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 request + +import ( + "k8s.io/apimachinery/pkg/util/sets" + "net/http" + "testing" +) + +func newTestRequestInfoResolver() RequestInfoResolver { + requestInfoResolver := &RequestInfoFactory{ + APIPrefixes: sets.NewString("api", "apis", "kapis", "kapi"), + GrouplessAPIPrefixes: sets.NewString("api", "kapi"), + } + + return requestInfoResolver +} + +func TestRequestInfoFactory_NewRequestInfo(t *testing.T) { + tests := []struct { + name string + url string + method string + expectedErr error + expectedVerb string + expectedResource string + expectedIsResourceRequest bool + expectedCluster string + expectedWorkspace string + expectedNamespace string + expectedKubernetesRequest bool + }{ + { + name: "login", + url: "/oauth/authorize?client_id=ks-console&response_type=token", + method: http.MethodPost, + expectedErr: nil, + expectedVerb: "POST", + expectedResource: "", + expectedIsResourceRequest: false, + expectedCluster: "", + expectedKubernetesRequest: false, + }, + { + name: "list clusterRoles of cluster gondor", + url: "/apis/clusters/gondor/rbac.authorization.k8s.io/v1/clusterroles", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "clusterroles", + expectedIsResourceRequest: true, + expectedCluster: "gondor", + expectedKubernetesRequest: true, + }, + { + name: "list nodes", + url: "/api/v1/nodes", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "nodes", + expectedIsResourceRequest: true, + expectedCluster: "", + expectedKubernetesRequest: true, + }, + { + name: "list nodes of cluster gondor", + url: "/api/clusters/gondor/v1/nodes", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "nodes", + expectedIsResourceRequest: true, + expectedCluster: "gondor", + expectedKubernetesRequest: true, + }, + { + name: "list roles of cluster gondor", + url: "/apis/clusters/gondor/rbac.authorization.k8s.io/v1/namespaces/namespace1/roles", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "roles", + expectedIsResourceRequest: true, + expectedNamespace: "namespace1", + expectedCluster: "gondor", + expectedKubernetesRequest: true, + }, + { + name: "list roles", + url: "/apis/rbac.authorization.k8s.io/v1/namespaces/namespace1/roles", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "roles", + expectedIsResourceRequest: true, + expectedCluster: "", + expectedNamespace: "namespace1", + expectedKubernetesRequest: true, + }, + { + name: "list namespaces", + url: "/kapis/resources.kubesphere.io/v1alpha3/workspaces/workspace1/namespaces", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "namespaces", + expectedIsResourceRequest: true, + expectedWorkspace: "workspace1", + expectedCluster: "", + expectedKubernetesRequest: false, + }, + { + name: "list namespaces of cluster gondor", + url: "/kapis/clusters/gondor/resources.kubesphere.io/v1alpha3/workspaces/workspace1/namespaces", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "namespaces", + expectedIsResourceRequest: true, + expectedWorkspace: "workspace1", + expectedCluster: "gondor", + expectedKubernetesRequest: false, + }, + { + name: "list clusters", + url: "/apis/cluster.kubesphere.io/v1alpha1/clusters", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedResource: "clusters", + expectedIsResourceRequest: true, + expectedWorkspace: "", + expectedCluster: "", + expectedKubernetesRequest: true, + }, + { + name: "get cluster gondor", + url: "/apis/cluster.kubesphere.io/v1alpha1/clusters/gondor", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "get", + expectedResource: "clusters", + expectedIsResourceRequest: true, + expectedWorkspace: "", + expectedCluster: "", + expectedKubernetesRequest: true, + }, + { + name: "random query", + url: "/foo/bar", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "GET", + expectedResource: "", + expectedIsResourceRequest: false, + expectedWorkspace: "", + expectedCluster: "", + expectedKubernetesRequest: false, + }, + { + name: "", + url: "/kapis/tenant.kubesphere.io/v1alpha2/workspaces", + method: http.MethodGet, + expectedErr: nil, + expectedVerb: "list", + expectedNamespace: "", + expectedCluster: "", + expectedWorkspace: "", + expectedKubernetesRequest: false, + expectedIsResourceRequest: true, + expectedResource: "workspaces", + }, + { + name: "kubesphere api without clusters", + url: "/kapis/foo/bar/", + method: http.MethodPost, + expectedErr: nil, + expectedVerb: "POST", + expectedResource: "", + expectedNamespace: "", + expectedWorkspace: "", + expectedCluster: "", + expectedIsResourceRequest: false, + expectedKubernetesRequest: false, + }, + } + + requestInfoResolver := newTestRequestInfoResolver() + + for _, test := range tests { + t.Run(test.url, func(t *testing.T) { + req, err := http.NewRequest(test.method, test.url, nil) + if err != nil { + t.Fatal(err) + } + requestInfo, err := requestInfoResolver.NewRequestInfo(req) + + if err != nil { + if test.expectedErr != err { + t.Errorf("%s: expected error %v, actual %v", test.name, test.expectedErr, err) + } + } else { + if test.expectedVerb != requestInfo.Verb { + t.Errorf("%s: expected verb %v, actual %+v", test.name, test.expectedVerb, requestInfo.Verb) + } + if test.expectedResource != requestInfo.Resource { + t.Errorf("%s: expected resource %v, actual %+v", test.name, test.expectedResource, requestInfo.Resource) + } + if test.expectedIsResourceRequest != requestInfo.IsResourceRequest { + t.Errorf("%s: expected is resource request %v, actual %+v", test.name, test.expectedIsResourceRequest, requestInfo.IsResourceRequest) + } + if test.expectedCluster != requestInfo.Cluster { + t.Errorf("%s: expected cluster %v, actual %+v", test.name, test.expectedCluster, requestInfo.Cluster) + } + if test.expectedWorkspace != requestInfo.Workspace { + t.Errorf("%s: expected workspace %v, actual %+v", test.name, test.expectedWorkspace, requestInfo.Workspace) + } + if test.expectedNamespace != requestInfo.Namespace { + t.Errorf("%s: expected namespace %v, actual %+v", test.name, test.expectedNamespace, requestInfo.Namespace) + } + + if test.expectedKubernetesRequest != requestInfo.IsKubernetesRequest { + t.Errorf("%s: expected kubernetes request %v, actual %+v", test.name, test.expectedKubernetesRequest, requestInfo.IsKubernetesRequest) + } + } + }) + + } +} diff --git a/pkg/apiserver/resources/resources.go b/pkg/apiserver/resources/resources.go deleted file mode 100644 index 484c6c5cc..000000000 --- a/pkg/apiserver/resources/resources.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/models/resources" - "net/http" - - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" -) - -func ListNamespacedResources(req *restful.Request, resp *restful.Response) { - ListResources(req, resp) -} - -func ListResources(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - resourceName := req.PathParameter("resources") - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, resources.CreateTime) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := resources.ListResources(namespace, resourceName, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} diff --git a/pkg/apiserver/resources/storage.go b/pkg/apiserver/resources/storage.go deleted file mode 100644 index 41ba4cc67..000000000 --- a/pkg/apiserver/resources/storage.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/emicklei/go-restful" - "k8s.io/api/core/v1" - "k8s.io/klog" - "net/http" - - storagev1 "k8s.io/api/storage/v1" - "kubesphere.io/kubesphere/pkg/models/storage" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -type pvcList struct { - Name string `json:"name"` - Items []*v1.PersistentVolumeClaim `json:"items"` -} - -type podListByPvc struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - Pods []*v1.Pod `json:"pods"` -} - -// List all pods of a specific PVC -// Extended API URL: "GET /api/v1alpha2/namespaces/{namespace}/persistentvolumeclaims/{name}/pods" -func GetPodListByPvc(request *restful.Request, response *restful.Response) { - - pvcName := request.PathParameter("pvc") - nsName := request.PathParameter("namespace") - pods, err := storage.GetPodListByPvc(pvcName, nsName) - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - result := podListByPvc{Name: pvcName, Namespace: nsName, Pods: pods} - response.WriteAsJson(result) -} - -// List all PersistentVolumeClaims of a specific StorageClass -// Extended API URL: "GET /api/v1alpha2/storageclasses/{storageclass}/persistentvolumeclaims" -func GetPvcListBySc(request *restful.Request, response *restful.Response) { - scName := request.PathParameter("storageclass") - claims, err := storage.GetPvcListBySc(scName) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - result := pvcList{ - Name: scName, Items: claims, - } - - response.WriteAsJson(result) -} - -func PatchStorageClass(request *restful.Request, response *restful.Response) { - scObj := &storagev1.StorageClass{} - err := request.ReadEntity(scObj) - if err != nil { - klog.Errorf("read entity error: %s", err.Error()) - response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - klog.V(5).Infof("succeed to read entity %v", scObj) - scName := request.PathParameter("storageclass") - if scObj.Annotations[storage.IsDefaultStorageClassAnnotation] == "true" { - // Set default storage class - sc, err := storage.SetDefaultStorageClass(scName) - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - response.WriteEntity(sc) - return - } - response.WriteEntity(errors.None) -} diff --git a/pkg/apiserver/resources/user.go b/pkg/apiserver/resources/user.go deleted file mode 100644 index 81175ff1f..000000000 --- a/pkg/apiserver/resources/user.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/emicklei/go-restful" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/klog" - "net/http" - - "kubesphere.io/kubesphere/pkg/models/kubeconfig" - "kubesphere.io/kubesphere/pkg/models/kubectl" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func GetKubectl(req *restful.Request, resp *restful.Response) { - - user := req.PathParameter("user") - - kubectlPod, err := kubectl.GetKubectlPod(user) - - if err != nil { - klog.Error(err) - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(kubectlPod) -} - -func GetKubeconfig(req *restful.Request, resp *restful.Response) { - - user := req.PathParameter("user") - - kubectlConfig, err := kubeconfig.GetKubeConfig(user) - - if err != nil { - klog.Error(err) - if k8serr.IsNotFound(err) { - // recreate - kubeconfig.CreateKubeConfig(user) - resp.WriteHeaderAndJson(http.StatusNotFound, errors.Wrap(err), restful.MIME_JSON) - } else { - resp.WriteHeaderAndJson(http.StatusInternalServerError, errors.Wrap(err), restful.MIME_JSON) - } - return - } - - resp.Write([]byte(kubectlConfig)) -} diff --git a/pkg/apiserver/revisions/revisions.go b/pkg/apiserver/revisions/revisions.go deleted file mode 100644 index 895c6f5ea..000000000 --- a/pkg/apiserver/revisions/revisions.go +++ /dev/null @@ -1,82 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 revisions - -import ( - "net/http" - "strconv" - - "github.com/emicklei/go-restful" - "kubesphere.io/kubesphere/pkg/models/revisions" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func GetDaemonSetRevision(req *restful.Request, resp *restful.Response) { - daemonset := req.PathParameter("daemonset") - namespace := req.PathParameter("namespace") - revision, err := strconv.Atoi(req.PathParameter("revision")) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := revisions.GetDaemonSetRevision(namespace, daemonset, revision) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func GetDeployRevision(req *restful.Request, resp *restful.Response) { - deploy := req.PathParameter("deployment") - namespace := req.PathParameter("namespace") - revision := req.PathParameter("revision") - - result, err := revisions.GetDeployRevision(namespace, deploy, revision) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func GetStatefulSetRevision(req *restful.Request, resp *restful.Response) { - statefulset := req.PathParameter("statefulset") - namespace := req.PathParameter("namespace") - revision, err := strconv.Atoi(req.PathParameter("revision")) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := revisions.GetStatefulSetRevision(namespace, statefulset, revision) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} diff --git a/pkg/apiserver/routers/routers.go b/pkg/apiserver/routers/routers.go deleted file mode 100644 index 992f02441..000000000 --- a/pkg/apiserver/routers/routers.go +++ /dev/null @@ -1,151 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 routers - -import ( - "fmt" - "github.com/emicklei/go-restful" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "net/http" - - "kubesphere.io/kubesphere/pkg/server/errors" - - "strings" - - "k8s.io/api/core/v1" - - "kubesphere.io/kubesphere/pkg/models/routers" -) - -type Router struct { - RouterType string `json:"type"` - Annotations map[string]string `json:"annotations"` -} - -// Get all namespace ingress controller services -func GetAllRouters(request *restful.Request, response *restful.Response) { - - routers, err := routers.GetAllRouters() - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(routers) -} - -// Get ingress controller service for specified namespace -func GetRouter(request *restful.Request, response *restful.Response) { - - namespace := request.PathParameter("namespace") - router, err := routers.GetRouter(namespace) - - if err != nil { - if k8serr.IsNotFound(err) { - response.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - response.WriteAsJson(router) -} - -// Create ingress controller and related services -func CreateRouter(request *restful.Request, response *restful.Response) { - - namespace := request.PathParameter("namespace") - - newRouter := Router{} - err := request.ReadEntity(&newRouter) - - if err != nil { - response.WriteAsJson(err) - return - } - - var router *v1.Service - - serviceType, annotationMap, err := parseParameter(newRouter) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("wrong annotations, missing key or value"))) - return - } - - router, err = routers.CreateRouter(namespace, serviceType, annotationMap) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(router) -} - -// Delete ingress controller and services -func DeleteRouter(request *restful.Request, response *restful.Response) { - namespace := request.PathParameter("namespace") - - router, err := routers.DeleteRouter(namespace) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(router) -} - -func UpdateRouter(request *restful.Request, response *restful.Response) { - - namespace := request.PathParameter("namespace") - - newRouter := Router{} - err := request.ReadEntity(&newRouter) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - serviceType, annotationMap, err := parseParameter(newRouter) - - router, err := routers.UpdateRouter(namespace, serviceType, annotationMap) - - if err != nil { - response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - response.WriteAsJson(router) -} - -func parseParameter(router Router) (routerType v1.ServiceType, annotationMap map[string]string, err error) { - - routerType = v1.ServiceTypeNodePort - - if strings.Compare(strings.ToLower(router.RouterType), "loadbalancer") == 0 { - routerType = v1.ServiceTypeLoadBalancer - } - - return routerType, router.Annotations, nil -} diff --git a/pkg/apiserver/server/handler.go b/pkg/apiserver/server/handler.go new file mode 100644 index 000000000..abb4e431a --- /dev/null +++ b/pkg/apiserver/server/handler.go @@ -0,0 +1 @@ +package server diff --git a/pkg/apiserver/servicemesh/metrics/handlers.go b/pkg/apiserver/servicemesh/metrics/handlers.go deleted file mode 100644 index 249c22d86..000000000 --- a/pkg/apiserver/servicemesh/metrics/handlers.go +++ /dev/null @@ -1,70 +0,0 @@ -package metrics - -import ( - "fmt" - "github.com/emicklei/go-restful" - "github.com/kiali/kiali/handlers" -) - -// Get app metrics -func GetAppMetrics(request *restful.Request, response *restful.Response) { - handlers.AppMetrics(request, response) -} - -// Get workload metrics -func GetWorkloadMetrics(request *restful.Request, response *restful.Response) { - namespace := request.PathParameter("namespace") - workload := request.PathParameter("workload") - - if len(namespace) > 0 && len(workload) > 0 { - request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s&workload=%s", request.Request.URL.RawQuery, namespace, workload) - } - - handlers.WorkloadMetrics(request, response) -} - -// Get service metrics -func GetServiceMetrics(request *restful.Request, response *restful.Response) { - handlers.ServiceMetrics(request, response) -} - -// Get namespace metrics -func GetNamespaceMetrics(request *restful.Request, response *restful.Response) { - handlers.NamespaceMetrics(request, response) -} - -// Get service graph for namespace -func GetNamespaceGraph(request *restful.Request, response *restful.Response) { - namespace := request.PathParameter("namespace") - - if len(namespace) > 0 { - request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace) - } - - handlers.GetNamespaceGraph(request, response) -} - -// Get service graph for namespaces -func GetNamespacesGraph(request *restful.Request, response *restful.Response) { - handlers.GraphNamespaces(request, response) -} - -// Get namespace health -func GetNamespaceHealth(request *restful.Request, response *restful.Response) { - handlers.NamespaceHealth(request, response) -} - -// Get workload health -func GetWorkloadHealth(request *restful.Request, response *restful.Response) { - handlers.WorkloadHealth(request, response) -} - -// Get app health -func GetAppHealth(request *restful.Request, response *restful.Response) { - handlers.AppHealth(request, response) -} - -// Get service health -func GetServiceHealth(request *restful.Request, response *restful.Response) { - handlers.ServiceHealth(request, response) -} diff --git a/pkg/apiserver/servicemesh/tracing/handlers.go b/pkg/apiserver/servicemesh/tracing/handlers.go deleted file mode 100644 index be6adaaa4..000000000 --- a/pkg/apiserver/servicemesh/tracing/handlers.go +++ /dev/null @@ -1,45 +0,0 @@ -package tracing - -import ( - "fmt" - "github.com/emicklei/go-restful" - "io/ioutil" - "log" - "net/http" -) - -var JaegerQueryUrl = "http://jaeger-query.istio-system.svc:16686/jaeger" - -func GetServiceTracing(request *restful.Request, response *restful.Response) { - namespace := request.PathParameter("namespace") - service := request.PathParameter("service") - - serviceName := fmt.Sprintf("%s.%s", service, namespace) - - url := fmt.Sprintf("%s/api/traces?%s&service=%s", JaegerQueryUrl, request.Request.URL.RawQuery, serviceName) - - resp, err := http.Get(url) - - if err != nil { - log.Printf("query jaeger faile with err %v", err) - response.WriteError(http.StatusInternalServerError, err) - return - } - - body, err := ioutil.ReadAll(resp.Body) - defer resp.Body.Close() - - if err != nil { - log.Printf("read response error : %v", err) - response.WriteError(http.StatusInternalServerError, err) - return - } - - // need to set header for proper response - response.Header().Set("Content-Type", "application/json") - _, err = response.Write(body) - - if err != nil { - log.Printf("write response failed %v", err) - } -} diff --git a/pkg/apiserver/tenant/tenant.go b/pkg/apiserver/tenant/tenant.go deleted file mode 100644 index d0f6a35b2..000000000 --- a/pkg/apiserver/tenant/tenant.go +++ /dev/null @@ -1,440 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 tenant - -import ( - "github.com/emicklei/go-restful" - "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - k8serr "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/net" - "k8s.io/klog" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/apiserver/logging" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/metrics" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/models/tenant" - "kubesphere.io/kubesphere/pkg/models/workspaces" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" - "strings" -) - -func ListWorkspaceRules(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(rules) -} - -func ListWorkspaces(req *restful.Request, resp *restful.Response) { - username := req.HeaderParameter(constants.UserNameHeader) - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if orderBy == "" { - orderBy = resources.CreateTime - reverse = true - } - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - resp.WriteAsJson(result) -} - -func DescribeWorkspace(req *restful.Request, resp *restful.Response) { - username := req.HeaderParameter(constants.UserNameHeader) - workspaceName := req.PathParameter("workspace") - - result, err := tenant.DescribeWorkspace(username, workspaceName) - - if err != nil { - klog.Errorf("describe workspace failed: %+v", err) - if k8serr.IsNotFound(err) { - resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - resp.WriteAsJson(result) -} -func ListNamespacesByUsername(req *restful.Request, resp *restful.Response) { - ListNamespaces(req, resp) -} - -func ListNamespaces(req *restful.Request, resp *restful.Response) { - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - // /workspaces/{workspace}/members/{username}/namespaces - if username == "" { - // /workspaces/{workspace}/namespaces - username = req.HeaderParameter(constants.UserNameHeader) - } - - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - orderBy := req.QueryParameter(params.OrderByParam) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - reverse := params.ParseReverse(req) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - conditions.Match[constants.WorkspaceLabelKey] = workspace - - result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - - namespaces := make([]*v1.Namespace, 0) - - for _, item := range result.Items { - namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy()) - } - - namespaces = metrics.GetNamespacesWithMetrics(namespaces) - - items := make([]interface{}, 0) - - for _, item := range namespaces { - items = append(items, item) - } - - result.Items = items - - resp.WriteAsJson(result) -} - -func CreateNamespace(req *restful.Request, resp *restful.Response) { - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - var namespace v1.Namespace - err := req.ReadEntity(&namespace) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - workspace, err := tenant.GetWorkspace(workspaceName) - - err = checkResourceQuotas(workspace) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - return - } - - if err != nil { - if k8serr.IsNotFound(err) { - resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err)) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - } - return - } - - created, err := tenant.CreateNamespace(workspaceName, &namespace, username) - - if err != nil { - if k8serr.IsAlreadyExists(err) { - resp.WriteHeaderAndEntity(http.StatusConflict, err) - } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, err) - } - return - } - resp.WriteAsJson(created) -} - -func DeleteNamespace(req *restful.Request, resp *restful.Response) { - workspaceName := req.PathParameter("workspace") - namespaceName := req.PathParameter("namespace") - - err := workspaces.DeleteNamespace(workspaceName, namespaceName) - - if err != nil { - resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) - return - } - - resp.WriteAsJson(errors.None) -} - -func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error { - return nil -} - -func ListDevopsProjectsByUsername(req *restful.Request, resp *restful.Response) { - ListDevopsProjects(req, resp) -} - -func ListDevopsProjects(req *restful.Request, resp *restful.Response) { - - workspace := req.PathParameter("workspace") - username := req.PathParameter("member") - if username == "" { - username = req.HeaderParameter(constants.UserNameHeader) - } - orderBy := req.QueryParameter(params.OrderByParam) - reverse := params.ParseReverse(req) - limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam)) - conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam)) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - result, err := tenant.ListDevopsProjects(workspace, username, conditions, orderBy, reverse, limit, offset) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(result) -} - -func GetDevOpsProjectsCount(req *restful.Request, resp *restful.Response) { - username := req.HeaderParameter(constants.UserNameHeader) - - result, err := tenant.GetDevOpsProjectsCount(username) - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - resp.WriteAsJson(struct { - Count uint32 `json:"count"` - }{Count: result}) -} -func DeleteDevopsProject(req *restful.Request, resp *restful.Response) { - projectId := req.PathParameter("devops") - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - _, err := tenant.GetWorkspace(workspaceName) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - err = tenant.DeleteDevOpsProject(projectId, username) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(errors.None) -} - -func CreateDevopsProject(req *restful.Request, resp *restful.Response) { - - workspaceName := req.PathParameter("workspace") - username := req.HeaderParameter(constants.UserNameHeader) - - var devops devopsv1alpha2.DevOpsProject - - err := req.ReadEntity(&devops) - - if err != nil { - klog.Infof("%+v", err) - errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) - return - } - - klog.Infoln("create workspace", username, workspaceName, devops) - project, err := tenant.CreateDevopsProject(username, workspaceName, &devops) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(project) -} - -func ListNamespaceRules(req *restful.Request, resp *restful.Response) { - namespace := req.PathParameter("namespace") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := iam.GetUserNamespaceSimpleRules(namespace, username) - - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - - resp.WriteAsJson(rules) -} - -func ListDevopsRules(req *restful.Request, resp *restful.Response) { - - devops := req.PathParameter("devops") - username := req.HeaderParameter(constants.UserNameHeader) - - rules, err := tenant.GetUserDevopsSimpleRules(username, devops) - - if err != nil { - klog.Errorf("%+v", err) - errors.ParseSvcErr(err, resp) - return - } - - resp.WriteAsJson(rules) -} - -func LogQuery(req *restful.Request, resp *restful.Response) { - operation := req.QueryParameter("operation") - req, err := regenerateLoggingRequest(req) - switch { - case err != nil: - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - case req != nil: - logging.LoggingQueryCluster(req, resp) - default: - if operation == "export" { - resp.Header().Set(restful.HEADER_ContentType, "text/plain") - resp.Header().Set("Content-Disposition", "attachment") - resp.Write(nil) - } else { - resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)}) - } - } -} - -// override namespace query conditions -func regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) { - - username := req.HeaderParameter(constants.UserNameHeader) - - // regenerate the request for log query - newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster") - values := req.Request.URL.Query() - - clusterRules, err := iam.GetUserClusterRules(username) - if err != nil { - klog.Errorln(err) - return nil, err - } - - hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) - // if the user is not a cluster admin - if !hasClusterLogAccess { - queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",") - // then the user can only view logs of namespaces he belongs to - namespaces := make([]string, 0) - roles, err := iam.GetUserRoles("", username) - if err != nil { - klog.Errorln(err) - return nil, err - } - for _, role := range roles { - if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) { - namespaces = append(namespaces, role.Namespace) - } - } - - // if the user belongs to no namespace - // then no log visible - if len(namespaces) == 0 { - return nil, nil - } else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" { - values.Set("namespaces", strings.Join(namespaces, ",")) - } else { - inter := intersection(queryNamespaces, namespaces) - if len(inter) == 0 { - return nil, nil - } - values.Set("namespaces", strings.Join(inter, ",")) - } - } - - newUrl.RawQuery = values.Encode() - - // forward the request to logging model - newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil) - return restful.NewRequest(newHttpRequest), nil -} - -func intersection(s1, s2 []string) (inter []string) { - hash := make(map[string]bool) - for _, e := range s1 { - hash[e] = true - } - for _, e := range s2 { - // If elements present in the hashmap then append intersection list. - if hash[e] { - inter = append(inter, e) - } - } - //Remove dups from slice. - inter = removeDups(inter) - return -} - -//Remove dups from slice. -func removeDups(elements []string) (nodups []string) { - encountered := make(map[string]bool) - for _, element := range elements { - if !encountered[element] { - nodups = append(nodups, element) - encountered[element] = true - } - } - return -} diff --git a/pkg/apiserver/terminal/terminal.go b/pkg/apiserver/terminal/terminal.go deleted file mode 100644 index 8bf8a19b7..000000000 --- a/pkg/apiserver/terminal/terminal.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 terminal - -import ( - "github.com/emicklei/go-restful" - "github.com/gorilla/websocket" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/terminal" - "net/http" -) - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - // Allow connections from any Origin - CheckOrigin: func(r *http.Request) bool { return true }, -} - -// Handles execute shell API call -func HandleTerminalSession(request *restful.Request, resp *restful.Response) { - - namespace := request.PathParameter("namespace") - podName := request.PathParameter("pod") - containerName := request.QueryParameter("container") - shell := request.QueryParameter("shell") - - conn, err := upgrader.Upgrade(resp.ResponseWriter, request.Request, nil) - if err != nil { - klog.Warning(err) - return - } - - terminal.HandleSession(shell, namespace, podName, containerName, conn) -} diff --git a/pkg/apiserver/workloadstatuses/workloadstatuses.go b/pkg/apiserver/workloadstatuses/workloadstatuses.go deleted file mode 100644 index fe5ebfd26..000000000 --- a/pkg/apiserver/workloadstatuses/workloadstatuses.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 workloadstatuses - -import ( - "github.com/emicklei/go-restful" - "net/http" - - "kubesphere.io/kubesphere/pkg/models/status" - "kubesphere.io/kubesphere/pkg/server/errors" -) - -func GetClusterAbnormalWorkloads(req *restful.Request, resp *restful.Response) { - res, err := status.GetClusterResourceStatus() - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteAsJson(res) -} - -func GetNamespacedAbnormalWorkloads(req *restful.Request, resp *restful.Response) { - res, err := status.GetNamespacesResourceStatus(req.PathParameter("namespace")) - if err != nil { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) - return - } - resp.WriteAsJson(res) -} diff --git a/pkg/apiserver/workspaces/workspaces.go b/pkg/apiserver/workspaces/workspaces.go deleted file mode 100644 index d09231c12..000000000 --- a/pkg/apiserver/workspaces/workspaces.go +++ /dev/null @@ -1,18 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 workspaces diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index cdf28675e..53f393899 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -24,7 +24,10 @@ import ( discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/tenant/v1alpha1" @@ -32,7 +35,10 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface + ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface + DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface + IamV1alpha2() iamv1alpha2.IamV1alpha2Interface NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface ServicemeshV1alpha2() servicemeshv1alpha2.ServicemeshV1alpha2Interface TenantV1alpha1() tenantv1alpha1.TenantV1alpha1Interface @@ -42,17 +48,35 @@ type Interface interface { // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient + clusterV1alpha1 *clusterv1alpha1.ClusterV1alpha1Client devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client + devopsV1alpha3 *devopsv1alpha3.DevopsV1alpha3Client + iamV1alpha2 *iamv1alpha2.IamV1alpha2Client networkV1alpha1 *networkv1alpha1.NetworkV1alpha1Client servicemeshV1alpha2 *servicemeshv1alpha2.ServicemeshV1alpha2Client tenantV1alpha1 *tenantv1alpha1.TenantV1alpha1Client } +// ClusterV1alpha1 retrieves the ClusterV1alpha1Client +func (c *Clientset) ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface { + return c.clusterV1alpha1 +} + // DevopsV1alpha1 retrieves the DevopsV1alpha1Client func (c *Clientset) DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface { return c.devopsV1alpha1 } +// DevopsV1alpha3 retrieves the DevopsV1alpha3Client +func (c *Clientset) DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface { + return c.devopsV1alpha3 +} + +// IamV1alpha2 retrieves the IamV1alpha2Client +func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface { + return c.iamV1alpha2 +} + // NetworkV1alpha1 retrieves the NetworkV1alpha1Client func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface { return c.networkV1alpha1 @@ -89,10 +113,22 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error + cs.clusterV1alpha1, err = clusterv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.devopsV1alpha1, err = devopsv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } + cs.devopsV1alpha3, err = devopsv1alpha3.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + cs.iamV1alpha2, err = iamv1alpha2.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.networkV1alpha1, err = networkv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err @@ -117,7 +153,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset + cs.clusterV1alpha1 = clusterv1alpha1.NewForConfigOrDie(c) cs.devopsV1alpha1 = devopsv1alpha1.NewForConfigOrDie(c) + cs.devopsV1alpha3 = devopsv1alpha3.NewForConfigOrDie(c) + cs.iamV1alpha2 = iamv1alpha2.NewForConfigOrDie(c) cs.networkV1alpha1 = networkv1alpha1.NewForConfigOrDie(c) cs.servicemeshV1alpha2 = servicemeshv1alpha2.NewForConfigOrDie(c) cs.tenantV1alpha1 = tenantv1alpha1.NewForConfigOrDie(c) @@ -129,7 +168,10 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset + cs.clusterV1alpha1 = clusterv1alpha1.New(c) cs.devopsV1alpha1 = devopsv1alpha1.New(c) + cs.devopsV1alpha3 = devopsv1alpha3.New(c) + cs.iamV1alpha2 = iamv1alpha2.New(c) cs.networkV1alpha1 = networkv1alpha1.New(c) cs.servicemeshV1alpha2 = servicemeshv1alpha2.New(c) cs.tenantV1alpha1 = tenantv1alpha1.New(c) diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 2eb0a0a07..e43f51d16 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -25,8 +25,14 @@ import ( fakediscovery "k8s.io/client-go/discovery/fake" "k8s.io/client-go/testing" clientset "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" + fakeclusterv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1" fakedevopsv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3" + fakedevopsv1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2" + fakeiamv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake" networkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1" fakenetworkv1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1/fake" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/servicemesh/v1alpha2" @@ -82,11 +88,26 @@ func (c *Clientset) Tracker() testing.ObjectTracker { var _ clientset.Interface = &Clientset{} +// ClusterV1alpha1 retrieves the ClusterV1alpha1Client +func (c *Clientset) ClusterV1alpha1() clusterv1alpha1.ClusterV1alpha1Interface { + return &fakeclusterv1alpha1.FakeClusterV1alpha1{Fake: &c.Fake} +} + // DevopsV1alpha1 retrieves the DevopsV1alpha1Client func (c *Clientset) DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface { return &fakedevopsv1alpha1.FakeDevopsV1alpha1{Fake: &c.Fake} } +// DevopsV1alpha3 retrieves the DevopsV1alpha3Client +func (c *Clientset) DevopsV1alpha3() devopsv1alpha3.DevopsV1alpha3Interface { + return &fakedevopsv1alpha3.FakeDevopsV1alpha3{Fake: &c.Fake} +} + +// IamV1alpha2 retrieves the IamV1alpha2Client +func (c *Clientset) IamV1alpha2() iamv1alpha2.IamV1alpha2Interface { + return &fakeiamv1alpha2.FakeIamV1alpha2{Fake: &c.Fake} +} + // NetworkV1alpha1 retrieves the NetworkV1alpha1Client func (c *Clientset) NetworkV1alpha1() networkv1alpha1.NetworkV1alpha1Interface { return &fakenetworkv1alpha1.FakeNetworkV1alpha1{Fake: &c.Fake} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 36f2e736e..d981d695b 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -24,7 +24,10 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" serializer "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" @@ -34,7 +37,10 @@ var scheme = runtime.NewScheme() var codecs = serializer.NewCodecFactory(scheme) var parameterCodec = runtime.NewParameterCodec(scheme) var localSchemeBuilder = runtime.SchemeBuilder{ + clusterv1alpha1.AddToScheme, devopsv1alpha1.AddToScheme, + devopsv1alpha3.AddToScheme, + iamv1alpha2.AddToScheme, networkv1alpha1.AddToScheme, servicemeshv1alpha2.AddToScheme, tenantv1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index 30434c545..13f4b014c 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -24,7 +24,10 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" serializer "k8s.io/apimachinery/pkg/runtime/serializer" utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" @@ -34,7 +37,10 @@ var Scheme = runtime.NewScheme() var Codecs = serializer.NewCodecFactory(Scheme) var ParameterCodec = runtime.NewParameterCodec(Scheme) var localSchemeBuilder = runtime.SchemeBuilder{ + clusterv1alpha1.AddToScheme, devopsv1alpha1.AddToScheme, + devopsv1alpha3.AddToScheme, + iamv1alpha2.AddToScheme, networkv1alpha1.AddToScheme, servicemeshv1alpha2.AddToScheme, tenantv1alpha1.AddToScheme, diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster.go new file mode 100644 index 000000000..d5fc7e6d8 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// ClustersGetter has a method to return a ClusterInterface. +// A group's client should implement this interface. +type ClustersGetter interface { + Clusters() ClusterInterface +} + +// ClusterInterface has methods to work with Cluster resources. +type ClusterInterface interface { + Create(*v1alpha1.Cluster) (*v1alpha1.Cluster, error) + Update(*v1alpha1.Cluster) (*v1alpha1.Cluster, error) + UpdateStatus(*v1alpha1.Cluster) (*v1alpha1.Cluster, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Cluster, error) + List(opts v1.ListOptions) (*v1alpha1.ClusterList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Cluster, err error) + ClusterExpansion +} + +// clusters implements ClusterInterface +type clusters struct { + client rest.Interface +} + +// newClusters returns a Clusters +func newClusters(c *ClusterV1alpha1Client) *clusters { + return &clusters{ + client: c.RESTClient(), + } +} + +// Get takes name of the cluster, and returns the corresponding cluster object, and an error if there is any. +func (c *clusters) Get(name string, options v1.GetOptions) (result *v1alpha1.Cluster, err error) { + result = &v1alpha1.Cluster{} + err = c.client.Get(). + Resource("clusters"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Clusters that match those selectors. +func (c *clusters) List(opts v1.ListOptions) (result *v1alpha1.ClusterList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.ClusterList{} + err = c.client.Get(). + Resource("clusters"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusters. +func (c *clusters) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clusters"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a cluster and creates it. Returns the server's representation of the cluster, and an error, if there is any. +func (c *clusters) Create(cluster *v1alpha1.Cluster) (result *v1alpha1.Cluster, err error) { + result = &v1alpha1.Cluster{} + err = c.client.Post(). + Resource("clusters"). + Body(cluster). + Do(). + Into(result) + return +} + +// Update takes the representation of a cluster and updates it. Returns the server's representation of the cluster, and an error, if there is any. +func (c *clusters) Update(cluster *v1alpha1.Cluster) (result *v1alpha1.Cluster, err error) { + result = &v1alpha1.Cluster{} + err = c.client.Put(). + Resource("clusters"). + Name(cluster.Name). + Body(cluster). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *clusters) UpdateStatus(cluster *v1alpha1.Cluster) (result *v1alpha1.Cluster, err error) { + result = &v1alpha1.Cluster{} + err = c.client.Put(). + Resource("clusters"). + Name(cluster.Name). + SubResource("status"). + Body(cluster). + Do(). + Into(result) + return +} + +// Delete takes name of the cluster and deletes it. Returns an error if one occurs. +func (c *clusters) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusters"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clusters"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched cluster. +func (c *clusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Cluster, err error) { + result = &v1alpha1.Cluster{} + err = c.client.Patch(pt). + Resource("clusters"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go new file mode 100644 index 000000000..fef8f441c --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/cluster_client.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + rest "k8s.io/client-go/rest" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +type ClusterV1alpha1Interface interface { + RESTClient() rest.Interface + ClustersGetter +} + +// ClusterV1alpha1Client is used to interact with features provided by the cluster.kubesphere.io group. +type ClusterV1alpha1Client struct { + restClient rest.Interface +} + +func (c *ClusterV1alpha1Client) Clusters() ClusterInterface { + return newClusters(c) +} + +// NewForConfig creates a new ClusterV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*ClusterV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ClusterV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new ClusterV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ClusterV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ClusterV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *ClusterV1alpha1Client { + return &ClusterV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + 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 *ClusterV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/doc.go new file mode 100644 index 000000000..f2efa4141 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/doc.go new file mode 100644 index 000000000..329c98fb5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster.go new file mode 100644 index 000000000..6691c17bd --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster.go @@ -0,0 +1,131 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" +) + +// FakeClusters implements ClusterInterface +type FakeClusters struct { + Fake *FakeClusterV1alpha1 +} + +var clustersResource = schema.GroupVersionResource{Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"} + +var clustersKind = schema.GroupVersionKind{Group: "cluster.kubesphere.io", Version: "v1alpha1", Kind: "Cluster"} + +// Get takes name of the cluster, and returns the corresponding cluster object, and an error if there is any. +func (c *FakeClusters) Get(name string, options v1.GetOptions) (result *v1alpha1.Cluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(clustersResource, name), &v1alpha1.Cluster{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cluster), err +} + +// List takes label and field selectors, and returns the list of Clusters that match those selectors. +func (c *FakeClusters) List(opts v1.ListOptions) (result *v1alpha1.ClusterList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(clustersResource, clustersKind, opts), &v1alpha1.ClusterList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterList{ListMeta: obj.(*v1alpha1.ClusterList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterList).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 clusters. +func (c *FakeClusters) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(clustersResource, opts)) +} + +// Create takes the representation of a cluster and creates it. Returns the server's representation of the cluster, and an error, if there is any. +func (c *FakeClusters) Create(cluster *v1alpha1.Cluster) (result *v1alpha1.Cluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(clustersResource, cluster), &v1alpha1.Cluster{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cluster), err +} + +// Update takes the representation of a cluster and updates it. Returns the server's representation of the cluster, and an error, if there is any. +func (c *FakeClusters) Update(cluster *v1alpha1.Cluster) (result *v1alpha1.Cluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(clustersResource, cluster), &v1alpha1.Cluster{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cluster), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeClusters) UpdateStatus(cluster *v1alpha1.Cluster) (*v1alpha1.Cluster, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(clustersResource, "status", cluster), &v1alpha1.Cluster{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cluster), err +} + +// Delete takes name of the cluster and deletes it. Returns an error if one occurs. +func (c *FakeClusters) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(clustersResource, name), &v1alpha1.Cluster{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusters) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(clustersResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterList{}) + return err +} + +// Patch applies the patch and returns the patched cluster. +func (c *FakeClusters) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Cluster, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(clustersResource, name, pt, data, subresources...), &v1alpha1.Cluster{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cluster), err +} diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster_client.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster_client.go new file mode 100644 index 000000000..8168d68fb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/fake/fake_cluster_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" +) + +type FakeClusterV1alpha1 struct { + *testing.Fake +} + +func (c *FakeClusterV1alpha1) Clusters() v1alpha1.ClusterInterface { + return &FakeClusters{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeClusterV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..8a541985e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/cluster/v1alpha1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +type ClusterExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go index 36295141c..c74a68e5f 100644 --- a/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go @@ -27,6 +27,9 @@ import ( type DevopsV1alpha1Interface interface { RESTClient() rest.Interface S2iBinariesGetter + S2iBuildersGetter + S2iBuilderTemplatesGetter + S2iRunsGetter } // DevopsV1alpha1Client is used to interact with features provided by the devops.kubesphere.io group. @@ -38,6 +41,18 @@ func (c *DevopsV1alpha1Client) S2iBinaries(namespace string) S2iBinaryInterface return newS2iBinaries(c, namespace) } +func (c *DevopsV1alpha1Client) S2iBuilders(namespace string) S2iBuilderInterface { + return newS2iBuilders(c, namespace) +} + +func (c *DevopsV1alpha1Client) S2iBuilderTemplates() S2iBuilderTemplateInterface { + return newS2iBuilderTemplates(c) +} + +func (c *DevopsV1alpha1Client) S2iRuns(namespace string) S2iRunInterface { + return newS2iRuns(c, namespace) +} + // NewForConfig creates a new DevopsV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*DevopsV1alpha1Client, error) { config := *c diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_devops_client.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_devops_client.go index 85be9ad2f..18561e813 100644 --- a/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_devops_client.go +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_devops_client.go @@ -32,6 +32,18 @@ func (c *FakeDevopsV1alpha1) S2iBinaries(namespace string) v1alpha1.S2iBinaryInt return &FakeS2iBinaries{c, namespace} } +func (c *FakeDevopsV1alpha1) S2iBuilders(namespace string) v1alpha1.S2iBuilderInterface { + return &FakeS2iBuilders{c, namespace} +} + +func (c *FakeDevopsV1alpha1) S2iBuilderTemplates() v1alpha1.S2iBuilderTemplateInterface { + return &FakeS2iBuilderTemplates{c} +} + +func (c *FakeDevopsV1alpha1) S2iRuns(namespace string) v1alpha1.S2iRunInterface { + return &FakeS2iRuns{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeDevopsV1alpha1) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuilder.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuilder.go new file mode 100644 index 000000000..bd38feef7 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuilder.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// FakeS2iBuilders implements S2iBuilderInterface +type FakeS2iBuilders struct { + Fake *FakeDevopsV1alpha1 + ns string +} + +var s2ibuildersResource = schema.GroupVersionResource{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"} + +var s2ibuildersKind = schema.GroupVersionKind{Group: "devops.kubesphere.io", Version: "v1alpha1", Kind: "S2iBuilder"} + +// Get takes name of the s2iBuilder, and returns the corresponding s2iBuilder object, and an error if there is any. +func (c *FakeS2iBuilders) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilder, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(s2ibuildersResource, c.ns, name), &v1alpha1.S2iBuilder{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilder), err +} + +// List takes label and field selectors, and returns the list of S2iBuilders that match those selectors. +func (c *FakeS2iBuilders) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(s2ibuildersResource, s2ibuildersKind, c.ns, opts), &v1alpha1.S2iBuilderList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.S2iBuilderList{ListMeta: obj.(*v1alpha1.S2iBuilderList).ListMeta} + for _, item := range obj.(*v1alpha1.S2iBuilderList).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 s2iBuilders. +func (c *FakeS2iBuilders) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(s2ibuildersResource, c.ns, opts)) + +} + +// Create takes the representation of a s2iBuilder and creates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. +func (c *FakeS2iBuilders) Create(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(s2ibuildersResource, c.ns, s2iBuilder), &v1alpha1.S2iBuilder{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilder), err +} + +// Update takes the representation of a s2iBuilder and updates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. +func (c *FakeS2iBuilders) Update(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(s2ibuildersResource, c.ns, s2iBuilder), &v1alpha1.S2iBuilder{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilder), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeS2iBuilders) UpdateStatus(s2iBuilder *v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(s2ibuildersResource, "status", c.ns, s2iBuilder), &v1alpha1.S2iBuilder{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilder), err +} + +// Delete takes name of the s2iBuilder and deletes it. Returns an error if one occurs. +func (c *FakeS2iBuilders) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(s2ibuildersResource, c.ns, name), &v1alpha1.S2iBuilder{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeS2iBuilders) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(s2ibuildersResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.S2iBuilderList{}) + return err +} + +// Patch applies the patch and returns the patched s2iBuilder. +func (c *FakeS2iBuilders) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilder, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(s2ibuildersResource, c.ns, name, pt, data, subresources...), &v1alpha1.S2iBuilder{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilder), err +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuildertemplate.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuildertemplate.go new file mode 100644 index 000000000..372d4f05a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2ibuildertemplate.go @@ -0,0 +1,131 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// FakeS2iBuilderTemplates implements S2iBuilderTemplateInterface +type FakeS2iBuilderTemplates struct { + Fake *FakeDevopsV1alpha1 +} + +var s2ibuildertemplatesResource = schema.GroupVersionResource{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuildertemplates"} + +var s2ibuildertemplatesKind = schema.GroupVersionKind{Group: "devops.kubesphere.io", Version: "v1alpha1", Kind: "S2iBuilderTemplate"} + +// Get takes name of the s2iBuilderTemplate, and returns the corresponding s2iBuilderTemplate object, and an error if there is any. +func (c *FakeS2iBuilderTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilderTemplate, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(s2ibuildertemplatesResource, name), &v1alpha1.S2iBuilderTemplate{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilderTemplate), err +} + +// List takes label and field selectors, and returns the list of S2iBuilderTemplates that match those selectors. +func (c *FakeS2iBuilderTemplates) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderTemplateList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(s2ibuildertemplatesResource, s2ibuildertemplatesKind, opts), &v1alpha1.S2iBuilderTemplateList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.S2iBuilderTemplateList{ListMeta: obj.(*v1alpha1.S2iBuilderTemplateList).ListMeta} + for _, item := range obj.(*v1alpha1.S2iBuilderTemplateList).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 s2iBuilderTemplates. +func (c *FakeS2iBuilderTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(s2ibuildertemplatesResource, opts)) +} + +// Create takes the representation of a s2iBuilderTemplate and creates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. +func (c *FakeS2iBuilderTemplates) Create(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(s2ibuildertemplatesResource, s2iBuilderTemplate), &v1alpha1.S2iBuilderTemplate{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilderTemplate), err +} + +// Update takes the representation of a s2iBuilderTemplate and updates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. +func (c *FakeS2iBuilderTemplates) Update(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(s2ibuildertemplatesResource, s2iBuilderTemplate), &v1alpha1.S2iBuilderTemplate{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilderTemplate), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeS2iBuilderTemplates) UpdateStatus(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(s2ibuildertemplatesResource, "status", s2iBuilderTemplate), &v1alpha1.S2iBuilderTemplate{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilderTemplate), err +} + +// Delete takes name of the s2iBuilderTemplate and deletes it. Returns an error if one occurs. +func (c *FakeS2iBuilderTemplates) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(s2ibuildertemplatesResource, name), &v1alpha1.S2iBuilderTemplate{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeS2iBuilderTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(s2ibuildertemplatesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.S2iBuilderTemplateList{}) + return err +} + +// Patch applies the patch and returns the patched s2iBuilderTemplate. +func (c *FakeS2iBuilderTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilderTemplate, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(s2ibuildertemplatesResource, name, pt, data, subresources...), &v1alpha1.S2iBuilderTemplate{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iBuilderTemplate), err +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2irun.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2irun.go new file mode 100644 index 000000000..d3bf81db5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/fake/fake_s2irun.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// FakeS2iRuns implements S2iRunInterface +type FakeS2iRuns struct { + Fake *FakeDevopsV1alpha1 + ns string +} + +var s2irunsResource = schema.GroupVersionResource{Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2iruns"} + +var s2irunsKind = schema.GroupVersionKind{Group: "devops.kubesphere.io", Version: "v1alpha1", Kind: "S2iRun"} + +// Get takes name of the s2iRun, and returns the corresponding s2iRun object, and an error if there is any. +func (c *FakeS2iRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(s2irunsResource, c.ns, name), &v1alpha1.S2iRun{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iRun), err +} + +// List takes label and field selectors, and returns the list of S2iRuns that match those selectors. +func (c *FakeS2iRuns) List(opts v1.ListOptions) (result *v1alpha1.S2iRunList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(s2irunsResource, s2irunsKind, c.ns, opts), &v1alpha1.S2iRunList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.S2iRunList{ListMeta: obj.(*v1alpha1.S2iRunList).ListMeta} + for _, item := range obj.(*v1alpha1.S2iRunList).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 s2iRuns. +func (c *FakeS2iRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(s2irunsResource, c.ns, opts)) + +} + +// Create takes the representation of a s2iRun and creates it. Returns the server's representation of the s2iRun, and an error, if there is any. +func (c *FakeS2iRuns) Create(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(s2irunsResource, c.ns, s2iRun), &v1alpha1.S2iRun{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iRun), err +} + +// Update takes the representation of a s2iRun and updates it. Returns the server's representation of the s2iRun, and an error, if there is any. +func (c *FakeS2iRuns) Update(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(s2irunsResource, c.ns, s2iRun), &v1alpha1.S2iRun{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iRun), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeS2iRuns) UpdateStatus(s2iRun *v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(s2irunsResource, "status", c.ns, s2iRun), &v1alpha1.S2iRun{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iRun), err +} + +// Delete takes name of the s2iRun and deletes it. Returns an error if one occurs. +func (c *FakeS2iRuns) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(s2irunsResource, c.ns, name), &v1alpha1.S2iRun{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeS2iRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(s2irunsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.S2iRunList{}) + return err +} + +// Patch applies the patch and returns the patched s2iRun. +func (c *FakeS2iRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(s2irunsResource, c.ns, name, pt, data, subresources...), &v1alpha1.S2iRun{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.S2iRun), err +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go index 6e2c4ea31..df67162b9 100644 --- a/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go @@ -19,3 +19,9 @@ limitations under the License. package v1alpha1 type S2iBinaryExpansion interface{} + +type S2iBuilderExpansion interface{} + +type S2iBuilderTemplateExpansion interface{} + +type S2iRunExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go new file mode 100644 index 000000000..34b37a777 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go @@ -0,0 +1,191 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// S2iBuildersGetter has a method to return a S2iBuilderInterface. +// A group's client should implement this interface. +type S2iBuildersGetter interface { + S2iBuilders(namespace string) S2iBuilderInterface +} + +// S2iBuilderInterface has methods to work with S2iBuilder resources. +type S2iBuilderInterface interface { + Create(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) + Update(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) + UpdateStatus(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.S2iBuilder, error) + List(opts v1.ListOptions) (*v1alpha1.S2iBuilderList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilder, err error) + S2iBuilderExpansion +} + +// s2iBuilders implements S2iBuilderInterface +type s2iBuilders struct { + client rest.Interface + ns string +} + +// newS2iBuilders returns a S2iBuilders +func newS2iBuilders(c *DevopsV1alpha1Client, namespace string) *s2iBuilders { + return &s2iBuilders{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the s2iBuilder, and returns the corresponding s2iBuilder object, and an error if there is any. +func (c *s2iBuilders) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilder, err error) { + result = &v1alpha1.S2iBuilder{} + err = c.client.Get(). + Namespace(c.ns). + Resource("s2ibuilders"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of S2iBuilders that match those selectors. +func (c *s2iBuilders) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.S2iBuilderList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("s2ibuilders"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested s2iBuilders. +func (c *s2iBuilders) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("s2ibuilders"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a s2iBuilder and creates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. +func (c *s2iBuilders) Create(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { + result = &v1alpha1.S2iBuilder{} + err = c.client.Post(). + Namespace(c.ns). + Resource("s2ibuilders"). + Body(s2iBuilder). + Do(). + Into(result) + return +} + +// Update takes the representation of a s2iBuilder and updates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. +func (c *s2iBuilders) Update(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { + result = &v1alpha1.S2iBuilder{} + err = c.client.Put(). + Namespace(c.ns). + Resource("s2ibuilders"). + Name(s2iBuilder.Name). + Body(s2iBuilder). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *s2iBuilders) UpdateStatus(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { + result = &v1alpha1.S2iBuilder{} + err = c.client.Put(). + Namespace(c.ns). + Resource("s2ibuilders"). + Name(s2iBuilder.Name). + SubResource("status"). + Body(s2iBuilder). + Do(). + Into(result) + return +} + +// Delete takes name of the s2iBuilder and deletes it. Returns an error if one occurs. +func (c *s2iBuilders) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("s2ibuilders"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *s2iBuilders) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("s2ibuilders"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched s2iBuilder. +func (c *s2iBuilders) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilder, err error) { + result = &v1alpha1.S2iBuilder{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("s2ibuilders"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go new file mode 100644 index 000000000..ee6ceacb3 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// S2iBuilderTemplatesGetter has a method to return a S2iBuilderTemplateInterface. +// A group's client should implement this interface. +type S2iBuilderTemplatesGetter interface { + S2iBuilderTemplates() S2iBuilderTemplateInterface +} + +// S2iBuilderTemplateInterface has methods to work with S2iBuilderTemplate resources. +type S2iBuilderTemplateInterface interface { + Create(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) + Update(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) + UpdateStatus(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.S2iBuilderTemplate, error) + List(opts v1.ListOptions) (*v1alpha1.S2iBuilderTemplateList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilderTemplate, err error) + S2iBuilderTemplateExpansion +} + +// s2iBuilderTemplates implements S2iBuilderTemplateInterface +type s2iBuilderTemplates struct { + client rest.Interface +} + +// newS2iBuilderTemplates returns a S2iBuilderTemplates +func newS2iBuilderTemplates(c *DevopsV1alpha1Client) *s2iBuilderTemplates { + return &s2iBuilderTemplates{ + client: c.RESTClient(), + } +} + +// Get takes name of the s2iBuilderTemplate, and returns the corresponding s2iBuilderTemplate object, and an error if there is any. +func (c *s2iBuilderTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilderTemplate, err error) { + result = &v1alpha1.S2iBuilderTemplate{} + err = c.client.Get(). + Resource("s2ibuildertemplates"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of S2iBuilderTemplates that match those selectors. +func (c *s2iBuilderTemplates) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderTemplateList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.S2iBuilderTemplateList{} + err = c.client.Get(). + Resource("s2ibuildertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested s2iBuilderTemplates. +func (c *s2iBuilderTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("s2ibuildertemplates"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a s2iBuilderTemplate and creates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. +func (c *s2iBuilderTemplates) Create(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { + result = &v1alpha1.S2iBuilderTemplate{} + err = c.client.Post(). + Resource("s2ibuildertemplates"). + Body(s2iBuilderTemplate). + Do(). + Into(result) + return +} + +// Update takes the representation of a s2iBuilderTemplate and updates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. +func (c *s2iBuilderTemplates) Update(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { + result = &v1alpha1.S2iBuilderTemplate{} + err = c.client.Put(). + Resource("s2ibuildertemplates"). + Name(s2iBuilderTemplate.Name). + Body(s2iBuilderTemplate). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *s2iBuilderTemplates) UpdateStatus(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { + result = &v1alpha1.S2iBuilderTemplate{} + err = c.client.Put(). + Resource("s2ibuildertemplates"). + Name(s2iBuilderTemplate.Name). + SubResource("status"). + Body(s2iBuilderTemplate). + Do(). + Into(result) + return +} + +// Delete takes name of the s2iBuilderTemplate and deletes it. Returns an error if one occurs. +func (c *s2iBuilderTemplates) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("s2ibuildertemplates"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *s2iBuilderTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("s2ibuildertemplates"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched s2iBuilderTemplate. +func (c *s2iBuilderTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilderTemplate, err error) { + result = &v1alpha1.S2iBuilderTemplate{} + err = c.client.Patch(pt). + Resource("s2ibuildertemplates"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go new file mode 100644 index 000000000..c7eb01513 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go @@ -0,0 +1,191 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "time" + + 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" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// S2iRunsGetter has a method to return a S2iRunInterface. +// A group's client should implement this interface. +type S2iRunsGetter interface { + S2iRuns(namespace string) S2iRunInterface +} + +// S2iRunInterface has methods to work with S2iRun resources. +type S2iRunInterface interface { + Create(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) + Update(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) + UpdateStatus(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.S2iRun, error) + List(opts v1.ListOptions) (*v1alpha1.S2iRunList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iRun, err error) + S2iRunExpansion +} + +// s2iRuns implements S2iRunInterface +type s2iRuns struct { + client rest.Interface + ns string +} + +// newS2iRuns returns a S2iRuns +func newS2iRuns(c *DevopsV1alpha1Client, namespace string) *s2iRuns { + return &s2iRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the s2iRun, and returns the corresponding s2iRun object, and an error if there is any. +func (c *s2iRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iRun, err error) { + result = &v1alpha1.S2iRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("s2iruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of S2iRuns that match those selectors. +func (c *s2iRuns) List(opts v1.ListOptions) (result *v1alpha1.S2iRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.S2iRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("s2iruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested s2iRuns. +func (c *s2iRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("s2iruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a s2iRun and creates it. Returns the server's representation of the s2iRun, and an error, if there is any. +func (c *s2iRuns) Create(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { + result = &v1alpha1.S2iRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("s2iruns"). + Body(s2iRun). + Do(). + Into(result) + return +} + +// Update takes the representation of a s2iRun and updates it. Returns the server's representation of the s2iRun, and an error, if there is any. +func (c *s2iRuns) Update(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { + result = &v1alpha1.S2iRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("s2iruns"). + Name(s2iRun.Name). + Body(s2iRun). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *s2iRuns) UpdateStatus(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { + result = &v1alpha1.S2iRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("s2iruns"). + Name(s2iRun.Name). + SubResource("status"). + Body(s2iRun). + Do(). + Into(result) + return +} + +// Delete takes name of the s2iRun and deletes it. Returns an error if one occurs. +func (c *s2iRuns) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("s2iruns"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *s2iRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("s2iruns"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched s2iRun. +func (c *s2iRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iRun, err error) { + result = &v1alpha1.S2iRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("s2iruns"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/devops_client.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/devops_client.go new file mode 100644 index 000000000..8884644db --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/devops_client.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + rest "k8s.io/client-go/rest" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +type DevopsV1alpha3Interface interface { + RESTClient() rest.Interface + DevOpsProjectsGetter + PipelinesGetter +} + +// DevopsV1alpha3Client is used to interact with features provided by the devops.kubesphere.io group. +type DevopsV1alpha3Client struct { + restClient rest.Interface +} + +func (c *DevopsV1alpha3Client) DevOpsProjects() DevOpsProjectInterface { + return newDevOpsProjects(c) +} + +func (c *DevopsV1alpha3Client) Pipelines(namespace string) PipelineInterface { + return newPipelines(c, namespace) +} + +// NewForConfig creates a new DevopsV1alpha3Client for the given config. +func NewForConfig(c *rest.Config) (*DevopsV1alpha3Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &DevopsV1alpha3Client{client}, nil +} + +// NewForConfigOrDie creates a new DevopsV1alpha3Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *DevopsV1alpha3Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new DevopsV1alpha3Client for the given RESTClient. +func New(c rest.Interface) *DevopsV1alpha3Client { + return &DevopsV1alpha3Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha3.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + 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 *DevopsV1alpha3Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/devopsproject.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/devopsproject.go new file mode 100644 index 000000000..f923e6e9d --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/devopsproject.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + "time" + + 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" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// DevOpsProjectsGetter has a method to return a DevOpsProjectInterface. +// A group's client should implement this interface. +type DevOpsProjectsGetter interface { + DevOpsProjects() DevOpsProjectInterface +} + +// DevOpsProjectInterface has methods to work with DevOpsProject resources. +type DevOpsProjectInterface interface { + Create(*v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) + Update(*v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) + UpdateStatus(*v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha3.DevOpsProject, error) + List(opts v1.ListOptions) (*v1alpha3.DevOpsProjectList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DevOpsProject, err error) + DevOpsProjectExpansion +} + +// devOpsProjects implements DevOpsProjectInterface +type devOpsProjects struct { + client rest.Interface +} + +// newDevOpsProjects returns a DevOpsProjects +func newDevOpsProjects(c *DevopsV1alpha3Client) *devOpsProjects { + return &devOpsProjects{ + client: c.RESTClient(), + } +} + +// Get takes name of the devOpsProject, and returns the corresponding devOpsProject object, and an error if there is any. +func (c *devOpsProjects) Get(name string, options v1.GetOptions) (result *v1alpha3.DevOpsProject, err error) { + result = &v1alpha3.DevOpsProject{} + err = c.client.Get(). + Resource("devopsprojects"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of DevOpsProjects that match those selectors. +func (c *devOpsProjects) List(opts v1.ListOptions) (result *v1alpha3.DevOpsProjectList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha3.DevOpsProjectList{} + err = c.client.Get(). + Resource("devopsprojects"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested devOpsProjects. +func (c *devOpsProjects) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("devopsprojects"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a devOpsProject and creates it. Returns the server's representation of the devOpsProject, and an error, if there is any. +func (c *devOpsProjects) Create(devOpsProject *v1alpha3.DevOpsProject) (result *v1alpha3.DevOpsProject, err error) { + result = &v1alpha3.DevOpsProject{} + err = c.client.Post(). + Resource("devopsprojects"). + Body(devOpsProject). + Do(). + Into(result) + return +} + +// Update takes the representation of a devOpsProject and updates it. Returns the server's representation of the devOpsProject, and an error, if there is any. +func (c *devOpsProjects) Update(devOpsProject *v1alpha3.DevOpsProject) (result *v1alpha3.DevOpsProject, err error) { + result = &v1alpha3.DevOpsProject{} + err = c.client.Put(). + Resource("devopsprojects"). + Name(devOpsProject.Name). + Body(devOpsProject). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *devOpsProjects) UpdateStatus(devOpsProject *v1alpha3.DevOpsProject) (result *v1alpha3.DevOpsProject, err error) { + result = &v1alpha3.DevOpsProject{} + err = c.client.Put(). + Resource("devopsprojects"). + Name(devOpsProject.Name). + SubResource("status"). + Body(devOpsProject). + Do(). + Into(result) + return +} + +// Delete takes name of the devOpsProject and deletes it. Returns an error if one occurs. +func (c *devOpsProjects) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("devopsprojects"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *devOpsProjects) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("devopsprojects"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched devOpsProject. +func (c *devOpsProjects) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DevOpsProject, err error) { + result = &v1alpha3.DevOpsProject{} + err = c.client.Patch(pt). + Resource("devopsprojects"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/doc.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/doc.go new file mode 100644 index 000000000..a4ae49dd1 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha3 diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/doc.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/doc.go new file mode 100644 index 000000000..329c98fb5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devops_client.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devops_client.go new file mode 100644 index 000000000..ba7a1ebc0 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devops_client.go @@ -0,0 +1,44 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha3 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/devops/v1alpha3" +) + +type FakeDevopsV1alpha3 struct { + *testing.Fake +} + +func (c *FakeDevopsV1alpha3) DevOpsProjects() v1alpha3.DevOpsProjectInterface { + return &FakeDevOpsProjects{c} +} + +func (c *FakeDevopsV1alpha3) Pipelines(namespace string) v1alpha3.PipelineInterface { + return &FakePipelines{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeDevopsV1alpha3) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devopsproject.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devopsproject.go new file mode 100644 index 000000000..1fb7fe594 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_devopsproject.go @@ -0,0 +1,131 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +// FakeDevOpsProjects implements DevOpsProjectInterface +type FakeDevOpsProjects struct { + Fake *FakeDevopsV1alpha3 +} + +var devopsprojectsResource = schema.GroupVersionResource{Group: "devops.kubesphere.io", Version: "v1alpha3", Resource: "devopsprojects"} + +var devopsprojectsKind = schema.GroupVersionKind{Group: "devops.kubesphere.io", Version: "v1alpha3", Kind: "DevOpsProject"} + +// Get takes name of the devOpsProject, and returns the corresponding devOpsProject object, and an error if there is any. +func (c *FakeDevOpsProjects) Get(name string, options v1.GetOptions) (result *v1alpha3.DevOpsProject, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(devopsprojectsResource, name), &v1alpha3.DevOpsProject{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.DevOpsProject), err +} + +// List takes label and field selectors, and returns the list of DevOpsProjects that match those selectors. +func (c *FakeDevOpsProjects) List(opts v1.ListOptions) (result *v1alpha3.DevOpsProjectList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(devopsprojectsResource, devopsprojectsKind, opts), &v1alpha3.DevOpsProjectList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha3.DevOpsProjectList{ListMeta: obj.(*v1alpha3.DevOpsProjectList).ListMeta} + for _, item := range obj.(*v1alpha3.DevOpsProjectList).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 devOpsProjects. +func (c *FakeDevOpsProjects) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(devopsprojectsResource, opts)) +} + +// Create takes the representation of a devOpsProject and creates it. Returns the server's representation of the devOpsProject, and an error, if there is any. +func (c *FakeDevOpsProjects) Create(devOpsProject *v1alpha3.DevOpsProject) (result *v1alpha3.DevOpsProject, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(devopsprojectsResource, devOpsProject), &v1alpha3.DevOpsProject{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.DevOpsProject), err +} + +// Update takes the representation of a devOpsProject and updates it. Returns the server's representation of the devOpsProject, and an error, if there is any. +func (c *FakeDevOpsProjects) Update(devOpsProject *v1alpha3.DevOpsProject) (result *v1alpha3.DevOpsProject, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(devopsprojectsResource, devOpsProject), &v1alpha3.DevOpsProject{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.DevOpsProject), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeDevOpsProjects) UpdateStatus(devOpsProject *v1alpha3.DevOpsProject) (*v1alpha3.DevOpsProject, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(devopsprojectsResource, "status", devOpsProject), &v1alpha3.DevOpsProject{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.DevOpsProject), err +} + +// Delete takes name of the devOpsProject and deletes it. Returns an error if one occurs. +func (c *FakeDevOpsProjects) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(devopsprojectsResource, name), &v1alpha3.DevOpsProject{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeDevOpsProjects) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(devopsprojectsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha3.DevOpsProjectList{}) + return err +} + +// Patch applies the patch and returns the patched devOpsProject. +func (c *FakeDevOpsProjects) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.DevOpsProject, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(devopsprojectsResource, name, pt, data, subresources...), &v1alpha3.DevOpsProject{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.DevOpsProject), err +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_pipeline.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_pipeline.go new file mode 100644 index 000000000..5622bc57f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/fake/fake_pipeline.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +// FakePipelines implements PipelineInterface +type FakePipelines struct { + Fake *FakeDevopsV1alpha3 + ns string +} + +var pipelinesResource = schema.GroupVersionResource{Group: "devops.kubesphere.io", Version: "v1alpha3", Resource: "pipelines"} + +var pipelinesKind = schema.GroupVersionKind{Group: "devops.kubesphere.io", Version: "v1alpha3", Kind: "Pipeline"} + +// Get takes name of the pipeline, and returns the corresponding pipeline object, and an error if there is any. +func (c *FakePipelines) Get(name string, options v1.GetOptions) (result *v1alpha3.Pipeline, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(pipelinesResource, c.ns, name), &v1alpha3.Pipeline{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.Pipeline), err +} + +// List takes label and field selectors, and returns the list of Pipelines that match those selectors. +func (c *FakePipelines) List(opts v1.ListOptions) (result *v1alpha3.PipelineList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(pipelinesResource, pipelinesKind, c.ns, opts), &v1alpha3.PipelineList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha3.PipelineList{ListMeta: obj.(*v1alpha3.PipelineList).ListMeta} + for _, item := range obj.(*v1alpha3.PipelineList).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 pipelines. +func (c *FakePipelines) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(pipelinesResource, c.ns, opts)) + +} + +// Create takes the representation of a pipeline and creates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *FakePipelines) Create(pipeline *v1alpha3.Pipeline) (result *v1alpha3.Pipeline, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(pipelinesResource, c.ns, pipeline), &v1alpha3.Pipeline{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.Pipeline), err +} + +// Update takes the representation of a pipeline and updates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *FakePipelines) Update(pipeline *v1alpha3.Pipeline) (result *v1alpha3.Pipeline, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(pipelinesResource, c.ns, pipeline), &v1alpha3.Pipeline{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.Pipeline), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakePipelines) UpdateStatus(pipeline *v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(pipelinesResource, "status", c.ns, pipeline), &v1alpha3.Pipeline{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.Pipeline), err +} + +// Delete takes name of the pipeline and deletes it. Returns an error if one occurs. +func (c *FakePipelines) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(pipelinesResource, c.ns, name), &v1alpha3.Pipeline{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakePipelines) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(pipelinesResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha3.PipelineList{}) + return err +} + +// Patch applies the patch and returns the patched pipeline. +func (c *FakePipelines) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Pipeline, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(pipelinesResource, c.ns, name, pt, data, subresources...), &v1alpha3.Pipeline{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha3.Pipeline), err +} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/generated_expansion.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/generated_expansion.go new file mode 100644 index 000000000..e854e495d --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/generated_expansion.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha3 + +type DevOpsProjectExpansion interface{} + +type PipelineExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/devops/v1alpha3/pipeline.go b/pkg/client/clientset/versioned/typed/devops/v1alpha3/pipeline.go new file mode 100644 index 000000000..675e4bfbf --- /dev/null +++ b/pkg/client/clientset/versioned/typed/devops/v1alpha3/pipeline.go @@ -0,0 +1,191 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + "time" + + 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" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// PipelinesGetter has a method to return a PipelineInterface. +// A group's client should implement this interface. +type PipelinesGetter interface { + Pipelines(namespace string) PipelineInterface +} + +// PipelineInterface has methods to work with Pipeline resources. +type PipelineInterface interface { + Create(*v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) + Update(*v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) + UpdateStatus(*v1alpha3.Pipeline) (*v1alpha3.Pipeline, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha3.Pipeline, error) + List(opts v1.ListOptions) (*v1alpha3.PipelineList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Pipeline, err error) + PipelineExpansion +} + +// pipelines implements PipelineInterface +type pipelines struct { + client rest.Interface + ns string +} + +// newPipelines returns a Pipelines +func newPipelines(c *DevopsV1alpha3Client, namespace string) *pipelines { + return &pipelines{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the pipeline, and returns the corresponding pipeline object, and an error if there is any. +func (c *pipelines) Get(name string, options v1.GetOptions) (result *v1alpha3.Pipeline, err error) { + result = &v1alpha3.Pipeline{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Pipelines that match those selectors. +func (c *pipelines) List(opts v1.ListOptions) (result *v1alpha3.PipelineList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha3.PipelineList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested pipelines. +func (c *pipelines) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a pipeline and creates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Create(pipeline *v1alpha3.Pipeline) (result *v1alpha3.Pipeline, err error) { + result = &v1alpha3.Pipeline{} + err = c.client.Post(). + Namespace(c.ns). + Resource("pipelines"). + Body(pipeline). + Do(). + Into(result) + return +} + +// Update takes the representation of a pipeline and updates it. Returns the server's representation of the pipeline, and an error, if there is any. +func (c *pipelines) Update(pipeline *v1alpha3.Pipeline) (result *v1alpha3.Pipeline, err error) { + result = &v1alpha3.Pipeline{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelines"). + Name(pipeline.Name). + Body(pipeline). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *pipelines) UpdateStatus(pipeline *v1alpha3.Pipeline) (result *v1alpha3.Pipeline, err error) { + result = &v1alpha3.Pipeline{} + err = c.client.Put(). + Namespace(c.ns). + Resource("pipelines"). + Name(pipeline.Name). + SubResource("status"). + Body(pipeline). + Do(). + Into(result) + return +} + +// Delete takes name of the pipeline and deletes it. Returns an error if one occurs. +func (c *pipelines) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *pipelines) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("pipelines"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched pipeline. +func (c *pipelines) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Pipeline, err error) { + result = &v1alpha3.Pipeline{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("pipelines"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/doc.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/doc.go new file mode 100644 index 000000000..02d20203a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha2 diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/doc.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/doc.go new file mode 100644 index 000000000..329c98fb5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go new file mode 100644 index 000000000..5393ec2d2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoles implements GlobalRoleInterface +type FakeGlobalRoles struct { + Fake *FakeIamV1alpha2 +} + +var globalrolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalroles"} + +var globalrolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRole"} + +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *FakeGlobalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *FakeGlobalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolesResource, globalrolesKind, opts), &v1alpha2.GlobalRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleList{ListMeta: obj.(*v1alpha2.GlobalRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleList).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 globalRoles. +func (c *FakeGlobalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolesResource, opts)) +} + +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *FakeGlobalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolesResource, globalRole), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} + +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolesResource, name), &v1alpha2.GlobalRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleList{}) + return err +} + +// Patch applies the patch and returns the patched globalRole. +func (c *FakeGlobalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolesResource, name, pt, data, subresources...), &v1alpha2.GlobalRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go new file mode 100644 index 000000000..161de682a --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_globalrolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeGlobalRoleBindings implements GlobalRoleBindingInterface +type FakeGlobalRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var globalrolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "globalrolebindings"} + +var globalrolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "GlobalRoleBinding"} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *FakeGlobalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *FakeGlobalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(globalrolebindingsResource, globalrolebindingsKind, opts), &v1alpha2.GlobalRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.GlobalRoleBindingList{ListMeta: obj.(*v1alpha2.GlobalRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.GlobalRoleBindingList).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 globalRoleBindings. +func (c *FakeGlobalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(globalrolebindingsResource, opts)) +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *FakeGlobalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(globalrolebindingsResource, globalRoleBinding), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeGlobalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(globalrolebindingsResource, name), &v1alpha2.GlobalRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeGlobalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(globalrolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.GlobalRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *FakeGlobalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(globalrolebindingsResource, name, pt, data, subresources...), &v1alpha2.GlobalRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.GlobalRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go new file mode 100644 index 000000000..ce41b4a89 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_iam_client.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/iam/v1alpha2" +) + +type FakeIamV1alpha2 struct { + *testing.Fake +} + +func (c *FakeIamV1alpha2) GlobalRoles() v1alpha2.GlobalRoleInterface { + return &FakeGlobalRoles{c} +} + +func (c *FakeIamV1alpha2) GlobalRoleBindings() v1alpha2.GlobalRoleBindingInterface { + return &FakeGlobalRoleBindings{c} +} + +func (c *FakeIamV1alpha2) Users() v1alpha2.UserInterface { + return &FakeUsers{c} +} + +func (c *FakeIamV1alpha2) WorkspaceRoles() v1alpha2.WorkspaceRoleInterface { + return &FakeWorkspaceRoles{c} +} + +func (c *FakeIamV1alpha2) WorkspaceRoleBindings() v1alpha2.WorkspaceRoleBindingInterface { + return &FakeWorkspaceRoleBindings{c} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeIamV1alpha2) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_user.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_user.go new file mode 100644 index 000000000..f8a507714 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_user.go @@ -0,0 +1,131 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeUsers implements UserInterface +type FakeUsers struct { + Fake *FakeIamV1alpha2 +} + +var usersResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "users"} + +var usersKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "User"} + +// Get takes name of the user, and returns the corresponding user object, and an error if there is any. +func (c *FakeUsers) Get(name string, options v1.GetOptions) (result *v1alpha2.User, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(usersResource, name), &v1alpha2.User{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.User), err +} + +// List takes label and field selectors, and returns the list of Users that match those selectors. +func (c *FakeUsers) List(opts v1.ListOptions) (result *v1alpha2.UserList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(usersResource, usersKind, opts), &v1alpha2.UserList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.UserList{ListMeta: obj.(*v1alpha2.UserList).ListMeta} + for _, item := range obj.(*v1alpha2.UserList).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 users. +func (c *FakeUsers) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(usersResource, opts)) +} + +// Create takes the representation of a user and creates it. Returns the server's representation of the user, and an error, if there is any. +func (c *FakeUsers) Create(user *v1alpha2.User) (result *v1alpha2.User, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(usersResource, user), &v1alpha2.User{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.User), err +} + +// Update takes the representation of a user and updates it. Returns the server's representation of the user, and an error, if there is any. +func (c *FakeUsers) Update(user *v1alpha2.User) (result *v1alpha2.User, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(usersResource, user), &v1alpha2.User{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.User), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeUsers) UpdateStatus(user *v1alpha2.User) (*v1alpha2.User, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(usersResource, "status", user), &v1alpha2.User{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.User), err +} + +// Delete takes name of the user and deletes it. Returns an error if one occurs. +func (c *FakeUsers) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(usersResource, name), &v1alpha2.User{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeUsers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(usersResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.UserList{}) + return err +} + +// Patch applies the patch and returns the patched user. +func (c *FakeUsers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(usersResource, name, pt, data, subresources...), &v1alpha2.User{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.User), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go new file mode 100644 index 000000000..b7f81ed2e --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerole.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoles implements WorkspaceRoleInterface +type FakeWorkspaceRoles struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolesResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspaceroles"} + +var workspacerolesKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRole"} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *FakeWorkspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *FakeWorkspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolesResource, workspacerolesKind, opts), &v1alpha2.WorkspaceRoleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleList{ListMeta: obj.(*v1alpha2.WorkspaceRoleList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleList).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 workspaceRoles. +func (c *FakeWorkspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolesResource, opts)) +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *FakeWorkspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolesResource, workspaceRole), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolesResource, name), &v1alpha2.WorkspaceRole{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolesResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *FakeWorkspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolesResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRole{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRole), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go new file mode 100644 index 000000000..96504cdcb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/fake/fake_workspacerolebinding.go @@ -0,0 +1,120 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package fake + +import ( + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// FakeWorkspaceRoleBindings implements WorkspaceRoleBindingInterface +type FakeWorkspaceRoleBindings struct { + Fake *FakeIamV1alpha2 +} + +var workspacerolebindingsResource = schema.GroupVersionResource{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "workspacerolebindings"} + +var workspacerolebindingsKind = schema.GroupVersionKind{Group: "iam.kubesphere.io", Version: "v1alpha2", Kind: "WorkspaceRoleBinding"} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *FakeWorkspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *FakeWorkspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(workspacerolebindingsResource, workspacerolebindingsKind, opts), &v1alpha2.WorkspaceRoleBindingList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.WorkspaceRoleBindingList{ListMeta: obj.(*v1alpha2.WorkspaceRoleBindingList).ListMeta} + for _, item := range obj.(*v1alpha2.WorkspaceRoleBindingList).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 workspaceRoleBindings. +func (c *FakeWorkspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(workspacerolebindingsResource, opts)) +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *FakeWorkspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(workspacerolebindingsResource, workspaceRoleBinding), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *FakeWorkspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(workspacerolebindingsResource, name), &v1alpha2.WorkspaceRoleBinding{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeWorkspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(workspacerolebindingsResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha2.WorkspaceRoleBindingList{}) + return err +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *FakeWorkspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(workspacerolebindingsResource, name, pt, data, subresources...), &v1alpha2.WorkspaceRoleBinding{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.WorkspaceRoleBinding), err +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go new file mode 100644 index 000000000..edc5b88b5 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/generated_expansion.go @@ -0,0 +1,29 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +type GlobalRoleExpansion interface{} + +type GlobalRoleBindingExpansion interface{} + +type UserExpansion interface{} + +type WorkspaceRoleExpansion interface{} + +type WorkspaceRoleBindingExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go new file mode 100644 index 000000000..ae6e9eeb3 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrole.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// GlobalRolesGetter has a method to return a GlobalRoleInterface. +// A group's client should implement this interface. +type GlobalRolesGetter interface { + GlobalRoles() GlobalRoleInterface +} + +// GlobalRoleInterface has methods to work with GlobalRole resources. +type GlobalRoleInterface interface { + Create(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) + Update(*v1alpha2.GlobalRole) (*v1alpha2.GlobalRole, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRole, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) + GlobalRoleExpansion +} + +// globalRoles implements GlobalRoleInterface +type globalRoles struct { + client rest.Interface +} + +// newGlobalRoles returns a GlobalRoles +func newGlobalRoles(c *IamV1alpha2Client) *globalRoles { + return &globalRoles{ + client: c.RESTClient(), + } +} + +// Get takes name of the globalRole, and returns the corresponding globalRole object, and an error if there is any. +func (c *globalRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} + err = c.client.Get(). + Resource("globalroles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of GlobalRoles that match those selectors. +func (c *globalRoles) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.GlobalRoleList{} + err = c.client.Get(). + Resource("globalroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested globalRoles. +func (c *globalRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("globalroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a globalRole and creates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Create(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} + err = c.client.Post(). + Resource("globalroles"). + Body(globalRole). + Do(). + Into(result) + return +} + +// Update takes the representation of a globalRole and updates it. Returns the server's representation of the globalRole, and an error, if there is any. +func (c *globalRoles) Update(globalRole *v1alpha2.GlobalRole) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} + err = c.client.Put(). + Resource("globalroles"). + Name(globalRole.Name). + Body(globalRole). + Do(). + Into(result) + return +} + +// Delete takes name of the globalRole and deletes it. Returns an error if one occurs. +func (c *globalRoles) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("globalroles"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *globalRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("globalroles"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched globalRole. +func (c *globalRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRole, err error) { + result = &v1alpha2.GlobalRole{} + err = c.client.Patch(pt). + Resource("globalroles"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 000000000..8fdec09aa --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// GlobalRoleBindingsGetter has a method to return a GlobalRoleBindingInterface. +// A group's client should implement this interface. +type GlobalRoleBindingsGetter interface { + GlobalRoleBindings() GlobalRoleBindingInterface +} + +// GlobalRoleBindingInterface has methods to work with GlobalRoleBinding resources. +type GlobalRoleBindingInterface interface { + Create(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Update(*v1alpha2.GlobalRoleBinding) (*v1alpha2.GlobalRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.GlobalRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.GlobalRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) + GlobalRoleBindingExpansion +} + +// globalRoleBindings implements GlobalRoleBindingInterface +type globalRoleBindings struct { + client rest.Interface +} + +// newGlobalRoleBindings returns a GlobalRoleBindings +func newGlobalRoleBindings(c *IamV1alpha2Client) *globalRoleBindings { + return &globalRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the globalRoleBinding, and returns the corresponding globalRoleBinding object, and an error if there is any. +func (c *globalRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Get(). + Resource("globalrolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of GlobalRoleBindings that match those selectors. +func (c *globalRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.GlobalRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.GlobalRoleBindingList{} + err = c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested globalRoleBindings. +func (c *globalRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("globalrolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a globalRoleBinding and creates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Create(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Post(). + Resource("globalrolebindings"). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a globalRoleBinding and updates it. Returns the server's representation of the globalRoleBinding, and an error, if there is any. +func (c *globalRoleBindings) Update(globalRoleBinding *v1alpha2.GlobalRoleBinding) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Put(). + Resource("globalrolebindings"). + Name(globalRoleBinding.Name). + Body(globalRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the globalRoleBinding and deletes it. Returns an error if one occurs. +func (c *globalRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("globalrolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *globalRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("globalrolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched globalRoleBinding. +func (c *globalRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.GlobalRoleBinding, err error) { + result = &v1alpha2.GlobalRoleBinding{} + err = c.client.Patch(pt). + Resource("globalrolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go new file mode 100644 index 000000000..be20644fb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/iam_client.go @@ -0,0 +1,109 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + rest "k8s.io/client-go/rest" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +type IamV1alpha2Interface interface { + RESTClient() rest.Interface + GlobalRolesGetter + GlobalRoleBindingsGetter + UsersGetter + WorkspaceRolesGetter + WorkspaceRoleBindingsGetter +} + +// IamV1alpha2Client is used to interact with features provided by the iam.kubesphere.io group. +type IamV1alpha2Client struct { + restClient rest.Interface +} + +func (c *IamV1alpha2Client) GlobalRoles() GlobalRoleInterface { + return newGlobalRoles(c) +} + +func (c *IamV1alpha2Client) GlobalRoleBindings() GlobalRoleBindingInterface { + return newGlobalRoleBindings(c) +} + +func (c *IamV1alpha2Client) Users() UserInterface { + return newUsers(c) +} + +func (c *IamV1alpha2Client) WorkspaceRoles() WorkspaceRoleInterface { + return newWorkspaceRoles(c) +} + +func (c *IamV1alpha2Client) WorkspaceRoleBindings() WorkspaceRoleBindingInterface { + return newWorkspaceRoleBindings(c) +} + +// NewForConfig creates a new IamV1alpha2Client for the given config. +func NewForConfig(c *rest.Config) (*IamV1alpha2Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &IamV1alpha2Client{client}, nil +} + +// NewForConfigOrDie creates a new IamV1alpha2Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *IamV1alpha2Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new IamV1alpha2Client for the given RESTClient. +func New(c rest.Interface) *IamV1alpha2Client { + return &IamV1alpha2Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha2.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + 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 *IamV1alpha2Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/user.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/user.go new file mode 100644 index 000000000..ea25d4ac2 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/user.go @@ -0,0 +1,180 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// UsersGetter has a method to return a UserInterface. +// A group's client should implement this interface. +type UsersGetter interface { + Users() UserInterface +} + +// UserInterface has methods to work with User resources. +type UserInterface interface { + Create(*v1alpha2.User) (*v1alpha2.User, error) + Update(*v1alpha2.User) (*v1alpha2.User, error) + UpdateStatus(*v1alpha2.User) (*v1alpha2.User, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.User, error) + List(opts v1.ListOptions) (*v1alpha2.UserList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error) + UserExpansion +} + +// users implements UserInterface +type users struct { + client rest.Interface +} + +// newUsers returns a Users +func newUsers(c *IamV1alpha2Client) *users { + return &users{ + client: c.RESTClient(), + } +} + +// Get takes name of the user, and returns the corresponding user object, and an error if there is any. +func (c *users) Get(name string, options v1.GetOptions) (result *v1alpha2.User, err error) { + result = &v1alpha2.User{} + err = c.client.Get(). + Resource("users"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Users that match those selectors. +func (c *users) List(opts v1.ListOptions) (result *v1alpha2.UserList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.UserList{} + err = c.client.Get(). + Resource("users"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested users. +func (c *users) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("users"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a user and creates it. Returns the server's representation of the user, and an error, if there is any. +func (c *users) Create(user *v1alpha2.User) (result *v1alpha2.User, err error) { + result = &v1alpha2.User{} + err = c.client.Post(). + Resource("users"). + Body(user). + Do(). + Into(result) + return +} + +// Update takes the representation of a user and updates it. Returns the server's representation of the user, and an error, if there is any. +func (c *users) Update(user *v1alpha2.User) (result *v1alpha2.User, err error) { + result = &v1alpha2.User{} + err = c.client.Put(). + Resource("users"). + Name(user.Name). + Body(user). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *users) UpdateStatus(user *v1alpha2.User) (result *v1alpha2.User, err error) { + result = &v1alpha2.User{} + err = c.client.Put(). + Resource("users"). + Name(user.Name). + SubResource("status"). + Body(user). + Do(). + Into(result) + return +} + +// Delete takes name of the user and deletes it. Returns an error if one occurs. +func (c *users) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("users"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *users) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("users"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched user. +func (c *users) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.User, err error) { + result = &v1alpha2.User{} + err = c.client.Patch(pt). + Resource("users"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go new file mode 100644 index 000000000..e47e4c871 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerole.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRolesGetter has a method to return a WorkspaceRoleInterface. +// A group's client should implement this interface. +type WorkspaceRolesGetter interface { + WorkspaceRoles() WorkspaceRoleInterface +} + +// WorkspaceRoleInterface has methods to work with WorkspaceRole resources. +type WorkspaceRoleInterface interface { + Create(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Update(*v1alpha2.WorkspaceRole) (*v1alpha2.WorkspaceRole, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRole, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) + WorkspaceRoleExpansion +} + +// workspaceRoles implements WorkspaceRoleInterface +type workspaceRoles struct { + client rest.Interface +} + +// newWorkspaceRoles returns a WorkspaceRoles +func newWorkspaceRoles(c *IamV1alpha2Client) *workspaceRoles { + return &workspaceRoles{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRole, and returns the corresponding workspaceRole object, and an error if there is any. +func (c *workspaceRoles) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Get(). + Resource("workspaceroles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoles that match those selectors. +func (c *workspaceRoles) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleList{} + err = c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoles. +func (c *workspaceRoles) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspaceroles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRole and creates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Create(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Post(). + Resource("workspaceroles"). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRole and updates it. Returns the server's representation of the workspaceRole, and an error, if there is any. +func (c *workspaceRoles) Update(workspaceRole *v1alpha2.WorkspaceRole) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Put(). + Resource("workspaceroles"). + Name(workspaceRole.Name). + Body(workspaceRole). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRole and deletes it. Returns an error if one occurs. +func (c *workspaceRoles) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspaceroles"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoles) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspaceroles"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRole. +func (c *workspaceRoles) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRole, err error) { + result = &v1alpha2.WorkspaceRole{} + err = c.client.Patch(pt). + Resource("workspaceroles"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..17c4406e4 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "time" + + 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" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" +) + +// WorkspaceRoleBindingsGetter has a method to return a WorkspaceRoleBindingInterface. +// A group's client should implement this interface. +type WorkspaceRoleBindingsGetter interface { + WorkspaceRoleBindings() WorkspaceRoleBindingInterface +} + +// WorkspaceRoleBindingInterface has methods to work with WorkspaceRoleBinding resources. +type WorkspaceRoleBindingInterface interface { + Create(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Update(*v1alpha2.WorkspaceRoleBinding) (*v1alpha2.WorkspaceRoleBinding, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha2.WorkspaceRoleBinding, error) + List(opts v1.ListOptions) (*v1alpha2.WorkspaceRoleBindingList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) + WorkspaceRoleBindingExpansion +} + +// workspaceRoleBindings implements WorkspaceRoleBindingInterface +type workspaceRoleBindings struct { + client rest.Interface +} + +// newWorkspaceRoleBindings returns a WorkspaceRoleBindings +func newWorkspaceRoleBindings(c *IamV1alpha2Client) *workspaceRoleBindings { + return &workspaceRoleBindings{ + client: c.RESTClient(), + } +} + +// Get takes name of the workspaceRoleBinding, and returns the corresponding workspaceRoleBinding object, and an error if there is any. +func (c *workspaceRoleBindings) Get(name string, options v1.GetOptions) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Get(). + Resource("workspacerolebindings"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of WorkspaceRoleBindings that match those selectors. +func (c *workspaceRoleBindings) List(opts v1.ListOptions) (result *v1alpha2.WorkspaceRoleBindingList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.WorkspaceRoleBindingList{} + err = c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested workspaceRoleBindings. +func (c *workspaceRoleBindings) Watch(opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("workspacerolebindings"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a workspaceRoleBinding and creates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Create(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Post(). + Resource("workspacerolebindings"). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Update takes the representation of a workspaceRoleBinding and updates it. Returns the server's representation of the workspaceRoleBinding, and an error, if there is any. +func (c *workspaceRoleBindings) Update(workspaceRoleBinding *v1alpha2.WorkspaceRoleBinding) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Put(). + Resource("workspacerolebindings"). + Name(workspaceRoleBinding.Name). + Body(workspaceRoleBinding). + Do(). + Into(result) + return +} + +// Delete takes name of the workspaceRoleBinding and deletes it. Returns an error if one occurs. +func (c *workspaceRoleBindings) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("workspacerolebindings"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *workspaceRoleBindings) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("workspacerolebindings"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched workspaceRoleBinding. +func (c *workspaceRoleBindings) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha2.WorkspaceRoleBinding, err error) { + result = &v1alpha2.WorkspaceRoleBinding{} + err = c.client.Patch(pt). + Resource("workspacerolebindings"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go b/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go index f62741d98..571150be6 100644 --- a/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go +++ b/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_network_client.go @@ -32,10 +32,6 @@ func (c *FakeNetworkV1alpha1) NamespaceNetworkPolicies(namespace string) v1alpha return &FakeNamespaceNetworkPolicies{c, namespace} } -func (c *FakeNetworkV1alpha1) WorkspaceNetworkPolicies() v1alpha1.WorkspaceNetworkPolicyInterface { - return &FakeWorkspaceNetworkPolicies{c} -} - // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeNetworkV1alpha1) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_workspacenetworkpolicy.go b/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_workspacenetworkpolicy.go deleted file mode 100644 index 8d4b78c3f..000000000 --- a/pkg/client/clientset/versioned/typed/network/v1alpha1/fake/fake_workspacenetworkpolicy.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package fake - -import ( - 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" - v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) - -// FakeWorkspaceNetworkPolicies implements WorkspaceNetworkPolicyInterface -type FakeWorkspaceNetworkPolicies struct { - Fake *FakeNetworkV1alpha1 -} - -var workspacenetworkpoliciesResource = schema.GroupVersionResource{Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "workspacenetworkpolicies"} - -var workspacenetworkpoliciesKind = schema.GroupVersionKind{Group: "network.kubesphere.io", Version: "v1alpha1", Kind: "WorkspaceNetworkPolicy"} - -// Get takes name of the workspaceNetworkPolicy, and returns the corresponding workspaceNetworkPolicy object, and an error if there is any. -func (c *FakeWorkspaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(workspacenetworkpoliciesResource, name), &v1alpha1.WorkspaceNetworkPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), err -} - -// List takes label and field selectors, and returns the list of WorkspaceNetworkPolicies that match those selectors. -func (c *FakeWorkspaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceNetworkPolicyList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(workspacenetworkpoliciesResource, workspacenetworkpoliciesKind, opts), &v1alpha1.WorkspaceNetworkPolicyList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.WorkspaceNetworkPolicyList{ListMeta: obj.(*v1alpha1.WorkspaceNetworkPolicyList).ListMeta} - for _, item := range obj.(*v1alpha1.WorkspaceNetworkPolicyList).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 workspaceNetworkPolicies. -func (c *FakeWorkspaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(workspacenetworkpoliciesResource, opts)) -} - -// Create takes the representation of a workspaceNetworkPolicy and creates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any. -func (c *FakeWorkspaceNetworkPolicies) Create(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(workspacenetworkpoliciesResource, workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), err -} - -// Update takes the representation of a workspaceNetworkPolicy and updates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any. -func (c *FakeWorkspaceNetworkPolicies) Update(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(workspacenetworkpoliciesResource, workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeWorkspaceNetworkPolicies) UpdateStatus(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(workspacenetworkpoliciesResource, "status", workspaceNetworkPolicy), &v1alpha1.WorkspaceNetworkPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), err -} - -// Delete takes name of the workspaceNetworkPolicy and deletes it. Returns an error if one occurs. -func (c *FakeWorkspaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(workspacenetworkpoliciesResource, name), &v1alpha1.WorkspaceNetworkPolicy{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeWorkspaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(workspacenetworkpoliciesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.WorkspaceNetworkPolicyList{}) - return err -} - -// Patch applies the patch and returns the patched workspaceNetworkPolicy. -func (c *FakeWorkspaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(workspacenetworkpoliciesResource, name, pt, data, subresources...), &v1alpha1.WorkspaceNetworkPolicy{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), err -} diff --git a/pkg/client/clientset/versioned/typed/network/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/network/v1alpha1/generated_expansion.go index c38debbc3..e4de164f8 100644 --- a/pkg/client/clientset/versioned/typed/network/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/network/v1alpha1/generated_expansion.go @@ -19,5 +19,3 @@ limitations under the License. package v1alpha1 type NamespaceNetworkPolicyExpansion interface{} - -type WorkspaceNetworkPolicyExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/network/v1alpha1/network_client.go b/pkg/client/clientset/versioned/typed/network/v1alpha1/network_client.go index 822b93e38..92f0d3277 100644 --- a/pkg/client/clientset/versioned/typed/network/v1alpha1/network_client.go +++ b/pkg/client/clientset/versioned/typed/network/v1alpha1/network_client.go @@ -27,7 +27,6 @@ import ( type NetworkV1alpha1Interface interface { RESTClient() rest.Interface NamespaceNetworkPoliciesGetter - WorkspaceNetworkPoliciesGetter } // NetworkV1alpha1Client is used to interact with features provided by the network.kubesphere.io group. @@ -39,10 +38,6 @@ func (c *NetworkV1alpha1Client) NamespaceNetworkPolicies(namespace string) Names return newNamespaceNetworkPolicies(c, namespace) } -func (c *NetworkV1alpha1Client) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface { - return newWorkspaceNetworkPolicies(c) -} - // NewForConfig creates a new NetworkV1alpha1Client for the given config. func NewForConfig(c *rest.Config) (*NetworkV1alpha1Client, error) { config := *c diff --git a/pkg/client/clientset/versioned/typed/network/v1alpha1/workspacenetworkpolicy.go b/pkg/client/clientset/versioned/typed/network/v1alpha1/workspacenetworkpolicy.go deleted file mode 100644 index a8f51139e..000000000 --- a/pkg/client/clientset/versioned/typed/network/v1alpha1/workspacenetworkpolicy.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - 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" - v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - scheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" -) - -// WorkspaceNetworkPoliciesGetter has a method to return a WorkspaceNetworkPolicyInterface. -// A group's client should implement this interface. -type WorkspaceNetworkPoliciesGetter interface { - WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInterface -} - -// WorkspaceNetworkPolicyInterface has methods to work with WorkspaceNetworkPolicy resources. -type WorkspaceNetworkPolicyInterface interface { - Create(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error) - Update(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error) - UpdateStatus(*v1alpha1.WorkspaceNetworkPolicy) (*v1alpha1.WorkspaceNetworkPolicy, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.WorkspaceNetworkPolicy, error) - List(opts v1.ListOptions) (*v1alpha1.WorkspaceNetworkPolicyList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error) - WorkspaceNetworkPolicyExpansion -} - -// workspaceNetworkPolicies implements WorkspaceNetworkPolicyInterface -type workspaceNetworkPolicies struct { - client rest.Interface -} - -// newWorkspaceNetworkPolicies returns a WorkspaceNetworkPolicies -func newWorkspaceNetworkPolicies(c *NetworkV1alpha1Client) *workspaceNetworkPolicies { - return &workspaceNetworkPolicies{ - client: c.RESTClient(), - } -} - -// Get takes name of the workspaceNetworkPolicy, and returns the corresponding workspaceNetworkPolicy object, and an error if there is any. -func (c *workspaceNetworkPolicies) Get(name string, options v1.GetOptions) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - result = &v1alpha1.WorkspaceNetworkPolicy{} - err = c.client.Get(). - Resource("workspacenetworkpolicies"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of WorkspaceNetworkPolicies that match those selectors. -func (c *workspaceNetworkPolicies) List(opts v1.ListOptions) (result *v1alpha1.WorkspaceNetworkPolicyList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.WorkspaceNetworkPolicyList{} - err = c.client.Get(). - Resource("workspacenetworkpolicies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested workspaceNetworkPolicies. -func (c *workspaceNetworkPolicies) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("workspacenetworkpolicies"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a workspaceNetworkPolicy and creates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any. -func (c *workspaceNetworkPolicies) Create(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - result = &v1alpha1.WorkspaceNetworkPolicy{} - err = c.client.Post(). - Resource("workspacenetworkpolicies"). - Body(workspaceNetworkPolicy). - Do(). - Into(result) - return -} - -// Update takes the representation of a workspaceNetworkPolicy and updates it. Returns the server's representation of the workspaceNetworkPolicy, and an error, if there is any. -func (c *workspaceNetworkPolicies) Update(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - result = &v1alpha1.WorkspaceNetworkPolicy{} - err = c.client.Put(). - Resource("workspacenetworkpolicies"). - Name(workspaceNetworkPolicy.Name). - Body(workspaceNetworkPolicy). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *workspaceNetworkPolicies) UpdateStatus(workspaceNetworkPolicy *v1alpha1.WorkspaceNetworkPolicy) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - result = &v1alpha1.WorkspaceNetworkPolicy{} - err = c.client.Put(). - Resource("workspacenetworkpolicies"). - Name(workspaceNetworkPolicy.Name). - SubResource("status"). - Body(workspaceNetworkPolicy). - Do(). - Into(result) - return -} - -// Delete takes name of the workspaceNetworkPolicy and deletes it. Returns an error if one occurs. -func (c *workspaceNetworkPolicies) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("workspacenetworkpolicies"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *workspaceNetworkPolicies) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("workspacenetworkpolicies"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched workspaceNetworkPolicy. -func (c *workspaceNetworkPolicies) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.WorkspaceNetworkPolicy, err error) { - result = &v1alpha1.WorkspaceNetworkPolicy{} - err = c.client.Patch(pt). - Resource("workspacenetworkpolicies"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/informers/externalversions/cluster/interface.go b/pkg/client/informers/externalversions/cluster/interface.go new file mode 100644 index 000000000..fd741d36b --- /dev/null +++ b/pkg/client/informers/externalversions/cluster/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package cluster + +import ( + v1alpha1 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster/v1alpha1" + internalinterfaces "kubesphere.io/kubesphere/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/cluster/v1alpha1/cluster.go b/pkg/client/informers/externalversions/cluster/v1alpha1/cluster.go new file mode 100644 index 000000000..883314e59 --- /dev/null +++ b/pkg/client/informers/externalversions/cluster/v1alpha1/cluster.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + 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" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1" +) + +// ClusterInformer provides access to a shared informer and lister for +// Clusters. +type ClusterInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterLister +} + +type clusterInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterInformer constructs a new informer for Cluster 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 NewClusterInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterInformer constructs a new informer for Cluster 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 NewFilteredClusterInformer(client versioned.Interface, 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.ClusterV1alpha1().Clusters().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.ClusterV1alpha1().Clusters().Watch(options) + }, + }, + &clusterv1alpha1.Cluster{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&clusterv1alpha1.Cluster{}, f.defaultInformer) +} + +func (f *clusterInformer) Lister() v1alpha1.ClusterLister { + return v1alpha1.NewClusterLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/cluster/v1alpha1/interface.go b/pkg/client/informers/externalversions/cluster/v1alpha1/interface.go new file mode 100644 index 000000000..383f79e85 --- /dev/null +++ b/pkg/client/informers/externalversions/cluster/v1alpha1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Clusters returns a ClusterInformer. + Clusters() ClusterInformer +} + +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} +} + +// Clusters returns a ClusterInformer. +func (v *version) Clusters() ClusterInformer { + return &clusterInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/devops/interface.go b/pkg/client/informers/externalversions/devops/interface.go index a4f26e4cd..f5e53aa44 100644 --- a/pkg/client/informers/externalversions/devops/interface.go +++ b/pkg/client/informers/externalversions/devops/interface.go @@ -20,6 +20,7 @@ package devops import ( v1alpha1 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops/v1alpha1" + v1alpha3 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops/v1alpha3" internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" ) @@ -27,6 +28,8 @@ import ( type Interface interface { // V1alpha1 provides access to shared informers for resources in V1alpha1. V1alpha1() v1alpha1.Interface + // V1alpha3 provides access to shared informers for resources in V1alpha3. + V1alpha3() v1alpha3.Interface } type group struct { @@ -44,3 +47,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (g *group) V1alpha1() v1alpha1.Interface { return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) } + +// V1alpha3 returns a new v1alpha3.Interface. +func (g *group) V1alpha3() v1alpha3.Interface { + return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha1/interface.go b/pkg/client/informers/externalversions/devops/v1alpha1/interface.go index e1a502aca..adadc49aa 100644 --- a/pkg/client/informers/externalversions/devops/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/devops/v1alpha1/interface.go @@ -26,6 +26,12 @@ import ( type Interface interface { // S2iBinaries returns a S2iBinaryInformer. S2iBinaries() S2iBinaryInformer + // S2iBuilders returns a S2iBuilderInformer. + S2iBuilders() S2iBuilderInformer + // S2iBuilderTemplates returns a S2iBuilderTemplateInformer. + S2iBuilderTemplates() S2iBuilderTemplateInformer + // S2iRuns returns a S2iRunInformer. + S2iRuns() S2iRunInformer } type version struct { @@ -43,3 +49,18 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) S2iBinaries() S2iBinaryInformer { return &s2iBinaryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// S2iBuilders returns a S2iBuilderInformer. +func (v *version) S2iBuilders() S2iBuilderInformer { + return &s2iBuilderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + +// S2iBuilderTemplates returns a S2iBuilderTemplateInformer. +func (v *version) S2iBuilderTemplates() S2iBuilderTemplateInformer { + return &s2iBuilderTemplateInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// S2iRuns returns a S2iRunInformer. +func (v *version) S2iRuns() S2iRunInformer { + return &s2iRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go b/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go new file mode 100644 index 000000000..e3aa128b9 --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + 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" + devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha1" +) + +// S2iBuilderInformer provides access to a shared informer and lister for +// S2iBuilders. +type S2iBuilderInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.S2iBuilderLister +} + +type s2iBuilderInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewS2iBuilderInformer constructs a new informer for S2iBuilder 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 NewS2iBuilderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredS2iBuilderInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredS2iBuilderInformer constructs a new informer for S2iBuilder 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 NewFilteredS2iBuilderInformer(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.DevopsV1alpha1().S2iBuilders(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DevopsV1alpha1().S2iBuilders(namespace).Watch(options) + }, + }, + &devopsv1alpha1.S2iBuilder{}, + resyncPeriod, + indexers, + ) +} + +func (f *s2iBuilderInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredS2iBuilderInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *s2iBuilderInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&devopsv1alpha1.S2iBuilder{}, f.defaultInformer) +} + +func (f *s2iBuilderInformer) Lister() v1alpha1.S2iBuilderLister { + return v1alpha1.NewS2iBuilderLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go b/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go new file mode 100644 index 000000000..2b97ae99d --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + 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" + devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha1" +) + +// S2iBuilderTemplateInformer provides access to a shared informer and lister for +// S2iBuilderTemplates. +type S2iBuilderTemplateInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.S2iBuilderTemplateLister +} + +type s2iBuilderTemplateInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewS2iBuilderTemplateInformer constructs a new informer for S2iBuilderTemplate 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 NewS2iBuilderTemplateInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredS2iBuilderTemplateInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredS2iBuilderTemplateInformer constructs a new informer for S2iBuilderTemplate 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 NewFilteredS2iBuilderTemplateInformer(client versioned.Interface, 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.DevopsV1alpha1().S2iBuilderTemplates().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DevopsV1alpha1().S2iBuilderTemplates().Watch(options) + }, + }, + &devopsv1alpha1.S2iBuilderTemplate{}, + resyncPeriod, + indexers, + ) +} + +func (f *s2iBuilderTemplateInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredS2iBuilderTemplateInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *s2iBuilderTemplateInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&devopsv1alpha1.S2iBuilderTemplate{}, f.defaultInformer) +} + +func (f *s2iBuilderTemplateInformer) Lister() v1alpha1.S2iBuilderTemplateLister { + return v1alpha1.NewS2iBuilderTemplateLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go b/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go new file mode 100644 index 000000000..f34e5233b --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + time "time" + + 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" + devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha1" +) + +// S2iRunInformer provides access to a shared informer and lister for +// S2iRuns. +type S2iRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.S2iRunLister +} + +type s2iRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewS2iRunInformer constructs a new informer for S2iRun 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 NewS2iRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredS2iRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredS2iRunInformer constructs a new informer for S2iRun 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 NewFilteredS2iRunInformer(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.DevopsV1alpha1().S2iRuns(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DevopsV1alpha1().S2iRuns(namespace).Watch(options) + }, + }, + &devopsv1alpha1.S2iRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *s2iRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredS2iRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *s2iRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&devopsv1alpha1.S2iRun{}, f.defaultInformer) +} + +func (f *s2iRunInformer) Lister() v1alpha1.S2iRunLister { + return v1alpha1.NewS2iRunLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha3/devopsproject.go b/pkg/client/informers/externalversions/devops/v1alpha3/devopsproject.go new file mode 100644 index 000000000..23de56b29 --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha3/devopsproject.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + time "time" + + 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" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha3 "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha3" +) + +// DevOpsProjectInformer provides access to a shared informer and lister for +// DevOpsProjects. +type DevOpsProjectInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha3.DevOpsProjectLister +} + +type devOpsProjectInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewDevOpsProjectInformer constructs a new informer for DevOpsProject 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 NewDevOpsProjectInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredDevOpsProjectInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredDevOpsProjectInformer constructs a new informer for DevOpsProject 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 NewFilteredDevOpsProjectInformer(client versioned.Interface, 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.DevopsV1alpha3().DevOpsProjects().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DevopsV1alpha3().DevOpsProjects().Watch(options) + }, + }, + &devopsv1alpha3.DevOpsProject{}, + resyncPeriod, + indexers, + ) +} + +func (f *devOpsProjectInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredDevOpsProjectInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *devOpsProjectInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&devopsv1alpha3.DevOpsProject{}, f.defaultInformer) +} + +func (f *devOpsProjectInformer) Lister() v1alpha3.DevOpsProjectLister { + return v1alpha3.NewDevOpsProjectLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha3/interface.go b/pkg/client/informers/externalversions/devops/v1alpha3/interface.go new file mode 100644 index 000000000..90b80745a --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha3/interface.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // DevOpsProjects returns a DevOpsProjectInformer. + DevOpsProjects() DevOpsProjectInformer + // Pipelines returns a PipelineInformer. + Pipelines() PipelineInformer +} + +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} +} + +// DevOpsProjects returns a DevOpsProjectInformer. +func (v *version) DevOpsProjects() DevOpsProjectInformer { + return &devOpsProjectInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Pipelines returns a PipelineInformer. +func (v *version) Pipelines() PipelineInformer { + return &pipelineInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/devops/v1alpha3/pipeline.go b/pkg/client/informers/externalversions/devops/v1alpha3/pipeline.go new file mode 100644 index 000000000..b1b6cce96 --- /dev/null +++ b/pkg/client/informers/externalversions/devops/v1alpha3/pipeline.go @@ -0,0 +1,89 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + time "time" + + 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" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha3 "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha3" +) + +// PipelineInformer provides access to a shared informer and lister for +// Pipelines. +type PipelineInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha3.PipelineLister +} + +type pipelineInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewPipelineInformer constructs a new informer for Pipeline 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 NewPipelineInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredPipelineInformer constructs a new informer for Pipeline 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 NewFilteredPipelineInformer(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.DevopsV1alpha3().Pipelines(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.DevopsV1alpha3().Pipelines(namespace).Watch(options) + }, + }, + &devopsv1alpha3.Pipeline{}, + resyncPeriod, + indexers, + ) +} + +func (f *pipelineInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredPipelineInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *pipelineInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&devopsv1alpha3.Pipeline{}, f.defaultInformer) +} + +func (f *pipelineInformer) Lister() v1alpha3.PipelineLister { + return v1alpha3.NewPipelineLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index 8134dd6ef..ae9c313aa 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -28,7 +28,9 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + cluster "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster" devops "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops" + iam "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam" internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" network "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network" servicemesh "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh" @@ -175,16 +177,26 @@ type SharedInformerFactory interface { ForResource(resource schema.GroupVersionResource) (GenericInformer, error) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + Cluster() cluster.Interface Devops() devops.Interface + Iam() iam.Interface Network() network.Interface Servicemesh() servicemesh.Interface Tenant() tenant.Interface } +func (f *sharedInformerFactory) Cluster() cluster.Interface { + return cluster.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Devops() devops.Interface { return devops.New(f, f.namespace, f.tweakListOptions) } +func (f *sharedInformerFactory) Iam() iam.Interface { + return iam.New(f, f.namespace, f.tweakListOptions) +} + func (f *sharedInformerFactory) Network() network.Interface { return network.New(f, f.namespace, f.tweakListOptions) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 47f806269..9b7160880 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -23,9 +23,12 @@ import ( schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" - v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - v1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" + servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" ) @@ -55,20 +58,46 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=devops.kubesphere.io, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("s2ibinaries"): + // Group=cluster.kubesphere.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clusters"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Cluster().V1alpha1().Clusters().Informer()}, nil + + // Group=devops.kubesphere.io, Version=v1alpha1 + case devopsv1alpha1.SchemeGroupVersion.WithResource("s2ibinaries"): return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBinaries().Informer()}, nil + case devopsv1alpha1.SchemeGroupVersion.WithResource("s2ibuilders"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBuilders().Informer()}, nil + case devopsv1alpha1.SchemeGroupVersion.WithResource("s2ibuildertemplates"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBuilderTemplates().Informer()}, nil + case devopsv1alpha1.SchemeGroupVersion.WithResource("s2iruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iRuns().Informer()}, nil + + // Group=devops.kubesphere.io, Version=v1alpha3 + case v1alpha3.SchemeGroupVersion.WithResource("devopsprojects"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().DevOpsProjects().Informer()}, nil + case v1alpha3.SchemeGroupVersion.WithResource("pipelines"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha3().Pipelines().Informer()}, nil + + // Group=iam.kubesphere.io, Version=v1alpha2 + case v1alpha2.SchemeGroupVersion.WithResource("globalroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("globalrolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().GlobalRoleBindings().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("users"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().Users().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspaceroles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoles().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("workspacerolebindings"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Iam().V1alpha2().WorkspaceRoleBindings().Informer()}, nil // Group=network.kubesphere.io, Version=v1alpha1 case networkv1alpha1.SchemeGroupVersion.WithResource("namespacenetworkpolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().NamespaceNetworkPolicies().Informer()}, nil - case networkv1alpha1.SchemeGroupVersion.WithResource("workspacenetworkpolicies"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Network().V1alpha1().WorkspaceNetworkPolicies().Informer()}, nil // Group=servicemesh.kubesphere.io, Version=v1alpha2 - case v1alpha2.SchemeGroupVersion.WithResource("servicepolicies"): + case servicemeshv1alpha2.SchemeGroupVersion.WithResource("servicepolicies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().ServicePolicies().Informer()}, nil - case v1alpha2.SchemeGroupVersion.WithResource("strategies"): + case servicemeshv1alpha2.SchemeGroupVersion.WithResource("strategies"): return &genericInformer{resource: resource.GroupResource(), informer: f.Servicemesh().V1alpha2().Strategies().Informer()}, nil // Group=tenant.kubesphere.io, Version=v1alpha1 diff --git a/pkg/client/informers/externalversions/iam/interface.go b/pkg/client/informers/externalversions/iam/interface.go new file mode 100644 index 000000000..544e94cd4 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package iam + +import ( + v1alpha2 "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha2 provides access to shared informers for resources in V1alpha2. + V1alpha2() v1alpha2.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} +} + +// V1alpha2 returns a new v1alpha2.Interface. +func (g *group) V1alpha2() v1alpha2.Interface { + return v1alpha2.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go new file mode 100644 index 000000000..c02b92031 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrole.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + 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" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// GlobalRoleInformer provides access to a shared informer and lister for +// GlobalRoles. +type GlobalRoleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.GlobalRoleLister +} + +type globalRoleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewGlobalRoleInformer constructs a new informer for GlobalRole 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 NewGlobalRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredGlobalRoleInformer constructs a new informer for GlobalRole 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 NewFilteredGlobalRoleInformer(client versioned.Interface, 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.IamV1alpha2().GlobalRoles().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().GlobalRoles().Watch(options) + }, + }, + &iamv1alpha2.GlobalRole{}, + resyncPeriod, + indexers, + ) +} + +func (f *globalRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *globalRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRole{}, f.defaultInformer) +} + +func (f *globalRoleInformer) Lister() v1alpha2.GlobalRoleLister { + return v1alpha2.NewGlobalRoleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 000000000..d5376608b --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + 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" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// GlobalRoleBindingInformer provides access to a shared informer and lister for +// GlobalRoleBindings. +type GlobalRoleBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.GlobalRoleBindingLister +} + +type globalRoleBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding 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 NewGlobalRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredGlobalRoleBindingInformer constructs a new informer for GlobalRoleBinding 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 NewFilteredGlobalRoleBindingInformer(client versioned.Interface, 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.IamV1alpha2().GlobalRoleBindings().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().GlobalRoleBindings().Watch(options) + }, + }, + &iamv1alpha2.GlobalRoleBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *globalRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredGlobalRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *globalRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.GlobalRoleBinding{}, f.defaultInformer) +} + +func (f *globalRoleBindingInformer) Lister() v1alpha2.GlobalRoleBindingLister { + return v1alpha2.NewGlobalRoleBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/interface.go b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go new file mode 100644 index 000000000..f6765fd61 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/interface.go @@ -0,0 +1,73 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // GlobalRoles returns a GlobalRoleInformer. + GlobalRoles() GlobalRoleInformer + // GlobalRoleBindings returns a GlobalRoleBindingInformer. + GlobalRoleBindings() GlobalRoleBindingInformer + // Users returns a UserInformer. + Users() UserInformer + // WorkspaceRoles returns a WorkspaceRoleInformer. + WorkspaceRoles() WorkspaceRoleInformer + // WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. + WorkspaceRoleBindings() WorkspaceRoleBindingInformer +} + +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} +} + +// GlobalRoles returns a GlobalRoleInformer. +func (v *version) GlobalRoles() GlobalRoleInformer { + return &globalRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// GlobalRoleBindings returns a GlobalRoleBindingInformer. +func (v *version) GlobalRoleBindings() GlobalRoleBindingInformer { + return &globalRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// Users returns a UserInformer. +func (v *version) Users() UserInformer { + return &userInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// WorkspaceRoles returns a WorkspaceRoleInformer. +func (v *version) WorkspaceRoles() WorkspaceRoleInformer { + return &workspaceRoleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + +// WorkspaceRoleBindings returns a WorkspaceRoleBindingInformer. +func (v *version) WorkspaceRoleBindings() WorkspaceRoleBindingInformer { + return &workspaceRoleBindingInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/user.go b/pkg/client/informers/externalversions/iam/v1alpha2/user.go new file mode 100644 index 000000000..c2cfb157f --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/user.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + 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" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// UserInformer provides access to a shared informer and lister for +// Users. +type UserInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.UserLister +} + +type userInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewUserInformer constructs a new informer for User 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 NewUserInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredUserInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredUserInformer constructs a new informer for User 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 NewFilteredUserInformer(client versioned.Interface, 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.IamV1alpha2().Users().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().Users().Watch(options) + }, + }, + &iamv1alpha2.User{}, + resyncPeriod, + indexers, + ) +} + +func (f *userInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredUserInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *userInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.User{}, f.defaultInformer) +} + +func (f *userInformer) Lister() v1alpha2.UserLister { + return v1alpha2.NewUserLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go new file mode 100644 index 000000000..be8b37540 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerole.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + 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" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// WorkspaceRoleInformer provides access to a shared informer and lister for +// WorkspaceRoles. +type WorkspaceRoleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.WorkspaceRoleLister +} + +type workspaceRoleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewWorkspaceRoleInformer constructs a new informer for WorkspaceRole 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 NewWorkspaceRoleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredWorkspaceRoleInformer constructs a new informer for WorkspaceRole 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 NewFilteredWorkspaceRoleInformer(client versioned.Interface, 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.IamV1alpha2().WorkspaceRoles().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoles().Watch(options) + }, + }, + &iamv1alpha2.WorkspaceRole{}, + resyncPeriod, + indexers, + ) +} + +func (f *workspaceRoleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *workspaceRoleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRole{}, f.defaultInformer) +} + +func (f *workspaceRoleInformer) Lister() v1alpha2.WorkspaceRoleLister { + return v1alpha2.NewWorkspaceRoleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..18b18b4d7 --- /dev/null +++ b/pkg/client/informers/externalversions/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,88 @@ +/* +Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + time "time" + + 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" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" +) + +// WorkspaceRoleBindingInformer provides access to a shared informer and lister for +// WorkspaceRoleBindings. +type WorkspaceRoleBindingInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.WorkspaceRoleBindingLister +} + +type workspaceRoleBindingInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding 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 NewWorkspaceRoleBindingInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredWorkspaceRoleBindingInformer constructs a new informer for WorkspaceRoleBinding 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 NewFilteredWorkspaceRoleBindingInformer(client versioned.Interface, 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.IamV1alpha2().WorkspaceRoleBindings().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.IamV1alpha2().WorkspaceRoleBindings().Watch(options) + }, + }, + &iamv1alpha2.WorkspaceRoleBinding{}, + resyncPeriod, + indexers, + ) +} + +func (f *workspaceRoleBindingInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredWorkspaceRoleBindingInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *workspaceRoleBindingInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&iamv1alpha2.WorkspaceRoleBinding{}, f.defaultInformer) +} + +func (f *workspaceRoleBindingInformer) Lister() v1alpha2.WorkspaceRoleBindingLister { + return v1alpha2.NewWorkspaceRoleBindingLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/network/v1alpha1/interface.go b/pkg/client/informers/externalversions/network/v1alpha1/interface.go index 37cbe69c7..5185f51af 100644 --- a/pkg/client/informers/externalversions/network/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/network/v1alpha1/interface.go @@ -26,8 +26,6 @@ import ( type Interface interface { // NamespaceNetworkPolicies returns a NamespaceNetworkPolicyInformer. NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer - // WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer. - WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer } type version struct { @@ -45,8 +43,3 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList func (v *version) NamespaceNetworkPolicies() NamespaceNetworkPolicyInformer { return &namespaceNetworkPolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } - -// WorkspaceNetworkPolicies returns a WorkspaceNetworkPolicyInformer. -func (v *version) WorkspaceNetworkPolicies() WorkspaceNetworkPolicyInformer { - return &workspaceNetworkPolicyInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/network/v1alpha1/workspacenetworkpolicy.go b/pkg/client/informers/externalversions/network/v1alpha1/workspacenetworkpolicy.go deleted file mode 100644 index b0773a40f..000000000 --- a/pkg/client/informers/externalversions/network/v1alpha1/workspacenetworkpolicy.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - 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" - networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - versioned "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - internalinterfaces "kubesphere.io/kubesphere/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1" -) - -// WorkspaceNetworkPolicyInformer provides access to a shared informer and lister for -// WorkspaceNetworkPolicies. -type WorkspaceNetworkPolicyInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.WorkspaceNetworkPolicyLister -} - -type workspaceNetworkPolicyInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewWorkspaceNetworkPolicyInformer constructs a new informer for WorkspaceNetworkPolicy 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 NewWorkspaceNetworkPolicyInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredWorkspaceNetworkPolicyInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredWorkspaceNetworkPolicyInformer constructs a new informer for WorkspaceNetworkPolicy 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 NewFilteredWorkspaceNetworkPolicyInformer(client versioned.Interface, 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.NetworkV1alpha1().WorkspaceNetworkPolicies().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.NetworkV1alpha1().WorkspaceNetworkPolicies().Watch(options) - }, - }, - &networkv1alpha1.WorkspaceNetworkPolicy{}, - resyncPeriod, - indexers, - ) -} - -func (f *workspaceNetworkPolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredWorkspaceNetworkPolicyInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *workspaceNetworkPolicyInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&networkv1alpha1.WorkspaceNetworkPolicy{}, f.defaultInformer) -} - -func (f *workspaceNetworkPolicyInformer) Lister() v1alpha1.WorkspaceNetworkPolicyLister { - return v1alpha1.NewWorkspaceNetworkPolicyLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/listers/cluster/v1alpha1/cluster.go b/pkg/client/listers/cluster/v1alpha1/cluster.go new file mode 100644 index 000000000..8408a6994 --- /dev/null +++ b/pkg/client/listers/cluster/v1alpha1/cluster.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" +) + +// ClusterLister helps list Clusters. +type ClusterLister interface { + // List lists all Clusters in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Cluster, err error) + // Get retrieves the Cluster from the index for a given name. + Get(name string) (*v1alpha1.Cluster, error) + ClusterListerExpansion +} + +// clusterLister implements the ClusterLister interface. +type clusterLister struct { + indexer cache.Indexer +} + +// NewClusterLister returns a new ClusterLister. +func NewClusterLister(indexer cache.Indexer) ClusterLister { + return &clusterLister{indexer: indexer} +} + +// List lists all Clusters in the indexer. +func (s *clusterLister) List(selector labels.Selector) (ret []*v1alpha1.Cluster, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Cluster)) + }) + return ret, err +} + +// Get retrieves the Cluster from the index for a given name. +func (s *clusterLister) Get(name string) (*v1alpha1.Cluster, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("cluster"), name) + } + return obj.(*v1alpha1.Cluster), nil +} diff --git a/pkg/client/listers/cluster/v1alpha1/expansion_generated.go b/pkg/client/listers/cluster/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..2711d0848 --- /dev/null +++ b/pkg/client/listers/cluster/v1alpha1/expansion_generated.go @@ -0,0 +1,23 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +// ClusterListerExpansion allows custom methods to be added to +// ClusterLister. +type ClusterListerExpansion interface{} diff --git a/pkg/client/listers/devops/v1alpha1/expansion_generated.go b/pkg/client/listers/devops/v1alpha1/expansion_generated.go index da230dd7c..e048c7050 100644 --- a/pkg/client/listers/devops/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/devops/v1alpha1/expansion_generated.go @@ -25,3 +25,23 @@ type S2iBinaryListerExpansion interface{} // S2iBinaryNamespaceListerExpansion allows custom methods to be added to // S2iBinaryNamespaceLister. type S2iBinaryNamespaceListerExpansion interface{} + +// S2iBuilderListerExpansion allows custom methods to be added to +// S2iBuilderLister. +type S2iBuilderListerExpansion interface{} + +// S2iBuilderNamespaceListerExpansion allows custom methods to be added to +// S2iBuilderNamespaceLister. +type S2iBuilderNamespaceListerExpansion interface{} + +// S2iBuilderTemplateListerExpansion allows custom methods to be added to +// S2iBuilderTemplateLister. +type S2iBuilderTemplateListerExpansion interface{} + +// S2iRunListerExpansion allows custom methods to be added to +// S2iRunLister. +type S2iRunListerExpansion interface{} + +// S2iRunNamespaceListerExpansion allows custom methods to be added to +// S2iRunNamespaceLister. +type S2iRunNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/devops/v1alpha1/s2ibuilder.go b/pkg/client/listers/devops/v1alpha1/s2ibuilder.go new file mode 100644 index 000000000..4e1ab1697 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha1/s2ibuilder.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// S2iBuilderLister helps list S2iBuilders. +type S2iBuilderLister interface { + // List lists all S2iBuilders in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) + // S2iBuilders returns an object that can list and get S2iBuilders. + S2iBuilders(namespace string) S2iBuilderNamespaceLister + S2iBuilderListerExpansion +} + +// s2iBuilderLister implements the S2iBuilderLister interface. +type s2iBuilderLister struct { + indexer cache.Indexer +} + +// NewS2iBuilderLister returns a new S2iBuilderLister. +func NewS2iBuilderLister(indexer cache.Indexer) S2iBuilderLister { + return &s2iBuilderLister{indexer: indexer} +} + +// List lists all S2iBuilders in the indexer. +func (s *s2iBuilderLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.S2iBuilder)) + }) + return ret, err +} + +// S2iBuilders returns an object that can list and get S2iBuilders. +func (s *s2iBuilderLister) S2iBuilders(namespace string) S2iBuilderNamespaceLister { + return s2iBuilderNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// S2iBuilderNamespaceLister helps list and get S2iBuilders. +type S2iBuilderNamespaceLister interface { + // List lists all S2iBuilders in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) + // Get retrieves the S2iBuilder from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.S2iBuilder, error) + S2iBuilderNamespaceListerExpansion +} + +// s2iBuilderNamespaceLister implements the S2iBuilderNamespaceLister +// interface. +type s2iBuilderNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all S2iBuilders in the indexer for a given namespace. +func (s s2iBuilderNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.S2iBuilder)) + }) + return ret, err +} + +// Get retrieves the S2iBuilder from the indexer for a given namespace and name. +func (s s2iBuilderNamespaceLister) Get(name string) (*v1alpha1.S2iBuilder, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("s2ibuilder"), name) + } + return obj.(*v1alpha1.S2iBuilder), nil +} diff --git a/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go b/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go new file mode 100644 index 000000000..d1d7ab543 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// S2iBuilderTemplateLister helps list S2iBuilderTemplates. +type S2iBuilderTemplateLister interface { + // List lists all S2iBuilderTemplates in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.S2iBuilderTemplate, err error) + // Get retrieves the S2iBuilderTemplate from the index for a given name. + Get(name string) (*v1alpha1.S2iBuilderTemplate, error) + S2iBuilderTemplateListerExpansion +} + +// s2iBuilderTemplateLister implements the S2iBuilderTemplateLister interface. +type s2iBuilderTemplateLister struct { + indexer cache.Indexer +} + +// NewS2iBuilderTemplateLister returns a new S2iBuilderTemplateLister. +func NewS2iBuilderTemplateLister(indexer cache.Indexer) S2iBuilderTemplateLister { + return &s2iBuilderTemplateLister{indexer: indexer} +} + +// List lists all S2iBuilderTemplates in the indexer. +func (s *s2iBuilderTemplateLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilderTemplate, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.S2iBuilderTemplate)) + }) + return ret, err +} + +// Get retrieves the S2iBuilderTemplate from the index for a given name. +func (s *s2iBuilderTemplateLister) Get(name string) (*v1alpha1.S2iBuilderTemplate, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("s2ibuildertemplate"), name) + } + return obj.(*v1alpha1.S2iBuilderTemplate), nil +} diff --git a/pkg/client/listers/devops/v1alpha1/s2irun.go b/pkg/client/listers/devops/v1alpha1/s2irun.go new file mode 100644 index 000000000..a9234bf67 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha1/s2irun.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" +) + +// S2iRunLister helps list S2iRuns. +type S2iRunLister interface { + // List lists all S2iRuns in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) + // S2iRuns returns an object that can list and get S2iRuns. + S2iRuns(namespace string) S2iRunNamespaceLister + S2iRunListerExpansion +} + +// s2iRunLister implements the S2iRunLister interface. +type s2iRunLister struct { + indexer cache.Indexer +} + +// NewS2iRunLister returns a new S2iRunLister. +func NewS2iRunLister(indexer cache.Indexer) S2iRunLister { + return &s2iRunLister{indexer: indexer} +} + +// List lists all S2iRuns in the indexer. +func (s *s2iRunLister) List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.S2iRun)) + }) + return ret, err +} + +// S2iRuns returns an object that can list and get S2iRuns. +func (s *s2iRunLister) S2iRuns(namespace string) S2iRunNamespaceLister { + return s2iRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// S2iRunNamespaceLister helps list and get S2iRuns. +type S2iRunNamespaceLister interface { + // List lists all S2iRuns in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) + // Get retrieves the S2iRun from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.S2iRun, error) + S2iRunNamespaceListerExpansion +} + +// s2iRunNamespaceLister implements the S2iRunNamespaceLister +// interface. +type s2iRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all S2iRuns in the indexer for a given namespace. +func (s s2iRunNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.S2iRun)) + }) + return ret, err +} + +// Get retrieves the S2iRun from the indexer for a given namespace and name. +func (s s2iRunNamespaceLister) Get(name string) (*v1alpha1.S2iRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("s2irun"), name) + } + return obj.(*v1alpha1.S2iRun), nil +} diff --git a/pkg/client/listers/devops/v1alpha3/devopsproject.go b/pkg/client/listers/devops/v1alpha3/devopsproject.go new file mode 100644 index 000000000..8174f1873 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha3/devopsproject.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +// DevOpsProjectLister helps list DevOpsProjects. +type DevOpsProjectLister interface { + // List lists all DevOpsProjects in the indexer. + List(selector labels.Selector) (ret []*v1alpha3.DevOpsProject, err error) + // Get retrieves the DevOpsProject from the index for a given name. + Get(name string) (*v1alpha3.DevOpsProject, error) + DevOpsProjectListerExpansion +} + +// devOpsProjectLister implements the DevOpsProjectLister interface. +type devOpsProjectLister struct { + indexer cache.Indexer +} + +// NewDevOpsProjectLister returns a new DevOpsProjectLister. +func NewDevOpsProjectLister(indexer cache.Indexer) DevOpsProjectLister { + return &devOpsProjectLister{indexer: indexer} +} + +// List lists all DevOpsProjects in the indexer. +func (s *devOpsProjectLister) List(selector labels.Selector) (ret []*v1alpha3.DevOpsProject, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha3.DevOpsProject)) + }) + return ret, err +} + +// Get retrieves the DevOpsProject from the index for a given name. +func (s *devOpsProjectLister) Get(name string) (*v1alpha3.DevOpsProject, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha3.Resource("devopsproject"), name) + } + return obj.(*v1alpha3.DevOpsProject), nil +} diff --git a/pkg/client/listers/devops/v1alpha3/expansion_generated.go b/pkg/client/listers/devops/v1alpha3/expansion_generated.go new file mode 100644 index 000000000..919ab74c3 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha3/expansion_generated.go @@ -0,0 +1,31 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha3 + +// DevOpsProjectListerExpansion allows custom methods to be added to +// DevOpsProjectLister. +type DevOpsProjectListerExpansion interface{} + +// PipelineListerExpansion allows custom methods to be added to +// PipelineLister. +type PipelineListerExpansion interface{} + +// PipelineNamespaceListerExpansion allows custom methods to be added to +// PipelineNamespaceLister. +type PipelineNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/devops/v1alpha3/pipeline.go b/pkg/client/listers/devops/v1alpha3/pipeline.go new file mode 100644 index 000000000..2a26d23b6 --- /dev/null +++ b/pkg/client/listers/devops/v1alpha3/pipeline.go @@ -0,0 +1,94 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +// PipelineLister helps list Pipelines. +type PipelineLister interface { + // List lists all Pipelines in the indexer. + List(selector labels.Selector) (ret []*v1alpha3.Pipeline, err error) + // Pipelines returns an object that can list and get Pipelines. + Pipelines(namespace string) PipelineNamespaceLister + PipelineListerExpansion +} + +// pipelineLister implements the PipelineLister interface. +type pipelineLister struct { + indexer cache.Indexer +} + +// NewPipelineLister returns a new PipelineLister. +func NewPipelineLister(indexer cache.Indexer) PipelineLister { + return &pipelineLister{indexer: indexer} +} + +// List lists all Pipelines in the indexer. +func (s *pipelineLister) List(selector labels.Selector) (ret []*v1alpha3.Pipeline, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha3.Pipeline)) + }) + return ret, err +} + +// Pipelines returns an object that can list and get Pipelines. +func (s *pipelineLister) Pipelines(namespace string) PipelineNamespaceLister { + return pipelineNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// PipelineNamespaceLister helps list and get Pipelines. +type PipelineNamespaceLister interface { + // List lists all Pipelines in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha3.Pipeline, err error) + // Get retrieves the Pipeline from the indexer for a given namespace and name. + Get(name string) (*v1alpha3.Pipeline, error) + PipelineNamespaceListerExpansion +} + +// pipelineNamespaceLister implements the PipelineNamespaceLister +// interface. +type pipelineNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Pipelines in the indexer for a given namespace. +func (s pipelineNamespaceLister) List(selector labels.Selector) (ret []*v1alpha3.Pipeline, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha3.Pipeline)) + }) + return ret, err +} + +// Get retrieves the Pipeline from the indexer for a given namespace and name. +func (s pipelineNamespaceLister) Get(name string) (*v1alpha3.Pipeline, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha3.Resource("pipeline"), name) + } + return obj.(*v1alpha3.Pipeline), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/expansion_generated.go b/pkg/client/listers/iam/v1alpha2/expansion_generated.go new file mode 100644 index 000000000..7d88b069d --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/expansion_generated.go @@ -0,0 +1,39 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +// GlobalRoleListerExpansion allows custom methods to be added to +// GlobalRoleLister. +type GlobalRoleListerExpansion interface{} + +// GlobalRoleBindingListerExpansion allows custom methods to be added to +// GlobalRoleBindingLister. +type GlobalRoleBindingListerExpansion interface{} + +// UserListerExpansion allows custom methods to be added to +// UserLister. +type UserListerExpansion interface{} + +// WorkspaceRoleListerExpansion allows custom methods to be added to +// WorkspaceRoleLister. +type WorkspaceRoleListerExpansion interface{} + +// WorkspaceRoleBindingListerExpansion allows custom methods to be added to +// WorkspaceRoleBindingLister. +type WorkspaceRoleBindingListerExpansion interface{} diff --git a/pkg/client/listers/iam/v1alpha2/globalrole.go b/pkg/client/listers/iam/v1alpha2/globalrole.go new file mode 100644 index 000000000..b601db313 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/globalrole.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// GlobalRoleLister helps list GlobalRoles. +type GlobalRoleLister interface { + // List lists all GlobalRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) + // Get retrieves the GlobalRole from the index for a given name. + Get(name string) (*v1alpha2.GlobalRole, error) + GlobalRoleListerExpansion +} + +// globalRoleLister implements the GlobalRoleLister interface. +type globalRoleLister struct { + indexer cache.Indexer +} + +// NewGlobalRoleLister returns a new GlobalRoleLister. +func NewGlobalRoleLister(indexer cache.Indexer) GlobalRoleLister { + return &globalRoleLister{indexer: indexer} +} + +// List lists all GlobalRoles in the indexer. +func (s *globalRoleLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRole, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.GlobalRole)) + }) + return ret, err +} + +// Get retrieves the GlobalRole from the index for a given name. +func (s *globalRoleLister) Get(name string) (*v1alpha2.GlobalRole, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("globalrole"), name) + } + return obj.(*v1alpha2.GlobalRole), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/globalrolebinding.go b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go new file mode 100644 index 000000000..ea549adc1 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/globalrolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// GlobalRoleBindingLister helps list GlobalRoleBindings. +type GlobalRoleBindingLister interface { + // List lists all GlobalRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) + // Get retrieves the GlobalRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.GlobalRoleBinding, error) + GlobalRoleBindingListerExpansion +} + +// globalRoleBindingLister implements the GlobalRoleBindingLister interface. +type globalRoleBindingLister struct { + indexer cache.Indexer +} + +// NewGlobalRoleBindingLister returns a new GlobalRoleBindingLister. +func NewGlobalRoleBindingLister(indexer cache.Indexer) GlobalRoleBindingLister { + return &globalRoleBindingLister{indexer: indexer} +} + +// List lists all GlobalRoleBindings in the indexer. +func (s *globalRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.GlobalRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.GlobalRoleBinding)) + }) + return ret, err +} + +// Get retrieves the GlobalRoleBinding from the index for a given name. +func (s *globalRoleBindingLister) Get(name string) (*v1alpha2.GlobalRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("globalrolebinding"), name) + } + return obj.(*v1alpha2.GlobalRoleBinding), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/user.go b/pkg/client/listers/iam/v1alpha2/user.go new file mode 100644 index 000000000..99090df75 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/user.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// UserLister helps list Users. +type UserLister interface { + // List lists all Users in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.User, err error) + // Get retrieves the User from the index for a given name. + Get(name string) (*v1alpha2.User, error) + UserListerExpansion +} + +// userLister implements the UserLister interface. +type userLister struct { + indexer cache.Indexer +} + +// NewUserLister returns a new UserLister. +func NewUserLister(indexer cache.Indexer) UserLister { + return &userLister{indexer: indexer} +} + +// List lists all Users in the indexer. +func (s *userLister) List(selector labels.Selector) (ret []*v1alpha2.User, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.User)) + }) + return ret, err +} + +// Get retrieves the User from the index for a given name. +func (s *userLister) Get(name string) (*v1alpha2.User, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("user"), name) + } + return obj.(*v1alpha2.User), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerole.go b/pkg/client/listers/iam/v1alpha2/workspacerole.go new file mode 100644 index 000000000..8e9e74965 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerole.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleLister helps list WorkspaceRoles. +type WorkspaceRoleLister interface { + // List lists all WorkspaceRoles in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) + // Get retrieves the WorkspaceRole from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRole, error) + WorkspaceRoleListerExpansion +} + +// workspaceRoleLister implements the WorkspaceRoleLister interface. +type workspaceRoleLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleLister returns a new WorkspaceRoleLister. +func NewWorkspaceRoleLister(indexer cache.Indexer) WorkspaceRoleLister { + return &workspaceRoleLister{indexer: indexer} +} + +// List lists all WorkspaceRoles in the indexer. +func (s *workspaceRoleLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRole, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRole)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRole from the index for a given name. +func (s *workspaceRoleLister) Get(name string) (*v1alpha2.WorkspaceRole, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerole"), name) + } + return obj.(*v1alpha2.WorkspaceRole), nil +} diff --git a/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go new file mode 100644 index 000000000..175f17d31 --- /dev/null +++ b/pkg/client/listers/iam/v1alpha2/workspacerolebinding.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" +) + +// WorkspaceRoleBindingLister helps list WorkspaceRoleBindings. +type WorkspaceRoleBindingLister interface { + // List lists all WorkspaceRoleBindings in the indexer. + List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) + // Get retrieves the WorkspaceRoleBinding from the index for a given name. + Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) + WorkspaceRoleBindingListerExpansion +} + +// workspaceRoleBindingLister implements the WorkspaceRoleBindingLister interface. +type workspaceRoleBindingLister struct { + indexer cache.Indexer +} + +// NewWorkspaceRoleBindingLister returns a new WorkspaceRoleBindingLister. +func NewWorkspaceRoleBindingLister(indexer cache.Indexer) WorkspaceRoleBindingLister { + return &workspaceRoleBindingLister{indexer: indexer} +} + +// List lists all WorkspaceRoleBindings in the indexer. +func (s *workspaceRoleBindingLister) List(selector labels.Selector) (ret []*v1alpha2.WorkspaceRoleBinding, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.WorkspaceRoleBinding)) + }) + return ret, err +} + +// Get retrieves the WorkspaceRoleBinding from the index for a given name. +func (s *workspaceRoleBindingLister) Get(name string) (*v1alpha2.WorkspaceRoleBinding, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("workspacerolebinding"), name) + } + return obj.(*v1alpha2.WorkspaceRoleBinding), nil +} diff --git a/pkg/client/listers/network/v1alpha1/expansion_generated.go b/pkg/client/listers/network/v1alpha1/expansion_generated.go index 85c61d6d0..664c9488c 100644 --- a/pkg/client/listers/network/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/network/v1alpha1/expansion_generated.go @@ -25,7 +25,3 @@ type NamespaceNetworkPolicyListerExpansion interface{} // NamespaceNetworkPolicyNamespaceListerExpansion allows custom methods to be added to // NamespaceNetworkPolicyNamespaceLister. type NamespaceNetworkPolicyNamespaceListerExpansion interface{} - -// WorkspaceNetworkPolicyListerExpansion allows custom methods to be added to -// WorkspaceNetworkPolicyLister. -type WorkspaceNetworkPolicyListerExpansion interface{} diff --git a/pkg/client/listers/network/v1alpha1/workspacenetworkpolicy.go b/pkg/client/listers/network/v1alpha1/workspacenetworkpolicy.go deleted file mode 100644 index 16d90efc1..000000000 --- a/pkg/client/listers/network/v1alpha1/workspacenetworkpolicy.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" - v1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) - -// WorkspaceNetworkPolicyLister helps list WorkspaceNetworkPolicies. -type WorkspaceNetworkPolicyLister interface { - // List lists all WorkspaceNetworkPolicies in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.WorkspaceNetworkPolicy, err error) - // Get retrieves the WorkspaceNetworkPolicy from the index for a given name. - Get(name string) (*v1alpha1.WorkspaceNetworkPolicy, error) - WorkspaceNetworkPolicyListerExpansion -} - -// workspaceNetworkPolicyLister implements the WorkspaceNetworkPolicyLister interface. -type workspaceNetworkPolicyLister struct { - indexer cache.Indexer -} - -// NewWorkspaceNetworkPolicyLister returns a new WorkspaceNetworkPolicyLister. -func NewWorkspaceNetworkPolicyLister(indexer cache.Indexer) WorkspaceNetworkPolicyLister { - return &workspaceNetworkPolicyLister{indexer: indexer} -} - -// List lists all WorkspaceNetworkPolicies in the indexer. -func (s *workspaceNetworkPolicyLister) List(selector labels.Selector) (ret []*v1alpha1.WorkspaceNetworkPolicy, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.WorkspaceNetworkPolicy)) - }) - return ret, err -} - -// Get retrieves the WorkspaceNetworkPolicy from the index for a given name. -func (s *workspaceNetworkPolicyLister) Get(name string) (*v1alpha1.WorkspaceNetworkPolicy, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("workspacenetworkpolicy"), name) - } - return obj.(*v1alpha1.WorkspaceNetworkPolicy), nil -} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index a3a4482d3..4fe75a331 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -31,11 +31,10 @@ const ( PorterNamespace = "porter-system" IngressControllerNamespace = KubeSphereControlNamespace AdminUserName = "admin" - DataHome = "/etc/kubesphere" - IngressControllerFolder = DataHome + "/ingress-controller" IngressControllerPrefix = "kubesphere-router-" WorkspaceLabelKey = "kubesphere.io/workspace" + NamespaceLabelKey = "kubesphere.io/namespace" DisplayNameAnnotationKey = "kubesphere.io/alias-name" DescriptionAnnotationKey = "kubesphere.io/description" CreatorAnnotationKey = "kubesphere.io/creator" @@ -48,6 +47,7 @@ const ( WorkspacesManager = "workspaces-manager" DevopsOwner = "owner" DevopsReporter = "reporter" + DevOpsProjectLabelKey = "kubesphere.io/devopsproject" UserNameHeader = "X-Token-Username" @@ -60,6 +60,7 @@ const ( OpenpitrixTag = "Openpitrix Resources" VerificationTag = "Verification" RegistryTag = "Docker Registry" + NetworkTopologyTag = "Network Topology" UserResourcesTag = "User Resources" DevOpsProjectTag = "DevOps Project" DevOpsProjectCredentialTag = "DevOps Project Credential" @@ -77,8 +78,9 @@ const ( WorkloadMetricsTag = "Workload Metrics" WorkspaceMetricsTag = "Workspace Metrics" ComponentMetricsTag = "Component Metrics" + CustomMetricsTag = "Custom Metrics" LogQueryTag = "Log Query" - FluentBitSetting = "Fluent Bit Setting" + TerminalTag = "Terminal" ) var ( diff --git a/pkg/controller/add_clusterrolebinding.go b/pkg/controller/add_clusterrolebinding.go deleted file mode 100644 index 2fc0def5d..000000000 --- a/pkg/controller/add_clusterrolebinding.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 controller - -import "kubesphere.io/kubesphere/pkg/controller/clusterrolebinding" - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, clusterrolebinding.Add) -} diff --git a/pkg/controller/add_namespace.go b/pkg/controller/add_namespace.go deleted file mode 100644 index 2e1a0b5d0..000000000 --- a/pkg/controller/add_namespace.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 controller - -import "kubesphere.io/kubesphere/pkg/controller/namespace" - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, namespace.Add) -} diff --git a/pkg/controller/add_strategy.go b/pkg/controller/add_strategy.go deleted file mode 100644 index 90c465c59..000000000 --- a/pkg/controller/add_strategy.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 controller - -import ( - "github.com/kubernetes-sigs/application/pkg/controller/application" -) - -func init() { - // Add application to manager functions - AddToManagerFuncs = append(AddToManagerFuncs, application.Add) - -} diff --git a/pkg/controller/add_workspace.go b/pkg/controller/add_workspace.go deleted file mode 100644 index 1194ff0a7..000000000 --- a/pkg/controller/add_workspace.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 controller - -import "kubesphere.io/kubesphere/pkg/controller/workspace" - -func init() { - // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. - AddToManagerFuncs = append(AddToManagerFuncs, workspace.Add) -} diff --git a/pkg/controller/cluster/cluster_controller.go b/pkg/controller/cluster/cluster_controller.go new file mode 100644 index 000000000..3d0e7044f --- /dev/null +++ b/pkg/controller/cluster/cluster_controller.go @@ -0,0 +1,561 @@ +package cluster + +import ( + "fmt" + v1 "k8s.io/api/core/v1" + apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/intstr" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + clusterclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1" + clusterinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster/v1alpha1" + clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1" + "math/rand" + "reflect" + fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" + "time" +) + +const ( + // maxRetries is the number of times a service will be retried before it is dropped out of the queue. + // With the current rate-limiter in use (5ms*2^(maxRetries-1)) the following numbers represent the + // sequence of delays between successive queuings of a service. + // + // 5ms, 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.3s, 2.6s, 5.1s, 10.2s, 20.4s, 41s, 82s + maxRetries = 15 + + kubefedNamespace = "kube-federation-system" + + hostClusterName = "kubesphere" + + // allocate kubernetesAPIServer port in range [portRangeMin, portRangeMax] for agents if port is not specified + // kubesphereAPIServer port is defaulted to kubernetesAPIServerPort + 10000 + portRangeMin = 6000 + portRangeMax = 7000 + + // Service port + kubernetesPort = 6443 + kubespherePort = 80 + + defaultAgentNamespace = "kubesphere-system" +) + +type ClusterController struct { + eventBroadcaster record.EventBroadcaster + eventRecorder record.EventRecorder + + client kubernetes.Interface + hostConfig *rest.Config + + clusterClient clusterclient.ClusterInterface + + clusterLister clusterlister.ClusterLister + clusterHasSynced cache.InformerSynced + + queue workqueue.RateLimitingInterface + + workerLoopPeriod time.Duration +} + +func NewClusterController( + client kubernetes.Interface, + config *rest.Config, + clusterInformer clusterinformer.ClusterInformer, + clusterClient clusterclient.ClusterInterface, +) *ClusterController { + + broadcaster := record.NewBroadcaster() + broadcaster.StartLogging(func(format string, args ...interface{}) { + klog.Info(fmt.Sprintf(format, args)) + }) + broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.CoreV1().Events("")}) + recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cluster-controller"}) + + c := &ClusterController{ + eventBroadcaster: broadcaster, + eventRecorder: recorder, + client: client, + hostConfig: config, + clusterClient: clusterClient, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "cluster"), + workerLoopPeriod: time.Second, + } + + c.clusterLister = clusterInformer.Lister() + c.clusterHasSynced = clusterInformer.Informer().HasSynced + + clusterInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: c.addCluster, + UpdateFunc: func(oldObj, newObj interface{}) { + newCluster := newObj.(*clusterv1alpha1.Cluster) + oldCluster := oldObj.(*clusterv1alpha1.Cluster) + if newCluster.ResourceVersion == oldCluster.ResourceVersion { + return + } + c.addCluster(newObj) + }, + DeleteFunc: c.addCluster, + }) + + return c +} + +func (c *ClusterController) Start(stopCh <-chan struct{}) error { + return c.Run(5, stopCh) +} + +func (c *ClusterController) Run(workers int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.queue.ShutDown() + + klog.V(0).Info("starting cluster controller") + defer klog.Info("shutting down cluster controller") + + if !cache.WaitForCacheSync(stopCh, c.clusterHasSynced) { + return fmt.Errorf("failed to wait for caches to sync") + } + + for i := 0; i < workers; i++ { + go wait.Until(c.worker, c.workerLoopPeriod, stopCh) + } + + <-stopCh + return nil +} + +func (c *ClusterController) worker() { + for c.processNextItem() { + } +} + +func (c *ClusterController) processNextItem() bool { + key, quit := c.queue.Get() + if quit { + return false + } + + defer c.queue.Done(key) + + err := c.syncCluster(key.(string)) + c.handleErr(err, key) + return true +} + +func (c *ClusterController) syncCluster(key string) error { + startTime := time.Now() + + _, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + klog.Errorf("not a valid controller key %s, %#v", key, err) + return err + } + + defer func() { + klog.V(4).Infof("Finished syncing cluster %s in %s", name, time.Since(startTime)) + }() + + cluster, err := c.clusterLister.Get(name) + if err != nil { + // cluster not found, possibly been deleted + // need to do the cleanup + if errors.IsNotFound(err) { + return nil + } + + klog.Errorf("Failed to get cluster with name %s, %#v", name, err) + return err + } + + // proxy service name if needed + serviceName := fmt.Sprintf("mc-%s", cluster.Name) + + if cluster.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !sets.NewString(cluster.ObjectMeta.Finalizers...).Has(clusterv1alpha1.Finalizer) { + cluster.ObjectMeta.Finalizers = append(cluster.ObjectMeta.Finalizers, clusterv1alpha1.Finalizer) + if cluster, err = c.clusterClient.Update(cluster); err != nil { + return err + } + } + } else { + // The object is being deleted + if sets.NewString(cluster.ObjectMeta.Finalizers...).Has(clusterv1alpha1.Finalizer) { + // need to unJoin federation first, before there are + // some cleanup work to do in member cluster which depends + // agent to proxy traffic + err = c.unJoinFederation(nil, name) + if err != nil { + klog.Errorf("Failed to unjoin federation for cluster %s, error %v", name, err) + return err + } + + _, err = c.client.CoreV1().Services(defaultAgentNamespace).Get(serviceName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + // nothing to do + } else { + klog.Errorf("Failed to get proxy service %s, error %v", serviceName, err) + return err + } + } else { + err = c.client.CoreV1().Services(defaultAgentNamespace).Delete(serviceName, metav1.NewDeleteOptions(0)) + if err != nil { + klog.Errorf("Unable to delete service %s, error %v", serviceName, err) + return err + } + } + + finalizers := sets.NewString(cluster.ObjectMeta.Finalizers...) + finalizers.Delete(clusterv1alpha1.Finalizer) + cluster.ObjectMeta.Finalizers = finalizers.List() + if _, err = c.clusterClient.Update(cluster); err != nil { + return err + } + } + return nil + } + + oldCluster := cluster.DeepCopy() + + // prepare for proxy to member cluster + if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeProxy { + if cluster.Spec.Connection.KubeSphereAPIServerPort == 0 || + cluster.Spec.Connection.KubernetesAPIServerPort == 0 { + port, err := c.allocatePort() + if err != nil { + klog.Error(err) + return err + } + + cluster.Spec.Connection.KubernetesAPIServerPort = port + cluster.Spec.Connection.KubeSphereAPIServerPort = port + 10000 + } + + // token uninitialized, generate a new token + if len(cluster.Spec.Connection.Token) == 0 { + cluster.Spec.Connection.Token = c.generateToken() + } + + mcService := v1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceName, + Namespace: cluster.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/name": serviceName, + "app": serviceName, + }, + }, + Spec: v1.ServiceSpec{ + Selector: map[string]string{ + "app.kubernetes.io/name": "tower", + "app": "tower", + }, + Ports: []v1.ServicePort{ + { + Name: "kubernetes", + Protocol: v1.ProtocolTCP, + Port: kubernetesPort, + TargetPort: intstr.FromInt(int(cluster.Spec.Connection.KubernetesAPIServerPort)), + }, + { + Name: "kubesphere", + Protocol: v1.ProtocolTCP, + Port: kubespherePort, + TargetPort: intstr.FromInt(int(cluster.Spec.Connection.KubeSphereAPIServerPort)), + }, + }, + }, + } + + service, err := c.client.CoreV1().Services(defaultAgentNamespace).Get(serviceName, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + service, err = c.client.CoreV1().Services(defaultAgentNamespace).Create(&mcService) + if err != nil { + return err + } + } + + return err + } else { + if !reflect.DeepEqual(service.Spec, mcService.Spec) { + mcService.ObjectMeta = service.ObjectMeta + mcService.Spec.ClusterIP = service.Spec.ClusterIP + + service, err = c.client.CoreV1().Services(defaultAgentNamespace).Update(&mcService) + if err != nil { + return err + } + } + } + + // populated the kubernetes apiEndpoint and kubesphere apiEndpoint + cluster.Spec.Connection.KubernetesAPIEndpoint = fmt.Sprintf("https://%s:%d", service.Spec.ClusterIP, kubernetesPort) + cluster.Spec.Connection.KubeSphereAPIEndpoint = fmt.Sprintf("http://%s:%d", service.Spec.ClusterIP, kubespherePort) + + if !reflect.DeepEqual(oldCluster.Spec, cluster.Spec) { + cluster, err = c.clusterClient.Update(cluster) + if err != nil { + klog.Errorf("Error updating cluster %s, error %s", cluster.Name, err) + return err + } + return nil + } + } + + if len(cluster.Spec.Connection.KubeConfig) == 0 { + return nil + } + + var clientSet kubernetes.Interface + var clusterConfig *rest.Config + + // prepare for + clientConfig, err := clientcmd.NewClientConfigFromBytes(cluster.Spec.Connection.KubeConfig) + if err != nil { + klog.Errorf("Unable to create client config from kubeconfig bytes, %#v", err) + return err + } + + clusterConfig, err = clientConfig.ClientConfig() + if err != nil { + klog.Errorf("Failed to get client config, %#v", err) + return err + } + + clientSet, err = kubernetes.NewForConfig(clusterConfig) + if err != nil { + klog.Errorf("Failed to create ClientSet from config, %#v", err) + return nil + } + + if !cluster.Spec.JoinFederation { // trying to unJoin federation + err = c.unJoinFederation(clusterConfig, cluster.Name) + if err != nil { + klog.Errorf("Failed to unJoin federation for cluster %s, error %v", cluster.Name, err) + c.eventRecorder.Event(cluster, v1.EventTypeWarning, "UnJoinFederation", err.Error()) + return err + } + } else { // join federation + _, err = c.joinFederation(clusterConfig, cluster.Name, cluster.Labels) + if err != nil { + klog.Errorf("Failed to join federation for cluster %s, error %v", cluster.Name, err) + c.eventRecorder.Event(cluster, v1.EventTypeWarning, "JoinFederation", err.Error()) + return err + } + c.eventRecorder.Event(cluster, v1.EventTypeNormal, "JoinFederation", "Cluster has joined federation.") + + federationReadyCondition := clusterv1alpha1.ClusterCondition{ + Type: clusterv1alpha1.ClusterFederated, + Status: v1.ConditionTrue, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: "", + Message: "Cluster has joined federation control plane successfully", + } + + c.updateClusterCondition(cluster, federationReadyCondition) + } + + // cluster agent is ready, we can pull kubernetes cluster info through agent + // since there is no agent necessary for host cluster, so updates for host cluster + // is safe. + if isConditionTrue(cluster, clusterv1alpha1.ClusterAgentAvailable) || + cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeDirect { + version, err := clientSet.Discovery().ServerVersion() + if err != nil { + klog.Errorf("Failed to get kubernetes version, %#v", err) + return err + } + + cluster.Status.KubernetesVersion = version.GitVersion + + nodes, err := clientSet.CoreV1().Nodes().List(metav1.ListOptions{}) + if err != nil { + klog.Errorf("Failed to get cluster nodes, %#v", err) + return err + } + + cluster.Status.NodeCount = len(nodes.Items) + + clusterReadyCondition := clusterv1alpha1.ClusterCondition{ + Type: clusterv1alpha1.ClusterReady, + Status: v1.ConditionTrue, + LastUpdateTime: metav1.Now(), + LastTransitionTime: metav1.Now(), + Reason: string(clusterv1alpha1.ClusterReady), + Message: "Cluster is available now", + } + + c.updateClusterCondition(cluster, clusterReadyCondition) + } + + if !reflect.DeepEqual(oldCluster, cluster) { + _, err = c.clusterClient.Update(cluster) + if err != nil { + klog.Errorf("Failed to update cluster status, %#v", err) + return err + } + } + + return nil +} + +func (c *ClusterController) addCluster(obj interface{}) { + cluster := obj.(*clusterv1alpha1.Cluster) + + key, err := cache.MetaNamespaceKeyFunc(obj) + if err != nil { + utilruntime.HandleError(fmt.Errorf("get cluster key %s failed", cluster.Name)) + return + } + + c.queue.Add(key) +} + +func (c *ClusterController) handleErr(err error, key interface{}) { + if err == nil { + c.queue.Forget(key) + return + } + + if c.queue.NumRequeues(key) < maxRetries { + klog.V(2).Infof("Error syncing cluster %s, retrying, %v", key, err) + c.queue.AddRateLimited(key) + return + } + + klog.V(4).Infof("Dropping cluster %s out of the queue.", key) + c.queue.Forget(key) + utilruntime.HandleError(err) +} + +func isConditionTrue(cluster *clusterv1alpha1.Cluster, conditionType clusterv1alpha1.ClusterConditionType) bool { + for _, condition := range cluster.Status.Conditions { + if condition.Type == conditionType && condition.Status == v1.ConditionTrue { + return true + } + } + return false +} + +// updateClusterCondition updates condition in cluster conditions using giving condition +// adds condition if not existed +func (c *ClusterController) updateClusterCondition(cluster *clusterv1alpha1.Cluster, condition clusterv1alpha1.ClusterCondition) { + if cluster.Status.Conditions == nil { + cluster.Status.Conditions = make([]clusterv1alpha1.ClusterCondition, 0) + } + + newConditions := make([]clusterv1alpha1.ClusterCondition, 0) + needToUpdate := true + for _, cond := range cluster.Status.Conditions { + if cond.Type == condition.Type { + if cond.Status == condition.Status { + needToUpdate = false + continue + } else { + newConditions = append(newConditions, cond) + } + } + newConditions = append(newConditions, cond) + } + + if needToUpdate { + newConditions = append(newConditions, condition) + cluster.Status.Conditions = newConditions + } +} + +func isHostCluster(cluster *clusterv1alpha1.Cluster) bool { + for k, v := range cluster.Annotations { + if k == clusterv1alpha1.IsHostCluster && v == "true" { + return true + } + } + + return false +} + +// joinFederation joins a cluster into federation clusters. +// return nil error if kubefed cluster already exists. +func (c *ClusterController) joinFederation(clusterConfig *rest.Config, joiningClusterName string, labels map[string]string) (*fedv1b1.KubeFedCluster, error) { + + return joinClusterForNamespace(c.hostConfig, + clusterConfig, + kubefedNamespace, + kubefedNamespace, + hostClusterName, + joiningClusterName, + fmt.Sprintf("%s-secret", joiningClusterName), + labels, + apiextv1b1.ClusterScoped, + false, + false) +} + +// unJoinFederation unjoins a cluster from federation control plane. +func (c *ClusterController) unJoinFederation(clusterConfig *rest.Config, unjoiningClusterName string) error { + return unjoinCluster(c.hostConfig, + clusterConfig, + kubefedNamespace, + hostClusterName, + unjoiningClusterName, + true, + false) +} + +// allocatePort find a available port between [portRangeMin, portRangeMax] in maximumRetries +// TODO: only works with handful clusters +func (c *ClusterController) allocatePort() (uint16, error) { + rand.Seed(time.Now().UnixNano()) + + clusters, err := c.clusterLister.List(labels.Everything()) + if err != nil { + return 0, err + } + + const maximumRetries = 10 + for i := 0; i < maximumRetries; i++ { + collision := false + port := uint16(portRangeMin + rand.Intn(portRangeMax-portRangeMin+1)) + + for _, item := range clusters { + if item.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeProxy && + item.Spec.Connection.KubernetesAPIServerPort != 0 && + item.Spec.Connection.KubeSphereAPIServerPort == port { + collision = true + break + } + } + + if !collision { + return port, nil + } + } + + return 0, fmt.Errorf("unable to allocate port after %d retries", maximumRetries) +} + +// generateToken returns a random 32-byte string as token +func (c *ClusterController) generateToken() string { + rand.Seed(time.Now().UnixNano()) + b := make([]byte, 32) + rand.Read(b) + return fmt.Sprintf("%x", b) +} diff --git a/pkg/controller/cluster/cluster_controller_test.go b/pkg/controller/cluster/cluster_controller_test.go new file mode 100644 index 000000000..916b1b53b --- /dev/null +++ b/pkg/controller/cluster/cluster_controller_test.go @@ -0,0 +1 @@ +package cluster diff --git a/pkg/controller/cluster/helper.go b/pkg/controller/cluster/helper.go new file mode 100644 index 000000000..916b1b53b --- /dev/null +++ b/pkg/controller/cluster/helper.go @@ -0,0 +1 @@ +package cluster diff --git a/pkg/controller/cluster/join.go b/pkg/controller/cluster/join.go new file mode 100644 index 000000000..f14a106f0 --- /dev/null +++ b/pkg/controller/cluster/join.go @@ -0,0 +1,720 @@ +package cluster + +import ( + "context" + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextv1b1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + kubeclient "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/klog" + "reflect" + "sigs.k8s.io/kubefed/pkg/kubefedctl/util" + "time" + + fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" + genericclient "sigs.k8s.io/kubefed/pkg/client/generic" +) + +var ( + // Policy rules allowing full access to resources in the cluster + // or namespace. + namespacedPolicyRules = []rbacv1.PolicyRule{ + { + Verbs: []string{rbacv1.VerbAll}, + APIGroups: []string{rbacv1.APIGroupAll}, + Resources: []string{rbacv1.ResourceAll}, + }, + } + clusterPolicyRules = []rbacv1.PolicyRule{ + namespacedPolicyRules[0], + { + NonResourceURLs: []string{rbacv1.NonResourceAll}, + Verbs: []string{"get"}, + }, + } +) + +const ( + tokenKey = "token" + serviceAccountSecretTimeout = 30 * time.Second +) + +// joinClusterForNamespace registers a cluster with a KubeFed control +// plane. The KubeFed namespace in the joining cluster is provided by +// the joiningNamespace parameter. +func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedNamespace, + joiningNamespace, hostClusterName, joiningClusterName, secretName string, labels map[string]string, + scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { + + hostClientset, err := HostClientset(hostConfig) + if err != nil { + klog.V(2).Infof("Failed to get host cluster clientset: %v", err) + return nil, err + } + + clusterClientset, err := ClusterClientset(clusterConfig) + if err != nil { + klog.V(2).Infof("Failed to get joining cluster clientset: %v", err) + return nil, err + } + + client, err := genericclient.New(hostConfig) + if err != nil { + klog.V(2).Infof("Failed to get kubefed clientset: %v", err) + return nil, err + } + + klog.V(2).Infof("Performing preflight checks.") + err = performPreflightChecks(clusterClientset, joiningClusterName, hostClusterName, joiningNamespace, errorOnExisting) + if err != nil { + return nil, err + } + + klog.V(2).Infof("Creating %s namespace in joining cluster", joiningNamespace) + _, err = createKubeFedNamespace(clusterClientset, joiningNamespace, joiningClusterName, dryRun) + if err != nil { + klog.V(2).Infof("Error creating %s namespace in joining cluster: %v", joiningNamespace, err) + return nil, err + } + klog.V(2).Infof("Created %s namespace in joining cluster", joiningNamespace) + + saName, err := createAuthorizedServiceAccount(clusterClientset, joiningNamespace, joiningClusterName, hostClusterName, scope, dryRun, errorOnExisting) + if err != nil { + return nil, err + } + + secret, _, err := populateSecretInHostCluster(clusterClientset, hostClientset, + saName, kubefedNamespace, joiningNamespace, joiningClusterName, secretName, dryRun) + if err != nil { + klog.V(2).Infof("Error creating secret in host cluster: %s due to: %v", hostClusterName, err) + return nil, err + } + + var disabledTLSValidations []fedv1b1.TLSValidation + if clusterConfig.TLSClientConfig.Insecure { + disabledTLSValidations = append(disabledTLSValidations, fedv1b1.TLSAll) + } + + kubefedCluster, err := createKubeFedCluster(client, joiningClusterName, clusterConfig.Host, + secret.Name, kubefedNamespace, clusterConfig.CAData, disabledTLSValidations, labels, dryRun, errorOnExisting) + if err != nil { + klog.V(2).Infof("Failed to create federated cluster resource: %v", err) + return nil, err + } + + klog.V(2).Info("Created federated cluster resource") + return kubefedCluster, nil +} + +// performPreflightChecks checks that the host and joining clusters are in +// a consistent state. +func performPreflightChecks(clusterClientset kubeclient.Interface, name, hostClusterName, + kubefedNamespace string, errorOnExisting bool) error { + // Make sure there is no existing service account in the joining cluster. + saName := util.ClusterServiceAccountName(name, hostClusterName) + _, err := clusterClientset.CoreV1().ServiceAccounts(kubefedNamespace).Get(saName, metav1.GetOptions{}) + + switch { + case apierrors.IsNotFound(err): + return nil + case err != nil: + return err + case errorOnExisting: + return errors.Errorf("service account: %s already exists in joining cluster: %s", saName, name) + default: + klog.V(2).Infof("Service account %s already exists in joining cluster %s", saName, name) + return nil + } +} + +// createKubeFedCluster creates a federated cluster resource that associates +// the cluster and secret. +func createKubeFedCluster(client genericclient.Client, joiningClusterName, apiEndpoint, + secretName, kubefedNamespace string, caBundle []byte, disabledTLSValidations []fedv1b1.TLSValidation, + labels map[string]string, dryRun, errorOnExisting bool) (*fedv1b1.KubeFedCluster, error) { + fedCluster := &fedv1b1.KubeFedCluster{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: kubefedNamespace, + Name: joiningClusterName, + Labels: labels, + }, + Spec: fedv1b1.KubeFedClusterSpec{ + APIEndpoint: apiEndpoint, + CABundle: caBundle, + SecretRef: fedv1b1.LocalSecretReference{ + Name: secretName, + }, + DisabledTLSValidations: disabledTLSValidations, + }, + } + + if dryRun { + return fedCluster, nil + } + + existingFedCluster := &fedv1b1.KubeFedCluster{} + err := client.Get(context.TODO(), existingFedCluster, kubefedNamespace, joiningClusterName) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not retrieve federated cluster %s due to %v", joiningClusterName, err) + return nil, err + case err == nil && errorOnExisting: + return nil, errors.Errorf("federated cluster %s already exists in host cluster", joiningClusterName) + case err == nil: + existingFedCluster.Spec = fedCluster.Spec + existingFedCluster.Labels = labels + err = client.Update(context.TODO(), existingFedCluster) + if err != nil { + klog.V(2).Infof("Could not update federated cluster %s due to %v", fedCluster.Name, err) + return nil, err + } + return existingFedCluster, nil + default: + err = client.Create(context.TODO(), fedCluster) + if err != nil { + klog.V(2).Infof("Could not create federated cluster %s due to %v", fedCluster.Name, err) + return nil, err + } + return fedCluster, nil + } +} + +// createKubeFedNamespace creates the kubefed namespace in the cluster +// associated with clusterClientset, if it doesn't already exist. +func createKubeFedNamespace(clusterClientset kubeclient.Interface, kubefedNamespace, + joiningClusterName string, dryRun bool) (*corev1.Namespace, error) { + fedNamespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: kubefedNamespace, + }, + } + + if dryRun { + return fedNamespace, nil + } + + _, err := clusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + klog.V(2).Infof("Could not get %s namespace: %v", kubefedNamespace, err) + return nil, err + } + + if err == nil { + klog.V(2).Infof("Already existing %s namespace", kubefedNamespace) + return fedNamespace, nil + } + + // Not found, so create. + _, err = clusterClientset.CoreV1().Namespaces().Create(fedNamespace) + if err != nil && !apierrors.IsAlreadyExists(err) { + klog.V(2).Infof("Could not create %s namespace: %v", kubefedNamespace, err) + return nil, err + } + return fedNamespace, nil +} + +// createAuthorizedServiceAccount creates a service account and grants +// the privileges required by the KubeFed control plane to manage +// resources in the joining cluster. The name of the created service +// account is returned on success. +func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface, + namespace, joiningClusterName, hostClusterName string, + scope apiextv1b1.ResourceScope, dryRun, errorOnExisting bool) (string, error) { + + klog.V(2).Infof("Creating service account in joining cluster: %s", joiningClusterName) + + saName, err := createServiceAccount(joiningClusterClientset, namespace, + joiningClusterName, hostClusterName, dryRun, errorOnExisting) + if err != nil { + klog.V(2).Infof("Error creating service account: %s in joining cluster: %s due to: %v", + saName, joiningClusterName, err) + return "", err + } + + klog.V(2).Infof("Created service account: %s in joining cluster: %s", saName, joiningClusterName) + + if scope == apiextv1b1.NamespaceScoped { + klog.V(2).Infof("Creating role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) + + err = createRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting) + if err != nil { + klog.V(2).Infof("Error creating role and binding for service account: %s in joining cluster: %s due to: %v", saName, joiningClusterName, err) + return "", err + } + + klog.V(2).Infof("Created role and binding for service account: %s in joining cluster: %s", + saName, joiningClusterName) + + klog.V(2).Infof("Creating health check cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) + + err = createHealthCheckClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, + dryRun, errorOnExisting) + if err != nil { + klog.V(2).Infof("Error creating health check cluster role and binding for service account: %s in joining cluster: %s due to: %v", + saName, joiningClusterName, err) + return "", err + } + + klog.V(2).Infof("Created health check cluster role and binding for service account: %s in joining cluster: %s", + saName, joiningClusterName) + + } else { + klog.V(2).Infof("Creating cluster role and binding for service account: %s in joining cluster: %s", saName, joiningClusterName) + + err = createClusterRoleAndBinding(joiningClusterClientset, saName, namespace, joiningClusterName, dryRun, errorOnExisting) + if err != nil { + klog.V(2).Infof("Error creating cluster role and binding for service account: %s in joining cluster: %s due to: %v", + saName, joiningClusterName, err) + return "", err + } + + klog.V(2).Infof("Created cluster role and binding for service account: %s in joining cluster: %s", + saName, joiningClusterName) + } + + return saName, nil +} + +// createServiceAccount creates a service account in the cluster associated +// with clusterClientset with credentials that will be used by the host cluster +// to access its API server. +func createServiceAccount(clusterClientset kubeclient.Interface, namespace, + joiningClusterName, hostClusterName string, dryRun, errorOnExisting bool) (string, error) { + saName := util.ClusterServiceAccountName(joiningClusterName, hostClusterName) + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: saName, + Namespace: namespace, + }, + } + + if dryRun { + return saName, nil + } + + // Create a new service account. + _, err := clusterClientset.CoreV1().ServiceAccounts(namespace).Create(sa) + switch { + case apierrors.IsAlreadyExists(err) && errorOnExisting: + klog.V(2).Infof("Service account %s/%s already exists in target cluster %s", namespace, saName, joiningClusterName) + return "", err + case err != nil && !apierrors.IsAlreadyExists(err): + klog.V(2).Infof("Could not create service account %s/%s in target cluster %s due to: %v", namespace, saName, joiningClusterName, err) + return "", err + default: + return saName, nil + } +} + +func bindingSubjects(saName, namespace string) []rbacv1.Subject { + return []rbacv1.Subject{ + { + Kind: rbacv1.ServiceAccountKind, + Name: saName, + Namespace: namespace, + }, + } +} + +// createClusterRoleAndBinding creates an RBAC cluster role and +// binding that allows the service account identified by saName to +// access all resources in all namespaces in the cluster associated +// with clientset. +func createClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { + if dryRun { + return nil + } + + roleName := util.RoleName(saName) + + role := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Rules: clusterPolicyRules, + } + existingRole, err := clientset.RbacV1().ClusterRoles().Get(roleName, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not get cluster role for service account %s in joining cluster %s due to %v", + saName, clusterName, err) + return err + case err == nil && errorOnExisting: + return errors.Errorf("cluster role for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + existingRole.Rules = role.Rules + _, err := clientset.RbacV1().ClusterRoles().Update(existingRole) + if err != nil { + klog.V(2).Infof("Could not update cluster role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + default: // role was not found + _, err := clientset.RbacV1().ClusterRoles().Create(role) + if err != nil { + klog.V(2).Infof("Could not create cluster role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + + // TODO: This should limit its access to only necessary resources. + binding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Subjects: bindingSubjects(saName, namespace), + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: roleName, + }, + } + existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not get cluster role binding for service account %s in joining cluster %s due to %v", + saName, clusterName, err) + return err + case err == nil && errorOnExisting: + return errors.Errorf("cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding + // must be deleted and recreated with the correct roleRef + if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { + err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{}) + if err != nil { + klog.V(2).Infof("Could not delete existing cluster role binding for service account %s in joining cluster %s due to: %v", + saName, clusterName, err) + return err + } + _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) + if err != nil { + klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } else { + existingBinding.Subjects = binding.Subjects + _, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding) + if err != nil { + klog.V(2).Infof("Could not update cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + default: + _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) + if err != nil { + klog.V(2).Infof("Could not create cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + return nil +} + +// createRoleAndBinding creates an RBAC role and binding +// that allows the service account identified by saName to access all +// resources in the specified namespace. +func createRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { + if dryRun { + return nil + } + + roleName := util.RoleName(saName) + + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Rules: namespacedPolicyRules, + } + existingRole, err := clientset.RbacV1().Roles(namespace).Get(roleName, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not retrieve role for service account %s in joining cluster %s due to %v", saName, clusterName, err) + return err + case errorOnExisting && err == nil: + return errors.Errorf("role for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + existingRole.Rules = role.Rules + _, err = clientset.RbacV1().Roles(namespace).Update(existingRole) + if err != nil { + klog.V(2).Infof("Could not update role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + default: + _, err := clientset.RbacV1().Roles(namespace).Create(role) + if err != nil { + klog.V(2).Infof("Could not create role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + + binding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Subjects: bindingSubjects(saName, namespace), + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: roleName, + }, + } + + existingBinding, err := clientset.RbacV1().RoleBindings(namespace).Get(binding.Name, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not retrieve role binding for service account %s in joining cluster %s due to: %v", + saName, clusterName, err) + return err + case err == nil && errorOnExisting: + return errors.Errorf("role binding for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding + // must be deleted and recreated with the correct roleRef + if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { + err = clientset.RbacV1().RoleBindings(namespace).Delete(existingBinding.Name, &metav1.DeleteOptions{}) + if err != nil { + klog.V(2).Infof("Could not delete existing role binding for service account %s in joining cluster %s due to: %v", + saName, clusterName, err) + return err + } + _, err = clientset.RbacV1().RoleBindings(namespace).Create(binding) + if err != nil { + klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } else { + existingBinding.Subjects = binding.Subjects + _, err = clientset.RbacV1().RoleBindings(namespace).Update(existingBinding) + if err != nil { + klog.V(2).Infof("Could not update role binding for service account %s in joining cluster %s due to: %v", + saName, clusterName, err) + return err + } + } + default: + _, err = clientset.RbacV1().RoleBindings(namespace).Create(binding) + if err != nil { + klog.V(2).Infof("Could not create role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + + return nil +} + +// createHealthCheckClusterRoleAndBinding creates an RBAC cluster role and +// binding that allows the service account identified by saName to +// access the health check path of the cluster. +func createHealthCheckClusterRoleAndBinding(clientset kubeclient.Interface, saName, namespace, clusterName string, dryRun, errorOnExisting bool) error { + if dryRun { + return nil + } + + roleName := util.HealthCheckRoleName(saName, namespace) + + role := &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"Get"}, + NonResourceURLs: []string{"/healthz"}, + }, + // The cluster client expects to be able to list nodes to retrieve zone and region details. + // TODO(marun) Consider making zone/region retrieval optional + { + Verbs: []string{"list"}, + APIGroups: []string{""}, + Resources: []string{"nodes"}, + }, + }, + } + existingRole, err := clientset.RbacV1().ClusterRoles().Get(role.Name, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not get health check cluster role for service account %s in joining cluster %s due to %v", + saName, clusterName, err) + return err + case err == nil && errorOnExisting: + return errors.Errorf("health check cluster role for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + existingRole.Rules = role.Rules + _, err := clientset.RbacV1().ClusterRoles().Update(existingRole) + if err != nil { + klog.V(2).Infof("Could not update health check cluster role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + default: // role was not found + _, err := clientset.RbacV1().ClusterRoles().Create(role) + if err != nil { + klog.V(2).Infof("Could not create health check cluster role for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + + binding := &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + }, + Subjects: bindingSubjects(saName, namespace), + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: roleName, + }, + } + existingBinding, err := clientset.RbacV1().ClusterRoleBindings().Get(binding.Name, metav1.GetOptions{}) + switch { + case err != nil && !apierrors.IsNotFound(err): + klog.V(2).Infof("Could not get health check cluster role binding for service account %s in joining cluster %s due to %v", + saName, clusterName, err) + return err + case err == nil && errorOnExisting: + return errors.Errorf("health check cluster role binding for service account %s in joining cluster %s already exists", saName, clusterName) + case err == nil: + // The roleRef cannot be updated, therefore if the existing roleRef is different, the existing rolebinding + // must be deleted and recreated with the correct roleRef + if !reflect.DeepEqual(existingBinding.RoleRef, binding.RoleRef) { + err = clientset.RbacV1().ClusterRoleBindings().Delete(existingBinding.Name, &metav1.DeleteOptions{}) + if err != nil { + klog.V(2).Infof("Could not delete existing health check cluster role binding for service account %s in joining cluster %s due to: %v", + saName, clusterName, err) + return err + } + _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) + if err != nil { + klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } else { + existingBinding.Subjects = binding.Subjects + _, err := clientset.RbacV1().ClusterRoleBindings().Update(existingBinding) + if err != nil { + klog.V(2).Infof("Could not update health check cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + default: + _, err = clientset.RbacV1().ClusterRoleBindings().Create(binding) + if err != nil { + klog.V(2).Infof("Could not create health check cluster role binding for service account: %s in joining cluster: %s due to: %v", + saName, clusterName, err) + return err + } + } + return nil +} + +// populateSecretInHostCluster copies the service account secret for saName +// from the cluster referenced by clusterClientset to the client referenced by +// hostClientset, putting it in a secret named secretName in the provided +// namespace. +func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Interface, + saName, hostNamespace, joiningNamespace, joiningClusterName, secretName string, + dryRun bool) (*corev1.Secret, []byte, error) { + + klog.V(2).Infof("Creating cluster credentials secret in host cluster") + + if dryRun { + dryRunSecret := &corev1.Secret{} + dryRunSecret.Name = secretName + return dryRunSecret, nil, nil + } + + // Get the secret from the joining cluster. + var secret *corev1.Secret + err := wait.PollImmediate(1*time.Second, serviceAccountSecretTimeout, func() (bool, error) { + sa, err := clusterClientset.CoreV1().ServiceAccounts(joiningNamespace).Get(saName, + metav1.GetOptions{}) + if err != nil { + return false, nil + } + + for _, objReference := range sa.Secrets { + saSecretName := objReference.Name + var err error + secret, err = clusterClientset.CoreV1().Secrets(joiningNamespace).Get(saSecretName, metav1.GetOptions{}) + if err != nil { + return false, nil + } + if secret.Type == corev1.SecretTypeServiceAccountToken { + klog.V(2).Infof("Using secret named: %s", secret.Name) + return true, nil + } + } + return false, nil + }) + + if err != nil { + klog.V(2).Infof("Could not get service account secret from joining cluster: %v", err) + return nil, nil, err + } + + token, ok := secret.Data[tokenKey] + if !ok { + return nil, nil, errors.Errorf("Key %q not found in service account secret", tokenKey) + } + + // Create a secret in the host cluster containing the token. + v1Secret := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: hostNamespace, + }, + Data: map[string][]byte{ + tokenKey: token, + }, + } + + if secretName == "" { + v1Secret.GenerateName = joiningClusterName + "-" + } else { + v1Secret.Name = secretName + } + + var v1SecretResult *corev1.Secret + _, err = hostClientset.CoreV1().Secrets(hostNamespace).Get(v1Secret.Name, metav1.GetOptions{}) + if err != nil { + if apierrors.IsNotFound(err) { + v1SecretResult, err = hostClientset.CoreV1().Secrets(hostNamespace).Create(&v1Secret) + if err != nil { + klog.V(2).Infof("Could not create secret in host cluster: %v", err) + return nil, nil, err + } + return v1SecretResult, nil, nil + } + klog.V(2).Infof("Could not get secret %s in host cluster: %v", v1Secret.Name, err) + return nil, nil, err + } else { + v1SecretResult, err = hostClientset.CoreV1().Secrets(hostNamespace).Update(&v1Secret) + if err != nil { + klog.V(2).Infof("Update secret %s in host cluster failed: %v", v1Secret.Name, err) + return nil, nil, err + } + } + + // caBundle is optional so no error is suggested if it is not + // found in the secret. + caBundle := secret.Data["ca.crt"] + + klog.V(2).Infof("Created secret in host cluster named: %s", v1SecretResult.Name) + return v1SecretResult, caBundle, nil +} diff --git a/pkg/controller/cluster/unjoin.go b/pkg/controller/cluster/unjoin.go new file mode 100644 index 000000000..d89727322 --- /dev/null +++ b/pkg/controller/cluster/unjoin.go @@ -0,0 +1,296 @@ +package cluster + +import ( + "context" + "github.com/pkg/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubeclient "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/klog" + fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1" + genericclient "sigs.k8s.io/kubefed/pkg/client/generic" + "sigs.k8s.io/kubefed/pkg/kubefedctl/util" +) + +// Following code copied from sigs.k8s.io/kubefed to avoid import collision + +// UnjoinCluster performs all the necessary steps to remove the +// registration of a cluster from a KubeFed control plane provided the +// required set of parameters are passed in. +func unjoinCluster(hostConfig, clusterConfig *rest.Config, kubefedNamespace, hostClusterName, unjoiningClusterName string, forceDeletion, dryRun bool) error { + + hostClientset, err := util.HostClientset(hostConfig) + if err != nil { + klog.V(2).Infof("Failed to get host cluster clientset: %v", err) + return err + } + + var clusterClientset *kubeclient.Clientset + if clusterConfig != nil { + clusterClientset, err = util.ClusterClientset(clusterConfig) + if err != nil { + klog.V(2).Infof("Failed to get unjoining cluster clientset: %v", err) + if !forceDeletion { + return err + } + } + } + + client, err := genericclient.New(hostConfig) + if err != nil { + klog.V(2).Infof("Failed to get kubefed clientset: %v", err) + return err + } + + if clusterClientset != nil { + err := deleteRBACResources(clusterClientset, kubefedNamespace, unjoiningClusterName, hostClusterName, forceDeletion, dryRun) + if err != nil { + if !forceDeletion { + return err + } + klog.V(2).Infof("Failed to delete RBAC resources: %v", err) + } + + err = deleteFedNSFromUnjoinCluster(hostClientset, clusterClientset, kubefedNamespace, unjoiningClusterName, dryRun) + if err != nil { + if !forceDeletion { + return err + } + klog.V(2).Infof("Failed to delete kubefed namespace: %v", err) + } + } + + // deletionSucceeded when all operations in deleteRBACResources and deleteFedNSFromUnjoinCluster succeed. + err = deleteFederatedClusterAndSecret(hostClientset, client, kubefedNamespace, unjoiningClusterName, forceDeletion, dryRun) + if err != nil { + return err + } + return nil +} + +// deleteKubeFedClusterAndSecret deletes a federated cluster resource that associates +// the cluster and secret. +func deleteFederatedClusterAndSecret(hostClientset kubeclient.Interface, client genericclient.Client, + kubefedNamespace, unjoiningClusterName string, forceDeletion, dryRun bool) error { + if dryRun { + return nil + } + + klog.V(2).Infof("Deleting kubefed cluster resource from namespace %q for unjoin cluster %q", + kubefedNamespace, unjoiningClusterName) + + fedCluster := &fedv1b1.KubeFedCluster{} + err := client.Get(context.TODO(), fedCluster, kubefedNamespace, unjoiningClusterName) + if err != nil { + if apierrors.IsNotFound(err) { + return nil + } + return errors.Wrapf(err, "Failed to get kubefed cluster \"%s/%s\"", kubefedNamespace, unjoiningClusterName) + } + + err = hostClientset.CoreV1().Secrets(kubefedNamespace).Delete(fedCluster.Spec.SecretRef.Name, + &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Secret \"%s/%s\" does not exist in the host cluster.", kubefedNamespace, fedCluster.Spec.SecretRef.Name) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Failed to delete secret \"%s/%s\" for unjoin cluster %q", + kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleted secret \"%s/%s\" for unjoin cluster %q", kubefedNamespace, fedCluster.Spec.SecretRef.Name, unjoiningClusterName) + } + + err = client.Delete(context.TODO(), fedCluster, fedCluster.Namespace, fedCluster.Name) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("KubeFed cluster \"%s/%s\" does not exist in the host cluster.", fedCluster.Namespace, fedCluster.Name) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Failed to delete kubefed cluster \"%s/%s\" for unjoin cluster %q", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleted kubefed cluster \"%s/%s\" for unjoin cluster %q.", fedCluster.Namespace, fedCluster.Name, unjoiningClusterName) + } + + return nil +} + +// deleteRBACResources deletes the cluster role, cluster rolebindings and service account +// from the unjoining cluster. +func deleteRBACResources(unjoiningClusterClientset kubeclient.Interface, + namespace, unjoiningClusterName, hostClusterName string, forceDeletion, dryRun bool) error { + + saName := ClusterServiceAccountName(unjoiningClusterName, hostClusterName) + + err := deleteClusterRoleAndBinding(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, forceDeletion, dryRun) + if err != nil { + return err + } + + err = deleteServiceAccount(unjoiningClusterClientset, saName, namespace, unjoiningClusterName, dryRun) + if err != nil { + return err + } + + return nil +} + +// deleteFedNSFromUnjoinCluster deletes the kubefed namespace from +// the unjoining cluster so long as the unjoining cluster is not the +// host cluster. +func deleteFedNSFromUnjoinCluster(hostClientset, unjoiningClusterClientset kubeclient.Interface, + kubefedNamespace, unjoiningClusterName string, dryRun bool) error { + + if dryRun { + return nil + } + + hostClusterNamespace, err := hostClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "Error retrieving namespace %q from host cluster", kubefedNamespace) + } + + unjoiningClusterNamespace, err := unjoiningClusterClientset.CoreV1().Namespaces().Get(kubefedNamespace, metav1.GetOptions{}) + if err != nil { + return errors.Wrapf(err, "Error retrieving namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName) + } + + if IsPrimaryCluster(hostClusterNamespace, unjoiningClusterNamespace) { + klog.V(2).Infof("The kubefed namespace %q does not need to be deleted from the host cluster by unjoin.", kubefedNamespace) + return nil + } + + klog.V(2).Infof("Deleting kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) + err = unjoiningClusterClientset.CoreV1().Namespaces().Delete(kubefedNamespace, &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("The kubefed namespace %q no longer exists in unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) + return nil + } else if err != nil { + return errors.Wrapf(err, "Could not delete kubefed namespace %q from unjoining cluster %q", kubefedNamespace, unjoiningClusterName) + } else { + klog.V(2).Infof("Deleted kubefed namespace %q from unjoining cluster %q.", kubefedNamespace, unjoiningClusterName) + } + + return nil +} + +// deleteServiceAccount deletes a service account in the cluster associated +// with clusterClientset with credentials that are used by the host cluster +// to access its API server. +func deleteServiceAccount(clusterClientset kubeclient.Interface, saName, + namespace, unjoiningClusterName string, dryRun bool) error { + if dryRun { + return nil + } + + klog.V(2).Infof("Deleting service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName) + + // Delete a service account. + err := clusterClientset.CoreV1().ServiceAccounts(namespace).Delete(saName, + &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Service account \"%s/%s\" does not exist.", namespace, saName) + } else if err != nil { + return errors.Wrapf(err, "Could not delete service account \"%s/%s\"", namespace, saName) + } else { + klog.V(2).Infof("Deleted service account \"%s/%s\" in unjoining cluster %q.", namespace, saName, unjoiningClusterName) + } + + return nil +} + +// deleteClusterRoleAndBinding deletes an RBAC cluster role and binding that +// allows the service account identified by saName to access all resources in +// all namespaces in the cluster associated with clusterClientset. +func deleteClusterRoleAndBinding(clusterClientset kubeclient.Interface, + saName, namespace, unjoiningClusterName string, forceDeletion, dryRun bool) error { + if dryRun { + return nil + } + + roleName := util.RoleName(saName) + healthCheckRoleName := util.HealthCheckRoleName(saName, namespace) + + // Attempt to delete all role and role bindings created by join + for _, name := range []string{roleName, healthCheckRoleName} { + klog.V(2).Infof("Deleting cluster role binding %q for service account %q in unjoining cluster %q.", + name, saName, unjoiningClusterName) + + err := clusterClientset.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Cluster role binding %q for service account %q does not exist in unjoining cluster %q.", + name, saName, unjoiningClusterName) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Could not delete cluster role binding %q for service account %q in unjoining cluster %q", + name, saName, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleted cluster role binding %q for service account %q in unjoining cluster %q.", + name, saName, unjoiningClusterName) + } + + klog.V(2).Infof("Deleting cluster role %q for service account %q in unjoining cluster %q.", + name, saName, unjoiningClusterName) + err = clusterClientset.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Cluster role %q for service account %q does not exist in unjoining cluster %q.", + name, saName, unjoiningClusterName) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Could not delete cluster role %q for service account %q in unjoining cluster %q", + name, saName, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleted cluster role %q for service account %q in unjoining cluster %q.", + name, saName, unjoiningClusterName) + } + } + + klog.V(2).Infof("Deleting role binding \"%s/%s\" for service account %q in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + err := clusterClientset.RbacV1().RoleBindings(namespace).Delete(roleName, &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Role binding \"%s/%s\" for service account %q does not exist in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Could not delete role binding \"%s/%s\" for service account %q in unjoining cluster %q", + namespace, roleName, saName, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleted role binding \"%s/%s\" for service account %q in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + } + + klog.V(2).Infof("Deleting role \"%s/%s\" for service account %q in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + err = clusterClientset.RbacV1().Roles(namespace).Delete(roleName, &metav1.DeleteOptions{}) + if apierrors.IsNotFound(err) { + klog.V(2).Infof("Role \"%s/%s\" for service account %q does not exist in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + } else if err != nil { + wrappedErr := errors.Wrapf(err, "Could not delete role \"%s/%s\" for service account %q in unjoining cluster %q", + namespace, roleName, saName, unjoiningClusterName) + if !forceDeletion { + return wrappedErr + } + klog.V(2).Infof("%v", wrappedErr) + } else { + klog.V(2).Infof("Deleting Role \"%s/%s\" for service account %q in unjoining cluster %q.", + namespace, roleName, saName, unjoiningClusterName) + } + + return nil +} diff --git a/pkg/controller/cluster/util.go b/pkg/controller/cluster/util.go new file mode 100644 index 000000000..27c71309c --- /dev/null +++ b/pkg/controller/cluster/util.go @@ -0,0 +1,166 @@ +package cluster + +import ( + "fmt" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + pkgruntime "k8s.io/apimachinery/pkg/runtime" + kubeclient "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "strings" +) + +// Default values for the federated group and version used by +// the enable and disable subcommands of `kubefedctl`. +const ( + DefaultFederatedGroup = "types.kubefed.io" + DefaultFederatedVersion = "v1beta1" + + FederatedKindPrefix = "Federated" +) + +// FedConfig provides a rest config based on the filesystem kubeconfig (via +// pathOptions) and context in order to talk to the host kubernetes cluster +// and the joining kubernetes cluster. +type FedConfig interface { + HostConfig(context, kubeconfigPath string) (*rest.Config, error) + ClusterConfig(context, kubeconfigPath string) (*rest.Config, error) + GetClientConfig(ontext, kubeconfigPath string) clientcmd.ClientConfig +} + +// fedConfig implements the FedConfig interface. +type fedConfig struct { + pathOptions *clientcmd.PathOptions +} + +// NewFedConfig creates a fedConfig for `kubefedctl` commands. +func NewFedConfig(pathOptions *clientcmd.PathOptions) FedConfig { + return &fedConfig{ + pathOptions: pathOptions, + } +} + +// HostConfig provides a rest config to talk to the host kubernetes cluster +// based on the context and kubeconfig passed in. +func (a *fedConfig) HostConfig(context, kubeconfigPath string) (*rest.Config, error) { + hostConfig := a.GetClientConfig(context, kubeconfigPath) + hostClientConfig, err := hostConfig.ClientConfig() + if err != nil { + return nil, err + } + + return hostClientConfig, nil +} + +// ClusterConfig provides a rest config to talk to the joining kubernetes +// cluster based on the context and kubeconfig passed in. +func (a *fedConfig) ClusterConfig(context, kubeconfigPath string) (*rest.Config, error) { + clusterConfig := a.GetClientConfig(context, kubeconfigPath) + clusterClientConfig, err := clusterConfig.ClientConfig() + if err != nil { + return nil, err + } + + return clusterClientConfig, nil +} + +// getClientConfig is a helper method to create a client config from the +// context and kubeconfig passed as arguments. +func (a *fedConfig) GetClientConfig(context, kubeconfigPath string) clientcmd.ClientConfig { + loadingRules := *a.pathOptions.LoadingRules + loadingRules.Precedence = a.pathOptions.GetLoadingPrecedence() + loadingRules.ExplicitPath = kubeconfigPath + overrides := &clientcmd.ConfigOverrides{ + CurrentContext: context, + } + + return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides) +} + +// HostClientset provides a kubernetes API compliant clientset to +// communicate with the host cluster's kubernetes API server. +func HostClientset(config *rest.Config) (*kubeclient.Clientset, error) { + return kubeclient.NewForConfig(config) +} + +// ClusterClientset provides a kubernetes API compliant clientset to +// communicate with the joining cluster's kubernetes API server. +func ClusterClientset(config *rest.Config) (*kubeclient.Clientset, error) { + return kubeclient.NewForConfig(config) +} + +// ClusterServiceAccountName returns the name of a service account whose +// credentials are used by the host cluster to access the client cluster. +func ClusterServiceAccountName(joiningClusterName, hostClusterName string) string { + return fmt.Sprintf("%s-%s", joiningClusterName, hostClusterName) +} + +// RoleName returns the name of a Role or ClusterRole and its +// associated RoleBinding or ClusterRoleBinding that are used to allow +// the service account to access necessary resources on the cluster. +func RoleName(serviceAccountName string) string { + return fmt.Sprintf("kubefed-controller-manager:%s", serviceAccountName) +} + +// HealthCheckRoleName returns the name of a ClusterRole and its +// associated ClusterRoleBinding that is used to allow the service +// account to check the health of the cluster and list nodes. +func HealthCheckRoleName(serviceAccountName, namespace string) string { + return fmt.Sprintf("kubefed-controller-manager:%s:healthcheck-%s", namespace, serviceAccountName) +} + +// IsFederatedAPIResource checks if a resource with the given Kind and group is a Federated one +func IsFederatedAPIResource(kind, group string) bool { + return strings.HasPrefix(kind, FederatedKindPrefix) && group == DefaultFederatedGroup +} + +// GetNamespace returns namespace of the current context +func GetNamespace(hostClusterContext string, kubeconfig string, config FedConfig) (string, error) { + clientConfig := config.GetClientConfig(hostClusterContext, kubeconfig) + currentContext, err := CurrentContext(clientConfig) + if err != nil { + return "", err + } + + ns, _, err := clientConfig.Namespace() + if err != nil { + return "", errors.Wrapf(err, "Failed to get ClientConfig for host cluster context %q and kubeconfig %q", + currentContext, kubeconfig) + } + + if len(ns) == 0 { + ns = "default" + } + return ns, nil +} + +// CurrentContext retrieves the current context from the provided config. +func CurrentContext(config clientcmd.ClientConfig) (string, error) { + rawConfig, err := config.RawConfig() + if err != nil { + return "", errors.Wrap(err, "Failed to get current context from config") + } + return rawConfig.CurrentContext, nil +} + +// IsPrimaryCluster checks if the caller is working with objects for the +// primary cluster by checking if the UIDs match for both ObjectMetas passed +// in. +// TODO (font): Need to revisit this when cluster ID is available. +func IsPrimaryCluster(obj, clusterObj pkgruntime.Object) bool { + meta := MetaAccessor(obj) + clusterMeta := MetaAccessor(clusterObj) + return meta.GetUID() == clusterMeta.GetUID() +} + +func MetaAccessor(obj pkgruntime.Object) metav1.Object { + accessor, err := meta.Accessor(obj) + if err != nil { + // This should always succeed if obj is not nil. Also, + // adapters are slated for replacement by unstructured. + return nil + } + return accessor +} diff --git a/pkg/controller/clusterrolebinding/clusterrolebinding_controller.go b/pkg/controller/clusterrolebinding/clusterrolebinding_controller.go index 6058a738b..28cc58b61 100644 --- a/pkg/controller/clusterrolebinding/clusterrolebinding_controller.go +++ b/pkg/controller/clusterrolebinding/clusterrolebinding_controller.go @@ -169,7 +169,6 @@ func (r *ReconcileClusterRoleBinding) updateRoleBindings(clusterRoleBinding *rba if clusterRoleBinding.Name == getWorkspaceViewerRoleBindingName(workspaceName) { found := &rbac.RoleBinding{} - viewerBinding := &rbac.RoleBinding{} viewerBinding.Name = "viewer" viewerBinding.Namespace = namespace.Name diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go deleted file mode 100644 index 80e89c7a4..000000000 --- a/pkg/controller/controller.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 controller - -import ( - "sigs.k8s.io/controller-runtime/pkg/manager" -) - -// AddToManagerFuncs is a list of functions to add all Controllers to the Manager -var AddToManagerFuncs []func(manager.Manager) error - -// AddToManager adds all Controllers to the Manager -func AddToManager(m manager.Manager) error { - for _, f := range AddToManagerFuncs { - if err := f(m); err != nil { - return err - } - } - return nil -} diff --git a/pkg/controller/destinationrule/destinationrule_controller.go b/pkg/controller/destinationrule/destinationrule_controller.go index dc5c37371..db9bc7d98 100644 --- a/pkg/controller/destinationrule/destinationrule_controller.go +++ b/pkg/controller/destinationrule/destinationrule_controller.go @@ -199,7 +199,7 @@ func (v *DestinationRuleController) processNextWorkItem() bool { func (v *DestinationRuleController) syncService(key string) error { startTime := time.Now() defer func() { - log.V(4).Info("Finished syncing service destinationrule.", "key", key, "duration", time.Since(startTime)) + log.V(4).Infof("Finished syncing service destinationrule %s in %s.", key, time.Since(startTime)) }() namespace, name, err := cache.SplitMetaNamespaceKey(key) @@ -212,14 +212,14 @@ func (v *DestinationRuleController) syncService(key string) error { // delete the corresponding destinationrule if there is any, as the service has been deleted. err = v.destinationRuleClient.NetworkingV1alpha3().DestinationRules(namespace).Delete(name, nil) if err != nil && !errors.IsNotFound(err) { - log.Error(err, "delete destination rule failed", "namespace", namespace, "name", name) + log.Errorf("delete destination rule failed %s/%s, error %v.", namespace, name, err) return err } // delete orphan service policy if there is any err = v.servicemeshClient.ServicemeshV1alpha2().ServicePolicies(namespace).Delete(name, nil) if err != nil && !errors.IsNotFound(err) { - log.Error(err, "delete orphan service policy failed", "namespace", namespace, "name", name) + log.Errorf("delete orphan service policy %s/%s failed, %#v", namespace, name, err) return err } @@ -259,7 +259,7 @@ func (v *DestinationRuleController) syncService(key string) error { version := util.GetComponentVersion(&deployment.ObjectMeta) if len(version) == 0 { - log.V(4).Info("Deployment doesn't have a version label", "key", types.NamespacedName{Namespace: deployment.Namespace, Name: deployment.Name}.String()) + log.V(4).Infof("Deployment %s doesn't have a version label", types.NamespacedName{Namespace: deployment.Namespace, Name: deployment.Name}.String()) continue } diff --git a/pkg/controller/devopscredential/devopscredential_controller.go b/pkg/controller/devopscredential/devopscredential_controller.go new file mode 100644 index 000000000..4206fd978 --- /dev/null +++ b/pkg/controller/devopscredential/devopscredential_controller.go @@ -0,0 +1,284 @@ +package devopscredential + +import ( + "fmt" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + corev1informer "k8s.io/client-go/informers/core/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + v1core "k8s.io/client-go/kubernetes/typed/core/v1" + corev1lister "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/constants" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "net/http" + "reflect" + "strings" + "time" +) + +/** + DevOps project controller is used to maintain the state of the DevOps project. +*/ + +type Controller struct { + client clientset.Interface + kubesphereClient kubesphereclient.Interface + + eventBroadcaster record.EventBroadcaster + eventRecorder record.EventRecorder + + secretLister corev1lister.SecretLister + secretSynced cache.InformerSynced + + namespaceLister corev1lister.NamespaceLister + namespaceSynced cache.InformerSynced + + workqueue workqueue.RateLimitingInterface + + workerLoopPeriod time.Duration + + devopsClient devopsClient.Interface +} + +func NewController(client clientset.Interface, + devopsClinet devopsClient.Interface, + namespaceInformer corev1informer.NamespaceInformer, + secretInformer corev1informer.SecretInformer) *Controller { + + broadcaster := record.NewBroadcaster() + broadcaster.StartLogging(func(format string, args ...interface{}) { + klog.Info(fmt.Sprintf(format, args)) + }) + broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")}) + recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "devopscredential-controller"}) + + v := &Controller{ + client: client, + devopsClient: devopsClinet, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "devopscredential"), + secretLister: secretInformer.Lister(), + secretSynced: secretInformer.Informer().HasSynced, + namespaceLister: namespaceInformer.Lister(), + namespaceSynced: namespaceInformer.Informer().HasSynced, + workerLoopPeriod: time.Second, + } + + v.eventBroadcaster = broadcaster + v.eventRecorder = recorder + + secretInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + secret, ok := obj.(*v1.Secret) + if ok && strings.HasPrefix(string(secret.Type), devopsv1alpha3.DevOpsCredentialPrefix) { + v.enqueueSecret(obj) + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + old, ook := oldObj.(*v1.Secret) + new, nok := newObj.(*v1.Secret) + if ook && nok && old.ResourceVersion == new.ResourceVersion { + return + } + if ook && nok && strings.HasPrefix(string(new.Type), devopsv1alpha3.DevOpsCredentialPrefix) { + v.enqueueSecret(newObj) + } + }, + DeleteFunc: func(obj interface{}) { + secret, ok := obj.(*v1.Secret) + if ok && strings.HasPrefix(string(secret.Type), devopsv1alpha3.DevOpsCredentialPrefix) { + v.enqueueSecret(obj) + } + }, + }) + return v +} + +// enqueueSecret takes a Foo resource and converts it into a namespace/name +// string which is then put onto the work workqueue. This method should *not* be +// passed resources of any type other than DevOpsProject. +func (c *Controller) enqueueSecret(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + err := func(obj interface{}) error { + defer c.workqueue.Done(obj) + var key string + var ok bool + + if key, ok = obj.(string); !ok { + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + if err := c.syncHandler(key); err != nil { + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) + } + c.workqueue.Forget(obj) + klog.V(5).Infof("Successfully synced '%s'", key) + return nil + }(obj) + + if err != nil { + klog.Error(err, "could not reconcile devopsProject") + utilruntime.HandleError(err) + return true + } + + return true +} + +func (c *Controller) worker() { + + for c.processNextWorkItem() { + } +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(1, stopCh) +} + +func (c *Controller) Run(workers int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workqueue.ShutDown() + + klog.Info("starting devopscredential controller") + defer klog.Info("shutting down devopscredential controller") + + if !cache.WaitForCacheSync(stopCh, c.secretSynced) { + return fmt.Errorf("failed to wait for caches to sync") + } + + for i := 0; i < workers; i++ { + go wait.Until(c.worker, c.workerLoopPeriod, stopCh) + } + + <-stopCh + return nil +} + +// syncHandler compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the secret resource +// with the current status of the resource. +func (c *Controller) syncHandler(key string) error { + nsName, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + klog.Error(err, fmt.Sprintf("could not split copySecret meta %s ", key)) + return nil + } + namespace, err := c.namespaceLister.Get(nsName) + if err != nil { + if errors.IsNotFound(err) { + klog.Info(fmt.Sprintf("namespace '%s' in work queue no longer exists ", key)) + return nil + } + klog.Error(err, fmt.Sprintf("could not get namespace %s ", key)) + return err + } + if !isDevOpsProjectAdminNamespace(namespace) { + err := fmt.Errorf("cound not create credential in normal namespaces %s", namespace.Name) + klog.Warning(err) + return err + } + + secret, err := c.secretLister.Secrets(nsName).Get(name) + if err != nil { + if errors.IsNotFound(err) { + klog.Info(fmt.Sprintf("secret '%s' in work queue no longer exists ", key)) + return nil + } + klog.Error(err, fmt.Sprintf("could not get secret %s ", key)) + return err + } + + copySecret := secret.DeepCopy() + // DeletionTimestamp.IsZero() means copySecret has not been deleted. + if copySecret.ObjectMeta.DeletionTimestamp.IsZero() { + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers + if !sliceutil.HasString(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) { + copySecret.ObjectMeta.Finalizers = append(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) + } + // Check secret config exists, otherwise we will create it. + // if secret exists, update config + _, err := c.devopsClient.GetCredentialInProject(nsName, secret.Name) + if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get secret %s ", key)) + return err + } else if err != nil { + _, err := c.devopsClient.CreateCredentialInProject(nsName, copySecret) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to create secret %s ", key)) + return err + } + } else { + if _, ok := copySecret.Annotations[devopsv1alpha3.CredentialAutoSyncAnnoKey]; ok { + _, err := c.devopsClient.UpdateCredentialInProject(nsName, copySecret) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update secret %s ", key)) + return err + } + } + } + + } else { + // Finalizers processing logic + if sliceutil.HasString(copySecret.ObjectMeta.Finalizers, devopsv1alpha3.CredentialFinalizerName) { + _, err := c.devopsClient.GetCredentialInProject(nsName, secret.Name) + if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get secret %s ", key)) + return err + } else if err != nil && devopsClient.GetDevOpsStatusCode(err) == http.StatusNotFound { + } else { + if _, err := c.devopsClient.DeleteCredentialInProject(nsName, secret.Name); err != nil { + klog.Error(err, fmt.Sprintf("failed to delete secret %s in devops", key)) + return err + } + } + copySecret.ObjectMeta.Finalizers = sliceutil.RemoveString(copySecret.ObjectMeta.Finalizers, func(item string) bool { + return item == devopsv1alpha3.CredentialFinalizerName + }) + + } + } + if !reflect.DeepEqual(secret, copySecret) { + _, err = c.client.CoreV1().Secrets(nsName).Update(copySecret) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update secret %s ", key)) + return err + } + } + + return nil +} + +func isDevOpsProjectAdminNamespace(namespace *v1.Namespace) bool { + _, ok := namespace.Labels[constants.DevOpsProjectLabelKey] + + return ok && k8sutil.IsControlledBy(namespace.OwnerReferences, + devopsv1alpha3.ResourceKindDevOpsProject, "") + +} diff --git a/pkg/controller/devopscredential/devopscredential_controller_test.go b/pkg/controller/devopscredential/devopscredential_controller_test.go new file mode 100644 index 000000000..d2900c43d --- /dev/null +++ b/pkg/controller/devopscredential/devopscredential_controller_test.go @@ -0,0 +1,409 @@ +package devopscredential + +import ( + v1 "k8s.io/api/core/v1" + "kubesphere.io/kubesphere/pkg/constants" + fakeDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + kubeinformers "k8s.io/client-go/informers" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + devops "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + kubeclient *k8sfake.Clientset + namespaceLister []*v1.Namespace + secretLister []*v1.Secret + kubeactions []core.Action + + kubeobjects []runtime.Object + // Objects from here preloaded into NewSimpleFake. + objects []runtime.Object + // Objects from here preloaded into devops + initDevOpsProject string + initCredential []*v1.Secret + expectCredential []*v1.Secret +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + return f +} + +func newNamespace(name string, projectName string) *v1.Namespace { + ns := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{constants.DevOpsProjectLabelKey: projectName}, + }, + } + TRUE := true + ns.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: devops.SchemeGroupVersion.String(), + Kind: devops.ResourceKindDevOpsProject, + Name: projectName, + BlockOwnerDeletion: &TRUE, + Controller: &TRUE, + }, + } + + return ns +} + +func newSecret(namespace, name string, data map[string][]byte, withFinalizers bool, autoSync bool) *v1.Secret { + secret := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: devops.ResourceKindPipeline, + APIVersion: devops.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: data, + Type: devops.DevOpsCredentialPrefix + "test", + } + if withFinalizers { + secret.Finalizers = append(secret.Finalizers, devops.CredentialFinalizerName) + } + if autoSync { + if secret.Annotations == nil { + secret.Annotations = map[string]string{} + } + secret.Annotations[devops.CredentialAutoSyncAnnoKey] = "true" + } + return secret +} + +func newDeletingSecret(namespace, name string) *v1.Secret { + now := metav1.Now() + pipeline := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: devops.ResourceKindPipeline, + APIVersion: devops.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + DeletionTimestamp: &now, + }, + Type: devops.DevOpsCredentialPrefix + "test", + } + pipeline.Finalizers = append(pipeline.Finalizers, devops.CredentialFinalizerName) + + return pipeline +} + +func (f *fixture) newController() (*Controller, kubeinformers.SharedInformerFactory, *fakeDevOps.Devops) { + f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...) + + k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) + dI := fakeDevOps.NewWithCredentials(f.initDevOpsProject, f.initCredential...) + + c := NewController(f.kubeclient, dI, k8sI.Core().V1().Namespaces(), + k8sI.Core().V1().Secrets()) + + c.secretSynced = alwaysReady + c.eventRecorder = &record.FakeRecorder{} + + for _, f := range f.secretLister { + k8sI.Core().V1().Secrets().Informer().GetIndexer().Add(f) + } + + for _, d := range f.namespaceLister { + k8sI.Core().V1().Namespaces().Informer().GetIndexer().Add(d) + } + + return c, k8sI, dI +} + +func (f *fixture) run(fooName string) { + f.runController(fooName, true, false) +} + +func (f *fixture) runExpectError(fooName string) { + f.runController(fooName, true, true) +} + +func (f *fixture) runController(name string, startInformers bool, expectError bool) { + c, k8sI, dI := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + k8sI.Start(stopCh) + } + + err := c.syncHandler(name) + if !expectError && err != nil { + f.t.Errorf("error syncing foo: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing foo, got nil") + } + + k8sActions := filterInformerActions(f.kubeclient.Actions()) + for i, action := range k8sActions { + if len(f.kubeactions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:]) + break + } + + expectedAction := f.kubeactions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.kubeactions) > len(k8sActions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) + } + + if len(dI.Credentials[f.initDevOpsProject]) != len(f.expectCredential) { + f.t.Errorf(" unexpected objects: %v", dI.Projects) + } + for _, credential := range f.expectCredential { + actualCredential := dI.Credentials[f.initDevOpsProject][credential.Name] + if !reflect.DeepEqual(actualCredential, credential) { + f.t.Errorf(" credential %+v not match \n %+v", credential, actualCredential) + } + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + ret := []core.Action{} + for _, action := range actions { + if len(action.GetNamespace()) == 0 && + (action.Matches("list", "secrets") || + action.Matches("watch", "secrets") || + action.Matches("list", "namespaces") || + action.Matches("watch", "namespaces")) { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdateSecretAction(p *v1.Secret) { + action := core.NewUpdateAction(schema.GroupVersionResource{ + Version: "v1", + Resource: "secrets", + }, p.Namespace, p) + f.kubeactions = append(f.kubeactions, action) +} + +func getKey(p *v1.Secret, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(p) + if err != nil { + t.Errorf("Unexpected error getting key for pipeline %v: %v", p.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + secret := newSecret(nsName, secretName, nil, true, true) + + f.secretLister = append(f.secretLister, secret) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, secret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{secret} + f.expectCredential = []*v1.Secret{secret} + + f.run(getKey(secret, t)) +} + +func TestAddCredentialFinalizers(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + secret := newSecret(nsName, secretName, nil, false, true) + + expectSecret := newSecret(nsName, secretName, nil, true, true) + + f.secretLister = append(f.secretLister, secret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, secret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{secret} + f.expectCredential = []*v1.Secret{expectSecret} + f.expectUpdateSecretAction(expectSecret) + f.run(getKey(secret, t)) +} + +func TestCreateCredential(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + secret := newSecret(nsName, secretName, nil, true, true) + + f.secretLister = append(f.secretLister, secret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, secret) + f.initDevOpsProject = nsName + f.expectCredential = []*v1.Secret{secret} + f.run(getKey(secret, t)) +} + +func TestDeleteCredential(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + secret := newDeletingSecret(nsName, secretName) + + expectSecret := secret.DeepCopy() + expectSecret.Finalizers = []string{} + f.secretLister = append(f.secretLister, secret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, secret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{secret} + f.expectCredential = []*v1.Secret{} + f.expectUpdateSecretAction(expectSecret) + f.run(getKey(secret, t)) +} + +func TestDeleteNotExistCredential(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + secret := newDeletingSecret(nsName, pipelineName) + + expectSecret := secret.DeepCopy() + expectSecret.Finalizers = []string{} + f.secretLister = append(f.secretLister, secret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, secret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{} + f.expectCredential = []*v1.Secret{} + f.expectUpdateSecretAction(expectSecret) + f.run(getKey(secret, t)) +} + +func TestUpdateCredential(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + initSecret := newSecret(nsName, secretName, nil, true, true) + expectSecret := newSecret(nsName, secretName, map[string][]byte{"a": []byte("aa")}, true, true) + f.secretLister = append(f.secretLister, expectSecret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, expectSecret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{initSecret} + f.expectCredential = []*v1.Secret{expectSecret} + f.run(getKey(expectSecret, t)) +} + +func TestNotUpdateCredential(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + secretName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + initSecret := newSecret(nsName, secretName, nil, true, false) + expectSecret := newSecret(nsName, secretName, map[string][]byte{"a": []byte("aa")}, true, false) + f.secretLister = append(f.secretLister, expectSecret) + f.namespaceLister = append(f.namespaceLister, ns) + f.kubeobjects = append(f.kubeobjects, expectSecret) + f.initDevOpsProject = nsName + f.initCredential = []*v1.Secret{initSecret} + f.expectCredential = []*v1.Secret{initSecret} + f.run(getKey(expectSecret, t)) +} diff --git a/pkg/controller/devopsproject/devopsproject_controller.go b/pkg/controller/devopsproject/devopsproject_controller.go new file mode 100644 index 000000000..f61669abe --- /dev/null +++ b/pkg/controller/devopsproject/devopsproject_controller.go @@ -0,0 +1,348 @@ +package devopsproject + +import ( + "fmt" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + corev1informer "k8s.io/client-go/informers/core/v1" + clientset "k8s.io/client-go/kubernetes" + v1core "k8s.io/client-go/kubernetes/typed/core/v1" + corev1lister "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" + "kubesphere.io/kubesphere/pkg/constants" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "net/http" + "reflect" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "time" + + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + devopsinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops/v1alpha3" + devopslisters "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha3" +) + +/** + DevOps project controller is used to maintain the state of the DevOps project. +*/ + +type Controller struct { + client clientset.Interface + kubesphereClient kubesphereclient.Interface + + eventBroadcaster record.EventBroadcaster + eventRecorder record.EventRecorder + + devOpsProjectLister devopslisters.DevOpsProjectLister + devOpsProjectSynced cache.InformerSynced + + namespaceLister corev1lister.NamespaceLister + namespaceSynced cache.InformerSynced + + workqueue workqueue.RateLimitingInterface + + workerLoopPeriod time.Duration + + devopsClient devopsClient.Interface +} + +func NewController(client clientset.Interface, + kubesphereClient kubesphereclient.Interface, + devopsClinet devopsClient.Interface, + namespaceInformer corev1informer.NamespaceInformer, + devopsInformer devopsinformers.DevOpsProjectInformer) *Controller { + + broadcaster := record.NewBroadcaster() + broadcaster.StartLogging(func(format string, args ...interface{}) { + klog.Info(fmt.Sprintf(format, args)) + }) + broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")}) + recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "devopsproject-controller"}) + + v := &Controller{ + client: client, + devopsClient: devopsClinet, + kubesphereClient: kubesphereClient, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "devopsproject"), + devOpsProjectLister: devopsInformer.Lister(), + devOpsProjectSynced: devopsInformer.Informer().HasSynced, + namespaceLister: namespaceInformer.Lister(), + namespaceSynced: namespaceInformer.Informer().HasSynced, + workerLoopPeriod: time.Second, + } + + v.eventBroadcaster = broadcaster + v.eventRecorder = recorder + + devopsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: v.enqueueDevOpsProject, + UpdateFunc: func(oldObj, newObj interface{}) { + old := oldObj.(*devopsv1alpha3.DevOpsProject) + new := newObj.(*devopsv1alpha3.DevOpsProject) + if old.ResourceVersion == new.ResourceVersion { + return + } + v.enqueueDevOpsProject(newObj) + }, + DeleteFunc: v.enqueueDevOpsProject, + }) + return v +} + +// enqueueDevOpsProject takes a Foo resource and converts it into a namespace/name +// string which is then put onto the work workqueue. This method should *not* be +// passed resources of any type other than DevOpsProject. +func (c *Controller) enqueueDevOpsProject(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + err := func(obj interface{}) error { + defer c.workqueue.Done(obj) + var key string + var ok bool + + if key, ok = obj.(string); !ok { + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + if err := c.syncHandler(key); err != nil { + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) + } + c.workqueue.Forget(obj) + klog.V(5).Infof("Successfully synced '%s'", key) + return nil + }(obj) + + if err != nil { + klog.Error(err, "could not reconcile devopsProject") + utilruntime.HandleError(err) + return true + } + + return true +} + +func (c *Controller) worker() { + + for c.processNextWorkItem() { + } +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(1, stopCh) +} + +func (c *Controller) Run(workers int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workqueue.ShutDown() + + klog.Info("starting devops project controller") + defer klog.Info("shutting down devops project controller") + + if !cache.WaitForCacheSync(stopCh, c.devOpsProjectSynced) { + return fmt.Errorf("failed to wait for caches to sync") + } + + for i := 0; i < workers; i++ { + go wait.Until(c.worker, c.workerLoopPeriod, stopCh) + } + + <-stopCh + return nil +} + +// syncHandler compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the devopsproject resource +// with the current status of the resource. +func (c *Controller) syncHandler(key string) error { + project, err := c.devOpsProjectLister.Get(key) + if err != nil { + if errors.IsNotFound(err) { + klog.Info(fmt.Sprintf("devopsproject '%s' in work queue no longer exists ", key)) + return nil + } + klog.Error(err, fmt.Sprintf("could not get devopsproject %s ", key)) + return err + } + copyProject := project.DeepCopy() + // DeletionTimestamp.IsZero() means DevOps project has not been deleted. + if project.ObjectMeta.DeletionTimestamp.IsZero() { + // Use Finalizers to sync DevOps status when DevOps project was deleted + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers + if !sliceutil.HasString(project.ObjectMeta.Finalizers, devopsv1alpha3.DevOpsProjectFinalizerName) { + copyProject.ObjectMeta.Finalizers = append(copyProject.ObjectMeta.Finalizers, devopsv1alpha3.DevOpsProjectFinalizerName) + } + + if project.Status.AdminNamespace != "" { + ns, err := c.namespaceLister.Get(project.Status.AdminNamespace) + if err != nil && !errors.IsNotFound(err) { + klog.Error(err, fmt.Sprintf("faild to get namespace")) + return err + } else if errors.IsNotFound(err) { + // if admin ns is not found, clean project status, rerun reconcile + copyProject.Status.AdminNamespace = "" + _, err := c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update project %s ", key)) + return err + } + c.enqueueDevOpsProject(key) + return nil + } + // If ns exists, but the associated attributes with the project are not set correctly, + // then reset the associated attributes + if k8sutil.IsControlledBy(ns.OwnerReferences, + devopsv1alpha3.ResourceKindDevOpsProject, project.Name) && + ns.Labels[constants.DevOpsProjectLabelKey] == project.Name { + } else { + copyNs := ns.DeepCopy() + err := controllerutil.SetControllerReference(copyProject, copyNs, scheme.Scheme) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to set ownerreference %s ", key)) + return err + } + copyNs.Labels[constants.DevOpsProjectLabelKey] = project.Name + _, err = c.client.CoreV1().Namespaces().Update(copyNs) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update ns %s ", key)) + return err + } + } + + } else { + // list ns by devops project + namespaces, err := c.namespaceLister.List( + labels.SelectorFromSet(labels.Set{constants.DevOpsProjectLabelKey: project.Name})) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to list ns %s ", key)) + return err + } + // if there is no ns, generate new one + if len(namespaces) == 0 { + ns := c.generateNewNamespace(project) + ns, err := c.client.CoreV1().Namespaces().Create(ns) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to create ns %s ", key)) + return err + } + copyProject.Status.AdminNamespace = ns.Name + } else if len(namespaces) != 0 { + ns := namespaces[0] + // reset ownerReferences + if !k8sutil.IsControlledBy(ns.OwnerReferences, + devopsv1alpha3.ResourceKindDevOpsProject, project.Name) { + copyNs := ns.DeepCopy() + err := controllerutil.SetControllerReference(copyProject, copyNs, scheme.Scheme) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to set ownerreference %s ", key)) + return err + } + copyNs.Labels[constants.DevOpsProjectLabelKey] = project.Name + _, err = c.client.CoreV1().Namespaces().Update(copyNs) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update ns %s ", key)) + return err + } + } + copyProject.Status.AdminNamespace = ns.Name + } + } + + if !reflect.DeepEqual(copyProject, project) { + _, err := c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update ns %s ", key)) + return err + } + } + // Check project exists, otherwise we will create it. + _, err := c.devopsClient.GetDevOpsProject(copyProject.Status.AdminNamespace) + if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get project %s ", key)) + return err + } else { + _, err := c.devopsClient.CreateDevOpsProject(copyProject.Status.AdminNamespace) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to get project %s ", key)) + return err + } + } + + } else { + // Finalizers processing logic + if sliceutil.HasString(project.ObjectMeta.Finalizers, devopsv1alpha3.DevOpsProjectFinalizerName) { + _, err := c.devopsClient.GetDevOpsProject(key) + if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get project %s ", key)) + return err + } else if err != nil && devopsClient.GetDevOpsStatusCode(err) == http.StatusNotFound { + } else { + if err := c.deleteDevOpsProjectInDevOps(project); err != nil { + klog.Error(err, fmt.Sprintf("failed to delete resource %s in devops", key)) + return err + } + } + project.ObjectMeta.Finalizers = sliceutil.RemoveString(project.ObjectMeta.Finalizers, func(item string) bool { + return item == devopsv1alpha3.DevOpsProjectFinalizerName + }) + + _, err = c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(project) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update project %s ", key)) + return err + } + } + } + + return nil +} + +func (c *Controller) deleteDevOpsProjectInDevOps(project *devopsv1alpha3.DevOpsProject) error { + + err := c.devopsClient.DeleteDevOpsProject(project.Name) + if err != nil { + klog.Errorf("error happened while deleting %s, %v", project.Name, err) + } + + return nil +} + +func (c *Controller) generateNewNamespace(project *devopsv1alpha3.DevOpsProject) *v1.Namespace { + ns := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + GenerateName: project.Name, + Labels: map[string]string{constants.DevOpsProjectLabelKey: project.Name}, + }, + } + controllerutil.SetControllerReference(project, ns, scheme.Scheme) + return ns +} diff --git a/pkg/controller/devopsproject/devopsproject_controller_test.go b/pkg/controller/devopsproject/devopsproject_controller_test.go new file mode 100644 index 000000000..900a58088 --- /dev/null +++ b/pkg/controller/devopsproject/devopsproject_controller_test.go @@ -0,0 +1,406 @@ +package devopsproject + +import ( + v1 "k8s.io/api/core/v1" + devopsprojects "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/constants" + fakeDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + kubeinformers "k8s.io/client-go/informers" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + devops "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + client *fake.Clientset + kubeclient *k8sfake.Clientset + // Objects to put in the store. + devopsProjectLister []*devops.DevOpsProject + namespaceLister []*v1.Namespace + actions []core.Action + kubeactions []core.Action + + kubeobjects []runtime.Object + // Objects from here preloaded into NewSimpleFake. + objects []runtime.Object + // Objects from here preloaded into devops + initDevOpsProject []string + expectDevOpsProject []string +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + return f +} + +func newDevOpsProject(name string, nsName string, withFinalizers bool, withStatus bool) *devopsprojects.DevOpsProject { + project := &devopsprojects.DevOpsProject{ + TypeMeta: metav1.TypeMeta{APIVersion: devopsprojects.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + if withFinalizers { + project.Finalizers = []string{devopsprojects.DevOpsProjectFinalizerName} + } + if withStatus { + project.Status = devops.DevOpsProjectStatus{AdminNamespace: nsName} + } + return project +} + +func newNamespace(name string, projectName string, useGenerateName, withOwnerReference bool) *v1.Namespace { + ns := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{constants.DevOpsProjectLabelKey: projectName}, + }, + } + if useGenerateName { + ns.ObjectMeta.Name = "" + ns.ObjectMeta.GenerateName = projectName + } + if withOwnerReference { + TRUE := true + ns.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: devops.SchemeGroupVersion.String(), + Kind: devops.ResourceKindDevOpsProject, + Name: projectName, + BlockOwnerDeletion: &TRUE, + Controller: &TRUE, + }, + } + } + return ns +} + +func newDeletingDevOpsProject(name string) *devopsprojects.DevOpsProject { + now := metav1.Now() + return &devopsprojects.DevOpsProject{ + TypeMeta: metav1.TypeMeta{APIVersion: devopsprojects.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + DeletionTimestamp: &now, + Finalizers: []string{devopsprojects.DevOpsProjectFinalizerName}, + }, + } +} + +func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory, *fakeDevOps.Devops) { + f.client = fake.NewSimpleClientset(f.objects...) + f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...) + + i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) + k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) + dI := fakeDevOps.New(f.initDevOpsProject...) + + c := NewController(f.kubeclient, f.client, dI, k8sI.Core().V1().Namespaces(), + i.Devops().V1alpha3().DevOpsProjects()) + + c.devOpsProjectSynced = alwaysReady + c.eventRecorder = &record.FakeRecorder{} + + for _, f := range f.devopsProjectLister { + i.Devops().V1alpha3().DevOpsProjects().Informer().GetIndexer().Add(f) + } + + for _, d := range f.namespaceLister { + k8sI.Core().V1().Namespaces().Informer().GetIndexer().Add(d) + } + + return c, i, k8sI, dI +} + +func (f *fixture) run(fooName string) { + f.runController(fooName, true, false) +} + +func (f *fixture) runExpectError(fooName string) { + f.runController(fooName, true, true) +} + +func (f *fixture) runController(projectName string, startInformers bool, expectError bool) { + c, i, k8sI, dI := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + i.Start(stopCh) + k8sI.Start(stopCh) + } + + err := c.syncHandler(projectName) + if !expectError && err != nil { + f.t.Errorf("error syncing foo: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing foo, got nil") + } + + actions := filterInformerActions(f.client.Actions()) + for i, action := range actions { + if len(f.actions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) + break + } + + expectedAction := f.actions[i] + checkAction(expectedAction, action, f.t) + } + k8sActions := filterInformerActions(f.kubeclient.Actions()) + for i, action := range k8sActions { + if len(f.kubeactions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:]) + break + } + + expectedAction := f.kubeactions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.kubeactions) > len(k8sActions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) + } + + if len(f.actions) > len(actions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) + } + if len(dI.Projects) != len(f.expectDevOpsProject) { + f.t.Errorf(" unexpected objects: %v", dI.Projects) + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + ret := []core.Action{} + for _, action := range actions { + if len(action.GetNamespace()) == 0 && + (action.Matches("list", devopsprojects.ResourcePluralDevOpsProject) || + action.Matches("watch", devopsprojects.ResourcePluralDevOpsProject) || + action.Matches("list", "namespaces") || + action.Matches("watch", "namespaces")) { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdateDevOpsProjectAction(p *devopsprojects.DevOpsProject) { + action := core.NewUpdateAction(schema.GroupVersionResource{Resource: devopsprojects.ResourcePluralDevOpsProject}, + p.Namespace, p) + f.actions = append(f.actions, action) +} + +func (f *fixture) expectUpdateNamespaceAction(p *v1.Namespace) { + action := core.NewUpdateAction(schema.GroupVersionResource{ + Version: "v1", + Resource: "namespaces", + }, p.Namespace, p) + f.kubeactions = append(f.kubeactions, action) +} + +func (f *fixture) expectCreateNamespaceAction(p *v1.Namespace) { + action := core.NewCreateAction(schema.GroupVersionResource{ + Version: "v1", + Resource: "namespaces", + }, p.Namespace, p) + f.kubeactions = append(f.kubeactions, action) +} + +func getKey(p *devopsprojects.DevOpsProject, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(p) + if err != nil { + t.Errorf("Unexpected error getting key for devopsprojects %v: %v", p.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + projectName := "test" + project := newDevOpsProject(projectName, nsName, true, true) + ns := newNamespace(nsName, projectName, false, true) + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, project) + f.initDevOpsProject = []string{ns.Name} + f.expectDevOpsProject = []string{ns.Name} + + f.run(getKey(project, t)) +} + +func TestUpdateProjectFinalizers(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + projectName := "test" + project := newDevOpsProject(projectName, nsName, false, true) + ns := newNamespace(nsName, projectName, false, true) + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, project) + f.kubeobjects = append(f.kubeobjects, ns) + f.initDevOpsProject = []string{ns.Name} + f.expectDevOpsProject = []string{ns.Name} + expectUpdateProject := project.DeepCopy() + expectUpdateProject.Finalizers = []string{devops.DevOpsProjectFinalizerName} + f.expectUpdateDevOpsProjectAction(expectUpdateProject) + f.run(getKey(project, t)) +} + +func TestUpdateProjectStatus(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + projectName := "test" + project := newDevOpsProject(projectName, nsName, true, false) + ns := newNamespace(nsName, projectName, false, true) + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, project) + f.kubeobjects = append(f.kubeobjects, ns) + f.initDevOpsProject = []string{ns.Name} + f.expectDevOpsProject = []string{ns.Name} + expectUpdateProject := project.DeepCopy() + expectUpdateProject.Status.AdminNamespace = nsName + f.expectUpdateDevOpsProjectAction(expectUpdateProject) + f.run(getKey(project, t)) +} + +func TestUpdateNsOwnerReference(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + projectName := "test" + project := newDevOpsProject(projectName, nsName, true, true) + ns := newNamespace(nsName, projectName, false, false) + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, project) + f.kubeobjects = append(f.kubeobjects, ns) + f.initDevOpsProject = []string{ns.Name} + f.expectDevOpsProject = []string{ns.Name} + expectUpdateNs := newNamespace(nsName, projectName, false, true) + + f.expectUpdateNamespaceAction(expectUpdateNs) + f.run(getKey(project, t)) +} + +func TestCreateDevOpsProjects(t *testing.T) { + f := newFixture(t) + project := newDevOpsProject("test", "", true, false) + ns := newNamespace("test-123", "test", true, true) + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.objects = append(f.objects, project) + f.expectDevOpsProject = []string{""} + + // because generateName not work in fakeClient, so DevOpsProject would not be update + // f.expectUpdateDevOpsProjectAction(project) + f.expectCreateNamespaceAction(ns) + f.run(getKey(project, t)) +} + +func TestDeleteDevOpsProjects(t *testing.T) { + f := newFixture(t) + project := newDeletingDevOpsProject("test") + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.objects = append(f.objects, project) + f.initDevOpsProject = []string{project.Name} + f.expectDevOpsProject = []string{} + expectProject := project.DeepCopy() + expectProject.Finalizers = []string{} + f.expectUpdateDevOpsProjectAction(expectProject) + f.run(getKey(project, t)) +} + +func TestDeleteDevOpsProjectsWithNull(t *testing.T) { + f := newFixture(t) + project := newDeletingDevOpsProject("test") + + f.devopsProjectLister = append(f.devopsProjectLister, project) + f.objects = append(f.objects, project) + f.expectDevOpsProject = []string{} + expectProject := project.DeepCopy() + expectProject.Finalizers = []string{} + f.expectUpdateDevOpsProjectAction(expectProject) + f.run(getKey(project, t)) +} diff --git a/pkg/controller/namespace/namespace_controller.go b/pkg/controller/namespace/namespace_controller.go index 0f23c88e8..e3cdd94df 100644 --- a/pkg/controller/namespace/namespace_controller.go +++ b/pkg/controller/namespace/namespace_controller.go @@ -24,7 +24,6 @@ import ( "github.com/golang/protobuf/ptypes/wrappers" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -32,12 +31,9 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/constants" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/sliceutil" "openpitrix.io/openpitrix/pkg/pb" - "reflect" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -47,22 +43,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" ) -const ( - adminDescription = "Allows admin access to perform any action on any resource, it gives full control over every resource in the namespace." - operatorDescription = "The maintainer of the namespace who can manage resources other than users and roles in the namespace." - viewerDescription = "Allows viewer access to view all resources in the namespace." -) - -var ( - admin = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "admin", Annotations: map[string]string{constants.DescriptionAnnotationKey: adminDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}} - operator = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "operator", Annotations: map[string]string{constants.DescriptionAnnotationKey: operatorDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}, - {Verbs: []string{"*"}, APIGroups: []string{"apps", "extensions", "batch", "logging.kubesphere.io", "monitoring.kubesphere.io", "iam.kubesphere.io", "autoscaling", "alerting.kubesphere.io", "openpitrix.io", "app.k8s.io", "servicemesh.kubesphere.io", "operations.kubesphere.io", "devops.kubesphere.io"}, Resources: []string{"*"}}, - {Verbs: []string{"*"}, APIGroups: []string{"", "resources.kubesphere.io"}, Resources: []string{"jobs", "cronjobs", "daemonsets", "deployments", "horizontalpodautoscalers", "ingresses", "endpoints", "configmaps", "events", "persistentvolumeclaims", "pods", "podtemplates", "pods", "secrets", "services"}}, - }} - viewer = rbac.Role{ObjectMeta: metav1.ObjectMeta{Name: "viewer", Annotations: map[string]string{constants.DescriptionAnnotationKey: viewerDescription, constants.CreatorAnnotationKey: constants.System}}, Rules: []rbac.PolicyRule{{Verbs: []string{"get", "list", "watch"}, APIGroups: []string{"*"}, Resources: []string{"*"}}}} - defaultRoles = []rbac.Role{admin, operator, viewer} -) - /** * USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller * business logic. Delete these comments after modifying this file.* @@ -70,13 +50,17 @@ var ( // Add creates a new Namespace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller // and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return add(mgr, newReconciler(mgr)) +func Add(mgr manager.Manager, openpitrixClient openpitrix.Client) error { + return add(mgr, newReconciler(mgr, openpitrixClient)) } // newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - return &ReconcileNamespace{Client: mgr.GetClient(), scheme: mgr.GetScheme()} +func newReconciler(mgr manager.Manager, openpitrixClient openpitrix.Client) reconcile.Reconciler { + return &ReconcileNamespace{ + Client: mgr.GetClient(), + scheme: mgr.GetScheme(), + openpitrixClient: openpitrixClient, + } } // add adds a new Controller to mgr with r as the reconcile.Reconciler @@ -101,7 +85,8 @@ var _ reconcile.Reconciler = &ReconcileNamespace{} // ReconcileNamespace reconciles a Namespace object type ReconcileNamespace struct { client.Client - scheme *runtime.Scheme + openpitrixClient openpitrix.Client + scheme *runtime.Scheme } // Reconcile reads that state of the cluster for a Namespace object and makes changes based on the state read @@ -132,6 +117,10 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res // then lets add the finalizer and update the object. if !sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) { instance.ObjectMeta.Finalizers = append(instance.ObjectMeta.Finalizers, finalizer) + if instance.Labels == nil { + instance.Labels = make(map[string]string) + } + instance.Labels[constants.NamespaceLabelKey] = instance.Name if err := r.Update(context.Background(), instance); err != nil { return reconcile.Result{}, err } @@ -162,31 +151,10 @@ func (r *ReconcileNamespace) Reconcile(request reconcile.Request) (reconcile.Res return reconcile.Result{}, nil } - controlledByWorkspace, err := r.isControlledByWorkspace(instance) - - if err != nil { - return reconcile.Result{}, err - } - - if !controlledByWorkspace { - - err = r.deleteRoleBindings(instance) - - return reconcile.Result{}, err - } - if err = r.checkAndBindWorkspace(instance); err != nil { return reconcile.Result{}, err } - if err = r.checkAndCreateRoles(instance); err != nil { - return reconcile.Result{}, err - } - - if err = r.checkAndCreateRoleBindings(instance); err != nil { - return reconcile.Result{}, err - } - if err := r.checkAndCreateRuntime(instance); err != nil { return reconcile.Result{}, err } @@ -206,152 +174,6 @@ func (r *ReconcileNamespace) isControlledByWorkspace(namespace *corev1.Namespace return true, nil } -// Create default roles -func (r *ReconcileNamespace) checkAndCreateRoles(namespace *corev1.Namespace) error { - for _, role := range defaultRoles { - found := &rbac.Role{} - err := r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: role.Name}, found) - if err != nil { - if errors.IsNotFound(err) { - role := role.DeepCopy() - role.Namespace = namespace.Name - err = r.Create(context.TODO(), role) - if err != nil { - klog.Error(err) - return err - } - } else { - klog.Error(err) - return err - } - } - if !reflect.DeepEqual(found.Rules, role.Rules) { - found.Rules = role.Rules - if err := r.Update(context.TODO(), found); err != nil { - klog.Error(err) - return err - } - } - } - return nil -} - -func (r *ReconcileNamespace) checkAndCreateRoleBindings(namespace *corev1.Namespace) error { - - workspaceName := namespace.Labels[constants.WorkspaceLabelKey] - creatorName := namespace.Annotations[constants.CreatorAnnotationKey] - - creator := rbac.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: creatorName} - - workspaceAdminBinding := &rbac.ClusterRoleBinding{} - - err := r.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf("workspace:%s:admin", workspaceName)}, workspaceAdminBinding) - - if err != nil { - return err - } - - adminBinding := &rbac.RoleBinding{} - adminBinding.Name = admin.Name - adminBinding.Namespace = namespace.Name - adminBinding.RoleRef = rbac.RoleRef{Name: admin.Name, APIGroup: "rbac.authorization.k8s.io", Kind: "Role"} - adminBinding.Subjects = workspaceAdminBinding.Subjects - - if creator.Name != "" { - if adminBinding.Subjects == nil { - adminBinding.Subjects = make([]rbac.Subject, 0) - } - if !k8sutil.ContainsUser(adminBinding.Subjects, creatorName) { - adminBinding.Subjects = append(adminBinding.Subjects, creator) - } - } - - found := &rbac.RoleBinding{} - - err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: adminBinding.Name}, found) - - if errors.IsNotFound(err) { - err = r.Create(context.TODO(), adminBinding) - if err != nil { - klog.Errorf("creating role binding namespace: %s,role binding: %s, error: %s", namespace.Name, adminBinding.Name, err) - return err - } - found = adminBinding - } else if err != nil { - klog.Errorf("get role binding namespace: %s,role binding: %s, error: %s", namespace.Name, adminBinding.Name, err) - return err - } - - if !reflect.DeepEqual(found.RoleRef, adminBinding.RoleRef) { - err = r.Delete(context.TODO(), found) - if err != nil { - klog.Errorf("deleting role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err) - return err - } - err = fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, adminBinding.Name) - klog.Errorf("conflict role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err) - return err - } - - if !reflect.DeepEqual(found.Subjects, adminBinding.Subjects) { - found.Subjects = adminBinding.Subjects - err = r.Update(context.TODO(), found) - if err != nil { - klog.Errorf("updating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, adminBinding.Name, err) - return err - } - } - - workspaceViewerBinding := &rbac.ClusterRoleBinding{} - - err = r.Get(context.TODO(), types.NamespacedName{Name: fmt.Sprintf("workspace:%s:viewer", workspaceName)}, workspaceViewerBinding) - - if err != nil { - return err - } - - viewerBinding := &rbac.RoleBinding{} - viewerBinding.Name = viewer.Name - viewerBinding.Namespace = namespace.Name - viewerBinding.RoleRef = rbac.RoleRef{Name: viewer.Name, APIGroup: "rbac.authorization.k8s.io", Kind: "Role"} - viewerBinding.Subjects = workspaceViewerBinding.Subjects - - err = r.Get(context.TODO(), types.NamespacedName{Namespace: namespace.Name, Name: viewerBinding.Name}, found) - - if errors.IsNotFound(err) { - err = r.Create(context.TODO(), viewerBinding) - if err != nil { - klog.Errorf("creating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err) - return err - } - found = viewerBinding - } else if err != nil { - return err - } - - if !reflect.DeepEqual(found.RoleRef, viewerBinding.RoleRef) { - err = r.Delete(context.TODO(), found) - if err != nil { - klog.Errorf("deleting conflict role binding namespace: %s, role binding: %s, %s", namespace.Name, viewerBinding.Name, err) - return err - } - err = fmt.Errorf("conflict role binding %s.%s, waiting for recreate", namespace.Name, viewerBinding.Name) - klog.Errorf("conflict role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err) - return err - } - - if !reflect.DeepEqual(found.Subjects, viewerBinding.Subjects) { - found.Subjects = viewerBinding.Subjects - err = r.Update(context.TODO(), found) - if err != nil { - klog.Errorf("updating role binding namespace: %s, role binding: %s, error: %s", namespace.Name, viewerBinding.Name, err) - return err - } - } - - return nil -} - // Create openpitrix runtime func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) error { @@ -359,18 +181,9 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) return nil } - openPitrixClient, err := cs.ClientSets().OpenPitrix() - - if _, notEnabled := err.(cs.ClientSetNotEnabledError); notEnabled { - return nil - } else if err != nil { - klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) - return err - } - adminKubeConfigName := fmt.Sprintf("kubeconfig-%s", constants.AdminUserName) - runtimeCredentials, err := openPitrixClient.Runtime().DescribeRuntimeCredentials(openpitrix.SystemContext(), &pb.DescribeRuntimeCredentialsRequest{SearchWord: &wrappers.StringValue{Value: adminKubeConfigName}, Limit: 1}) + runtimeCredentials, err := r.openpitrixClient.DescribeRuntimeCredentials(openpitrix.SystemContext(), &pb.DescribeRuntimeCredentialsRequest{SearchWord: &wrappers.StringValue{Value: adminKubeConfigName}, Limit: 1}) if err != nil { klog.Error(fmt.Sprintf("create runtime, namespace: %s, error: %s", namespace.Name, err)) @@ -391,7 +204,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) return err } - resp, err := openPitrixClient.Runtime().CreateRuntimeCredential(openpitrix.SystemContext(), &pb.CreateRuntimeCredentialRequest{ + resp, err := r.openpitrixClient.CreateRuntimeCredential(openpitrix.SystemContext(), &pb.CreateRuntimeCredentialRequest{ Name: &wrappers.StringValue{Value: adminKubeConfigName}, Provider: &wrappers.StringValue{Value: "kubernetes"}, Description: &wrappers.StringValue{Value: "kubeconfig"}, @@ -408,7 +221,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) } // TODO runtime id is invalid when recreate runtime - runtimeId, err := openPitrixClient.Runtime().CreateRuntime(openpitrix.SystemContext(), &pb.CreateRuntimeRequest{ + runtimeId, err := r.openpitrixClient.CreateRuntime(openpitrix.SystemContext(), &pb.CreateRuntimeRequest{ Name: &wrappers.StringValue{Value: namespace.Name}, RuntimeCredentialId: &wrappers.StringValue{Value: kubesphereRuntimeCredentialId}, Provider: &wrappers.StringValue{Value: openpitrix.KubernetesProvider}, @@ -429,17 +242,7 @@ func (r *ReconcileNamespace) checkAndCreateRuntime(namespace *corev1.Namespace) func (r *ReconcileNamespace) deleteRuntime(namespace *corev1.Namespace) error { if runtimeId := namespace.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" { - - openPitrixClient, err := cs.ClientSets().OpenPitrix() - - if _, notEnabled := err.(cs.ClientSetNotEnabledError); notEnabled { - return nil - } else if err != nil { - klog.Errorf("delete openpitrix runtime: %s, error: %s", runtimeId, err) - return err - } - - _, err = openPitrixClient.Runtime().DeleteRuntimes(openpitrix.SystemContext(), &pb.DeleteRuntimesRequest{RuntimeId: []string{runtimeId}, Force: &wrappers.BoolValue{Value: true}}) + _, err := r.openpitrixClient.DeleteRuntimes(openpitrix.SystemContext(), &pb.DeleteRuntimesRequest{RuntimeId: []string{runtimeId}, Force: &wrappers.BoolValue{Value: true}}) if err == nil || openpitrix.IsNotFound(err) || openpitrix.IsDeleted(err) { return nil @@ -528,24 +331,3 @@ func (r *ReconcileNamespace) deleteRouter(namespace string) error { return nil } - -func (r *ReconcileNamespace) deleteRoleBindings(namespace *corev1.Namespace) error { - klog.V(4).Info("deleting role bindings namespace: ", namespace.Name) - adminBinding := &rbac.RoleBinding{} - adminBinding.Name = admin.Name - adminBinding.Namespace = namespace.Name - err := r.Delete(context.TODO(), adminBinding) - if err != nil && !errors.IsNotFound(err) { - klog.Errorf("deleting role binding namespace: %s, role binding: %s,error: %s", namespace.Name, adminBinding.Name, err) - return err - } - viewerBinding := &rbac.RoleBinding{} - viewerBinding.Name = viewer.Name - viewerBinding.Namespace = namespace.Name - err = r.Delete(context.TODO(), viewerBinding) - if err != nil && !errors.IsNotFound(err) { - klog.Errorf("deleting role binding namespace: %s,role binding: %s,error: %s", namespace.Name, viewerBinding.Name, err) - return err - } - return nil -} diff --git a/pkg/controller/network/controllerapi/interface.go b/pkg/controller/network/controllerapi/interface.go deleted file mode 100644 index 1c93094f4..000000000 --- a/pkg/controller/network/controllerapi/interface.go +++ /dev/null @@ -1,6 +0,0 @@ -package controllerapi - -// Controller expose Run method -type Controller interface { - Run(threadiness int, stopCh <-chan struct{}) error -} diff --git a/pkg/controller/network/doc.go b/pkg/controller/network/doc.go index 5aa1ac406..1fc222d26 100644 --- a/pkg/controller/network/doc.go +++ b/pkg/controller/network/doc.go @@ -1,5 +1,5 @@ -package network - -// +kubebuilder:rbac:groups=network.kubesphere.io,resources=workspacenetworkpolicies;namespacenetworkpolicies,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups:core,resource=namespaces,verbs=get;list;watch;create;update;patch +// +kubebuilder:rbac:groups=network.kubesphere.io,resources=namespacenetworkpolicies,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups:core,resource=namespaces,verbs=get;list;watch;create;update;patch +// +kubebuilder:rbac:groups:core,resource=services,verbs=get;list;watch;create;update;patch +package network diff --git a/pkg/controller/network/nsnetworkpolicy/controller.go b/pkg/controller/network/nsnetworkpolicy/controller.go index 7b380aa41..cdd903a17 100644 --- a/pkg/controller/network/nsnetworkpolicy/controller.go +++ b/pkg/controller/network/nsnetworkpolicy/controller.go @@ -2,176 +2,658 @@ package nsnetworkpolicy import ( "fmt" + "net" "time" corev1 "k8s.io/api/core/v1" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" + netv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + typev1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/intstr" + uruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" + v1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" "k8s.io/klog" - "k8s.io/klog/klogr" - kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" - networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1" - networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1" - "kubesphere.io/kubesphere/pkg/controller/network/controllerapi" + "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + workspacev1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + ksnetclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1" + nspolicy "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1" + workspace "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/controller/network/provider" ) -const controllerAgentName = "nsnp-controller" +const ( + //TODO use set to track service:map + //use period sync service label in NSNP + defaultSleepDuration = 60 * time.Second -type controller struct { - kubeClientset kubernetes.Interface - kubesphereClientset kubesphereclient.Interface + defaultThread = 5 + defaultSync = "5m" - nsnpInformer networkinformer.NamespaceNetworkPolicyInformer - nsnpLister networklister.NamespaceNetworkPolicyLister - nsnpSynced cache.InformerSynced - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder - nsNetworkPolicyProvider provider.NsNetworkPolicyProvider -} + //whether network isolate is enable in namespace + NamespaceNPAnnotationKey = "kubesphere.io/network-isolate" + NamespaceNPAnnotationEnabled = "enabled" -var ( - log = klogr.New().WithName("Controller").WithValues("Component", controllerAgentName) - errCount = 0 + AnnotationNPNAME = "network-isolate" + + //TODO: configure it + DNSLocalIP = "169.254.25.10" + DNSPort = 53 + DNSNamespace = "kube-system" + DNSServiceName = "kube-dns" + DNSServiceCoreDNS = "coredns" ) -func NewController(kubeclientset kubernetes.Interface, - kubesphereclientset kubesphereclient.Interface, - nsnpInformer networkinformer.NamespaceNetworkPolicyInformer, - nsNetworkPolicyProvider provider.NsNetworkPolicyProvider) controllerapi.Controller { - utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme)) - log.V(4).Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(klog.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - ctl := &controller{ - kubeClientset: kubeclientset, - kubesphereClientset: kubesphereclientset, - nsnpInformer: nsnpInformer, - nsnpLister: nsnpInformer.Lister(), - nsnpSynced: nsnpInformer.Informer().HasSynced, - nsNetworkPolicyProvider: nsNetworkPolicyProvider, +// namespacenpController implements the Controller interface for managing kubesphere network policies +// and convery them to k8s NetworkPolicies, then syncing them to the provider. +type NSNetworkPolicyController struct { + client kubernetes.Interface + ksclient ksnetclient.NetworkV1alpha1Interface + informer nspolicy.NamespaceNetworkPolicyInformer + informerSynced cache.InformerSynced - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "NamespaceNetworkPolicies"), - recorder: recorder, - } - log.Info("Setting up event handlers") - nsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: ctl.enqueueNSNP, - UpdateFunc: func(old, new interface{}) { - ctl.enqueueNSNP(new) - }, - DeleteFunc: ctl.enqueueNSNP, - }) - return ctl + //TODO support service in same namespace + serviceInformer v1.ServiceInformer + serviceInformerSynced cache.InformerSynced + + nodeInformer v1.NodeInformer + nodeInformerSynced cache.InformerSynced + + workspaceInformer workspace.WorkspaceInformer + workspaceInformerSynced cache.InformerSynced + + namespaceInformer v1.NamespaceInformer + namespaceInformerSynced cache.InformerSynced + + provider provider.NsNetworkPolicyProvider + + nsQueue workqueue.RateLimitingInterface + nsnpQueue workqueue.RateLimitingInterface } -func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - defer c.workqueue.ShutDown() - - //init client - - // Start the informer factories to begin populating the informer caches - log.V(1).Info("Starting WSNP controller") - - // Wait for the caches to be synced before starting workers - log.V(2).Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.nsnpSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") +func stringToCIDR(ipStr string) (string, error) { + cidr := "" + if ip := net.ParseIP(ipStr); ip != nil { + if ip.To4() != nil { + cidr = ipStr + "/32" + } else { + cidr = ipStr + "/128" + } + } else { + return cidr, fmt.Errorf("ip string %s parse error\n", ipStr) } - log.Info("Starting workers") - // Launch two workers to process Foo resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) + return cidr, nil +} + +func generateDNSRule(nameServers []string) (netv1.NetworkPolicyEgressRule, error) { + var rule netv1.NetworkPolicyEgressRule + for _, nameServer := range nameServers { + cidr, err := stringToCIDR(nameServer) + if err != nil { + return rule, err + } + rule.To = append(rule.To, netv1.NetworkPolicyPeer{ + IPBlock: &netv1.IPBlock{ + CIDR: cidr, + }, + }) + } + + protocolTCP := corev1.ProtocolTCP + protocolUDP := corev1.ProtocolUDP + dnsPort := intstr.FromInt(DNSPort) + rule.Ports = append(rule.Ports, netv1.NetworkPolicyPort{ + Protocol: &protocolTCP, + Port: &dnsPort, + }, netv1.NetworkPolicyPort{ + Protocol: &protocolUDP, + Port: &dnsPort, + }) + + return rule, nil +} + +func (c *NSNetworkPolicyController) generateDNSServiceRule() (netv1.NetworkPolicyEgressRule, error) { + peer, ports, err := c.handlerPeerService(DNSNamespace, DNSServiceName, false) + if err != nil { + peer, ports, err = c.handlerPeerService(DNSNamespace, DNSServiceCoreDNS, false) + } + + return netv1.NetworkPolicyEgressRule{ + Ports: ports, + To: []netv1.NetworkPolicyPeer{peer}, + }, err +} + +func (c *NSNetworkPolicyController) handlerPeerService(namespace string, name string, ingress bool) (netv1.NetworkPolicyPeer, []netv1.NetworkPolicyPort, error) { + peerNP := netv1.NetworkPolicyPeer{} + var ports []netv1.NetworkPolicyPort + + service, err := c.serviceInformer.Lister().Services(namespace).Get(name) + if err != nil { + return peerNP, nil, err + } + + peerNP.PodSelector = new(metav1.LabelSelector) + peerNP.NamespaceSelector = new(metav1.LabelSelector) + + if len(service.Spec.Selector) <= 0 { + return peerNP, nil, fmt.Errorf("service %s/%s has no podselect", namespace, name) + } + + peerNP.PodSelector.MatchLabels = make(map[string]string) + for key, value := range service.Spec.Selector { + peerNP.PodSelector.MatchLabels[key] = value + } + peerNP.NamespaceSelector.MatchLabels = make(map[string]string) + peerNP.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace + + //only allow traffic to service exposed ports + if !ingress { + ports = make([]netv1.NetworkPolicyPort, 0) + for _, port := range service.Spec.Ports { + portIntString := intstr.FromInt(int(port.Port)) + ports = append(ports, netv1.NetworkPolicyPort{ + Protocol: &port.Protocol, + Port: &portIntString, + }) + } + } + + return peerNP, ports, err +} + +func (c *NSNetworkPolicyController) convertPeer(peer v1alpha1.NetworkPolicyPeer, ingress bool) (netv1.NetworkPolicyPeer, []netv1.NetworkPolicyPort, error) { + peerNP := netv1.NetworkPolicyPeer{} + var ports []netv1.NetworkPolicyPort + + if peer.ServiceSelector != nil { + namespace := peer.ServiceSelector.Namespace + name := peer.ServiceSelector.Name + + return c.handlerPeerService(namespace, name, ingress) + } else if peer.NamespaceSelector != nil { + name := peer.NamespaceSelector.Name + + peerNP.NamespaceSelector = new(metav1.LabelSelector) + peerNP.NamespaceSelector.MatchLabels = make(map[string]string) + peerNP.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = name + } else if peer.IPBlock != nil { + peerNP.IPBlock = peer.IPBlock + } else { + klog.Errorf("Invalid nsnp peer %v\n", peer) + return peerNP, nil, fmt.Errorf("Invalid nsnp peer %v\n", peer) + } + + return peerNP, ports, nil +} + +func (c *NSNetworkPolicyController) convertToK8sNP(n *v1alpha1.NamespaceNetworkPolicy) (*netv1.NetworkPolicy, error) { + np := &netv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: n.Name, + Namespace: n.Namespace, + }, + Spec: netv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + PolicyTypes: make([]netv1.PolicyType, 0), + }, + } + + if n.Spec.Egress != nil { + np.Spec.Egress = make([]netv1.NetworkPolicyEgressRule, 0) + for _, egress := range n.Spec.Egress { + tmpRule := netv1.NetworkPolicyEgressRule{} + for _, peer := range egress.To { + peer, ports, err := c.convertPeer(peer, false) + if err != nil { + return nil, err + } + if ports != nil { + np.Spec.Egress = append(np.Spec.Egress, netv1.NetworkPolicyEgressRule{ + Ports: ports, + To: []netv1.NetworkPolicyPeer{peer}, + }) + continue + } + tmpRule.To = append(tmpRule.To, peer) + } + tmpRule.Ports = egress.Ports + if tmpRule.To == nil { + continue + } + np.Spec.Egress = append(np.Spec.Egress, tmpRule) + } + np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeEgress) + } + + if n.Spec.Ingress != nil { + np.Spec.Ingress = make([]netv1.NetworkPolicyIngressRule, 0) + for _, ingress := range n.Spec.Ingress { + tmpRule := netv1.NetworkPolicyIngressRule{} + for _, peer := range ingress.From { + peer, ports, err := c.convertPeer(peer, true) + if err != nil { + return nil, err + } + if ports != nil { + np.Spec.Ingress = append(np.Spec.Ingress, netv1.NetworkPolicyIngressRule{ + Ports: ports, + From: []netv1.NetworkPolicyPeer{peer}, + }) + } + tmpRule.From = append(tmpRule.From, peer) + } + tmpRule.Ports = ingress.Ports + np.Spec.Ingress = append(np.Spec.Ingress, tmpRule) + } + np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeIngress) + } + + return np, nil +} + +func (c *NSNetworkPolicyController) generateNodeRule() (netv1.NetworkPolicyIngressRule, error) { + var rule netv1.NetworkPolicyIngressRule + + nodes, err := c.nodeInformer.Lister().List(labels.Everything()) + if err != nil { + return rule, err + } + for _, node := range nodes { + for _, address := range node.Status.Addresses { + cidr, err := stringToCIDR(address.Address) + if err != nil { + klog.V(4).Infof("Error when parse address %s", address.Address) + continue + } + rule.From = append(rule.From, netv1.NetworkPolicyPeer{ + IPBlock: &netv1.IPBlock{ + CIDR: cidr, + }, + }) + } + } + + return rule, nil +} + +func generateNSNP(workspace string, namespace string, matchWorkspace bool) *netv1.NetworkPolicy { + policy := &netv1.NetworkPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: AnnotationNPNAME, + Namespace: namespace, + }, + Spec: netv1.NetworkPolicySpec{ + PodSelector: metav1.LabelSelector{}, + PolicyTypes: make([]netv1.PolicyType, 0), + Ingress: []netv1.NetworkPolicyIngressRule{{ + From: []netv1.NetworkPolicyPeer{{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{}, + }, + }}, + }}, + Egress: []netv1.NetworkPolicyEgressRule{{ + To: []netv1.NetworkPolicyPeer{{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{}, + }, + }}, + }}, + }, + } + + policy.Spec.PolicyTypes = append(policy.Spec.PolicyTypes, netv1.PolicyTypeIngress, netv1.PolicyTypeEgress) + + if matchWorkspace { + policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace + policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace + } else { + policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace + policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace + } + + return policy +} + +func (c *NSNetworkPolicyController) nsEnqueue(ns *corev1.Namespace) { + key, err := cache.MetaNamespaceKeyFunc(ns) + if err != nil { + uruntime.HandleError(fmt.Errorf("Get namespace key %s failed", ns.Name)) + return + } + + klog.V(4).Infof("Enqueue namespace %s", ns.Name) + c.nsQueue.Add(key) +} + +func (c *NSNetworkPolicyController) addWorkspace(newObj interface{}) { + new := newObj.(*workspacev1alpha1.Workspace) + + klog.V(4).Infof("Add workspace %s", new.Name) + + label := labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: new.Name}) + nsList, err := c.namespaceInformer.Lister().List(label) + if err != nil { + klog.Errorf("Error while list namespace by label %s", label.String()) + return + } + + for _, ns := range nsList { + c.nsEnqueue(ns) + } +} + +func (c *NSNetworkPolicyController) addNamespace(obj interface{}) { + ns := obj.(*corev1.Namespace) + + workspaceName := ns.Labels[constants.WorkspaceLabelKey] + if workspaceName == "" { + return + } + + klog.V(4).Infof("Add namespace %s", ns.Name) + + c.nsEnqueue(ns) +} + +func isNetworkIsolateEnabled(ns *corev1.Namespace) bool { + if ns.Annotations[NamespaceNPAnnotationKey] == NamespaceNPAnnotationEnabled { + return true + } + + return false +} + +func hadNamespaceLabel(ns *corev1.Namespace) bool { + if ns.Annotations[constants.NamespaceLabelKey] == ns.Name { + return true + } + + return false +} + +func (c *NSNetworkPolicyController) syncNs(key string) error { + klog.V(4).Infof("Sync namespace %s", key) + + _, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + klog.Errorf("Not a valid controller key %s, %#v", key, err) + return err + } + + ns, err := c.namespaceInformer.Lister().Get(name) + if err != nil { + // not found, possibly been deleted + if errors.IsNotFound(err) { + klog.V(2).Infof("Namespace %v has been deleted", key) + return nil + } + + return err + } + + workspaceName := ns.Labels[constants.WorkspaceLabelKey] + if workspaceName == "" { + klog.Error("Workspace name should not be empty") + return nil + } + wksp, err := c.workspaceInformer.Lister().Get(workspaceName) + if err != nil { + //Should not be here + if errors.IsNotFound(err) { + klog.V(2).Infof("Workspace %v has been deleted", workspaceName) + return nil + } + + return err + } + + //Maybe some ns not labeled + if !hadNamespaceLabel(ns) { + ns.Labels[constants.NamespaceLabelKey] = ns.Name + _, err := c.client.CoreV1().Namespaces().Update(ns) + if err != nil { + //Just log, label can also be added by namespace controller + klog.Errorf("cannot label namespace %s", ns.Name) + } + } + + matchWorkspace := false + delete := false + if isNetworkIsolateEnabled(ns) { + matchWorkspace = false + } else if wksp.Spec.NetworkIsolation { + matchWorkspace = true + } else { + delete = true + } + + policy := generateNSNP(workspaceName, ns.Name, matchWorkspace) + ruleDNS, err := generateDNSRule([]string{DNSLocalIP}) + if err != nil { + return err + } + policy.Spec.Egress = append(policy.Spec.Egress, ruleDNS) + ruleDNSService, err := c.generateDNSServiceRule() + if err == nil { + policy.Spec.Egress = append(policy.Spec.Egress, ruleDNSService) + } else { + klog.Warningf("Cannot handle service %s or %s", DNSServiceName, DNSServiceCoreDNS) + } + ruleNode, err := c.generateNodeRule() + if err != nil { + return err + } + policy.Spec.Ingress = append(policy.Spec.Ingress, ruleNode) + if delete { + c.provider.Delete(c.provider.GetKey(AnnotationNPNAME, ns.Name)) + //delete all namespace np when networkisolate not active + if c.ksclient.NamespaceNetworkPolicies(ns.Name).DeleteCollection(nil, typev1.ListOptions{}) != nil { + klog.Errorf("Error when delete all nsnps in namespace %s", ns.Name) + } + } else { + err = c.provider.Set(policy) + if err != nil { + klog.Errorf("Error while converting %#v to provider's network policy.", policy) + return err + } } - klog.V(2).Info("Started workers") - <-stopCh - log.V(2).Info("Shutting down workers") return nil } -func (c *controller) enqueueNSNP(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - utilruntime.HandleError(err) - return - } - c.workqueue.Add(key) -} - -func (c *controller) runWorker() { - for c.processNextWorkItem() { +func (c *NSNetworkPolicyController) nsWorker() { + for c.processNsWorkItem() { } } -func (c *controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { +func (c *NSNetworkPolicyController) processNsWorkItem() bool { + key, quit := c.nsQueue.Get() + if quit { return false } + defer c.nsQueue.Done(key) - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the reconcile, passing it the namespace/name string of the - // Foo resource to be synced. - if err := c.reconcile(key); err != nil { - // Put the item back on the workqueue to handle any transient errors. - c.workqueue.AddRateLimited(key) - return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - log.Info("Successfully synced", "key", key) - return nil - }(obj) - - if err != nil { - utilruntime.HandleError(err) - return true + if err := c.syncNs(key.(string)); err != nil { + klog.Errorf("Error when syncns %s", err) } return true } + +func (c *NSNetworkPolicyController) nsnpEnqueue(obj interface{}) { + nsnp := obj.(*v1alpha1.NamespaceNetworkPolicy) + + key, err := cache.MetaNamespaceKeyFunc(nsnp) + if err != nil { + uruntime.HandleError(fmt.Errorf("get namespace network policy key %s failed", nsnp.Name)) + return + } + + c.nsnpQueue.Add(key) +} + +func (c *NSNetworkPolicyController) syncNSNP(key string) error { + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + klog.Errorf("Not a valid controller key %s, %#v", key, err) + return err + } + + nsnp, err := c.informer.Lister().NamespaceNetworkPolicies(namespace).Get(name) + if err != nil { + if errors.IsNotFound(err) { + klog.V(4).Infof("NSNP %v has been deleted", key) + c.provider.Delete(c.provider.GetKey(name, namespace)) + return nil + } + + return err + } + + np, err := c.convertToK8sNP(nsnp) + if err != nil { + klog.Errorf("Error while convert nsnp to k8snp: %s", err) + return err + } + err = c.provider.Set(np) + if err != nil { + return err + } + + return nil +} + +func (c *NSNetworkPolicyController) nsNPWorker() { + for c.processNSNPWorkItem() { + } +} + +func (c *NSNetworkPolicyController) processNSNPWorkItem() bool { + key, quit := c.nsnpQueue.Get() + if quit { + return false + } + defer c.nsnpQueue.Done(key) + + c.syncNSNP(key.(string)) + + return true +} + +// NewnamespacenpController returns a controller which manages NSNSP objects. +func NewNSNetworkPolicyController( + client kubernetes.Interface, + ksclient ksnetclient.NetworkV1alpha1Interface, + nsnpInformer nspolicy.NamespaceNetworkPolicyInformer, + serviceInformer v1.ServiceInformer, + nodeInformer v1.NodeInformer, + workspaceInformer workspace.WorkspaceInformer, + namespaceInformer v1.NamespaceInformer, + policyProvider provider.NsNetworkPolicyProvider) *NSNetworkPolicyController { + + controller := &NSNetworkPolicyController{ + client: client, + ksclient: ksclient, + informer: nsnpInformer, + informerSynced: nsnpInformer.Informer().HasSynced, + serviceInformer: serviceInformer, + serviceInformerSynced: serviceInformer.Informer().HasSynced, + nodeInformer: nodeInformer, + nodeInformerSynced: nodeInformer.Informer().HasSynced, + workspaceInformer: workspaceInformer, + workspaceInformerSynced: workspaceInformer.Informer().HasSynced, + namespaceInformer: namespaceInformer, + namespaceInformerSynced: namespaceInformer.Informer().HasSynced, + provider: policyProvider, + nsQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"), + nsnpQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespacenp"), + } + + workspaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: controller.addWorkspace, + UpdateFunc: func(oldObj, newObj interface{}) { + old := oldObj.(*workspacev1alpha1.Workspace) + new := oldObj.(*workspacev1alpha1.Workspace) + if old.Spec.NetworkIsolation == new.Spec.NetworkIsolation { + return + } + controller.addWorkspace(newObj) + }, + }) + + namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: controller.addNamespace, + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + old := oldObj.(*corev1.Namespace) + new := oldObj.(*corev1.Namespace) + if old.Annotations[NamespaceNPAnnotationKey] == new.Annotations[NamespaceNPAnnotationKey] { + return + } + controller.addNamespace(newObj) + }, + }) + + nsnpInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + klog.V(4).Infof("Got ADD event for NSNSP: %#v", obj) + controller.nsnpEnqueue(obj) + }, + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + klog.V(4).Info("Got UPDATE event for NSNSP.") + klog.V(4).Infof("Old object: \n%#v\n", oldObj) + klog.V(4).Infof("New object: \n%#v\n", newObj) + controller.nsnpEnqueue(newObj) + }, + DeleteFunc: func(obj interface{}) { + klog.V(4).Infof("Got DELETE event for NSNP: %#v", obj) + controller.nsnpEnqueue(obj) + }, + }, defaultSleepDuration) + + return controller +} + +func (c *NSNetworkPolicyController) Start(stopCh <-chan struct{}) error { + return c.Run(defaultThread, defaultSync, stopCh) +} + +// Run starts the controller. +func (c *NSNetworkPolicyController) Run(threadiness int, reconcilerPeriod string, stopCh <-chan struct{}) error { + defer uruntime.HandleCrash() + + defer c.nsQueue.ShutDown() + defer c.nsnpQueue.ShutDown() + + klog.Info("Waiting to sync with Kubernetes API (NSNP)") + if ok := cache.WaitForCacheSync(stopCh, c.informerSynced, c.serviceInformerSynced, c.workspaceInformerSynced, c.namespaceInformerSynced, c.nodeInformerSynced); !ok { + return fmt.Errorf("Failed to wait for caches to sync") + } + klog.Info("Finished syncing with Kubernetes API (NSNP)") + + // Start a number of worker threads to read from the queue. Each worker + // will pull keys off the resource cache event queue and sync them to the + // K8s datastore. + for i := 0; i < threadiness; i++ { + go wait.Until(c.nsWorker, time.Second, stopCh) + go wait.Until(c.nsNPWorker, time.Second, stopCh) + } + + //Work to sync K8s NetworkPolicy + go c.provider.Start(stopCh) + + klog.Info("NSNP controller is now running") + <-stopCh + klog.Info("Stopping NSNP controller") + + return nil +} diff --git a/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_suite_test.go b/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_suite_test.go index 23e1eda18..fe2c10c91 100644 --- a/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_suite_test.go +++ b/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_suite_test.go @@ -11,8 +11,7 @@ import ( func TestNsnetworkpolicy(t *testing.T) { klog.InitFlags(nil) - flag.Set("logtostderr", "false") - flag.Set("alsologtostderr", "false") + flag.Set("logtostderr", "true") flag.Set("v", "4") flag.Parse() klog.SetOutput(GinkgoWriter) diff --git a/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_test.go b/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_test.go index 101fa29ce..2e32ee93d 100644 --- a/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_test.go +++ b/pkg/controller/network/nsnetworkpolicy/nsnetworkpolicy_test.go @@ -1,90 +1,337 @@ package nsnetworkpolicy import ( + "fmt" + "reflect" + "strings" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - nsnplister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1" - "kubesphere.io/kubesphere/pkg/controller/network/controllerapi" + corev1 "k8s.io/api/core/v1" + netv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/util/yaml" + kubeinformers "k8s.io/client-go/informers" + informerv1 "k8s.io/client-go/informers/core/v1" + kubefake "k8s.io/client-go/kubernetes/fake" + "k8s.io/klog" + netv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" + wkspv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + ksfake "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + nsnppolicyinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1" + workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/controller/network/provider" - controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing" ) var ( - fakeControllerBuilder *controllertesting.FakeControllerBuilder - c controllerapi.Controller - stopCh chan struct{} - calicoProvider *provider.FakeCalicoNetworkProvider - nsnpLister nsnplister.NamespaceNetworkPolicyLister + c *NSNetworkPolicyController + stopCh chan struct{} + nsnpInformer nsnppolicyinformer.NamespaceNetworkPolicyInformer + serviceInformer informerv1.ServiceInformer + workspaceInformer workspaceinformer.WorkspaceInformer + namespaceInformer informerv1.NamespaceInformer + alwaysReady = func() bool { return true } ) +const ( + workspaceNP = ` +apiVersion: "networking.k8s.io/v1" +kind: "NetworkPolicy" +metadata: + name: networkisolate + namespace: %s +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + %s: %s + Egress: + - To: + - namespaceSelector: + matchLabels: + %s: %s + policyTypes: + - Ingress + - Egress` + + serviceTmp = ` +apiVersion: v1 +kind: Service +metadata: + name: myservice + namespace: testns +spec: + clusterIP: 10.0.0.1 + selector: + app: mylbapp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 +` + + workspaceTmp = ` +apiVersion: tenant.kubesphere.io/v1alpha1 +kind: Workspace +metadata: + annotations: + kubesphere.io/creator: admin + name: testworkspace +spec: + manager: admin + networkIsolation: true +status: {} +` + + nsTmp = ` +apiVersion: v1 +kind: Namespace +metadata: + labels: + kubesphere.io/workspace: testworkspace + name: testns +spec: + finalizers: + - kubernetes +` +) + +func StringToObject(data string, obj interface{}) error { + reader := strings.NewReader(data) + return yaml.NewYAMLOrJSONDecoder(reader, 10).Decode(obj) +} + var _ = Describe("Nsnetworkpolicy", func() { BeforeEach(func() { - fakeControllerBuilder = controllertesting.NewFakeControllerBuilder() stopCh = make(chan struct{}) - informer, _ := fakeControllerBuilder.NewControllerInformer() - calicoProvider = provider.NewFakeCalicoNetworkProvider() - c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient, informer.Network().V1alpha1().NamespaceNetworkPolicies(), calicoProvider) - go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stopCh) - originalController := c.(*controller) - originalController.recorder = &record.FakeRecorder{} - go c.Run(1, stopCh) - nsnpLister = informer.Network().V1alpha1().NamespaceNetworkPolicies().Lister() + calicoProvider := provider.NewFakeNetworkProvider() + + kubeClient := kubefake.NewSimpleClientset() + ksClient := ksfake.NewSimpleClientset() + kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, 0) + ksInformer := ksinformers.NewSharedInformerFactory(ksClient, 0) + + nsnpInformer := ksInformer.Network().V1alpha1().NamespaceNetworkPolicies() + serviceInformer := kubeInformer.Core().V1().Services() + nodeInforemer := kubeInformer.Core().V1().Nodes() + workspaceInformer := ksInformer.Tenant().V1alpha1().Workspaces() + namespaceInformer := kubeInformer.Core().V1().Namespaces() + + c = NewNSNetworkPolicyController(kubeClient, ksClient.NetworkV1alpha1(), nsnpInformer, serviceInformer, nodeInforemer, workspaceInformer, namespaceInformer, calicoProvider) + + serviceObj := &corev1.Service{} + Expect(StringToObject(serviceTmp, serviceObj)).ShouldNot(HaveOccurred()) + Expect(serviceInformer.Informer().GetIndexer().Add(serviceObj)).ShouldNot(HaveOccurred()) + nsObj := &corev1.Namespace{} + Expect(StringToObject(nsTmp, nsObj)).ShouldNot(HaveOccurred()) + namespaceInformer.Informer().GetIndexer().Add(nsObj) + workspaceObj := &wkspv1alpha1.Workspace{} + Expect(StringToObject(workspaceTmp, workspaceObj)).ShouldNot(HaveOccurred()) + workspaceInformer.Informer().GetIndexer().Add(workspaceObj) + + c.namespaceInformerSynced = alwaysReady + c.serviceInformerSynced = alwaysReady + c.workspaceInformerSynced = alwaysReady + c.informerSynced = alwaysReady + + go c.Start(stopCh) }) - It("Should create a new calico object", func() { - objSrt := `{ - "apiVersion": "network.kubesphere.io/v1alpha1", - "kind": "NetworkPolicy", - "metadata": { - "name": "allow-tcp-6379", - "namespace": "production" - }, - "spec": { - "selector": "color == 'red'", - "ingress": [ - { - "action": "Allow", - "protocol": "TCP", - "source": { - "selector": "color == 'blue'" - }, - "destination": { - "ports": [ - 6379 - ] - } - } - ] - } - }` - obj := &v1alpha1.NamespaceNetworkPolicy{} - Expect(controllertesting.StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) - _, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Create(obj) + It("Should create ns networkisolate np correctly in workspace", func() { + objSrt := fmt.Sprintf(workspaceNP, "testns", constants.WorkspaceLabelKey, "testworkspace", constants.WorkspaceLabelKey, "testworkspace") + obj := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + + policy := generateNSNP("testworkspace", "testns", true) + Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue()) + }) + + It("Should create ns networkisolate np correctly in ns", func() { + objSrt := fmt.Sprintf(workspaceNP, "testns", constants.NamespaceLabelKey, "testns", constants.NamespaceLabelKey, "testns") + obj := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + + policy := generateNSNP("testworkspace", "testns", false) + Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue()) + }) + + It("test func convertToK8sNP", func() { + objSrt := ` +apiVersion: network.kubesphere.io/v1alpha1 +kind: NamespaceNetworkPolicy +metadata: + name: namespaceIPblockNP + namespace: testns +spec: + ingress: + - from: + - ipBlock: + cidr: 172.0.0.1/16 + ports: + - protocol: TCP + port: 80 +` + obj := &netv1alpha1.NamespaceNetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + policy, err := c.convertToK8sNP(obj) + + objSrt = ` +apiVersion: "networking.k8s.io/v1" +kind: "NetworkPolicy" +metadata: + name: IPblockNP + namespace: testns +spec: + ingress: + - from: + - ipBlock: + cidr: 172.0.0.1/16 + ports: + - protocol: TCP + port: 80 + policyTypes: + - Ingress +` + obj2 := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred()) - Eventually(func() bool { - exist, _ := calicoProvider.CheckExist(obj) - return exist - }).Should(BeTrue()) - obj, _ = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Get(obj.Name, metav1.GetOptions{}) - Expect(obj.Finalizers).To(HaveLen(1)) - // TestUpdate - newStr := "color == 'green'" - obj.Spec.Selector = newStr - _, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj) + Expect(reflect.DeepEqual(obj2.Spec, policy.Spec)).To(BeTrue()) + }) + + It("test func convertToK8sNP with namespace", func() { + objSrt := ` +apiVersion: network.kubesphere.io/v1alpha1 +kind: NamespaceNetworkPolicy +metadata: + name: testnamespace + namespace: testns2 +spec: + ingress: + - from: + - namespace: + name: testns +` + obj := &netv1alpha1.NamespaceNetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + + np, err := c.convertToK8sNP(obj) Expect(err).ShouldNot(HaveOccurred()) - Eventually(func() string { - o, err := calicoProvider.Get(obj) - if err != nil { - return err.Error() - } - n := o.(*v1alpha1.NamespaceNetworkPolicy) - return n.Spec.Selector - }).Should(Equal(newStr)) - // TestDelete - Expect(fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Delete(obj.Name, &metav1.DeleteOptions{})).ShouldNot(HaveOccurred()) + + objTmp := ` +apiVersion: "networking.k8s.io/v1" +kind: "NetworkPolicy" +metadata: + name: testnamespace + namespace: testns2 +spec: + podSelector: {} + ingress: + - from: + - namespaceSelector: + matchLabels: + %s: %s + policyTypes: + - Ingress` + objSrt = fmt.Sprintf(objTmp, constants.NamespaceLabelKey, "testns") + obj2 := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred()) + Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue()) + }) + + It("test func convertToK8sNP with service ingress", func() { + objSrt := ` +apiVersion: network.kubesphere.io/v1alpha1 +kind: NamespaceNetworkPolicy +metadata: + name: testnamespace + namespace: testns2 +spec: + ingress: + - from: + - service: + name: myservice + namespace: testns +` + obj := &netv1alpha1.NamespaceNetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + + np, err := c.convertToK8sNP(obj) + Expect(err).ShouldNot(HaveOccurred()) + + objSrt = ` +apiVersion: "networking.k8s.io/v1" +kind: NetworkPolicy +metadata: + name: networkisolate + namespace: testns +spec: + podSelector: {} + ingress: + - from: + - podSelector: + matchLabels: + app: mylbapp + namespaceSelector: + matchLabels: + kubesphere.io/namespace: testns + policyTypes: + - Ingress +` + obj2 := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred()) + klog.Errorf("\n%v\n%v\n", np.Spec, obj2.Spec) + Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue()) + }) + + It("test func convertToK8sNP with service egress", func() { + objSrt := ` +apiVersion: network.kubesphere.io/v1alpha1 +kind: NamespaceNetworkPolicy +metadata: + name: testnamespace + namespace: testns2 +spec: + egress: + - To: + - service: + name: myservice + namespace: testns +` + obj := &netv1alpha1.NamespaceNetworkPolicy{} + Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred()) + + np, err := c.convertToK8sNP(obj) + Expect(err).ShouldNot(HaveOccurred()) + + objSrt = ` +apiVersion: "networking.k8s.io/v1" +kind: NetworkPolicy +metadata: + name: networkisolate + namespace: testns +spec: + podSelector: {} + egress: + - to: + - podSelector: + matchLabels: + app: mylbapp + namespaceSelector: + matchLabels: + kubesphere.io/namespace: testns + ports: + - protocol: TCP + port: 80 + policyTypes: + - Egress +` + obj2 := &netv1.NetworkPolicy{} + Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred()) + klog.Errorf("\n%v\n%v\n", np.Spec, obj2.Spec) + Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue()) }) AfterEach(func() { diff --git a/pkg/controller/network/nsnetworkpolicy/reconcile.go b/pkg/controller/network/nsnetworkpolicy/reconcile.go deleted file mode 100644 index 1b65e9c7c..000000000 --- a/pkg/controller/network/nsnetworkpolicy/reconcile.go +++ /dev/null @@ -1,119 +0,0 @@ -package nsnetworkpolicy - -import ( - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/retry" - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - "kubesphere.io/kubesphere/pkg/controller/network/utils" -) - -const ( - controllerFinalizier = "nsnp.finalizers.networking.kubesphere.io" -) - -var clog logr.Logger - -func (c *controller) reconcile(key string) error { - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return err - } - clog = log.WithValues("name", name, "namespace", namespace) - clog.V(1).Info("---------Begin to reconcile--------") - defer clog.V(1).Info("---------Reconcile done--------") - obj, err := c.nsnpLister.NamespaceNetworkPolicies(namespace).Get(name) - if err != nil { - if errors.IsNotFound(err) { - clog.V(2).Info("Object is removed") - return nil - } - clog.Error(err, "Failed to get resource") - return err - } - stop, err := c.addOrRemoveFinalizer(obj) - if err != nil { - return err - } - if stop { - return nil - } - clog.V(2).Info("Check if we need a create or update") - ok, err := c.nsNetworkPolicyProvider.CheckExist(obj) - if err != nil { - clog.Error(err, "Failed to check exist of network policy") - return err - } - if !ok { - clog.V(1).Info("Create a new object in backend") - err = c.nsNetworkPolicyProvider.Add(obj) - if err != nil { - clog.Error(err, "Failed to create np") - return err - } - return nil - } - - needUpdate, err := c.nsNetworkPolicyProvider.NeedUpdate(obj) - if err != nil { - clog.Error(err, "Failed to check if object need a update") - return err - } - if needUpdate { - clog.V(1).Info("Update object in backend") - err = c.nsNetworkPolicyProvider.Update(obj) - if err != nil { - clog.Error(err, "Failed to update object") - return err - } - } - return nil -} - -func (c *controller) addOrRemoveFinalizer(obj *v1alpha1.NamespaceNetworkPolicy) (bool, error) { - if obj.ObjectMeta.DeletionTimestamp.IsZero() { - if !utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) { - clog.V(2).Info("Detect no finalizer") - obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, controllerFinalizier) - err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - _, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj) - return err - }) - if err != nil { - clog.Error(err, "Failed to add finalizer") - return false, err - } - return false, nil - } - } else { - // The object is being deleted - if utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) { - // our finalizer is present, so lets handle any external dependency - if err := c.deleteProviderNSNP(obj); err != nil { - // if fail to delete the external dependency here, return with error - // so that it can be retried - return false, err - } - clog.V(2).Info("Removing finalizer") - // remove our finalizer from the list and update it. - obj.ObjectMeta.Finalizers = utils.RemoveString(obj.ObjectMeta.Finalizers, controllerFinalizier) - err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - _, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj) - return err - }) - if err != nil { - clog.Error(err, "Failed to remove finalizer") - return false, err - } - return true, nil - } - } - return false, nil -} - -// deleteProviderNSNP delete network policy in the backend -func (c *controller) deleteProviderNSNP(obj *v1alpha1.NamespaceNetworkPolicy) error { - clog.V(2).Info("Deleting backend network policy") - return c.nsNetworkPolicyProvider.Delete(obj) -} diff --git a/pkg/controller/network/nsnetworkpolicy/webhook.go b/pkg/controller/network/nsnetworkpolicy/webhook.go new file mode 100644 index 000000000..1a1534562 --- /dev/null +++ b/pkg/controller/network/nsnetworkpolicy/webhook.go @@ -0,0 +1,38 @@ +package nsnetworkpolicy + +import ( + "context" + "fmt" + "net/http" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// +kubebuilder:webhook:path=/validate-service-nsnp-kubesphere-io-v1alpha1-network,name=validate-v1-service,mutating=false,failurePolicy=fail,groups="",resources=services,verbs=create;update,versions=v1 + +// serviceValidator validates service +type ServiceValidator struct { + decoder *admission.Decoder +} + +// Service must hash label, becasue nsnp will use it +func (v *ServiceValidator) Handle(ctx context.Context, req admission.Request) admission.Response { + service := &corev1.Service{} + + err := v.decoder.Decode(req, service) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + if service.Spec.Selector == nil { + return admission.Denied(fmt.Sprintf("missing label")) + } + + return admission.Allowed("") +} + +func (a *ServiceValidator) InjectDecoder(d *admission.Decoder) error { + a.decoder = d + return nil +} diff --git a/pkg/controller/network/provider/calico_k8s.go b/pkg/controller/network/provider/calico_k8s.go deleted file mode 100644 index 137cc0494..000000000 --- a/pkg/controller/network/provider/calico_k8s.go +++ /dev/null @@ -1,3 +0,0 @@ -package provider - -// +kubebuilder:rbac:groups="crd.projectcalico.org",resources=globalfelixconfigs;felixconfigurations;ippools;ipamblocks;globalnetworkpolicies;globalnetworksets;networkpolicies;networksets;clusterinformations;hostendpoints,verbs=get;list;watch;create;patch;update;delete diff --git a/pkg/controller/network/provider/fake_ns.go b/pkg/controller/network/provider/fake_ns.go new file mode 100644 index 000000000..a2ec4755d --- /dev/null +++ b/pkg/controller/network/provider/fake_ns.go @@ -0,0 +1,49 @@ +package provider + +import ( + "fmt" + + "github.com/projectcalico/kube-controllers/pkg/converter" + api "github.com/projectcalico/libcalico-go/lib/apis/v3" + constants "github.com/projectcalico/libcalico-go/lib/backend/k8s/conversion" + v1 "k8s.io/api/networking/v1" +) + +func NewFakeNetworkProvider() *FakeNetworkProvider { + f := new(FakeNetworkProvider) + f.NSNPData = make(map[string]*api.NetworkPolicy) + f.policyConverter = converter.NewPolicyConverter() + return f +} + +type FakeNetworkProvider struct { + NSNPData map[string]*api.NetworkPolicy + policyConverter converter.Converter +} + +func (f *FakeNetworkProvider) Delete(key string) { + delete(f.NSNPData, key) +} + +func (f *FakeNetworkProvider) Start(stopCh <-chan struct{}) { + +} + +func (f *FakeNetworkProvider) Set(np *v1.NetworkPolicy) error { + policy, err := f.policyConverter.Convert(np) + if err != nil { + return err + } + + // Add to cache. + k := f.policyConverter.GetKey(policy) + tmp := policy.(api.NetworkPolicy) + f.NSNPData[k] = &tmp + + return nil +} + +func (f *FakeNetworkProvider) GetKey(name, nsname string) string { + policyName := fmt.Sprintf(constants.K8sNetworkPolicyNamePrefix + name) + return fmt.Sprintf("%s/%s", nsname, policyName) +} diff --git a/pkg/controller/network/provider/fake_ns_calico.go b/pkg/controller/network/provider/fake_ns_calico.go deleted file mode 100644 index 973343a21..000000000 --- a/pkg/controller/network/provider/fake_ns_calico.go +++ /dev/null @@ -1,66 +0,0 @@ -package provider - -import ( - "reflect" - - "github.com/projectcalico/libcalico-go/lib/errors" - "k8s.io/client-go/tools/cache" - api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) - -func NewFakeCalicoNetworkProvider() *FakeCalicoNetworkProvider { - f := new(FakeCalicoNetworkProvider) - f.NSNPData = make(map[string]*api.NamespaceNetworkPolicy) - return f -} - -type FakeCalicoNetworkProvider struct { - NSNPData map[string]*api.NamespaceNetworkPolicy -} - -func (f *FakeCalicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - obj, ok := f.NSNPData[namespacename] - if !ok { - return nil, errors.ErrorResourceDoesNotExist{} - } - return obj, nil -} - -func (f *FakeCalicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - if _, ok := f.NSNPData[namespacename]; ok { - return errors.ErrorResourceAlreadyExists{} - } - f.NSNPData[namespacename] = o - return nil -} - -func (f *FakeCalicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - if _, ok := f.NSNPData[namespacename]; ok { - return true, nil - } - return false, nil -} - -func (f *FakeCalicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - store := f.NSNPData[namespacename] - if !reflect.DeepEqual(store, o) { - return true, nil - } - return false, nil -} - -func (f *FakeCalicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - f.NSNPData[namespacename] = o - return nil -} - -func (f *FakeCalicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error { - namespacename, _ := cache.MetaNamespaceKeyFunc(o) - delete(f.NSNPData, namespacename) - return nil -} diff --git a/pkg/controller/network/provider/global_np.go b/pkg/controller/network/provider/global_np.go deleted file mode 100644 index 4f504f668..000000000 --- a/pkg/controller/network/provider/global_np.go +++ /dev/null @@ -1 +0,0 @@ -package provider diff --git a/pkg/controller/network/provider/namespace_np.go b/pkg/controller/network/provider/namespace_np.go index 5d63e4a1a..bfb2fb433 100644 --- a/pkg/controller/network/provider/namespace_np.go +++ b/pkg/controller/network/provider/namespace_np.go @@ -1,35 +1,11 @@ package provider -import ( - k8snetworkinformer "k8s.io/client-go/informers/networking/v1" - k8snetworklister "k8s.io/client-go/listers/networking/v1" - api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) +import netv1 "k8s.io/api/networking/v1" // NsNetworkPolicyProvider is a interface to let different cnis to implement our api type NsNetworkPolicyProvider interface { - Add(*api.NamespaceNetworkPolicy) error - CheckExist(*api.NamespaceNetworkPolicy) (bool, error) - NeedUpdate(*api.NamespaceNetworkPolicy) (bool, error) - Update(*api.NamespaceNetworkPolicy) error - Delete(*api.NamespaceNetworkPolicy) error - Get(*api.NamespaceNetworkPolicy) (interface{}, error) -} - -// TODO: support no-calico CNI -type k8sNetworkProvider struct { - networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer - networkPolicyLister k8snetworklister.NetworkPolicyLister -} - -func (k *k8sNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error { - return nil -} - -func (k *k8sNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) { - return false, nil -} - -func (k *k8sNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error { - return nil + Delete(key string) + Set(policy *netv1.NetworkPolicy) error + Start(stopCh <-chan struct{}) + GetKey(name, nsname string) string } diff --git a/pkg/controller/network/provider/ns_calico.go b/pkg/controller/network/provider/ns_calico.go deleted file mode 100644 index fa7a24bba..000000000 --- a/pkg/controller/network/provider/ns_calico.go +++ /dev/null @@ -1,144 +0,0 @@ -package provider - -import ( - "context" - "encoding/json" - "reflect" - "time" - - v3 "github.com/projectcalico/libcalico-go/lib/apis/v3" - "github.com/projectcalico/libcalico-go/lib/clientv3" - "github.com/projectcalico/libcalico-go/lib/errors" - "github.com/projectcalico/libcalico-go/lib/options" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/klog/klogr" - api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) - -var log = klogr.New().WithName("calico-client") -var defaultBackoff = wait.Backoff{ - Steps: 4, - Duration: 10 * time.Millisecond, - Factor: 5.0, - Jitter: 0.1, -} - -type calicoNetworkProvider struct { - np clientv3.NetworkPolicyInterface -} - -func NewCalicoNetworkProvider(np clientv3.NetworkPolicyInterface) NsNetworkPolicyProvider { - return &calicoNetworkProvider{ - np: np, - } -} -func convertSpec(n *api.NamespaceNetworkPolicySpec) *v3.NetworkPolicySpec { - bytes, err := json.Marshal(&n) - if err != nil { - panic(err) - } - m := new(v3.NetworkPolicySpec) - err = json.Unmarshal(bytes, m) - if err != nil { - panic(err) - } - return m -} - -// ConvertAPIToCalico convert our api to calico api -func ConvertAPIToCalico(n *api.NamespaceNetworkPolicy) *v3.NetworkPolicy { - output := v3.NewNetworkPolicy() - //Object Metadata - output.ObjectMeta.Name = n.Name - output.Namespace = n.Namespace - output.Annotations = n.Annotations - output.Labels = n.Labels - //spec - output.Spec = *(convertSpec(&n.Spec)) - return output -} - -func (k *calicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) { - return k.np.Get(context.TODO(), o.Namespace, o.Name, options.GetOptions{}) -} - -func (k *calicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error { - log.V(3).Info("Creating network policy", "name", o.Name, "namespace", o.Namespace) - obj := ConvertAPIToCalico(o) - log.V(4).Info("Show object spe detail", "name", o.Name, "namespace", o.Namespace, "Spec", obj.Spec) - _, err := k.np.Create(context.TODO(), obj, options.SetOptions{}) - return err -} - -func (k *calicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) { - log.V(3).Info("Checking network policy whether exsits or not", "name", o.Name, "namespace", o.Namespace) - out, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{}) - if err != nil { - if _, ok := err.(errors.ErrorResourceDoesNotExist); ok { - return false, nil - } - return false, err - } - if out != nil { - return true, nil - } - return false, nil -} - -func (k *calicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error { - log.V(3).Info("Deleting network policy", "name", o.Name, "namespace", o.Namespace) - _, err := k.np.Delete(context.Background(), o.Namespace, o.Name, options.DeleteOptions{}) - return err -} - -func (k *calicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) { - store, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{}) - if err != nil { - log.Error(err, "Failed to get resource", "name", o.Name, "namespace", o.Namespace) - } - expected := ConvertAPIToCalico(o) - log.V(4).Info("Comparing Spec", "store", store.Spec, "current", expected.Spec) - if !reflect.DeepEqual(store.Spec, expected.Spec) { - return true, nil - } - return false, nil -} - -func (k *calicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error { - log.V(3).Info("Updating network policy", "name", o.Name, "namespace", o.Namespace) - updateObject, err := k.Get(o) - if err != nil { - log.Error(err, "Failed to get resource in store") - return err - } - up := updateObject.(*v3.NetworkPolicy) - up.Spec = *convertSpec(&o.Spec) - err = RetryOnConflict(defaultBackoff, func() error { - _, err := k.np.Update(context.Background(), up, options.SetOptions{}) - return err - }) - if err != nil { - log.Error(err, "Failed to update resource", "name", o.Name, "namespace", o.Namespace) - } - return err -} - -// RetryOnConflict is same as the function in k8s, but replaced with error in calico -func RetryOnConflict(backoff wait.Backoff, fn func() error) error { - var lastConflictErr error - err := wait.ExponentialBackoff(backoff, func() (bool, error) { - err := fn() - if err == nil { - return true, nil - } - if _, ok := err.(errors.ErrorResourceUpdateConflict); ok { - lastConflictErr = err - return false, nil - } - return false, err - }) - if err == wait.ErrWaitTimeout { - err = lastConflictErr - } - return err -} diff --git a/pkg/controller/network/provider/ns_k8s.go b/pkg/controller/network/provider/ns_k8s.go new file mode 100644 index 000000000..503ac9a8a --- /dev/null +++ b/pkg/controller/network/provider/ns_k8s.go @@ -0,0 +1,250 @@ +package provider + +import ( + "context" + "fmt" + "reflect" + "strings" + "sync" + "time" + + rcache "github.com/projectcalico/kube-controllers/pkg/cache" + netv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + uruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + informerv1 "k8s.io/client-go/informers/networking/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "k8s.io/klog" +) + +const ( + defaultSyncTime = 5 * time.Minute +) + +func (c *k8sPolicyController) GetKey(name, nsname string) string { + return fmt.Sprintf("%s/%s", nsname, name) +} + +func getkey(key string) (string, string) { + strs := strings.Split(key, "/") + return strs[0], strs[1] +} + +// policyController implements the Controller interface for managing Kubernetes network policies +// and syncing them to the k8s datastore as NetworkPolicies. +type k8sPolicyController struct { + client kubernetes.Interface + informer informerv1.NetworkPolicyInformer + ctx context.Context + resourceCache rcache.ResourceCache + hasSynced cache.InformerSynced +} + +func (c *k8sPolicyController) Start(stopCh <-chan struct{}) { + c.run(5, "5m", stopCh) +} + +func (c *k8sPolicyController) Set(np *netv1.NetworkPolicy) error { + klog.V(4).Infof("Set NetworkPolicy %s/%s %+v", np.Namespace, np.Name, np) + // Add to cache. + k := c.GetKey(np.Name, np.Namespace) + c.resourceCache.Set(k, *np) + + return nil +} + +func (c *k8sPolicyController) Delete(key string) { + klog.V(4).Infof("Delete NetworkPolicy %s", key) + c.resourceCache.Delete(key) +} + +// Run starts the controller. +func (c *k8sPolicyController) run(threadiness int, reconcilerPeriod string, stopCh <-chan struct{}) { + defer uruntime.HandleCrash() + + // Let the workers stop when we are done + workqueue := c.resourceCache.GetQueue() + defer workqueue.ShutDown() + + // Wait until we are in sync with the Kubernetes API before starting the + // resource cache. + klog.Info("Waiting to sync with Kubernetes API (NetworkPolicy)") + if ok := cache.WaitForCacheSync(stopCh, c.hasSynced); !ok { + } + klog.Infof("Finished syncing with Kubernetes API (NetworkPolicy)") + + // Start the resource cache - this will trigger the queueing of any keys + // that are out of sync onto the resource cache event queue. + c.resourceCache.Run(reconcilerPeriod) + + // Start a number of worker threads to read from the queue. Each worker + // will pull keys off the resource cache event queue and sync them to the + // k8s datastore. + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + klog.Info("NetworkPolicy controller is now running") + + <-stopCh + klog.Info("Stopping NetworkPolicy controller") +} + +func (c *k8sPolicyController) runWorker() { + for c.processNextItem() { + } +} + +// processNextItem waits for an event on the output queue from the resource cache and syncs +// any received keys to the datastore. +func (c *k8sPolicyController) processNextItem() bool { + // Wait until there is a new item in the work queue. + workqueue := c.resourceCache.GetQueue() + key, quit := workqueue.Get() + if quit { + return false + } + + // Sync the object to the k8s datastore. + if err := c.syncToDatastore(key.(string)); err != nil { + c.handleErr(err, key.(string)) + } + + // Indicate that we're done processing this key, allowing for safe parallel processing such that + // two objects with the same key are never processed in parallel. + workqueue.Done(key) + return true +} + +// syncToDatastore syncs the given update to the k8s datastore. The provided key can be used to +// find the corresponding resource within the resource cache. If the resource for the provided key +// exists in the cache, then the value should be written to the datastore. If it does not exist +// in the cache, then it should be deleted from the datastore. +func (c *k8sPolicyController) syncToDatastore(key string) error { + // Check if it exists in the controller's cache. + obj, exists := c.resourceCache.Get(key) + if !exists { + // The object no longer exists - delete from the datastore. + klog.Infof("Deleting NetworkPolicy %s from k8s datastore", key) + ns, name := getkey(key) + err := c.client.NetworkingV1().NetworkPolicies(ns).Delete(name, nil) + if errors.IsNotFound(err) { + return nil + } + return err + } else { + // The object exists - update the datastore to reflect. + klog.Infof("Create/Update NetworkPolicy %s in k8s datastore", key) + p := obj.(netv1.NetworkPolicy) + + // Lookup to see if this object already exists in the datastore. + gp, err := c.informer.Lister().NetworkPolicies(p.Namespace).Get(p.Name) + if err != nil { + if !errors.IsNotFound(err) { + klog.Warningf("Failed to get NetworkPolicy %s from datastore", key) + return err + } + + // Doesn't exist - create it. + _, err := c.client.NetworkingV1().NetworkPolicies(p.Namespace).Create(&p) + if err != nil { + klog.Warningf("Failed to create NetworkPolicy %s", key) + return err + } + klog.Infof("Successfully created NetworkPolicy %s", key) + return nil + } + + klog.V(4).Infof("New NetworkPolicy %s/%s %+v\n", p.Namespace, p.Name, p.Spec) + klog.V(4).Infof("Old NetworkPolicy %s/%s %+v\n", gp.Namespace, gp.Name, gp.Spec) + + // The policy already exists, update it and write it back to the datastore. + gp.Spec = p.Spec + _, err = c.client.NetworkingV1().NetworkPolicies(p.Namespace).Update(gp) + if err != nil { + klog.Warningf("Failed to update NetworkPolicy %s", key) + return err + } + klog.Infof("Successfully updated NetworkPolicy %s", key) + return nil + } +} + +// handleErr handles errors which occur while processing a key received from the resource cache. +// For a given error, we will re-queue the key in order to retry the datastore sync up to 5 times, +// at which point the update is dropped. +func (c *k8sPolicyController) handleErr(err error, key string) { + workqueue := c.resourceCache.GetQueue() + if err == nil { + // Forget about the #AddRateLimited history of the key on every successful synchronization. + // This ensures that future processing of updates for this key is not delayed because of + // an outdated error history. + workqueue.Forget(key) + return + } + + // This controller retries 5 times if something goes wrong. After that, it stops trying. + if workqueue.NumRequeues(key) < 5 { + // Re-enqueue the key rate limited. Based on the rate limiter on the + // queue and the re-enqueue history, the key will be processed later again. + klog.Errorf("Error syncing NetworkPolicy %v: %v", key, err) + workqueue.AddRateLimited(key) + return + } + workqueue.Forget(key) + + // Report to an external entity that, even after several retries, we could not successfully process this key + uruntime.HandleError(err) + klog.Errorf("Dropping NetworkPolicy %q out of the queue: %v", key, err) +} + +//NewNsNetworkPolicyProvider sync k8s NetworkPolicy +func NewNsNetworkPolicyProvider(client kubernetes.Interface, npInformer informerv1.NetworkPolicyInformer) (NsNetworkPolicyProvider, error) { + var once sync.Once + + c := &k8sPolicyController{ + client: client, + informer: npInformer, + ctx: context.Background(), + hasSynced: npInformer.Informer().HasSynced, + } + + // Function returns map of policyName:policy stored by policy controller + // in datastore. + listFunc := func() (map[string]interface{}, error) { + //Wait cache be set by NSNP Controller, otherwise NetworkPolicy will be delete + //by mistake + once.Do(func() { + time.Sleep(defaultSyncTime) + }) + + // Get all policies from datastore + //TODO filter np not belong to kubesphere + policies, err := npInformer.Lister().List(labels.Everything()) + if err != nil { + return nil, err + } + + // Filter in only objects that are written by policy controller. + m := make(map[string]interface{}) + for _, policy := range policies { + policy.ObjectMeta = metav1.ObjectMeta{Name: policy.Name, Namespace: policy.Namespace} + k := c.GetKey(policy.Name, policy.Namespace) + m[k] = *policy + } + + klog.Infof("Found %d policies in k8s datastore:", len(m)) + return m, nil + } + + cacheArgs := rcache.ResourceCacheArgs{ + ListFunc: listFunc, + ObjectType: reflect.TypeOf(netv1.NetworkPolicy{}), + } + c.resourceCache = rcache.NewResourceCache(cacheArgs) + + return c, nil +} diff --git a/pkg/controller/network/runoption/option.go b/pkg/controller/network/runoption/option.go deleted file mode 100644 index a4c2ec23a..000000000 --- a/pkg/controller/network/runoption/option.go +++ /dev/null @@ -1,80 +0,0 @@ -package runoption - -import ( - "time" - - "github.com/projectcalico/libcalico-go/lib/apiconfig" - "github.com/projectcalico/libcalico-go/lib/clientv3" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - ksinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy" - "kubesphere.io/kubesphere/pkg/controller/network/provider" -) - -const ( - certPath = "/calicocerts" - - KubernetesDataStore = "k8s" - EtcdDataStore = "etcd" -) - -type RunOption struct { - ProviderName string - DataStoreType string - EtcdEndpoints string - AllowInsecureEtcd bool -} - -func (r RunOption) Run() error { - klog.V(1).Info("Check config") - if err := r.check(); err != nil { - return err - } - klog.V(1).Info("Preparing kubernetes client") - config, err := rest.InClusterConfig() - if err != nil { - panic(err.Error()) - } - // creates the clientset - k8sClientset := kubernetes.NewForConfigOrDie(config) - ksClientset := versioned.NewForConfigOrDie(config) - informer := ksinformer.NewSharedInformerFactory(ksClientset, time.Minute*10) - klog.V(1).Info("Kubernetes client initialized successfully") - var npProvider provider.NsNetworkPolicyProvider - - if r.ProviderName == "calico" { - klog.V(1).Info("Preparing calico client") - config := apiconfig.NewCalicoAPIConfig() - config.Spec.EtcdEndpoints = r.EtcdEndpoints - if !r.AllowInsecureEtcd { - config.Spec.EtcdKeyFile = certPath + "/etcd-key" - config.Spec.EtcdCertFile = certPath + "/etcd-cert" - config.Spec.EtcdCACertFile = certPath + "/etcd-ca" - } - if r.DataStoreType == KubernetesDataStore { - config.Spec.DatastoreType = apiconfig.Kubernetes - } else { - config.Spec.DatastoreType = apiconfig.EtcdV3 - } - client, err := clientv3.New(*config) - if err != nil { - klog.Fatal("Failed to initialize calico client", err) - } - npProvider = provider.NewCalicoNetworkProvider(client.NetworkPolicies()) - klog.V(1).Info("Calico client initialized successfully") - } - - //TODO: support no-calico cni - c := nsnetworkpolicy.NewController(k8sClientset, ksClientset, informer.Network().V1alpha1().NamespaceNetworkPolicies(), npProvider) - stop := make(chan struct{}) - klog.V(1).Infof("Starting controller") - go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stop) - return c.Run(1, stop) -} - -func (r RunOption) check() error { - return nil -} diff --git a/pkg/controller/network/testing/controller.go b/pkg/controller/network/testing/controller.go deleted file mode 100644 index c958d5dc2..000000000 --- a/pkg/controller/network/testing/controller.go +++ /dev/null @@ -1,38 +0,0 @@ -package testing - -import ( - "time" - - "k8s.io/apimachinery/pkg/runtime" - kubeinformers "k8s.io/client-go/informers" - k8sfake "k8s.io/client-go/kubernetes/fake" - "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" - informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" -) - -var ( - AlwaysReady = func() bool { return true } - ResyncPeriodFunc = func() time.Duration { return 1 * time.Second } -) - -type FakeControllerBuilder struct { - KsClient *fake.Clientset - KubeClient *k8sfake.Clientset - Kubeobjects []runtime.Object - CRDObjects []runtime.Object -} - -func NewFakeControllerBuilder() *FakeControllerBuilder { - return &FakeControllerBuilder{ - Kubeobjects: make([]runtime.Object, 0), - CRDObjects: make([]runtime.Object, 0), - } -} - -func (f *FakeControllerBuilder) NewControllerInformer() (informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { - f.KsClient = fake.NewSimpleClientset(f.CRDObjects...) - f.KubeClient = k8sfake.NewSimpleClientset(f.Kubeobjects...) - i := informers.NewSharedInformerFactory(f.KsClient, ResyncPeriodFunc()) - k8sI := kubeinformers.NewSharedInformerFactory(f.KubeClient, ResyncPeriodFunc()) - return i, k8sI -} diff --git a/pkg/controller/network/testing/util.go b/pkg/controller/network/testing/util.go deleted file mode 100644 index a92efd1cd..000000000 --- a/pkg/controller/network/testing/util.go +++ /dev/null @@ -1,12 +0,0 @@ -package testing - -import ( - "strings" - - "k8s.io/apimachinery/pkg/util/yaml" -) - -func StringToObject(data string, obj interface{}) error { - reader := strings.NewReader(data) - return yaml.NewYAMLOrJSONDecoder(reader, 10).Decode(obj) -} diff --git a/pkg/controller/network/utils/strings.go b/pkg/controller/network/utils/strings.go deleted file mode 100644 index 86ffcf542..000000000 --- a/pkg/controller/network/utils/strings.go +++ /dev/null @@ -1,22 +0,0 @@ -package utils - -// ContainsString report if s is in a slice -func ContainsString(slice []string, s string) bool { - for _, item := range slice { - if item == s { - return true - } - } - return false -} - -// RemoveString remove s from slice if exists -func RemoveString(slice []string, s string) (result []string) { - for _, item := range slice { - if item == s { - continue - } - result = append(result, item) - } - return -} diff --git a/pkg/controller/network/wsnetworkpolicy/controller.go b/pkg/controller/network/wsnetworkpolicy/controller.go deleted file mode 100644 index 21fd6e3c9..000000000 --- a/pkg/controller/network/wsnetworkpolicy/controller.go +++ /dev/null @@ -1,280 +0,0 @@ -package wsnetworkpolicy - -import ( - "fmt" - "time" - - corev1 "k8s.io/api/core/v1" - k8snetwork "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - corev1informer "k8s.io/client-go/informers/core/v1" - k8snetworkinformer "k8s.io/client-go/informers/networking/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - corev1lister "k8s.io/client-go/listers/core/v1" - k8snetworklister "k8s.io/client-go/listers/networking/v1" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - "k8s.io/klog" - "k8s.io/klog/klogr" - workspaceapi "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" - kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" - networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1" - workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1" - networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1" - workspacelister "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/controller/network/controllerapi" -) - -const controllerAgentName = "wsnp-controller" - -var ( - log = klogr.New().WithName("Controller").WithValues(controllerAgentName) - errCount = 0 -) - -type controller struct { - kubeClientset kubernetes.Interface - kubesphereClientset kubesphereclient.Interface - - wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer - wsnpLister networklister.WorkspaceNetworkPolicyLister - wsnpSynced cache.InformerSynced - - networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer - networkPolicyLister k8snetworklister.NetworkPolicyLister - networkPolicySynced cache.InformerSynced - - namespaceLister corev1lister.NamespaceLister - namespaceInformer corev1informer.NamespaceInformer - namespaceSynced cache.InformerSynced - - workspaceLister workspacelister.WorkspaceLister - workspaceInformer workspaceinformer.WorkspaceInformer - workspaceSynced cache.InformerSynced - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder -} - -func NewController(kubeclientset kubernetes.Interface, - kubesphereclientset kubesphereclient.Interface, - wsnpInformer networkinformer.WorkspaceNetworkPolicyInformer, - networkPolicyInformer k8snetworkinformer.NetworkPolicyInformer, - namespaceInformer corev1informer.NamespaceInformer, - workspaceInformer workspaceinformer.WorkspaceInformer) controllerapi.Controller { - utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme)) - log.V(4).Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(klog.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - ctl := &controller{ - kubeClientset: kubeclientset, - kubesphereClientset: kubesphereclientset, - wsnpInformer: wsnpInformer, - wsnpLister: wsnpInformer.Lister(), - wsnpSynced: wsnpInformer.Informer().HasSynced, - networkPolicyInformer: networkPolicyInformer, - networkPolicyLister: networkPolicyInformer.Lister(), - networkPolicySynced: networkPolicyInformer.Informer().HasSynced, - namespaceInformer: namespaceInformer, - namespaceLister: namespaceInformer.Lister(), - namespaceSynced: namespaceInformer.Informer().HasSynced, - workspaceInformer: workspaceInformer, - workspaceLister: workspaceInformer.Lister(), - workspaceSynced: workspaceInformer.Informer().HasSynced, - - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "WorkspaceNetworkPolicies"), - recorder: recorder, - } - log.Info("Setting up event handlers") - wsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: ctl.enqueueWSNP, - UpdateFunc: func(old, new interface{}) { - ctl.enqueueWSNP(new) - }, - }) - networkPolicyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: ctl.handleNP, - UpdateFunc: func(old, new interface{}) { - newNP := new.(*k8snetwork.NetworkPolicy) - oldNP := old.(*k8snetwork.NetworkPolicy) - if newNP.ResourceVersion == oldNP.ResourceVersion { - return - } - ctl.handleNP(new) - }, - DeleteFunc: ctl.handleNP, - }) - workspaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: ctl.handleWS, - UpdateFunc: func(old, new interface{}) { - newNP := new.(*workspaceapi.Workspace) - oldNP := old.(*workspaceapi.Workspace) - if newNP.ResourceVersion == oldNP.ResourceVersion { - return - } - ctl.handleWS(new) - }, - DeleteFunc: ctl.handleNP, - }) - return ctl -} - -func (c *controller) handleWS(obj interface{}) { - ws := obj.(*workspaceapi.Workspace) - wsnps, err := c.wsnpLister.List(labels.Everything()) - if err != nil { - log.Error(err, "Failed to get WSNP when a workspace changed ") - return - } - for _, wsnp := range wsnps { - log.V(4).Info("Enqueue wsnp because a workspace being changed", "obj", ws.Name) - c.enqueueWSNP(wsnp) - } - return -} - -func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer utilruntime.HandleCrash() - defer c.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - log.Info("Starting WSNP controller") - - // Wait for the caches to be synced before starting workers - log.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.wsnpSynced, c.namespaceSynced, c.networkPolicySynced, c.workspaceSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - log.Info("Starting workers") - // Launch two workers to process Foo resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - klog.Info("Started workers") - <-stopCh - log.Info("Shutting down workers") - - return nil -} - -func (c *controller) enqueueWSNP(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - utilruntime.HandleError(err) - return - } - c.workqueue.Add(key) -} - -func (c *controller) handleNP(obj interface{}) { - var object metav1.Object - var ok bool - if object, ok = obj.(metav1.Object); !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - utilruntime.HandleError(fmt.Errorf("error decoding object, invalid type")) - return - } - object, ok = tombstone.Obj.(metav1.Object) - if !ok { - utilruntime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) - return - } - log.V(4).Info("Recovered deleted object from tombstone", "name", object.GetName()) - } - log.V(4).Info("Processing object:", "name", object.GetName()) - if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { - if ownerRef.Kind != "WorkspaceNetworkPol" { - return - } - - wsnp, err := c.wsnpLister.Get(ownerRef.Name) - if err != nil { - log.V(4).Info("ignoring orphaned object", "link", object.GetSelfLink(), "name", ownerRef.Name) - return - } - c.enqueueWSNP(wsnp) - return - } -} - -func (c *controller) runWorker() { - for c.processNextWorkItem() { - } -} - -func (c *controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the reconcile, passing it the namespace/name string of the - // Foo resource to be synced. - if err := c.reconcile(key); err != nil { - // Put the item back on the workqueue to handle any transient errors. - c.workqueue.AddRateLimited(key) - return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - log.Info("Successfully synced", key) - return nil - }(obj) - - if err != nil { - utilruntime.HandleError(err) - return true - } - - return true -} - -func (c *controller) handleError(err error) { - log.Error(err, "Error in handling") - errCount++ -} diff --git a/pkg/controller/network/wsnetworkpolicy/reconcile.go b/pkg/controller/network/wsnetworkpolicy/reconcile.go deleted file mode 100644 index f6bad5366..000000000 --- a/pkg/controller/network/wsnetworkpolicy/reconcile.go +++ /dev/null @@ -1,203 +0,0 @@ -package wsnetworkpolicy - -import ( - "fmt" - "reflect" - "sort" - - corev1 "k8s.io/api/core/v1" - ks8network "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - errutil "k8s.io/apimachinery/pkg/util/errors" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/util/retry" - wsnpapi "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" -) - -const ( - workspaceSelectorLabel = "kubesphere.io/workspace" - workspaceNetworkPolicyLabel = "networking.kubesphere.io/wsnp" - - MessageResourceExists = "Resource %q already exists and is not managed by WorkspaceNetworkPolicy" - ErrResourceExists = "ErrResourceExists" -) - -var everything = labels.Everything() -var reconcileCount = 0 - -// NetworkPolicyNameForWSNP return the name of the networkpolicy owned by this WNSP -func NetworkPolicyNameForWSNP(wsnp string) string { - return wsnp + "-np" -} - -func (c *controller) reconcile(key string) error { - reconcileCount++ - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - olog := log.WithName(name) - olog.Info("Begin to reconcile") - owner, err := c.wsnpLister.Get(name) - if err != nil { - if errors.IsNotFound(err) { - utilruntime.HandleError(fmt.Errorf("WSNP '%s' in work queue no longer exists", key)) - return nil - } - return err - } - namespaces, err := c.listNamespacesInWorkspace(owner.Spec.Workspace) - if err != nil { - return err - } - var errs []error - for _, ns := range namespaces { - err = c.reconcileNamespace(ns.Name, owner) - if err != nil { - errs = append(errs, err) - } - } - if len(errs) == 0 { - return nil - } - return errutil.NewAggregate(errs) -} - -func (c *controller) reconcileNamespace(name string, wsnp *wsnpapi.WorkspaceNetworkPolicy) error { - npname := NetworkPolicyNameForWSNP(wsnp.Name) - np, err := c.generateNPForNamesapce(name, wsnp) - if err != nil { - log.Error(nil, "Failed to generate NetworkPolicy", "wsnp", wsnp, "namespace", name) - return err - } - old, err := c.networkPolicyLister.NetworkPolicies(name).Get(npname) - if errors.IsNotFound(err) { - _, err = c.kubeClientset.NetworkingV1().NetworkPolicies(name).Create(np) - if err != nil { - log.Error(err, "cannot create networkpolicy of this wsnp", wsnp) - return err - } - return nil - } - if err != nil { - log.Error(err, "Failed to get networkPolicy") - return err - } - if !metav1.IsControlledBy(old, wsnp) { - msg := fmt.Sprintf(MessageResourceExists, old.Name) - c.recorder.Event(wsnp, corev1.EventTypeWarning, ErrResourceExists, msg) - return fmt.Errorf(msg) - } - if !reflect.DeepEqual(old.Spec, np.Spec) { - log.V(2).Info("Detect network policy changed, updating network policy", "the old one", old.Spec, "the new one", np.Spec) - err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { - _, err = c.kubeClientset.NetworkingV1().NetworkPolicies(name).Update(np) - return err - }) - if err != nil { - log.Error(err, "Failed to update wsnp") - return err - } - log.V(2).Info("updating completed") - } - return nil -} - -func (c *controller) generateNPForNamesapce(ns string, wsnp *wsnpapi.WorkspaceNetworkPolicy) (*ks8network.NetworkPolicy, error) { - np := &ks8network.NetworkPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: NetworkPolicyNameForWSNP(wsnp.Name), - Namespace: ns, - Labels: map[string]string{workspaceNetworkPolicyLabel: wsnp.Name}, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(wsnp, wsnpapi.SchemeGroupVersion.WithKind("WorkspaceNetworkPolicy")), - }, - }, - Spec: ks8network.NetworkPolicySpec{ - PolicyTypes: wsnp.Spec.PolicyTypes, - }, - } - - if wsnp.Spec.Ingress != nil { - np.Spec.Ingress = make([]ks8network.NetworkPolicyIngressRule, len(wsnp.Spec.Ingress)) - for index, ing := range wsnp.Spec.Ingress { - ingRule, err := c.transformWSNPIngressToK8sIngress(ing) - if err != nil { - return nil, err - } - np.Spec.Ingress[index] = *ingRule - } - } - return np, nil -} - -func (c *controller) transformWSNPIngressToK8sIngress(rule wsnpapi.WorkspaceNetworkPolicyIngressRule) (*ks8network.NetworkPolicyIngressRule, error) { - k8srule := &ks8network.NetworkPolicyIngressRule{ - Ports: rule.Ports, - From: make([]ks8network.NetworkPolicyPeer, len(rule.From)), - } - for index, f := range rule.From { - k8srule.From[index] = f.NetworkPolicyPeer - if f.WorkspaceSelector != nil { - if f.WorkspaceSelector.Size() == 0 { - k8srule.From[index].NamespaceSelector = &metav1.LabelSelector{} - } else { - selector, err := metav1.LabelSelectorAsSelector(f.WorkspaceSelector) - if err != nil { - log.Error(err, "Failed to convert label selectors") - return nil, err - } - ws, err := c.workspaceLister.List(selector) - if err != nil { - log.Error(err, "Failed to list workspaces") - return nil, err - } - if len(ws) == 0 { - log.Info("ws selector doesnot match anything") - continue - } - if k8srule.From[index].NamespaceSelector == nil { - k8srule.From[index].NamespaceSelector = &metav1.LabelSelector{} - } - if len(ws) == 1 { - if k8srule.From[index].NamespaceSelector.MatchLabels == nil { - k8srule.From[index].NamespaceSelector.MatchLabels = make(map[string]string) - } - k8srule.From[index].NamespaceSelector.MatchLabels[workspaceSelectorLabel] = ws[0].Name - } else { - if k8srule.From[index].NamespaceSelector.MatchExpressions == nil { - k8srule.From[index].NamespaceSelector.MatchExpressions = make([]metav1.LabelSelectorRequirement, 0) - } - re := metav1.LabelSelectorRequirement{ - Key: workspaceSelectorLabel, - Operator: metav1.LabelSelectorOpIn, - Values: make([]string, len(ws)), - } - for index, w := range ws { - re.Values[index] = w.Name - } - sort.Strings(re.Values) - k8srule.From[index].NamespaceSelector.MatchExpressions = append(k8srule.From[index].NamespaceSelector.MatchExpressions, re) - } - } - } - } - return k8srule, nil -} -func (c *controller) listNamespacesInWorkspace(workspace string) ([]*corev1.Namespace, error) { - selector, err := labels.Parse(workspaceSelectorLabel + "==" + workspace) - if err != nil { - log.Error(err, "Failed to parse label selector") - return nil, err - } - namespaces, err := c.namespaceLister.List(selector) - if err != nil { - log.Error(err, "Failed to list namespaces in this workspace") - return nil, err - } - return namespaces, nil -} diff --git a/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_suite_test.go b/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_suite_test.go deleted file mode 100644 index c6c007075..000000000 --- a/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_suite_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package wsnetworkpolicy - -import ( - "flag" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "k8s.io/klog" -) - -func TestWsnetworkpolicy(t *testing.T) { - klog.InitFlags(nil) - flag.Set("logtostderr", "false") - flag.Set("alsologtostderr", "false") - flag.Set("v", "4") - flag.Parse() - klog.SetOutput(GinkgoWriter) - RegisterFailHandler(Fail) - RunSpecs(t, "Wsnetworkpolicy Suite") -} diff --git a/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_test.go b/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_test.go deleted file mode 100644 index 189d30906..000000000 --- a/pkg/controller/network/wsnetworkpolicy/wsnetworkpolicy_test.go +++ /dev/null @@ -1,242 +0,0 @@ -package wsnetworkpolicy - -import ( - "fmt" - "time" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - . "github.com/onsi/gomega/gstruct" - corev1 "k8s.io/api/core/v1" - k8snetwork "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - netv1lister "k8s.io/client-go/listers/networking/v1" - "k8s.io/client-go/tools/record" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" - tenant "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/controller/network/controllerapi" - controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing" -) - -var ( - fakeControllerBuilder *controllertesting.FakeControllerBuilder - c controllerapi.Controller - npLister netv1lister.NetworkPolicyLister - stopCh chan struct{} - deletePolicy metav1.DeletionPropagation - testName string -) - -var _ = Describe("Wsnetworkpolicy", func() { - BeforeEach(func() { - deletePolicy = metav1.DeletePropagationBackground - fakeControllerBuilder = controllertesting.NewFakeControllerBuilder() - informer, k8sinformer := fakeControllerBuilder.NewControllerInformer() - stopCh = make(chan struct{}) - c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient, - informer.Network().V1alpha1().WorkspaceNetworkPolicies(), k8sinformer.Networking().V1().NetworkPolicies(), - k8sinformer.Core().V1().Namespaces(), informer.Tenant().V1alpha1().Workspaces()) - originalController := c.(*controller) - go originalController.wsnpInformer.Informer().Run(stopCh) - go originalController.networkPolicyInformer.Informer().Run(stopCh) - go originalController.namespaceInformer.Informer().Run(stopCh) - go originalController.workspaceInformer.Informer().Run(stopCh) - originalController.recorder = &record.FakeRecorder{} - go c.Run(1, stopCh) - npLister = k8sinformer.Networking().V1().NetworkPolicies().Lister() - testName = "test" - ns1 := newWorkspaceNamespaces("ns1", testName) - ns2 := newWorkspaceNamespaces("ns2", testName) - _, err := fakeControllerBuilder.KubeClient.CoreV1().Namespaces().Create(ns1) - Expect(err).ShouldNot(HaveOccurred()) - _, err = fakeControllerBuilder.KubeClient.CoreV1().Namespaces().Create(ns2) - Expect(err).ShouldNot(HaveOccurred()) - }) - - AfterEach(func() { - close(stopCh) - }) - - It("Should proper ingress rule when using workspaceSelector", func() { - label := map[string]string{"workspace": "test-selector"} - ws := &tenant.Workspace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Labels: label, - }, - } - _, err := fakeControllerBuilder.KsClient.TenantV1alpha1().Workspaces().Create(ws) - wsnp := newWorkspaceNP(testName) - wsnp.Spec.PolicyTypes = []k8snetwork.PolicyType{k8snetwork.PolicyTypeIngress} - wsnp.Spec.Ingress = []v1alpha1.WorkspaceNetworkPolicyIngressRule{ - { - From: []v1alpha1.WorkspaceNetworkPolicyPeer{ - { - WorkspaceSelector: &metav1.LabelSelector{ - MatchLabels: label, - }, - }, - }, - }} - _, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Create(wsnp) - Expect(err).ShouldNot(HaveOccurred()) - expect1Json := `{ - "apiVersion": "networking.k8s.io/v1", - "kind": "NetworkPolicy", - "metadata": { - "name": "test-np", - "namespace": "ns1", - "labels": { - "networking.kubesphere.io/wsnp": "test" - } - }, - "spec": { - "policyTypes": [ - "Ingress" - ], - "ingress": [ - { - "from": [ - { - "namespaceSelector": { - "matchLabels": { - "kubesphere.io/workspace": "test" - } - } - } - ] - } - ] - } - }` - expect1 := &k8snetwork.NetworkPolicy{} - Expect(controllertesting.StringToObject(expect1Json, expect1)).ShouldNot(HaveOccurred()) - nps := []*k8snetwork.NetworkPolicy{} - Eventually(func() error { - selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "==test") - nps, err = npLister.List(selector) - if err != nil { - klog.Errorf("Failed to list npmerr:%s", err.Error()) - return err - } - if len(nps) != 2 { - return fmt.Errorf("Length is not right, current length :%d", len(nps)) - } - return nil - }, time.Second*5, time.Second).ShouldNot(HaveOccurred()) - - for _, np := range nps { - Expect(np.Labels).To(Equal(expect1.Labels)) - Expect(np.Spec).To(Equal(expect1.Spec)) - } - // create a new ws will change the `From` - ws2 := &tenant.Workspace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test2", - Labels: label, - }, - } - _, err = fakeControllerBuilder.KsClient.TenantV1alpha1().Workspaces().Create(ws2) - Expect(err).ShouldNot(HaveOccurred()) - expect2Json := `{ - "apiVersion": "networking.k8s.io/v1", - "kind": "NetworkPolicy", - "metadata": { - "name": "test-np", - "namespace": "ns1", - "labels": { - "networking.kubesphere.io/wsnp": "test" - } - }, - "spec": { - "policyTypes": [ - "Ingress" - ], - "ingress": [ - { - "from": [ - { - "namespaceSelector": { - "matchExpressions": [{ - "key": "kubesphere.io/workspace", - "operator":"In", - "values": ["test", "test2"] - }] - } - } - ] - } - ] - } - }` - expect2 := &k8snetwork.NetworkPolicy{} - Expect(controllertesting.StringToObject(expect2Json, expect2)).ShouldNot(HaveOccurred()) - - id := func(element interface{}) string { - e := element.(*k8snetwork.NetworkPolicy) - return e.Namespace - } - Eventually(func() []*k8snetwork.NetworkPolicy { - selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "=test") - nps, err := npLister.List(selector) - if err != nil { - return nil - } - if len(nps) != 2 { - klog.Errorf("Length is not right, current length :%d", len(nps)) - return nil - } - return nps - }, time.Second*5, time.Second).Should(MatchAllElements(id, Elements{ - "ns1": PointTo(MatchFields(IgnoreExtras, Fields{ - "Spec": Equal(expect2.Spec), - })), - "ns2": PointTo(MatchFields(IgnoreExtras, Fields{ - "Spec": Equal(expect2.Spec), - })), - })) - }) - - It("Should create networkpolicies", func() { - //create a wsnp - _, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Create(newWorkspaceNP(testName)) - Expect(err).ShouldNot(HaveOccurred()) - Eventually(func() error { - selector, _ := labels.Parse(workspaceNetworkPolicyLabel + "=" + testName) - nps, err := npLister.List(selector) - if err != nil { - return err - } - if len(nps) != 2 { - return fmt.Errorf("Length is not right, current length :%d", len(nps)) - } - return nil - }, time.Second*5, time.Second).ShouldNot(HaveOccurred()) - err = fakeControllerBuilder.KsClient.NetworkV1alpha1().WorkspaceNetworkPolicies().Delete(testName, &metav1.DeleteOptions{ - PropagationPolicy: &deletePolicy, - }) - Expect(err).ShouldNot(HaveOccurred()) - }) -}) - -func newWorkspaceNP(name string) *v1alpha1.WorkspaceNetworkPolicy { - return &v1alpha1.WorkspaceNetworkPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Spec: v1alpha1.WorkspaceNetworkPolicySpec{ - Workspace: name, - }, - } -} - -func newWorkspaceNamespaces(ns, ws string) *corev1.Namespace { - return &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: ns, - Labels: map[string]string{workspaceSelectorLabel: ws}, - }, - } -} diff --git a/pkg/controller/pipeline/pipeline_controller.go b/pkg/controller/pipeline/pipeline_controller.go new file mode 100644 index 000000000..4f6c94bdf --- /dev/null +++ b/pkg/controller/pipeline/pipeline_controller.go @@ -0,0 +1,276 @@ +package pipeline + +import ( + "fmt" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + corev1informer "k8s.io/client-go/informers/core/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + v1core "k8s.io/client-go/kubernetes/typed/core/v1" + corev1lister "k8s.io/client-go/listers/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + devopsinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops/v1alpha3" + devopslisters "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/constants" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "net/http" + "reflect" + "time" +) + +/** + DevOps project controller is used to maintain the state of the DevOps project. +*/ + +type Controller struct { + client clientset.Interface + kubesphereClient kubesphereclient.Interface + + eventBroadcaster record.EventBroadcaster + eventRecorder record.EventRecorder + + devOpsProjectLister devopslisters.PipelineLister + pipelineSynced cache.InformerSynced + + namespaceLister corev1lister.NamespaceLister + namespaceSynced cache.InformerSynced + + workqueue workqueue.RateLimitingInterface + + workerLoopPeriod time.Duration + + devopsClient devopsClient.Interface +} + +func NewController(client clientset.Interface, + kubesphereClient kubesphereclient.Interface, + devopsClinet devopsClient.Interface, + namespaceInformer corev1informer.NamespaceInformer, + devopsInformer devopsinformers.PipelineInformer) *Controller { + + broadcaster := record.NewBroadcaster() + broadcaster.StartLogging(func(format string, args ...interface{}) { + klog.Info(fmt.Sprintf(format, args)) + }) + broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")}) + recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "pipeline-controller"}) + + v := &Controller{ + client: client, + devopsClient: devopsClinet, + kubesphereClient: kubesphereClient, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "pipeline"), + devOpsProjectLister: devopsInformer.Lister(), + pipelineSynced: devopsInformer.Informer().HasSynced, + namespaceLister: namespaceInformer.Lister(), + namespaceSynced: namespaceInformer.Informer().HasSynced, + workerLoopPeriod: time.Second, + } + + v.eventBroadcaster = broadcaster + v.eventRecorder = recorder + + devopsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: v.enqueuePipeline, + UpdateFunc: func(oldObj, newObj interface{}) { + old := oldObj.(*devopsv1alpha3.Pipeline) + new := newObj.(*devopsv1alpha3.Pipeline) + if old.ResourceVersion == new.ResourceVersion { + return + } + v.enqueuePipeline(newObj) + }, + DeleteFunc: v.enqueuePipeline, + }) + return v +} + +// enqueuePipeline takes a Foo resource and converts it into a namespace/name +// string which is then put onto the work workqueue. This method should *not* be +// passed resources of any type other than DevOpsProject. +func (c *Controller) enqueuePipeline(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + err := func(obj interface{}) error { + defer c.workqueue.Done(obj) + var key string + var ok bool + + if key, ok = obj.(string); !ok { + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + if err := c.syncHandler(key); err != nil { + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) + } + c.workqueue.Forget(obj) + klog.V(5).Infof("Successfully synced '%s'", key) + return nil + }(obj) + + if err != nil { + klog.Error(err, "could not reconcile devopsProject") + utilruntime.HandleError(err) + return true + } + + return true +} + +func (c *Controller) worker() { + + for c.processNextWorkItem() { + } +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(1, stopCh) +} + +func (c *Controller) Run(workers int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workqueue.ShutDown() + + klog.Info("starting pipeline controller") + defer klog.Info("shutting down pipeline controller") + + if !cache.WaitForCacheSync(stopCh, c.pipelineSynced) { + return fmt.Errorf("failed to wait for caches to sync") + } + + for i := 0; i < workers; i++ { + go wait.Until(c.worker, c.workerLoopPeriod, stopCh) + } + + <-stopCh + return nil +} + +// syncHandler compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the pipeline resource +// with the current status of the resource. +func (c *Controller) syncHandler(key string) error { + nsName, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + klog.Error(err, fmt.Sprintf("could not split copyPipeline meta %s ", key)) + return nil + } + namespace, err := c.namespaceLister.Get(nsName) + if err != nil { + if errors.IsNotFound(err) { + klog.Info(fmt.Sprintf("namespace '%s' in work queue no longer exists ", key)) + return nil + } + klog.Error(err, fmt.Sprintf("could not get namespace %s ", key)) + return err + } + if !isDevOpsProjectAdminNamespace(namespace) { + err := fmt.Errorf("cound not create copyPipeline in normal namespaces %s", namespace.Name) + klog.Warning(err) + return err + } + + pipeline, err := c.devOpsProjectLister.Pipelines(nsName).Get(name) + if err != nil { + if errors.IsNotFound(err) { + klog.Info(fmt.Sprintf("copyPipeline '%s' in work queue no longer exists ", key)) + return nil + } + klog.Error(err, fmt.Sprintf("could not get copyPipeline %s ", key)) + return err + } + + copyPipeline := pipeline.DeepCopy() + // DeletionTimestamp.IsZero() means copyPipeline has not been deleted. + if copyPipeline.ObjectMeta.DeletionTimestamp.IsZero() { + // https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers + if !sliceutil.HasString(copyPipeline.ObjectMeta.Finalizers, devopsv1alpha3.PipelineFinalizerName) { + copyPipeline.ObjectMeta.Finalizers = append(copyPipeline.ObjectMeta.Finalizers, devopsv1alpha3.PipelineFinalizerName) + } + + // Check pipeline config exists, otherwise we will create it. + // if pipeline exists, check & update config + jenkinsPipeline, err := c.devopsClient.GetProjectPipelineConfig(nsName, pipeline.Name) + if err == nil { + if !reflect.DeepEqual(jenkinsPipeline.Spec, copyPipeline.Spec) { + _, err := c.devopsClient.UpdateProjectPipeline(nsName, copyPipeline) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update pipeline config %s ", key)) + return err + } + } + } else if devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get copyPipeline %s ", key)) + return err + } else { + _, err := c.devopsClient.CreateProjectPipeline(nsName, copyPipeline) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to get copyPipeline %s ", key)) + return err + } + } + + } else { + // Finalizers processing logic + if sliceutil.HasString(copyPipeline.ObjectMeta.Finalizers, devopsv1alpha3.PipelineFinalizerName) { + _, err := c.devopsClient.GetProjectPipelineConfig(nsName, pipeline.Name) + if err != nil && devopsClient.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Error(err, fmt.Sprintf("failed to get pipeline %s ", key)) + return err + } else if err != nil && devopsClient.GetDevOpsStatusCode(err) == http.StatusNotFound { + } else { + if _, err := c.devopsClient.DeleteProjectPipeline(nsName, pipeline.Name); err != nil { + klog.Error(err, fmt.Sprintf("failed to delete pipeline %s in devops", key)) + return err + } + } + copyPipeline.ObjectMeta.Finalizers = sliceutil.RemoveString(copyPipeline.ObjectMeta.Finalizers, func(item string) bool { + return item == devopsv1alpha3.PipelineFinalizerName + }) + + } + } + if !reflect.DeepEqual(pipeline, copyPipeline) { + _, err = c.kubesphereClient.DevopsV1alpha3().Pipelines(nsName).Update(copyPipeline) + if err != nil { + klog.Error(err, fmt.Sprintf("failed to update pipeline %s ", key)) + return err + } + } + + return nil +} + +func isDevOpsProjectAdminNamespace(namespace *v1.Namespace) bool { + _, ok := namespace.Labels[constants.DevOpsProjectLabelKey] + + return ok && k8sutil.IsControlledBy(namespace.OwnerReferences, + devopsv1alpha3.ResourceKindDevOpsProject, "") + +} diff --git a/pkg/controller/pipeline/pipeline_controller_test.go b/pkg/controller/pipeline/pipeline_controller_test.go new file mode 100644 index 000000000..f02a4a6b6 --- /dev/null +++ b/pkg/controller/pipeline/pipeline_controller_test.go @@ -0,0 +1,406 @@ +package pipeline + +import ( + v1 "k8s.io/api/core/v1" + "kubesphere.io/kubesphere/pkg/constants" + fakeDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + kubeinformers "k8s.io/client-go/informers" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + devops "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + client *fake.Clientset + kubeclient *k8sfake.Clientset + namespaceLister []*v1.Namespace + pipelineLister []*devops.Pipeline + actions []core.Action + kubeactions []core.Action + + kubeobjects []runtime.Object + // Objects from here preloaded into NewSimpleFake. + objects []runtime.Object + // Objects from here preloaded into devops + initDevOpsProject string + initPipeline []*devops.Pipeline + expectPipeline []*devops.Pipeline +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + return f +} + +func newNamespace(name string, projectName string) *v1.Namespace { + ns := &v1.Namespace{ + TypeMeta: metav1.TypeMeta{ + Kind: "Namespace", + APIVersion: v1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{constants.DevOpsProjectLabelKey: projectName}, + }, + } + TRUE := true + ns.ObjectMeta.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: devops.SchemeGroupVersion.String(), + Kind: devops.ResourceKindDevOpsProject, + Name: projectName, + BlockOwnerDeletion: &TRUE, + Controller: &TRUE, + }, + } + + return ns +} + +func newPipeline(namespace, name string, spec devops.PipelineSpec, withFinalizers bool) *devops.Pipeline { + pipeline := &devops.Pipeline{ + TypeMeta: metav1.TypeMeta{ + Kind: devops.ResourceKindPipeline, + APIVersion: devops.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: spec, + Status: devops.PipelineStatus{}, + } + if withFinalizers { + pipeline.Finalizers = append(pipeline.Finalizers, devops.PipelineFinalizerName) + } + return pipeline +} + +func newDeletingPipeline(namespace, name string) *devops.Pipeline { + now := metav1.Now() + pipeline := &devops.Pipeline{ + TypeMeta: metav1.TypeMeta{ + Kind: devops.ResourceKindPipeline, + APIVersion: devops.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + DeletionTimestamp: &now, + }, + } + pipeline.Finalizers = append(pipeline.Finalizers, devops.PipelineFinalizerName) + + return pipeline +} + +func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory, *fakeDevOps.Devops) { + f.client = fake.NewSimpleClientset(f.objects...) + f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...) + + i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) + k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) + dI := fakeDevOps.NewWithPipelines(f.initDevOpsProject, f.initPipeline...) + + c := NewController(f.kubeclient, f.client, dI, k8sI.Core().V1().Namespaces(), + i.Devops().V1alpha3().Pipelines()) + + c.pipelineSynced = alwaysReady + c.eventRecorder = &record.FakeRecorder{} + + for _, f := range f.pipelineLister { + i.Devops().V1alpha3().Pipelines().Informer().GetIndexer().Add(f) + } + + for _, d := range f.namespaceLister { + k8sI.Core().V1().Namespaces().Informer().GetIndexer().Add(d) + } + + return c, i, k8sI, dI +} + +func (f *fixture) run(fooName string) { + f.runController(fooName, true, false) +} + +func (f *fixture) runExpectError(fooName string) { + f.runController(fooName, true, true) +} + +func (f *fixture) runController(projectName string, startInformers bool, expectError bool) { + c, i, k8sI, dI := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + i.Start(stopCh) + k8sI.Start(stopCh) + } + + err := c.syncHandler(projectName) + if !expectError && err != nil { + f.t.Errorf("error syncing foo: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing foo, got nil") + } + + actions := filterInformerActions(f.client.Actions()) + for i, action := range actions { + if len(f.actions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) + break + } + + expectedAction := f.actions[i] + checkAction(expectedAction, action, f.t) + } + k8sActions := filterInformerActions(f.kubeclient.Actions()) + for i, action := range k8sActions { + if len(f.kubeactions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:]) + break + } + + expectedAction := f.kubeactions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.kubeactions) > len(k8sActions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) + } + + if len(f.actions) > len(actions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) + } + if len(dI.Pipelines[f.initDevOpsProject]) != len(f.expectPipeline) { + f.t.Errorf(" unexpected objects: %v", dI.Projects) + } + for _, pipeline := range f.expectPipeline { + actualPipeline := dI.Pipelines[f.initDevOpsProject][pipeline.Name] + if !reflect.DeepEqual(actualPipeline, pipeline) { + f.t.Errorf(" pipeline %+v not match %+v", pipeline, actualPipeline) + } + + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + ret := []core.Action{} + for _, action := range actions { + if len(action.GetNamespace()) == 0 && + (action.Matches("list", devops.ResourcePluralPipeline) || + action.Matches("watch", devops.ResourcePluralPipeline) || + action.Matches("list", "namespaces") || + action.Matches("watch", "namespaces")) { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdatePipelineAction(p *devops.Pipeline) { + action := core.NewUpdateAction(schema.GroupVersionResource{ + Version: devops.SchemeGroupVersion.Version, + Resource: devops.ResourcePluralPipeline, + Group: devops.SchemeGroupVersion.Group, + }, p.Namespace, p) + f.actions = append(f.actions, action) +} + +func getKey(p *devops.Pipeline, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(p) + if err != nil { + t.Errorf("Unexpected error getting key for pipeline %v: %v", p.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + pipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{}, true) + + f.pipelineLister = append(f.pipelineLister, pipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, pipeline) + f.initDevOpsProject = nsName + f.initPipeline = []*devops.Pipeline{pipeline} + f.expectPipeline = []*devops.Pipeline{pipeline} + + f.run(getKey(pipeline, t)) +} + +func TestAddPipelineFinalizers(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + pipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{}, false) + + expectPipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{}, true) + + f.pipelineLister = append(f.pipelineLister, pipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, pipeline) + f.initDevOpsProject = nsName + f.initPipeline = []*devops.Pipeline{pipeline} + f.expectPipeline = []*devops.Pipeline{pipeline} + f.expectUpdatePipelineAction(expectPipeline) + f.run(getKey(pipeline, t)) +} + +func TestCreatePipeline(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + pipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{}, true) + + f.pipelineLister = append(f.pipelineLister, pipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, pipeline) + f.initDevOpsProject = nsName + f.expectPipeline = []*devops.Pipeline{pipeline} + f.run(getKey(pipeline, t)) +} + +func TestDeletePipeline(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + pipeline := newDeletingPipeline(nsName, pipelineName) + + expectPipeline := pipeline.DeepCopy() + expectPipeline.Finalizers = []string{} + f.pipelineLister = append(f.pipelineLister, pipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, pipeline) + f.initDevOpsProject = nsName + f.initPipeline = []*devops.Pipeline{pipeline} + f.expectPipeline = []*devops.Pipeline{} + f.expectUpdatePipelineAction(expectPipeline) + f.run(getKey(pipeline, t)) +} + +func TestDeleteNotExistPipeline(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + pipeline := newDeletingPipeline(nsName, pipelineName) + + expectPipeline := pipeline.DeepCopy() + expectPipeline.Finalizers = []string{} + f.pipelineLister = append(f.pipelineLister, pipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, pipeline) + f.initDevOpsProject = nsName + f.initPipeline = []*devops.Pipeline{} + f.expectPipeline = []*devops.Pipeline{} + f.expectUpdatePipelineAction(expectPipeline) + f.run(getKey(pipeline, t)) +} + +func TestUpdatePipelineConfig(t *testing.T) { + f := newFixture(t) + nsName := "test-123" + pipelineName := "test" + projectName := "test_project" + + ns := newNamespace(nsName, projectName) + initPipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{}, true) + expectPipeline := newPipeline(nsName, pipelineName, devops.PipelineSpec{Type: "aa"}, true) + f.pipelineLister = append(f.pipelineLister, expectPipeline) + f.namespaceLister = append(f.namespaceLister, ns) + f.objects = append(f.objects, expectPipeline) + f.initDevOpsProject = nsName + f.initPipeline = []*devops.Pipeline{initPipeline} + f.expectPipeline = []*devops.Pipeline{expectPipeline} + f.run(getKey(expectPipeline, t)) +} diff --git a/pkg/controller/s2ibinary/s2ibinary_controller.go b/pkg/controller/s2ibinary/s2ibinary_controller.go index ea2f10a3f..53d838ec1 100644 --- a/pkg/controller/s2ibinary/s2ibinary_controller.go +++ b/pkg/controller/s2ibinary/s2ibinary_controller.go @@ -2,9 +2,6 @@ package s2ibinary import ( "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/s3" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -16,7 +13,7 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/utils/sliceutil" "time" @@ -26,7 +23,11 @@ import ( devopslisters "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha1" ) -type S2iBinaryController struct { +/** +s2ibinary-controller used to handle s2ibinary's delete logic. +s2ibinary creation and file upload provided by kubesphere/kapis +*/ +type Controller struct { client clientset.Interface devopsClient devopsclient.Interface @@ -39,11 +40,14 @@ type S2iBinaryController struct { workqueue workqueue.RateLimitingInterface workerLoopPeriod time.Duration + + s3Client s3.Interface } -func NewController(devopsclientset devopsclient.Interface, - client clientset.Interface, - s2ibinInformer devopsinformers.S2iBinaryInformer) *S2iBinaryController { +func NewController(client clientset.Interface, + devopsclientset devopsclient.Interface, + s2ibinInformer devopsinformers.S2iBinaryInformer, + s3Client s3.Interface) *Controller { broadcaster := record.NewBroadcaster() broadcaster.StartLogging(func(format string, args ...interface{}) { @@ -52,13 +56,14 @@ func NewController(devopsclientset devopsclient.Interface, broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")}) recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "s2ibinary-controller"}) - v := &S2iBinaryController{ + v := &Controller{ client: client, devopsClient: devopsclientset, workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "s2ibinary"), s2iBinaryLister: s2ibinInformer.Lister(), s2iBinarySynced: s2ibinInformer.Informer().HasSynced, workerLoopPeriod: time.Second, + s3Client: s3Client, } v.eventBroadcaster = broadcaster @@ -82,7 +87,7 @@ func NewController(devopsclientset devopsclient.Interface, // enqueueS2iBinary takes a Foo resource and converts it into a namespace/name // string which is then put onto the work workqueue. This method should *not* be // passed resources of any type other than S2iBinary. -func (c *S2iBinaryController) enqueueS2iBinary(obj interface{}) { +func (c *Controller) enqueueS2iBinary(obj interface{}) { var key string var err error if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { @@ -92,7 +97,7 @@ func (c *S2iBinaryController) enqueueS2iBinary(obj interface{}) { c.workqueue.Add(key) } -func (c *S2iBinaryController) processNextWorkItem() bool { +func (c *Controller) processNextWorkItem() bool { obj, shutdown := c.workqueue.Get() if shutdown { @@ -127,17 +132,17 @@ func (c *S2iBinaryController) processNextWorkItem() bool { return true } -func (c *S2iBinaryController) worker() { +func (c *Controller) worker() { for c.processNextWorkItem() { } } -func (c *S2iBinaryController) Start(stopCh <-chan struct{}) error { +func (c *Controller) Start(stopCh <-chan struct{}) error { return c.Run(1, stopCh) } -func (c *S2iBinaryController) Run(workers int, stopCh <-chan struct{}) error { +func (c *Controller) Run(workers int, stopCh <-chan struct{}) error { defer utilruntime.HandleCrash() defer c.workqueue.ShutDown() @@ -159,7 +164,7 @@ func (c *S2iBinaryController) Run(workers int, stopCh <-chan struct{}) error { // syncHandler compares the actual state with the desired, and attempts to // converge the two. It then updates the Status block of the Foo resource // with the current status of the resource. -func (c *S2iBinaryController) syncHandler(key string) error { +func (c *Controller) syncHandler(key string) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { klog.Error(err, fmt.Sprintf("could not split s2ibin meta %s ", key)) @@ -204,30 +209,13 @@ func (c *S2iBinaryController) syncHandler(key string) error { return nil } -func (c *S2iBinaryController) deleteBinaryInS3(s2ibin *devopsv1alpha1.S2iBinary) error { - s3Client, err := client.ClientSets().S3() +func (c *Controller) deleteBinaryInS3(s2ibin *devopsv1alpha1.S2iBinary) error { + + key := fmt.Sprintf("%s-%s", s2ibin.Namespace, s2ibin.Name) + err := c.s3Client.Delete(key) if err != nil { - return err + klog.Errorf("error happened while deleting %s, %v", key, err) } - input := &s3.DeleteObjectInput{ - Bucket: s3Client.Bucket(), - Key: aws.String(fmt.Sprintf("%s-%s", s2ibin.Namespace, s2ibin.Name)), - } - _, err = s3Client.Client().DeleteObject(input) - if err != nil { - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case s3.ErrCodeNoSuchKey: - return nil - default: - klog.Error(err, fmt.Sprintf("failed to delete s2ibin %s/%s in s3", s2ibin.Namespace, s2ibin.Name)) - return err - } - } else { - klog.Error(err, fmt.Sprintf("failed to delete s2ibin %s/%s in s3", s2ibin.Namespace, s2ibin.Name)) - return err - } - } return nil } diff --git a/pkg/controller/s2ibinary/s2ibinary_controller_test.go b/pkg/controller/s2ibinary/s2ibinary_controller_test.go new file mode 100644 index 000000000..7483f8e05 --- /dev/null +++ b/pkg/controller/s2ibinary/s2ibinary_controller_test.go @@ -0,0 +1,235 @@ +package s2ibinary + +import ( + fakes3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake" + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + s2i "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + client *fake.Clientset + kubeclient *k8sfake.Clientset + // Objects to put in the store. + s2ibinaryLister []*s2i.S2iBinary + actions []core.Action + // Objects from here preloaded into NewSimpleFake. + objects []runtime.Object + // Objects from here preloaded into s3 + initS3Objects []*fakes3.Object + expectS3Objects []*fakes3.Object +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + return f +} + +func newS2iBinary(name string, spec s2i.S2iBinarySpec) *s2i.S2iBinary { + return &s2i.S2iBinary{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + }, + Spec: s2i.S2iBinarySpec{}, + } +} +func newDeletingS2iBinary(name string) *s2i.S2iBinary { + deleteTime := metav1.Now() + return &s2i.S2iBinary{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + Finalizers: []string{s2i.S2iBinaryFinalizerName}, + DeletionTimestamp: &deleteTime, + }, + } +} + +func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, *fakes3.FakeS3) { + f.client = fake.NewSimpleClientset(f.objects...) + f.kubeclient = k8sfake.NewSimpleClientset() + + i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) + s3I := fakes3.NewFakeS3(f.expectS3Objects...) + + c := NewController(f.kubeclient, f.client, i.Devops().V1alpha1().S2iBinaries(), s3I) + + c.s2iBinarySynced = alwaysReady + c.eventRecorder = &record.FakeRecorder{} + + for _, f := range f.s2ibinaryLister { + i.Devops().V1alpha1().S2iBinaries().Informer().GetIndexer().Add(f) + } + + return c, i, s3I +} + +func (f *fixture) run(fooName string) { + f.runController(fooName, true, false) +} + +func (f *fixture) runExpectError(fooName string) { + f.runController(fooName, true, true) +} + +func (f *fixture) runController(s2iBinaryName string, startInformers bool, expectError bool) { + c, i, s3I := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + i.Start(stopCh) + } + + err := c.syncHandler(s2iBinaryName) + if !expectError && err != nil { + f.t.Errorf("error syncing foo: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing foo, got nil") + } + + actions := filterInformerActions(f.client.Actions()) + for i, action := range actions { + if len(f.actions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) + break + } + + expectedAction := f.actions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.actions) > len(actions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) + } + if len(s3I.Storage) != len(f.expectS3Objects) { + f.t.Errorf(" unexpected objects: %v", s3I.Storage) + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + ret := []core.Action{} + for _, action := range actions { + if len(action.GetNamespace()) == 0 && + (action.Matches("list", s2i.ResourcePluralS2iBinary) || + action.Matches("watch", s2i.ResourcePluralS2iBinary)) { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdateS2iBinaryAction(s2ibinary *s2i.S2iBinary) { + action := core.NewUpdateAction(schema.GroupVersionResource{Resource: s2i.ResourcePluralS2iBinary}, s2ibinary.Namespace, s2ibinary) + f.actions = append(f.actions, action) +} + +func getKey(s2ibinary *s2i.S2iBinary, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(s2ibinary) + if err != nil { + t.Errorf("Unexpected error getting key for s2ibinary %v: %v", s2ibinary.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + s2iBinary := newS2iBinary("test", s2i.S2iBinarySpec{}) + + f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) + f.objects = append(f.objects, s2iBinary) + + f.expectUpdateS2iBinaryAction(s2iBinary) + f.run(getKey(s2iBinary, t)) +} + +func TestDeleteS3Object(t *testing.T) { + f := newFixture(t) + s2iBinary := newDeletingS2iBinary("test") + + f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) + f.objects = append(f.objects, s2iBinary) + f.initS3Objects = []*fakes3.Object{&fakes3.Object{ + Key: "default-test", + }} + f.expectS3Objects = []*fakes3.Object{} + f.expectUpdateS2iBinaryAction(s2iBinary) + f.run(getKey(s2iBinary, t)) + +} diff --git a/pkg/controller/s2irun/s2irun_controller.go b/pkg/controller/s2irun/s2irun_controller.go index a2cca9994..015b6535d 100644 --- a/pkg/controller/s2irun/s2irun_controller.go +++ b/pkg/controller/s2irun/s2irun_controller.go @@ -18,26 +18,25 @@ import ( "kubesphere.io/kubesphere/pkg/utils/sliceutil" "time" - s2iv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - s2iclient "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - s2iinformers "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1" - s2ilisters "github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" devopsclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" devopsinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/devops/v1alpha1" devopslisters "kubesphere.io/kubesphere/pkg/client/listers/devops/v1alpha1" ) -type S2iRunController struct { - client clientset.Interface - s2iClient s2iclient.Interface +/** + s2irun-controller used to handle s2irun's delete logic. + s2irun creation and operation provided by s2ioperator +*/ +type Controller struct { + client clientset.Interface devopsClient devopsclient.Interface eventBroadcaster record.EventBroadcaster eventRecorder record.EventRecorder - s2iRunLister s2ilisters.S2iRunLister + s2iRunLister devopslisters.S2iRunLister s2iRunSynced cache.InformerSynced s2iBinaryLister devopslisters.S2iBinaryLister @@ -48,9 +47,11 @@ type S2iRunController struct { workerLoopPeriod time.Duration } -func NewController(devopsclientset devopsclient.Interface, s2iclientset s2iclient.Interface, +func NewS2iRunController( client clientset.Interface, - s2ibinInformer devopsinformers.S2iBinaryInformer, s2iRunInformer s2iinformers.S2iRunInformer) *S2iRunController { + devopsClientSet devopsclient.Interface, + s2iBinInformer devopsinformers.S2iBinaryInformer, + s2iRunInformer devopsinformers.S2iRunInformer) *Controller { broadcaster := record.NewBroadcaster() broadcaster.StartLogging(func(format string, args ...interface{}) { @@ -59,13 +60,12 @@ func NewController(devopsclientset devopsclient.Interface, s2iclientset s2iclien broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")}) recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "s2irun-controller"}) - v := &S2iRunController{ + v := &Controller{ client: client, - devopsClient: devopsclientset, - s2iClient: s2iclientset, + devopsClient: devopsClientSet, workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "s2irun"), - s2iBinaryLister: s2ibinInformer.Lister(), - s2iBinarySynced: s2ibinInformer.Informer().HasSynced, + s2iBinaryLister: s2iBinInformer.Lister(), + s2iBinarySynced: s2iBinInformer.Informer().HasSynced, s2iRunLister: s2iRunInformer.Lister(), s2iRunSynced: s2iRunInformer.Informer().HasSynced, workerLoopPeriod: time.Second, @@ -77,8 +77,8 @@ func NewController(devopsclientset devopsclient.Interface, s2iclientset s2iclien s2iRunInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: v.enqueueS2iRun, UpdateFunc: func(oldObj, newObj interface{}) { - old := oldObj.(*s2iv1alpha1.S2iRun) - new := newObj.(*s2iv1alpha1.S2iRun) + old := oldObj.(*devopsv1alpha1.S2iRun) + new := newObj.(*devopsv1alpha1.S2iRun) if old.ResourceVersion == new.ResourceVersion { return } @@ -92,7 +92,7 @@ func NewController(devopsclientset devopsclient.Interface, s2iclientset s2iclien // enqueueFoo takes a Foo resource and converts it into a namespace/name // string which is then put onto the work workqueue. This method should *not* be // passed resources of any type other than Foo. -func (c *S2iRunController) enqueueS2iRun(obj interface{}) { +func (c Controller) enqueueS2iRun(obj interface{}) { var key string var err error if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { @@ -102,7 +102,7 @@ func (c *S2iRunController) enqueueS2iRun(obj interface{}) { c.workqueue.Add(key) } -func (c *S2iRunController) processNextWorkItem() bool { +func (c Controller) processNextWorkItem() bool { obj, shutdown := c.workqueue.Get() if shutdown { @@ -136,17 +136,17 @@ func (c *S2iRunController) processNextWorkItem() bool { return true } -func (c *S2iRunController) worker() { +func (c Controller) worker() { for c.processNextWorkItem() { } } -func (c *S2iRunController) Start(stopCh <-chan struct{}) error { +func (c Controller) Start(stopCh <-chan struct{}) error { return c.Run(1, stopCh) } -func (c *S2iRunController) Run(workers int, stopCh <-chan struct{}) error { +func (c Controller) Run(workers int, stopCh <-chan struct{}) error { defer utilruntime.HandleCrash() defer c.workqueue.ShutDown() @@ -168,7 +168,7 @@ func (c *S2iRunController) Run(workers int, stopCh <-chan struct{}) error { // syncHandler compares the actual state with the desired, and attempts to // converge the two. It then updates the Status block of the Foo resource // with the current status of the resource. -func (c *S2iRunController) syncHandler(key string) error { +func (c Controller) syncHandler(key string) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { klog.Error(err, fmt.Sprintf("could not split s2irun meta %s ", key)) @@ -189,7 +189,7 @@ func (c *S2iRunController) syncHandler(key string) error { if s2irun.ObjectMeta.DeletionTimestamp.IsZero() { if !sliceutil.HasString(s2irun.ObjectMeta.Finalizers, devopsv1alpha1.S2iBinaryFinalizerName) { s2irun.ObjectMeta.Finalizers = append(s2irun.ObjectMeta.Finalizers, devopsv1alpha1.S2iBinaryFinalizerName) - _, err := c.s2iClient.DevopsV1alpha1().S2iRuns(namespace).Update(s2irun) + _, err = c.devopsClient.DevopsV1alpha1().S2iRuns(namespace).Update(s2irun) if err != nil { klog.Error(err, fmt.Sprintf("failed to update s2irun %s", key)) return err @@ -205,7 +205,7 @@ func (c *S2iRunController) syncHandler(key string) error { s2irun.ObjectMeta.Finalizers = sliceutil.RemoveString(s2irun.ObjectMeta.Finalizers, func(item string) bool { return item == devopsv1alpha1.S2iBinaryFinalizerName }) - _, err := c.s2iClient.DevopsV1alpha1().S2iRuns(namespace).Update(s2irun) + _, err = c.devopsClient.DevopsV1alpha1().S2iRuns(namespace).Update(s2irun) if err != nil { klog.Error(err, fmt.Sprintf("failed to update s2irun %s ", key)) return err @@ -218,7 +218,12 @@ func (c *S2iRunController) syncHandler(key string) error { return nil } -func (c *S2iRunController) DeleteS2iBinary(s2irun *s2iv1alpha1.S2iRun) error { +/** + DeleteS2iBinary mainly cleans up two parts of S2iBinary + 1. s2ibinary bound to s2irun + 2. s2ibinary that has been created for more than 24 hours but has not been used +*/ +func (c Controller) DeleteS2iBinary(s2irun *devopsv1alpha1.S2iRun) error { s2iBinName := s2irun.Labels[devopsv1alpha1.S2iBinaryLabelKey] s2iBin, err := c.s2iBinaryLister.S2iBinaries(s2irun.Namespace).Get(s2iBinName) if err != nil { @@ -246,8 +251,8 @@ func (c *S2iRunController) DeleteS2iBinary(s2irun *s2iv1alpha1.S2iRun) error { } // cleanOtherS2iBinary clean up s2ibinary created for more than 24 hours without associated s2irun -func (c *S2iRunController) cleanOtherS2iBinary(namespace string) error { - s2iBins, err := c.s2iBinaryLister.S2iBinaries(namespace).List(nil) +func (c Controller) cleanOtherS2iBinary(namespace string) error { + s2iBins, err := c.s2iBinaryLister.S2iBinaries(namespace).List(labels.Everything()) if err != nil { klog.Error(err, fmt.Sprintf("failed to list s2ibin in %s ", namespace)) return err diff --git a/pkg/controller/s2irun/s2irun_controller_test.go b/pkg/controller/s2irun/s2irun_controller_test.go new file mode 100644 index 000000000..31f504afb --- /dev/null +++ b/pkg/controller/s2irun/s2irun_controller_test.go @@ -0,0 +1,305 @@ +package s2irun + +import ( + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + s2i "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + client *fake.Clientset + kubeclient *k8sfake.Clientset + // Objects to put in the store. + s2ibinaryLister []*s2i.S2iBinary + s2irunLister []*s2i.S2iRun + actions []core.Action + // Objects from here preloaded into NewSimpleFake. + objects []runtime.Object +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + return f +} + +func newS2iBinary(name string) *s2i.S2iBinary { + return &s2i.S2iBinary{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + }, + Spec: s2i.S2iBinarySpec{}, + } +} + +func newS2iBinaryWithCreateTime(name string, createTime metav1.Time) *s2i.S2iBinary { + return &s2i.S2iBinary{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + CreationTimestamp: createTime, + }, + Spec: s2i.S2iBinarySpec{}, + } +} + +func newS2iRun(name string, s2iBinaryName string) *s2i.S2iRun { + return &s2i.S2iRun{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + Labels: map[string]string{ + s2i.S2iBinaryLabelKey: s2iBinaryName, + }, + }, + } +} + +func newDeletetingS2iRun(name string, s2iBinaryName string) *s2i.S2iRun { + now := metav1.Now() + return &s2i.S2iRun{ + TypeMeta: metav1.TypeMeta{APIVersion: s2i.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: metav1.NamespaceDefault, + Labels: map[string]string{ + s2i.S2iBinaryLabelKey: s2iBinaryName, + }, + Finalizers: []string{s2i.S2iBinaryFinalizerName}, + DeletionTimestamp: &now, + }, + } +} + +func (f *fixture) newController() (*Controller, informers.SharedInformerFactory) { + f.client = fake.NewSimpleClientset(f.objects...) + f.kubeclient = k8sfake.NewSimpleClientset() + + i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) + + c := NewS2iRunController(f.kubeclient, f.client, + i.Devops().V1alpha1().S2iBinaries(), i.Devops().V1alpha1().S2iRuns()) + + c.s2iBinarySynced = alwaysReady + c.eventRecorder = &record.FakeRecorder{} + + for _, f := range f.s2ibinaryLister { + i.Devops().V1alpha1().S2iBinaries().Informer().GetIndexer().Add(f) + } + for _, f := range f.s2irunLister { + i.Devops().V1alpha1().S2iRuns().Informer().GetIndexer().Add(f) + } + + return c, i +} + +func (f *fixture) run(fooName string) { + f.runController(fooName, true, false) +} + +func (f *fixture) runExpectError(fooName string) { + f.runController(fooName, true, true) +} + +func (f *fixture) runController(s2iRunName string, startInformers bool, expectError bool) { + c, i := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + i.Start(stopCh) + } + + err := c.syncHandler(s2iRunName) + if !expectError && err != nil { + f.t.Errorf("error syncing foo: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing foo, got nil") + } + + actions := filterInformerActions(f.client.Actions()) + for i, action := range actions { + if len(f.actions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) + break + } + + expectedAction := f.actions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.actions) > len(actions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + case core.DeleteActionImpl: + e, _ := expected.(core.DeleteActionImpl) + + expName := e.GetName() + objectName := a.GetName() + + if !reflect.DeepEqual(expName, objectName) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expName, objectName)) + } + expNamespace := e.GetNamespace() + objectNamespace := a.GetNamespace() + if !reflect.DeepEqual(expNamespace, objectNamespace) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expNamespace, objectNamespace)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + ret := []core.Action{} + for _, action := range actions { + if len(action.GetNamespace()) == 0 && + (action.Matches("list", s2i.ResourcePluralS2iRun) || + action.Matches("watch", s2i.ResourcePluralS2iRun) || + action.Matches("list", s2i.ResourcePluralS2iBinary) || + action.Matches("watch", s2i.ResourcePluralS2iBinary)) { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdateS2iRunAction(s2iRun *s2i.S2iRun) { + action := core.NewUpdateAction(schema.GroupVersionResource{Resource: s2i.ResourcePluralS2iRun}, s2iRun.Namespace, s2iRun) + f.actions = append(f.actions, action) +} + +func (f *fixture) expectDeleteS2iBinaryAction(s2iBinary *s2i.S2iBinary) { + action := core.NewDeleteAction(schema.GroupVersionResource{Resource: s2i.ResourcePluralS2iBinary}, s2iBinary.Namespace, s2iBinary.Name) + f.actions = append(f.actions, action) +} + +func getKey(s2i *s2i.S2iRun, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(s2i) + if err != nil { + t.Errorf("Unexpected error getting key for s2i %v: %v", s2i.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + s2iBinary := newS2iBinary("test") + s2iRun := newS2iRun("test", s2iBinary.Name) + + f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) + f.s2irunLister = append(f.s2irunLister, s2iRun) + f.objects = append(f.objects, s2iBinary) + f.objects = append(f.objects, s2iRun) + + f.expectUpdateS2iRunAction(s2iRun) + f.run(getKey(s2iRun, t)) +} + +func TestDeleteS2iBinary(t *testing.T) { + f := newFixture(t) + s2iBinary := newS2iBinary("test") + s2iRun := newDeletetingS2iRun("test", s2iBinary.Name) + + f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) + f.s2irunLister = append(f.s2irunLister, s2iRun) + f.objects = append(f.objects, s2iBinary) + f.objects = append(f.objects, s2iRun) + + f.expectDeleteS2iBinaryAction(s2iBinary) + f.expectUpdateS2iRunAction(s2iRun) + f.run(getKey(s2iRun, t)) +} + +func TestDeleteOtherS2iBinary(t *testing.T) { + f := newFixture(t) + s2iBinary := newS2iBinary("test") + s2iRun := newDeletetingS2iRun("test", s2iBinary.Name) + otherS2iBinary := newS2iBinaryWithCreateTime("test2", metav1.NewTime(time.Date(2000, 1, 1, 12, 0, 0, 0, time.UTC))) + + f.s2ibinaryLister = append(f.s2ibinaryLister, s2iBinary) + f.s2ibinaryLister = append(f.s2ibinaryLister, otherS2iBinary) + f.s2irunLister = append(f.s2irunLister, s2iRun) + f.objects = append(f.objects, s2iBinary) + f.objects = append(f.objects, s2iRun) + f.objects = append(f.objects, otherS2iBinary) + f.expectDeleteS2iBinaryAction(s2iBinary) + f.expectDeleteS2iBinaryAction(otherS2iBinary) + f.expectUpdateS2iRunAction(s2iRun) + f.run(getKey(s2iRun, t)) +} diff --git a/pkg/controller/user/user_controller.go b/pkg/controller/user/user_controller.go new file mode 100644 index 000000000..7ab3557a6 --- /dev/null +++ b/pkg/controller/user/user_controller.go @@ -0,0 +1,230 @@ +/* +Copyright 2019 The KubeSphere 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 user + +import ( + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" + userinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/iam/v1alpha2" + userlister "kubesphere.io/kubesphere/pkg/client/listers/iam/v1alpha2" + "time" +) + +const ( + // SuccessSynced is used as part of the Event 'reason' when a Foo is synced + successSynced = "Synced" + // is synced successfully + messageResourceSynced = "User synced successfully" + controllerName = "user-controller" +) + +type Controller struct { + kubeClientset kubernetes.Interface + kubesphereClientset kubesphereclient.Interface + + userInformer userinformer.UserInformer + userLister userlister.UserLister + userSynced cache.InformerSynced + // workqueue is a rate limited work queue. This is used to queue work to be + // processed instead of performing it as soon as a change happens. This + // means we can ensure we only process a fixed amount of resources at a + // time, and makes it easy to ensure we are never processing the same item + // simultaneously in two different workers. + workqueue workqueue.RateLimitingInterface + // recorder is an event recorder for recording Event resources to the + // Kubernetes API. + recorder record.EventRecorder +} + +func NewController(kubeclientset kubernetes.Interface, + kubesphereklientset kubesphereclient.Interface, + userInformer userinformer.UserInformer) *Controller { + // Create event broadcaster + // Add sample-controller types to the default Kubernetes Scheme so Events can be + // logged for sample-controller types. + + utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme)) + klog.V(4).Info("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + eventBroadcaster.StartLogging(klog.Infof) + eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) + recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerName}) + ctl := &Controller{ + kubeClientset: kubeclientset, + kubesphereClientset: kubesphereklientset, + userInformer: userInformer, + userLister: userInformer.Lister(), + userSynced: userInformer.Informer().HasSynced, + workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Users"), + recorder: recorder, + } + klog.Info("Setting up event handlers") + userInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: ctl.enqueueUser, + UpdateFunc: func(old, new interface{}) { + ctl.enqueueUser(new) + }, + DeleteFunc: ctl.enqueueUser, + }) + return ctl +} + +func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { + defer utilruntime.HandleCrash() + defer c.workqueue.ShutDown() + + //init client + + // Start the informer factories to begin populating the informer caches + klog.Info("Starting User controller") + + // Wait for the caches to be synced before starting workers + klog.Info("Waiting for informer caches to sync") + if ok := cache.WaitForCacheSync(stopCh, c.userSynced); !ok { + return fmt.Errorf("failed to wait for caches to sync") + } + + klog.Info("Starting workers") + // Launch two workers to process Foo resources + for i := 0; i < threadiness; i++ { + go wait.Until(c.runWorker, time.Second, stopCh) + } + + klog.Info("Started workers") + <-stopCh + klog.Info("Shutting down workers") + return nil +} + +func (c *Controller) enqueueUser(obj interface{}) { + var key string + var err error + if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { + utilruntime.HandleError(err) + return + } + c.workqueue.Add(key) +} + +func (c *Controller) runWorker() { + for c.processNextWorkItem() { + } +} + +func (c *Controller) processNextWorkItem() bool { + obj, shutdown := c.workqueue.Get() + + if shutdown { + return false + } + + // We wrap this block in a func so we can defer c.workqueue.Done. + err := func(obj interface{}) error { + // We call Done here so the workqueue knows we have finished + // processing this item. We also must remember to call Forget if we + // do not want this work item being re-queued. For example, we do + // not call Forget if a transient error occurs, instead the item is + // put back on the workqueue and attempted again after a back-off + // period. + defer c.workqueue.Done(obj) + var key string + var ok bool + // We expect strings to come off the workqueue. These are of the + // form namespace/name. We do this as the delayed nature of the + // workqueue means the items in the informer cache may actually be + // more up to date that when the item was initially put onto the + // workqueue. + if key, ok = obj.(string); !ok { + // As the item in the workqueue is actually invalid, we call + // Forget here else we'd go into a loop of attempting to + // process a work item that is invalid. + c.workqueue.Forget(obj) + utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) + return nil + } + // Run the reconcile, passing it the namespace/name string of the + // Foo resource to be synced. + if err := c.reconcile(key); err != nil { + // Put the item back on the workqueue to handle any transient errors. + c.workqueue.AddRateLimited(key) + return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error()) + } + // Finally, if no error occurs we Forget this item so it does not + // get queued again until another change happens. + c.workqueue.Forget(obj) + klog.Infof("Successfully synced %s:%s", "key", key) + return nil + }(obj) + + if err != nil { + utilruntime.HandleError(err) + return true + } + + return true +} + +// syncHandler compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the Foo resource +// with the current status of the resource. +func (c *Controller) reconcile(key string) error { + + // Get the user with this name + user, err := c.userLister.Get(key) + if err != nil { + // The user may no longer exist, in which case we stop + // processing. + if errors.IsNotFound(err) { + utilruntime.HandleError(fmt.Errorf("user '%s' in work queue no longer exists", key)) + return nil + } + return err + } + + err = c.updateUserStatus(user) + + if err != nil { + return err + } + + c.recorder.Event(user, corev1.EventTypeNormal, successSynced, messageResourceSynced) + return nil +} + +func (c *Controller) updateUserStatus(user *iamv1alpha2.User) error { + userCopy := user.DeepCopy() + userCopy.Status.State = iamv1alpha2.UserActive + _, err := c.kubesphereClientset.IamV1alpha2().Users().Update(userCopy) + return err +} + +func (c *Controller) Start(stopCh <-chan struct{}) error { + return c.Run(4, stopCh) +} diff --git a/pkg/controller/user/user_controller_test.go b/pkg/controller/user/user_controller_test.go new file mode 100644 index 000000000..60b3afc06 --- /dev/null +++ b/pkg/controller/user/user_controller_test.go @@ -0,0 +1,245 @@ +/* +Copyright 2019 The KubeSphere 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 user + +import ( + "fmt" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/diff" + kubeinformers "k8s.io/client-go/informers" + k8sfake "k8s.io/client-go/kubernetes/fake" + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "reflect" + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + alwaysReady = func() bool { return true } + noResyncPeriodFunc = func() time.Duration { return 0 } +) + +type fixture struct { + t *testing.T + + client *fake.Clientset + kubeclient *k8sfake.Clientset + // Objects to put in the store. + userLister []*iamv1alpha2.User + // Actions expected to happen on the client. + kubeactions []core.Action + actions []core.Action + // Objects from here preloaded into NewSimpleFake. + kubeobjects []runtime.Object + objects []runtime.Object +} + +func newFixture(t *testing.T) *fixture { + f := &fixture{} + f.t = t + f.objects = []runtime.Object{} + f.kubeobjects = []runtime.Object{} + return f +} + +func newUser(name string) *iamv1alpha2.User { + return &iamv1alpha2.User{ + TypeMeta: metav1.TypeMeta{APIVersion: iamv1alpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: iamv1alpha2.UserSpec{ + Email: fmt.Sprintf("%s@kubesphere.io", name), + Lang: "zh-CN", + Description: "fake user", + }, + } +} + +func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, kubeinformers.SharedInformerFactory) { + f.client = fake.NewSimpleClientset(f.objects...) + f.kubeclient = k8sfake.NewSimpleClientset(f.kubeobjects...) + + i := informers.NewSharedInformerFactory(f.client, noResyncPeriodFunc()) + k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) + + for _, user := range f.userLister { + err := i.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user) + if err != nil { + f.t.Errorf("add user:%s", err) + } + } + + c := NewController(f.kubeclient, f.client, i.Iam().V1alpha2().Users()) + c.userSynced = alwaysReady + c.recorder = &record.FakeRecorder{} + + return c, i, k8sI +} + +func (f *fixture) run(userName string) { + f.runController(userName, true, false) +} + +func (f *fixture) runExpectError(userName string) { + f.runController(userName, true, true) +} + +func (f *fixture) runController(user string, startInformers bool, expectError bool) { + c, i, k8sI := f.newController() + if startInformers { + stopCh := make(chan struct{}) + defer close(stopCh) + i.Start(stopCh) + k8sI.Start(stopCh) + } + + err := c.reconcile(user) + if !expectError && err != nil { + f.t.Errorf("error syncing user: %v", err) + } else if expectError && err == nil { + f.t.Error("expected error syncing user, got nil") + } + + actions := filterInformerActions(f.client.Actions()) + for i, action := range actions { + if len(f.actions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) + break + } + + expectedAction := f.actions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.actions) > len(actions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) + } + + k8sActions := filterInformerActions(f.kubeclient.Actions()) + for i, action := range k8sActions { + if len(f.kubeactions) < i+1 { + f.t.Errorf("%d unexpected actions: %+v", len(k8sActions)-len(f.kubeactions), k8sActions[i:]) + break + } + + expectedAction := f.kubeactions[i] + checkAction(expectedAction, action, f.t) + } + + if len(f.kubeactions) > len(k8sActions) { + f.t.Errorf("%d additional expected actions:%+v", len(f.kubeactions)-len(k8sActions), f.kubeactions[len(k8sActions):]) + } +} + +// checkAction verifies that expected and actual actions are equal and both have +// same attached resources +func checkAction(expected, actual core.Action, t *testing.T) { + if !(expected.Matches(actual.GetVerb(), actual.GetResource().Resource) && actual.GetSubresource() == expected.GetSubresource()) { + t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expected, actual) + return + } + + if reflect.TypeOf(actual) != reflect.TypeOf(expected) { + t.Errorf("Action has wrong type. Expected: %t. Got: %t", expected, actual) + return + } + + switch a := actual.(type) { + case core.CreateActionImpl: + e, _ := expected.(core.CreateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.UpdateActionImpl: + e, _ := expected.(core.UpdateActionImpl) + expObject := e.GetObject() + object := a.GetObject() + + if !reflect.DeepEqual(expObject, object) { + t.Errorf("Action %s %s has wrong object\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expObject, object)) + } + case core.PatchActionImpl: + e, _ := expected.(core.PatchActionImpl) + expPatch := e.GetPatch() + patch := a.GetPatch() + + if !reflect.DeepEqual(expPatch, patch) { + t.Errorf("Action %s %s has wrong patch\nDiff:\n %s", + a.GetVerb(), a.GetResource().Resource, diff.ObjectGoPrintSideBySide(expPatch, patch)) + } + default: + t.Errorf("Uncaptured Action %s %s, you should explicitly add a case to capture it", + actual.GetVerb(), actual.GetResource().Resource) + } +} + +// filterInformerActions filters list and watch actions for testing resources. +// Since list and watch don't change resource state we can filter it to lower +// nose level in our tests. +func filterInformerActions(actions []core.Action) []core.Action { + var ret []core.Action + for _, action := range actions { + if action.Matches("list", "users") || + action.Matches("watch", "users") { + continue + } + ret = append(ret, action) + } + + return ret +} + +func (f *fixture) expectUpdateUserStatusAction(user *iamv1alpha2.User) { + expect := user.DeepCopy() + expect.Status.State = iamv1alpha2.UserActive + action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "users"}, "", expect) + f.actions = append(f.actions, action) +} + +func getKey(user *iamv1alpha2.User, t *testing.T) string { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(user) + if err != nil { + t.Errorf("Unexpected error getting key for user %v: %v", user.Name, err) + return "" + } + return key +} + +func TestDoNothing(t *testing.T) { + f := newFixture(t) + user := newUser("test") + + f.userLister = append(f.userLister, user) + f.objects = append(f.objects, user) + + f.expectUpdateUserStatusAction(user) + f.run(getKey(user, t)) +} diff --git a/pkg/controller/user/user_webhook.go b/pkg/controller/user/user_webhook.go new file mode 100644 index 000000000..65c90dba8 --- /dev/null +++ b/pkg/controller/user/user_webhook.go @@ -0,0 +1,120 @@ +/* + * + * Copyright 2020 The KubeSphere 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 user + +import ( + "context" + "encoding/json" + "golang.org/x/crypto/bcrypt" + "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "net/http" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + "strconv" +) + +const ( + encryptedAnnotation = "iam.kubesphere.io/password-encrypted" +) + +type EmailValidator struct { + Client client.Client + decoder *admission.Decoder +} + +type PasswordCipher struct { + Client client.Client + decoder *admission.Decoder +} + +func (a *EmailValidator) Handle(ctx context.Context, req admission.Request) admission.Response { + user := &v1alpha2.User{} + err := a.decoder.Decode(req, user) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + allUsers := v1alpha2.UserList{} + + err = a.Client.List(ctx, &allUsers, &client.ListOptions{}) + + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + alreadyExist := emailAlreadyExist(allUsers, user) + + if alreadyExist { + return admission.Denied("user email already exists") + } + + return admission.Allowed("") +} + +func emailAlreadyExist(users v1alpha2.UserList, user *v1alpha2.User) bool { + for _, exist := range users.Items { + if exist.Spec.Email == user.Spec.Email && exist.Name != user.Name { + return true + } + } + return false +} + +func (a *PasswordCipher) Handle(ctx context.Context, req admission.Request) admission.Response { + user := &v1alpha2.User{} + err := a.decoder.Decode(req, user) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + encrypted, err := strconv.ParseBool(user.Annotations[encryptedAnnotation]) + + if err != nil || !encrypted { + password, err := hashPassword(user.Spec.EncryptedPassword) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + user.Spec.EncryptedPassword = password + user.Annotations[encryptedAnnotation] = "true" + } + + marshaledUser, err := json.Marshal(user) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledUser) +} + +func hashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost) + return string(bytes), err +} + +// InjectDecoder injects the decoder. +func (a *PasswordCipher) InjectDecoder(d *admission.Decoder) error { + a.decoder = d + return nil +} + +// InjectDecoder injects the decoder. +func (a *EmailValidator) InjectDecoder(d *admission.Decoder) error { + a.decoder = d + return nil +} diff --git a/pkg/controller/virtualservice/virtualservice_controller.go b/pkg/controller/virtualservice/virtualservice_controller.go index a47f6925d..3f10b6a24 100644 --- a/pkg/controller/virtualservice/virtualservice_controller.go +++ b/pkg/controller/virtualservice/virtualservice_controller.go @@ -213,7 +213,7 @@ func (v *VirtualServiceController) syncService(key string) error { appName := name defer func() { - log.V(4).Info("Finished syncing service virtualservice.", "namespace", namespace, "name", name, "duration", time.Since(startTime)) + log.V(4).Infof("Finished syncing service virtualservice %s/%s in %s.", namespace, name, time.Since(startTime)) }() service, err := v.serviceLister.Services(namespace).Get(name) diff --git a/pkg/controller/workspace/workspace_controller.go b/pkg/controller/workspace/workspace_controller.go index 620197c38..5792d226e 100644 --- a/pkg/controller/workspace/workspace_controller.go +++ b/pkg/controller/workspace/workspace_controller.go @@ -20,31 +20,17 @@ package workspace import ( "context" - "fmt" - corev1 "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "reflect" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" "sigs.k8s.io/controller-runtime/pkg/source" - "sync" ) const ( @@ -53,13 +39,6 @@ const ( workspaceViewerDescription = "Allows viewer access to view all resources in the workspace." ) -var log = logf.Log.WithName("workspace-controller") - -/** -* USER ACTION REQUIRED: This is a scaffold file intended for the user to modify with their own Controller -* business logic. Delete these comments after modifying this file.* - */ - // Add creates a new Workspace Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller // and Start it when the Manager is Started. func Add(mgr manager.Manager) error { @@ -69,7 +48,7 @@ func Add(mgr manager.Manager) error { // newReconciler returns a new reconcile.Reconciler func newReconciler(mgr manager.Manager) reconcile.Reconciler { return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(), - recorder: mgr.GetEventRecorderFor("workspace-controller"), ksclient: cs.ClientSets().KubeSphere()} + recorder: mgr.GetEventRecorderFor("workspace-controller")} } // add adds a new Controller to mgr with r as the reconcile.Reconciler @@ -96,7 +75,6 @@ type ReconcileWorkspace struct { client.Client scheme *runtime.Scheme recorder record.EventRecorder - ksclient kubesphere.Interface } // Reconcile reads that state of the cluster for a Workspace object and makes changes based on the state read @@ -133,9 +111,6 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res // The object is being deleted if sliceutil.HasString(instance.ObjectMeta.Finalizers, finalizer) { // our finalizer is present, so lets handle our external dependency - if err := r.deleteDevOpsProjects(instance); err != nil { - return reconcile.Result{}, err - } // remove our finalizer from the list and update it. instance.ObjectMeta.Finalizers = sliceutil.RemoveString(instance.ObjectMeta.Finalizers, func(item string) bool { @@ -150,506 +125,5 @@ func (r *ReconcileWorkspace) Reconcile(request reconcile.Request) (reconcile.Res return reconcile.Result{}, nil } - if err = r.createWorkspaceAdmin(instance); err != nil { - return reconcile.Result{}, err - } - - if err = r.createWorkspaceRegular(instance); err != nil { - return reconcile.Result{}, err - } - - if err = r.createWorkspaceViewer(instance); err != nil { - return reconcile.Result{}, err - } - - if err = r.createWorkspaceRoleBindings(instance); err != nil { - return reconcile.Result{}, err - } - - if err = r.bindNamespaces(instance); err != nil { - return reconcile.Result{}, err - } - return reconcile.Result{}, nil } - -func (r *ReconcileWorkspace) createWorkspaceAdmin(instance *tenantv1alpha1.Workspace) error { - found := &rbac.ClusterRole{} - - admin := getWorkspaceAdmin(instance.Name) - - if err := controllerutil.SetControllerReference(instance, admin, r.scheme); err != nil { - return err - } - - err := r.Get(context.TODO(), types.NamespacedName{Name: admin.Name}, found) - - if err != nil && errors.IsNotFound(err) { - log.Info("Creating workspace role", "workspace", instance.Name, "name", admin.Name) - err = r.Create(context.TODO(), admin) - if err != nil { - return err - } - found = admin - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(admin.Rules, found.Rules) || !reflect.DeepEqual(admin.Labels, found.Labels) || !reflect.DeepEqual(admin.Annotations, found.Annotations) { - found.Rules = admin.Rules - found.Labels = admin.Labels - found.Annotations = admin.Annotations - log.Info("Updating workspace role", "workspace", instance.Name, "name", admin.Name) - err = r.Update(context.TODO(), found) - if err != nil { - return err - } - } - return nil -} - -func (r *ReconcileWorkspace) createWorkspaceRegular(instance *tenantv1alpha1.Workspace) error { - found := &rbac.ClusterRole{} - - regular := getWorkspaceRegular(instance.Name) - - if err := controllerutil.SetControllerReference(instance, regular, r.scheme); err != nil { - return err - } - - err := r.Get(context.TODO(), types.NamespacedName{Name: regular.Name}, found) - - if err != nil && errors.IsNotFound(err) { - - log.Info("Creating workspace role", "workspace", instance.Name, "name", regular.Name) - err = r.Create(context.TODO(), regular) - // Error reading the object - requeue the request. - if err != nil { - return err - } - found = regular - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(regular.Rules, found.Rules) || !reflect.DeepEqual(regular.Labels, found.Labels) || !reflect.DeepEqual(regular.Annotations, found.Annotations) { - found.Rules = regular.Rules - found.Labels = regular.Labels - found.Annotations = regular.Annotations - log.Info("Updating workspace role", "workspace", instance.Name, "name", regular.Name) - err = r.Update(context.TODO(), found) - if err != nil { - return err - } - } - - return nil -} - -func (r *ReconcileWorkspace) createWorkspaceViewer(instance *tenantv1alpha1.Workspace) error { - found := &rbac.ClusterRole{} - - viewer := getWorkspaceViewer(instance.Name) - - if err := controllerutil.SetControllerReference(instance, viewer, r.scheme); err != nil { - return err - } - - err := r.Get(context.TODO(), types.NamespacedName{Name: viewer.Name}, found) - - if err != nil && errors.IsNotFound(err) { - log.Info("Creating workspace role", "workspace", instance.Name, "name", viewer.Name) - err = r.Create(context.TODO(), viewer) - // Error reading the object - requeue the request. - if err != nil { - return err - } - found = viewer - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(viewer.Rules, found.Rules) || !reflect.DeepEqual(viewer.Labels, found.Labels) || !reflect.DeepEqual(viewer.Annotations, found.Annotations) { - found.Rules = viewer.Rules - found.Labels = viewer.Labels - found.Annotations = viewer.Annotations - log.Info("Updating workspace role", "workspace", instance.Name, "name", viewer.Name) - err = r.Update(context.TODO(), found) - if err != nil { - return err - } - } - - return nil -} - -func (r *ReconcileWorkspace) createGroup(instance *tenantv1alpha1.Workspace) error { - _, err := r.ksclient.DescribeGroup(instance.Name) - - group := &models.Group{ - Name: instance.Name, - } - - if err != nil && kubesphere.IsNotFound(err) { - log.Info("Creating group", "group name", instance.Name) - _, err = r.ksclient.CreateGroup(group) - if err != nil { - if kubesphere.IsExist(err) { - return nil - } - return err - } - } else if err != nil { - return err - } - - return nil -} - -func (r *ReconcileWorkspace) deleteGroup(instance *tenantv1alpha1.Workspace) error { - log.Info("Creating group", "group name", instance.Name) - if err := r.ksclient.DeleteGroup(instance.Name); err != nil { - if kubesphere.IsNotFound(err) { - return nil - } - return err - } - return nil -} - -func (r *ReconcileWorkspace) deleteDevOpsProjects(instance *tenantv1alpha1.Workspace) error { - if _, err := cs.ClientSets().Devops(); err != nil { - // skip if devops is not enabled - if _, notEnabled := err.(cs.ClientSetNotEnabledError); notEnabled { - return nil - } else { - log.Error(err, "") - return err - } - } - var wg sync.WaitGroup - - log.Info("Delete DevOps Projects") - for { - errChan := make(chan error, 10) - projects, err := r.ksclient.ListWorkspaceDevOpsProjects(instance.Name) - if err != nil { - log.Error(err, "Failed to Get Workspace's DevOps Projects", "ws", instance.Name) - return err - } - if projects.TotalCount == 0 { - return nil - } - for _, project := range projects.Items { - wg.Add(1) - go func(workspace, devops string) { - err := r.ksclient.DeleteWorkspaceDevOpsProjects(workspace, devops) - errChan <- err - wg.Done() - }(instance.Name, project.ProjectId) - } - wg.Wait() - close(errChan) - for err := range errChan { - if err != nil { - log.Error(err, "delete devops project error") - return err - } - } - - } -} - -func (r *ReconcileWorkspace) createWorkspaceRoleBindings(instance *tenantv1alpha1.Workspace) error { - - adminRoleBinding := &rbac.ClusterRoleBinding{} - adminRoleBinding.Name = getWorkspaceAdminRoleBindingName(instance.Name) - adminRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name} - adminRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceAdminRoleName(instance.Name)} - - workspaceManager := rbac.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: instance.Spec.Manager} - - if workspaceManager.Name != "" { - adminRoleBinding.Subjects = []rbac.Subject{workspaceManager} - } else { - adminRoleBinding.Subjects = []rbac.Subject{} - } - - if err := controllerutil.SetControllerReference(instance, adminRoleBinding, r.scheme); err != nil { - return err - } - - foundRoleBinding := &rbac.ClusterRoleBinding{} - - err := r.Get(context.TODO(), types.NamespacedName{Name: adminRoleBinding.Name}, foundRoleBinding) - - if err != nil && errors.IsNotFound(err) { - log.Info("Creating workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name) - err = r.Create(context.TODO(), adminRoleBinding) - // Error reading the object - requeue the request. - if err != nil { - return err - } - foundRoleBinding = adminRoleBinding - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(adminRoleBinding.RoleRef, foundRoleBinding.RoleRef) { - log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name) - err = r.Delete(context.TODO(), foundRoleBinding) - if err != nil { - return err - } - return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name) - } - - if workspaceManager.Name != "" && !hasSubject(foundRoleBinding.Subjects, workspaceManager) { - foundRoleBinding.Subjects = append(foundRoleBinding.Subjects, workspaceManager) - log.Info("Updating workspace role binding", "workspace", instance.Name, "name", adminRoleBinding.Name) - err = r.Update(context.TODO(), foundRoleBinding) - if err != nil { - return err - } - } - - regularRoleBinding := &rbac.ClusterRoleBinding{} - regularRoleBinding.Name = getWorkspaceRegularRoleBindingName(instance.Name) - regularRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name} - regularRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceRegularRoleName(instance.Name)} - regularRoleBinding.Subjects = []rbac.Subject{} - - if err = controllerutil.SetControllerReference(instance, regularRoleBinding, r.scheme); err != nil { - return err - } - - err = r.Get(context.TODO(), types.NamespacedName{Name: regularRoleBinding.Name}, foundRoleBinding) - - if err != nil && errors.IsNotFound(err) { - log.Info("Creating workspace role binding", "workspace", instance.Name, "name", regularRoleBinding.Name) - err = r.Create(context.TODO(), regularRoleBinding) - // Error reading the object - requeue the request. - if err != nil { - return err - } - foundRoleBinding = regularRoleBinding - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(regularRoleBinding.RoleRef, foundRoleBinding.RoleRef) { - log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", regularRoleBinding.Name) - err = r.Delete(context.TODO(), foundRoleBinding) - if err != nil { - return err - } - return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name) - } - - viewerRoleBinding := &rbac.ClusterRoleBinding{} - viewerRoleBinding.Name = getWorkspaceViewerRoleBindingName(instance.Name) - viewerRoleBinding.Labels = map[string]string{constants.WorkspaceLabelKey: instance.Name} - viewerRoleBinding.RoleRef = rbac.RoleRef{APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", Name: getWorkspaceViewerRoleName(instance.Name)} - viewerRoleBinding.Subjects = []rbac.Subject{} - - if err = controllerutil.SetControllerReference(instance, viewerRoleBinding, r.scheme); err != nil { - return err - } - - err = r.Get(context.TODO(), types.NamespacedName{Name: viewerRoleBinding.Name}, foundRoleBinding) - - if err != nil && errors.IsNotFound(err) { - log.Info("Creating workspace role binding", "workspace", instance.Name, "name", viewerRoleBinding.Name) - err = r.Create(context.TODO(), viewerRoleBinding) - // Error reading the object - requeue the request. - if err != nil { - return err - } - foundRoleBinding = viewerRoleBinding - } else if err != nil { - // Error reading the object - requeue the request. - return err - } - - // Update the found object and write the result back if there are any changes - if !reflect.DeepEqual(viewerRoleBinding.RoleRef, foundRoleBinding.RoleRef) { - log.Info("Deleting conflict workspace role binding", "workspace", instance.Name, "name", viewerRoleBinding.Name) - err = r.Delete(context.TODO(), foundRoleBinding) - if err != nil { - return err - } - return fmt.Errorf("conflict workspace role binding %s, waiting for recreate", foundRoleBinding.Name) - } - - return nil -} - -func (r *ReconcileWorkspace) bindNamespaces(instance *tenantv1alpha1.Workspace) error { - - nsList := &corev1.NamespaceList{} - options := client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: instance.Name})} - err := r.List(context.TODO(), nsList, &options) - - if err != nil { - log.Error(err, fmt.Sprintf("list workspace %s namespace failed", instance.Name)) - return err - } - - for _, namespace := range nsList.Items { - if !metav1.IsControlledBy(&namespace, instance) { - if err := controllerutil.SetControllerReference(instance, &namespace, r.scheme); err != nil { - return err - } - log.Info("Bind workspace", "namespace", namespace.Name, "workspace", instance.Name) - err = r.Update(context.TODO(), &namespace) - if err != nil { - return err - } - } - } - - return nil -} - -func hasSubject(subjects []rbac.Subject, user rbac.Subject) bool { - for _, subject := range subjects { - if reflect.DeepEqual(subject, user) { - return true - } - } - return false -} - -func getWorkspaceAdminRoleName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:admin", workspaceName) -} -func getWorkspaceRegularRoleName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:regular", workspaceName) -} -func getWorkspaceViewerRoleName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:viewer", workspaceName) -} - -func getWorkspaceAdminRoleBindingName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:admin", workspaceName) -} - -func getWorkspaceRegularRoleBindingName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:regular", workspaceName) -} - -func getWorkspaceViewerRoleBindingName(workspaceName string) string { - return fmt.Sprintf("workspace:%s:viewer", workspaceName) -} - -func getWorkspaceAdmin(workspaceName string) *rbac.ClusterRole { - admin := &rbac.ClusterRole{} - admin.Name = getWorkspaceAdminRoleName(workspaceName) - admin.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName} - admin.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceAdmin, constants.DescriptionAnnotationKey: workspaceAdminDescription, constants.CreatorAnnotationKey: constants.System} - admin.Rules = []rbac.PolicyRule{ - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - ResourceNames: []string{workspaceName}, - Resources: []string{"workspaces", "workspaces/*"}, - }, - { - Verbs: []string{"watch"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"categories"}, - }, - { - Verbs: []string{"*"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"applications", "apps", "apps/versions", "apps/events", "apps/action", "apps/audits", "repos", "repos/action", "attachments"}, - }, - } - - return admin -} - -func getWorkspaceRegular(workspaceName string) *rbac.ClusterRole { - regular := &rbac.ClusterRole{} - regular.Name = getWorkspaceRegularRoleName(workspaceName) - regular.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName} - regular.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceRegular, constants.DescriptionAnnotationKey: workspaceRegularDescription, constants.CreatorAnnotationKey: constants.System} - regular.Rules = []rbac.PolicyRule{ - { - Verbs: []string{"get"}, - APIGroups: []string{"*"}, - Resources: []string{"workspaces"}, - ResourceNames: []string{workspaceName}, - }, { - Verbs: []string{"create"}, - APIGroups: []string{"tenant.kubesphere.io"}, - Resources: []string{"workspaces/namespaces", "workspaces/devops"}, - ResourceNames: []string{workspaceName}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{"iam.kubesphere.io"}, - ResourceNames: []string{workspaceName}, - Resources: []string{"workspaces/members"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"apps/events", "apps/action", "apps/audits", "categories"}, - }, - - { - Verbs: []string{"*"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"applications", "apps", "apps/versions", "repos", "repos/action", "attachments"}, - }, - } - - return regular -} - -func getWorkspaceViewer(workspaceName string) *rbac.ClusterRole { - viewer := &rbac.ClusterRole{} - viewer.Name = getWorkspaceViewerRoleName(workspaceName) - viewer.Labels = map[string]string{constants.WorkspaceLabelKey: workspaceName} - viewer.Annotations = map[string]string{constants.DisplayNameAnnotationKey: constants.WorkspaceViewer, constants.DescriptionAnnotationKey: workspaceViewerDescription, constants.CreatorAnnotationKey: constants.System} - viewer.Rules = []rbac.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"*"}, - ResourceNames: []string{workspaceName}, - Resources: []string{"workspaces", "workspaces/*"}, - }, - { - Verbs: []string{"watch"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"applications", "apps", "apps/events", "apps/action", "apps/audits", "apps/versions", "repos", "categories", "attachments"}, - }, - } - return viewer -} diff --git a/pkg/gojenkins/_tests/build_history.txt b/pkg/gojenkins/_tests/build_history.txt deleted file mode 100644 index b9c777d37..000000000 --- a/pkg/gojenkins/_tests/build_history.txt +++ /dev/null @@ -1,4 +0,0 @@ - - - -
\ No newline at end of file diff --git a/pkg/gojenkins/_tests/job.xml b/pkg/gojenkins/_tests/job.xml deleted file mode 100644 index 76a65ec25..000000000 --- a/pkg/gojenkins/_tests/job.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - Some Job Description - false - - - - - params1 - description - defaultVal - - - - - - true - false - false - false - - false - - - - diff --git a/pkg/gojenkins/artifact.go b/pkg/gojenkins/artifact.go deleted file mode 100644 index 364940656..000000000 --- a/pkg/gojenkins/artifact.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "crypto/md5" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path" -) - -// Represents an Artifact -type Artifact struct { - Jenkins *Jenkins - Build *Build - FileName string - Path string -} - -// Get raw byte data of Artifact -func (a Artifact) GetData() ([]byte, error) { - var data string - response, err := a.Jenkins.Requester.Get(a.Path, &data, nil) - - if err != nil { - return nil, err - } - - code := response.StatusCode - if code != 200 { - Error.Printf("Jenkins responded with StatusCode: %d", code) - return nil, errors.New("Could not get File Contents") - } - return []byte(data), nil -} - -// Save artifact to a specific path, using your own filename. -func (a Artifact) Save(path string) (bool, error) { - data, err := a.GetData() - - if err != nil { - return false, errors.New("No Data received, not saving file.") - } - - if _, err = os.Stat(path); err == nil { - Warning.Println("Local Copy already exists, Overwriting...") - } - - err = ioutil.WriteFile(path, data, 0644) - a.validateDownload(path) - - if err != nil { - return false, err - } - return true, nil -} - -// Save Artifact to directory using Artifact filename. -func (a Artifact) SaveToDir(dir string) (bool, error) { - if _, err := os.Stat(dir); err != nil { - Error.Printf("can't save artifact: directory %s does not exist", dir) - return false, fmt.Errorf("can't save artifact: directory %s does not exist", dir) - } - saved, err := a.Save(path.Join(dir, a.FileName)) - if err != nil { - return saved, nil - } - return saved, nil -} - -// Compare Remote and local MD5 -func (a Artifact) validateDownload(path string) (bool, error) { - localHash := a.getMD5local(path) - - fp := FingerPrint{Jenkins: a.Jenkins, Base: "/fingerprint/", Id: localHash, Raw: new(FingerPrintResponse)} - - valid, err := fp.ValidateForBuild(a.FileName, a.Build) - - if err != nil { - return false, err - } - if !valid { - return false, errors.New("FingerPrint of the downloaded artifact could not be verified") - } - return true, nil -} - -// Get Local MD5 -func (a Artifact) getMD5local(path string) string { - h := md5.New() - localFile, err := os.Open(path) - if err != nil { - return "" - } - buffer := make([]byte, 2^20) - n, err := localFile.Read(buffer) - defer localFile.Close() - for err == nil { - io.WriteString(h, string(buffer[0:n])) - n, err = localFile.Read(buffer) - } - return fmt.Sprintf("%x", h.Sum(nil)) -} diff --git a/pkg/gojenkins/build.go b/pkg/gojenkins/build.go deleted file mode 100644 index 0d8af0a5e..000000000 --- a/pkg/gojenkins/build.go +++ /dev/null @@ -1,486 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "bytes" - "errors" - "net/http" - "net/url" - "regexp" - "strconv" - "time" -) - -const ( - Git = "git" - Hg = "hg" - Svn = "svc" -) - -type Build struct { - Raw *BuildResponse - Job *Job - Jenkins *Jenkins - Base string - Depth int -} - -type parameter struct { - Name string - Value string -} - -type branch struct { - SHA1 string `json:",omitempty"` - Name string `json:",omitempty"` -} - -type BuildRevision struct { - SHA1 string `json:"SHA1,omitempty"` - Branch []branch `json:"branch,omitempty"` -} - -type Builds struct { - BuildNumber int64 `json:"buildNumber"` - BuildResult interface{} `json:"buildResult"` - Marked BuildRevision `json:"marked"` - Revision BuildRevision `json:"revision"` -} - -type Culprit struct { - AbsoluteUrl string - FullName string -} - -type GeneralObj struct { - Parameters []parameter `json:"parameters,omitempty"` - Causes []map[string]interface{} `json:"causes,omitempty"` - BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"` - LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"` - RemoteUrls []string `json:"remoteUrls,omitempty"` - ScmName string `json:"scmName,omitempty"` - MercurialNodeName string `json:"mercurialNodeName,omitempty"` - MercurialRevisionNumber string `json:"mercurialRevisionNumber,omitempty"` - Subdir interface{} `json:"subdir,omitempty"` - ClassName string `json:"_class,omitempty"` - SonarTaskId string `json:"ceTaskId,omitempty"` - SonarServerUrl string `json:"serverUrl,omitempty"` - SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"` - TotalCount int64 `json:",omitempty"` - UrlName string `json:",omitempty"` -} - -type TestResult struct { - Duration int64 `json:"duration"` - Empty bool `json:"empty"` - FailCount int64 `json:"failCount"` - PassCount int64 `json:"passCount"` - SkipCount int64 `json:"skipCount"` - Suites []struct { - Cases []struct { - Age int64 `json:"age"` - ClassName string `json:"className"` - Duration int64 `json:"duration"` - ErrorDetails interface{} `json:"errorDetails"` - ErrorStackTrace interface{} `json:"errorStackTrace"` - FailedSince int64 `json:"failedSince"` - Name string `json:"name"` - Skipped bool `json:"skipped"` - SkippedMessage interface{} `json:"skippedMessage"` - Status string `json:"status"` - Stderr interface{} `json:"stderr"` - Stdout interface{} `json:"stdout"` - } `json:"cases"` - Duration int64 `json:"duration"` - ID interface{} `json:"id"` - Name string `json:"name"` - Stderr interface{} `json:"stderr"` - Stdout interface{} `json:"stdout"` - Timestamp interface{} `json:"timestamp"` - } `json:"suites"` -} - -type BuildResponse struct { - Actions []GeneralObj - Artifacts []struct { - DisplayPath string `json:"displayPath"` - FileName string `json:"fileName"` - RelativePath string `json:"relativePath"` - } `json:"artifacts"` - Building bool `json:"building"` - BuiltOn string `json:"builtOn"` - ChangeSet struct { - Items []struct { - AffectedPaths []string `json:"affectedPaths"` - Author struct { - AbsoluteUrl string `json:"absoluteUrl"` - FullName string `json:"fullName"` - } `json:"author"` - Comment string `json:"comment"` - CommitID string `json:"commitId"` - Date string `json:"date"` - ID string `json:"id"` - Msg string `json:"msg"` - Paths []struct { - EditType string `json:"editType"` - File string `json:"file"` - } `json:"paths"` - Timestamp int64 `json:"timestamp"` - } `json:"items"` - Kind string `json:"kind"` - Revisions []struct { - Module string - Revision int - } `json:"revision"` - } `json:"changeSet"` - Culprits []Culprit `json:"culprits"` - Description interface{} `json:"description"` - Duration int64 `json:"duration"` - EstimatedDuration int64 `json:"estimatedDuration"` - Executor interface{} `json:"executor"` - FullDisplayName string `json:"fullDisplayName"` - ID string `json:"id"` - KeepLog bool `json:"keepLog"` - Number int64 `json:"number"` - QueueID int64 `json:"queueId"` - Result string `json:"result"` - Timestamp int64 `json:"timestamp"` - URL string `json:"url"` - MavenArtifacts interface{} `json:"mavenArtifacts"` - MavenVersionUsed string `json:"mavenVersionUsed"` - FingerPrint []FingerPrintResponse - Runs []struct { - Number int64 - URL string - } `json:"runs"` -} - -// Builds -func (b *Build) Info() *BuildResponse { - return b.Raw -} - -func (b *Build) GetActions() []GeneralObj { - return b.Raw.Actions -} - -func (b *Build) GetUrl() string { - return b.Raw.URL -} - -func (b *Build) GetBuildNumber() int64 { - return b.Raw.Number -} -func (b *Build) GetResult() string { - return b.Raw.Result -} - -func (b *Build) GetArtifacts() []Artifact { - artifacts := make([]Artifact, len(b.Raw.Artifacts)) - for i, artifact := range b.Raw.Artifacts { - artifacts[i] = Artifact{ - Jenkins: b.Jenkins, - Build: b, - FileName: artifact.FileName, - Path: b.Base + "/artifact/" + artifact.RelativePath, - } - } - return artifacts -} - -func (b *Build) GetCulprits() []Culprit { - return b.Raw.Culprits -} - -func (b *Build) Stop() (bool, error) { - if b.IsRunning() { - response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil) - if err != nil { - return false, err - } - if response.StatusCode != http.StatusOK { - return false, errors.New(strconv.Itoa(response.StatusCode)) - } - } - return true, nil -} - -func (b *Build) GetConsoleOutput() string { - url := b.Base + "/consoleText" - var content string - b.Jenkins.Requester.GetXML(url, &content, nil) - return content -} - -func (b *Build) GetCauses() ([]map[string]interface{}, error) { - _, err := b.Poll() - if err != nil { - return nil, err - } - for _, a := range b.Raw.Actions { - if a.Causes != nil { - return a.Causes, nil - } - } - return nil, errors.New("No Causes") -} - -func (b *Build) GetParameters() []parameter { - for _, a := range b.Raw.Actions { - if a.Parameters != nil { - return a.Parameters - } - } - return nil -} - -func (b *Build) GetInjectedEnvVars() (map[string]string, error) { - var envVars struct { - EnvMap map[string]string `json:"envMap"` - } - endpoint := b.Base + "/injectedEnvVars" - _, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil) - if err != nil { - return envVars.EnvMap, err - } - return envVars.EnvMap, nil -} - -func (b *Build) GetDownstreamBuilds() ([]*Build, error) { - result := make([]*Build, 0) - downstreamJobs, err := b.Job.GetDownstreamJobs() - if err != nil { - return nil, err - } - for _, job := range downstreamJobs { - allBuildIDs, err := job.GetAllBuildIds() - if err != nil { - return nil, err - } - for _, buildID := range allBuildIDs { - build, err := job.GetBuild(buildID.Number) - if err != nil { - return nil, err - } - upstreamBuild, _ := build.GetUpstreamBuild() - // cannot compare only id, it can be from different job - if b.GetUrl() == upstreamBuild.GetUrl() { - result = append(result, build) - break - } - } - } - return result, nil -} - -func (b *Build) GetDownstreamJobNames() []string { - result := make([]string, 0) - downstreamJobs := b.Job.GetDownstreamJobsMetadata() - fingerprints := b.GetAllFingerPrints() - for _, fingerprint := range fingerprints { - for _, usage := range fingerprint.Raw.Usage { - for _, job := range downstreamJobs { - if job.Name == usage.Name { - result = append(result, job.Name) - } - } - } - } - return result -} - -func (b *Build) GetAllFingerPrints() []*FingerPrint { - b.Poll(3) - result := make([]*FingerPrint, len(b.Raw.FingerPrint)) - for i, f := range b.Raw.FingerPrint { - result[i] = &FingerPrint{Jenkins: b.Jenkins, Base: "/fingerprint/", Id: f.Hash, Raw: &f} - } - return result -} - -func (b *Build) GetUpstreamJob() (*Job, error) { - causes, err := b.GetCauses() - if err != nil { - return nil, err - } - if len(causes) > 0 { - if job, ok := causes[0]["upstreamProject"]; ok { - return b.Jenkins.GetJob(job.(string)) - } - } - return nil, errors.New("Unable to get Upstream Job") -} - -func (b *Build) GetUpstreamBuildNumber() (int64, error) { - causes, err := b.GetCauses() - if err != nil { - return 0, err - } - if len(causes) > 0 { - if build, ok := causes[0]["upstreamBuild"]; ok { - switch t := build.(type) { - default: - return t.(int64), nil - case float64: - return int64(t), nil - } - } - } - return 0, nil -} - -func (b *Build) GetUpstreamBuild() (*Build, error) { - job, err := b.GetUpstreamJob() - if err != nil { - return nil, err - } - if job != nil { - buildNumber, err := b.GetUpstreamBuildNumber() - if err == nil { - return job.GetBuild(buildNumber) - } - } - return nil, errors.New("Build not found") -} - -func (b *Build) GetMatrixRuns() ([]*Build, error) { - _, err := b.Poll(0) - if err != nil { - return nil, err - } - runs := b.Raw.Runs - result := make([]*Build, len(b.Raw.Runs)) - r, _ := regexp.Compile(`job/(.*?)/(.*?)/(\d+)/`) - - for i, run := range runs { - result[i] = &Build{Jenkins: b.Jenkins, Job: b.Job, Raw: new(BuildResponse), Depth: 1, Base: "/" + r.FindString(run.URL)} - result[i].Poll() - } - return result, nil -} - -func (b *Build) GetResultSet() (*TestResult, error) { - - url := b.Base + "/testReport" - var report TestResult - - _, err := b.Jenkins.Requester.GetJSON(url, &report, nil) - if err != nil { - return nil, err - } - - return &report, nil - -} - -func (b *Build) GetTimestamp() time.Time { - msInt := int64(b.Raw.Timestamp) - return time.Unix(0, msInt*int64(time.Millisecond)) -} - -func (b *Build) GetDuration() int64 { - return b.Raw.Duration -} - -func (b *Build) GetRevision() string { - vcs := b.Raw.ChangeSet.Kind - - if vcs == Git || vcs == Hg { - for _, a := range b.Raw.Actions { - if a.LastBuiltRevision.SHA1 != "" { - return a.LastBuiltRevision.SHA1 - } - if a.MercurialRevisionNumber != "" { - return a.MercurialRevisionNumber - } - } - } else if vcs == Svn { - return strconv.Itoa(b.Raw.ChangeSet.Revisions[0].Revision) - } - return "" -} - -func (b *Build) GetRevisionBranch() string { - vcs := b.Raw.ChangeSet.Kind - if vcs == Git { - for _, a := range b.Raw.Actions { - if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" { - return a.LastBuiltRevision.Branch[0].SHA1 - } - } - } else { - panic("Not implemented") - } - return "" -} - -func (b *Build) IsGood() bool { - return (!b.IsRunning() && b.Raw.Result == STATUS_SUCCESS) -} - -func (b *Build) IsRunning() bool { - _, err := b.Poll() - if err != nil { - return false - } - return b.Raw.Building -} - -func (b *Build) SetDescription(description string) error { - data := url.Values{} - data.Set("description", description) - _, err := b.Jenkins.Requester.Post(b.Base+"/submitDescription", bytes.NewBufferString(data.Encode()), nil, nil) - return err -} -func (b *Build) PauseToggle() error { - response, err := b.Jenkins.Requester.Post(b.Base+"/pause/toggle", nil, nil, nil) - if err != nil { - return err - } - if response.StatusCode != http.StatusOK { - return errors.New(strconv.Itoa(response.StatusCode)) - } - return nil -} - -// Poll for current data. Optional parameter - depth. -// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API -func (b *Build) Poll(options ...interface{}) (int, error) { - depth := "-1" - - for _, o := range options { - switch v := o.(type) { - case string: - depth = v - case int: - depth = strconv.Itoa(v) - case int64: - depth = strconv.FormatInt(v, 10) - } - } - if depth == "-1" { - depth = strconv.Itoa(b.Depth) - } - - qr := map[string]string{ - "depth": depth, - } - response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/gojenkins/build_history.go b/pkg/gojenkins/build_history.go deleted file mode 100644 index 2e7cae117..000000000 --- a/pkg/gojenkins/build_history.go +++ /dev/null @@ -1,109 +0,0 @@ -package gojenkins - -import ( - "io" - "strconv" - "strings" - - "golang.org/x/net/html" -) - -// Parse jenkins ajax response in order find the current jenkins build history -func parseBuildHistory(d io.Reader) []*History { - z := html.NewTokenizer(d) - depth := 0 - buildRowCellDepth := -1 - builds := make([]*History, 0) - var curBuild *History - for { - tt := z.Next() - switch tt { - case html.ErrorToken: - if z.Err() == io.EOF { - return builds - } - case html.SelfClosingTagToken: - tn, hasAttr := z.TagName() - // fmt.Println("START__", string(tn), hasAttr) - if hasAttr { - a := attr(z) - // Failed > Console Output - if string(tn) == "img" { - if hasCSSClass(a, "icon-sm") && buildRowCellDepth > -1 { - if alt, found := a["alt"]; found { - curBuild.BuildStatus = strings.Fields(alt)[0] - } - } - } - } - case html.StartTagToken: - depth++ - tn, hasAttr := z.TagName() - // fmt.Println("START__", string(tn), hasAttr) - if hasAttr { - a := attr(z) - // - if string(tn) == "td" { - if hasCSSClass(a, "build-row-cell") { - buildRowCellDepth = depth - curBuild = &History{} - builds = append(builds, curBuild) - } - } - // #227 - if string(tn) == "a" { - if hasCSSClass(a, "build-link") && buildRowCellDepth > -1 { - if href, found := a["href"]; found { - parts := strings.Split(href, "/") - if num, err := strconv.Atoi(parts[len(parts)-2]); err == nil { - curBuild.BuildNumber = num - } - } - } - } - //
...
- if string(tn) == "div" { - if hasCSSClass(a, "build-details") && buildRowCellDepth > -1 { - if t, found := a["time"]; found { - if msec, err := strconv.ParseInt(t, 10, 0); err == nil { - curBuild.BuildTimestamp = msec / 1000 - } - } - } - } - } - case html.EndTagToken: - tn, _ := z.TagName() - if string(tn) == "td" && depth == buildRowCellDepth { - buildRowCellDepth = -1 - curBuild = nil - } - depth-- - } - } -} - -func attr(z *html.Tokenizer) map[string]string { - a := make(map[string]string) - for { - k, v, more := z.TagAttr() - if k != nil && v != nil { - a[string(k)] = string(v) - } - if !more { - break - } - } - return a -} - -func hasCSSClass(a map[string]string, className string) bool { - if classes, found := a["class"]; found { - for _, class := range strings.Fields(classes) { - if class == className { - return true - } - } - } - return false -} diff --git a/pkg/gojenkins/constants.go b/pkg/gojenkins/constants.go deleted file mode 100644 index 1876fb958..000000000 --- a/pkg/gojenkins/constants.go +++ /dev/null @@ -1,20 +0,0 @@ -package gojenkins - -const ( - STATUS_FAIL = "FAIL" - STATUS_ERROR = "ERROR" - STATUS_ABORTED = "ABORTED" - STATUS_REGRESSION = "REGRESSION" - STATUS_SUCCESS = "SUCCESS" - STATUS_FIXED = "FIXED" - STATUS_PASSED = "PASSED" - RESULT_STATUS_FAILURE = "FAILURE" - RESULT_STATUS_FAILED = "FAILED" - RESULT_STATUS_SKIPPED = "SKIPPED" - STR_RE_SPLIT_VIEW = "(.*)/view/([^/]*)/?" -) - -const ( - GLOBAL_ROLE = "globalRoles" - PROJECT_ROLE = "projectRoles" -) diff --git a/pkg/gojenkins/credential.go b/pkg/gojenkins/credential.go deleted file mode 100644 index da41bb908..000000000 --- a/pkg/gojenkins/credential.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 gojenkins - -const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey" -const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource" -const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" -const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" -const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials" -const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource" -const GLOBALScope = "GLOBAL" - -type CreateSshCredentialRequest struct { - Credentials SshCredential `json:"credentials"` -} - -type CreateUsernamePasswordCredentialRequest struct { - Credentials UsernamePasswordCredential `json:"credentials"` -} - -type CreateSecretTextCredentialRequest struct { - Credentials SecretTextCredential `json:"credentials"` -} - -type CreateKubeconfigCredentialRequest struct { - Credentials KubeconfigCredential `json:"credentials"` -} - -type UsernamePasswordCredential struct { - Scope string `json:"scope"` - Id string `json:"id"` - Username string `json:"username"` - Password string `json:"password"` - Description string `json:"description"` - StaplerClass string `json:"stapler-class"` -} - -type SshCredential struct { - Scope string `json:"scope"` - Id string `json:"id"` - Username string `json:"username"` - Passphrase string `json:"passphrase"` - KeySource PrivateKeySource `json:"privateKeySource"` - Description string `json:"description"` - StaplerClass string `json:"stapler-class"` -} - -type SecretTextCredential struct { - Scope string `json:"scope"` - Id string `json:"id"` - Secret string `json:"secret"` - Description string `json:"description"` - StaplerClass string `json:"stapler-class"` -} - -type KubeconfigCredential struct { - Scope string `json:"scope"` - Id string `json:"id"` - Description string `json:"description"` - KubeconfigSource KubeconfigSource `json:"kubeconfigSource"` - StaplerClass string `json:"stapler-class"` -} - -type PrivateKeySource struct { - StaplerClass string `json:"stapler-class"` - PrivateKey string `json:"privateKey"` -} - -type KubeconfigSource struct { - StaplerClass string `json:"stapler-class"` - Content string `json:"content"` -} - -type CredentialResponse struct { - Id string `json:"id"` - TypeName string `json:"typeName"` - DisplayName string `json:"displayName"` - Fingerprint *struct { - FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` - Hash string `json:"hash,omitempty" description:"Credential's hash"` - Usage []*struct { - Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` - Ranges struct { - Ranges []*struct { - Start int `json:"start,omitempty" description:"Start build number"` - End int `json:"end,omitempty" description:"End build number"` - } `json:"ranges,omitempty"` - } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` - } `json:"usage,omitempty" description:"all usage of Credential"` - } `json:"fingerprint,omitempty" description:"usage of the Credential"` - Description string `json:"description,omitempty"` - Domain string `json:"domain"` -} - -func NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description string) *CreateSshCredentialRequest { - - keySource := PrivateKeySource{ - StaplerClass: DirectSSHCrenditalStaplerClass, - PrivateKey: privateKey, - } - - sshCredential := SshCredential{ - Scope: GLOBALScope, - Id: id, - Username: username, - Passphrase: passphrase, - KeySource: keySource, - Description: description, - StaplerClass: SSHCrenditalStaplerClass, - } - return &CreateSshCredentialRequest{ - Credentials: sshCredential, - } - -} - -func NewCreateUsernamePasswordRequest(id, username, password, description string) *CreateUsernamePasswordCredentialRequest { - credential := UsernamePasswordCredential{ - Scope: GLOBALScope, - Id: id, - Username: username, - Password: password, - Description: description, - StaplerClass: UsernamePassswordCredentialStaplerClass, - } - return &CreateUsernamePasswordCredentialRequest{ - Credentials: credential, - } -} - -func NewCreateSecretTextCredentialRequest(id, secret, description string) *CreateSecretTextCredentialRequest { - credential := SecretTextCredential{ - Scope: GLOBALScope, - Id: id, - Secret: secret, - Description: description, - StaplerClass: SecretTextCredentialStaplerClass, - } - return &CreateSecretTextCredentialRequest{ - Credentials: credential, - } -} - -func NewCreateKubeconfigCredentialRequest(id, content, description string) *CreateKubeconfigCredentialRequest { - - credentialSource := KubeconfigSource{ - StaplerClass: DirectKubeconfigCredentialStaperClass, - Content: content, - } - - credential := KubeconfigCredential{ - Scope: GLOBALScope, - Id: id, - Description: description, - KubeconfigSource: credentialSource, - StaplerClass: KubeconfigCredentialStaplerClass, - } - return &CreateKubeconfigCredentialRequest{ - credential, - } -} - -func NewSshCredential(id, username, passphrase, privateKey, description string) *SshCredential { - keySource := PrivateKeySource{ - StaplerClass: DirectSSHCrenditalStaplerClass, - PrivateKey: privateKey, - } - - return &SshCredential{ - Scope: GLOBALScope, - Id: id, - Username: username, - Passphrase: passphrase, - KeySource: keySource, - Description: description, - StaplerClass: SSHCrenditalStaplerClass, - } -} - -func NewUsernamePasswordCredential(id, username, password, description string) *UsernamePasswordCredential { - return &UsernamePasswordCredential{ - Scope: GLOBALScope, - Id: id, - Username: username, - Password: password, - Description: description, - StaplerClass: UsernamePassswordCredentialStaplerClass, - } -} - -func NewSecretTextCredential(id, secret, description string) *SecretTextCredential { - return &SecretTextCredential{ - Scope: GLOBALScope, - Id: id, - Secret: secret, - Description: description, - StaplerClass: SecretTextCredentialStaplerClass, - } -} - -func NewKubeconfigCredential(id, content, description string) *KubeconfigCredential { - credentialSource := KubeconfigSource{ - StaplerClass: DirectKubeconfigCredentialStaperClass, - Content: content, - } - - return &KubeconfigCredential{ - Scope: GLOBALScope, - Id: id, - Description: description, - KubeconfigSource: credentialSource, - StaplerClass: KubeconfigCredentialStaplerClass, - } -} diff --git a/pkg/gojenkins/executor.go b/pkg/gojenkins/executor.go deleted file mode 100644 index e5ba6f6c2..000000000 --- a/pkg/gojenkins/executor.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -type Executor struct { - Raw *ExecutorResponse - Jenkins *Jenkins -} -type ViewData struct { - Name string `json:"name"` - URL string `json:"url"` -} -type ExecutorResponse struct { - AssignedLabels []struct{} `json:"assignedLabels"` - Description interface{} `json:"description"` - Jobs []InnerJob `json:"jobs"` - Mode string `json:"mode"` - NodeDescription string `json:"nodeDescription"` - NodeName string `json:"nodeName"` - NumExecutors int64 `json:"numExecutors"` - OverallLoad struct{} `json:"overallLoad"` - PrimaryView struct { - Name string `json:"name"` - URL string `json:"url"` - } `json:"primaryView"` - QuietingDown bool `json:"quietingDown"` - SlaveAgentPort int64 `json:"slaveAgentPort"` - UnlabeledLoad struct{} `json:"unlabeledLoad"` - UseCrumbs bool `json:"useCrumbs"` - UseSecurity bool `json:"useSecurity"` - Views []ViewData `json:"views"` -} diff --git a/pkg/gojenkins/fingerprint.go b/pkg/gojenkins/fingerprint.go deleted file mode 100644 index 3afaa8b6a..000000000 --- a/pkg/gojenkins/fingerprint.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "errors" - "fmt" -) - -type FingerPrint struct { - Jenkins *Jenkins - Base string - Id string - Raw *FingerPrintResponse -} - -type FingerPrintResponse struct { - FileName string `json:"fileName"` - Hash string `json:"hash"` - Original struct { - Name string - Number int64 - } `json:"original"` - Timestamp int64 `json:"timestamp"` - Usage []struct { - Name string `json:"name"` - Ranges struct { - Ranges []struct { - End int64 `json:"end"` - Start int64 `json:"start"` - } `json:"ranges"` - } `json:"ranges"` - } `json:"usage"` -} - -func (f FingerPrint) Valid() (bool, error) { - status, err := f.Poll() - - if err != nil { - return false, err - } - - if status != 200 || f.Raw.Hash != f.Id { - return false, fmt.Errorf("Jenkins says %s is Invalid or the Status is unknown", f.Id) - } - return true, nil -} - -func (f FingerPrint) ValidateForBuild(filename string, build *Build) (bool, error) { - valid, err := f.Valid() - if err != nil { - return false, err - } - - if valid { - return true, nil - } - - if f.Raw.FileName != filename { - return false, errors.New("Filename does not Match") - } - if build != nil && f.Raw.Original.Name == build.Job.GetName() && - f.Raw.Original.Number == build.GetBuildNumber() { - return true, nil - } - return false, nil -} - -func (f FingerPrint) GetInfo() (*FingerPrintResponse, error) { - _, err := f.Poll() - if err != nil { - return nil, err - } - return f.Raw, nil -} - -func (f FingerPrint) Poll() (int, error) { - response, err := f.Jenkins.Requester.GetJSON(f.Base+f.Id, f.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/gojenkins/jenkins.go b/pkg/gojenkins/jenkins.go deleted file mode 100644 index feaf49c65..000000000 --- a/pkg/gojenkins/jenkins.go +++ /dev/null @@ -1,1083 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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. - -// Gojenkins is a Jenkins Client in Go, that exposes the jenkins REST api in a more developer friendly way. -package gojenkins - -import ( - "encoding/json" - "errors" - "fmt" - "log" - "net/http" - "os" - "reflect" - "strconv" - "strings" -) - -// Basic Authentication -type BasicAuth struct { - Username string - Password string -} - -type Jenkins struct { - Server string - Version string - Raw *ExecutorResponse - Requester *Requester -} - -// Loggers -var ( - Info *log.Logger - Warning *log.Logger - Error *log.Logger -) - -// Init Method. Should be called after creating a Jenkins Instance. -// e.g jenkins := CreateJenkins("url").Init() -// HTTP Client is set here, Connection to jenkins is tested here. -func (j *Jenkins) Init() (*Jenkins, error) { - j.initLoggers() - - // Check Connection - j.Raw = new(ExecutorResponse) - rsp, err := j.Requester.GetJSON("/", j.Raw, nil) - if err != nil { - return nil, err - } - - j.Version = rsp.Header.Get("X-Jenkins") - if j.Raw == nil { - return nil, errors.New("Connection Failed, Please verify that the host and credentials are correct.") - } - - return j, nil -} - -func (j *Jenkins) initLoggers() { - Info = log.New(os.Stdout, - "INFO: ", - log.Ldate|log.Ltime|log.Lshortfile) - - Warning = log.New(os.Stdout, - "WARNING: ", - log.Ldate|log.Ltime|log.Lshortfile) - - Error = log.New(os.Stderr, - "ERROR: ", - log.Ldate|log.Ltime|log.Lshortfile) -} - -// Get Basic Information About Jenkins -func (j *Jenkins) Info() (*ExecutorResponse, error) { - _, err := j.Requester.Get("/", j.Raw, nil) - - if err != nil { - return nil, err - } - return j.Raw, nil -} - -// Create a new Node -// Can be JNLPLauncher or SSHLauncher -// Example : jenkins.CreateNode("nodeName", 1, "Description", "/var/lib/jenkins", "jdk8 docker", map[string]string{"method": "JNLPLauncher"}) -// By Default JNLPLauncher is created -// Multiple labels should be separated by blanks -func (j *Jenkins) CreateNode(name string, numExecutors int, description string, remoteFS string, label string, options ...interface{}) (*Node, error) { - params := map[string]string{"method": "JNLPLauncher"} - - if len(options) > 0 { - params, _ = options[0].(map[string]string) - } - - if _, ok := params["method"]; !ok { - params["method"] = "JNLPLauncher" - } - - method := params["method"] - var launcher map[string]string - switch method { - case "": - fallthrough - case "JNLPLauncher": - launcher = map[string]string{"stapler-class": "hudson.slaves.JNLPLauncher"} - case "SSHLauncher": - launcher = map[string]string{ - "stapler-class": "hudson.plugins.sshslaves.SSHLauncher", - "$class": "hudson.plugins.sshslaves.SSHLauncher", - "host": params["host"], - "port": params["port"], - "credentialsId": params["credentialsId"], - "jvmOptions": params["jvmOptions"], - "javaPath": params["javaPath"], - "prefixStartSlaveCmd": params["prefixStartSlaveCmd"], - "suffixStartSlaveCmd": params["suffixStartSlaveCmd"], - "maxNumRetries": params["maxNumRetries"], - "retryWaitTime": params["retryWaitTime"], - "lanuchTimeoutSeconds": params["lanuchTimeoutSeconds"], - "type": "hudson.slaves.DumbSlave", - "stapler-class-bag": "true"} - default: - return nil, errors.New("launcher method not supported") - } - - node := &Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - NODE_TYPE := "hudson.slaves.DumbSlave$DescriptorImpl" - MODE := "NORMAL" - qr := map[string]string{ - "name": name, - "type": NODE_TYPE, - "json": makeJson(map[string]interface{}{ - "name": name, - "nodeDescription": description, - "remoteFS": remoteFS, - "numExecutors": numExecutors, - "mode": MODE, - "type": NODE_TYPE, - "labelString": label, - "retentionsStrategy": map[string]string{"stapler-class": "hudson.slaves.RetentionStrategy$Always"}, - "nodeProperties": map[string]string{"stapler-class-bag": "true"}, - "launcher": launcher, - }), - } - - resp, err := j.Requester.Post("/computer/doCreateItem", nil, nil, qr) - - if err != nil { - return nil, err - } - - if resp.StatusCode < 400 { - _, err := node.Poll() - if err != nil { - return nil, err - } - return node, nil - } - return nil, errors.New(strconv.Itoa(resp.StatusCode)) -} - -// Delete a Jenkins slave node -func (j *Jenkins) DeleteNode(name string) (bool, error) { - node := Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - return node.Delete() -} - -// Create a new folder -// This folder can be nested in other parent folders -// Example: jenkins.CreateFolder("newFolder", "grandparentFolder", "parentFolder") -func (j *Jenkins) CreateFolder(name, description string, parents ...string) (*Folder, error) { - folderObj := &Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, name), "/job/")} - folder, err := folderObj.Create(name, description) - if err != nil { - return nil, err - } - return folder, nil -} - -// Create a new job in the folder -// Example: jenkins.CreateJobInFolder("", "newJobName", "myFolder", "parentFolder") -func (j *Jenkins) CreateJobInFolder(config string, jobName string, parentIDs ...string) (*Job, error) { - jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, jobName), "/job/")} - qr := map[string]string{ - "name": jobName, - } - job, err := jobObj.Create(config, qr) - if err != nil { - return nil, err - } - return job, nil -} - -// Create a new job from config File -// Method takes XML string as first parameter, and if the name is not specified in the config file -// takes name as string as second parameter -// e.g jenkins.CreateJob("","newJobName") -func (j *Jenkins) CreateJob(config string, options ...interface{}) (*Job, error) { - qr := make(map[string]string) - if len(options) > 0 { - qr["name"] = options[0].(string) - } else { - return nil, errors.New("Error Creating Job, job name is missing") - } - jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + qr["name"]} - job, err := jobObj.Create(config, qr) - if err != nil { - return nil, err - } - return job, nil -} - -// Rename a job. -// First parameter job old name, Second parameter job new name. -func (j *Jenkins) RenameJob(job string, name string) *Job { - jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + job} - jobObj.Rename(name) - return &jobObj -} - -// Create a copy of a job. -// First parameter Name of the job to copy from, Second parameter new job name. -func (j *Jenkins) CopyJob(copyFrom string, newName string) (*Job, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + copyFrom} - _, err := job.Poll() - if err != nil { - return nil, err - } - return job.Copy(newName) -} - -// Delete a job. -func (j *Jenkins) DeleteJob(name string, parentIDs ...string) (bool, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, name), "/job/")} - return job.Delete() -} - -// Invoke a job. -// First parameter job name, second parameter is optional Build parameters. -func (j *Jenkins) BuildJob(name string, options ...interface{}) (int64, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + name} - var params map[string]string - if len(options) > 0 { - params, _ = options[0].(map[string]string) - } - return job.InvokeSimple(params) -} - -func (j *Jenkins) GetNode(name string) (*Node, error) { - node := Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - status, err := node.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &node, nil - } - return nil, errors.New("No node found") -} - -func (j *Jenkins) GetLabel(name string) (*Label, error) { - label := Label{Jenkins: j, Raw: new(LabelResponse), Base: "/label/" + name} - status, err := label.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &label, nil - } - return nil, errors.New("No label found") -} - -func (j *Jenkins) GetBuild(jobName string, number int64) (*Build, error) { - job, err := j.GetJob(jobName) - if err != nil { - return nil, err - } - build, err := job.GetBuild(number) - - if err != nil { - return nil, err - } - return build, nil -} - -func (j *Jenkins) GetJob(id string, parentIDs ...string) (*Job, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, id), "/job/")} - status, err := job.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &job, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Jenkins) GetSubJob(parentId string, childId string) (*Job, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + parentId + "/job/" + childId} - status, err := job.Poll() - if err != nil { - return nil, fmt.Errorf("trouble polling job: %v", err) - } - if status == 200 { - return &job, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Jenkins) GetFolder(id string, parents ...string) (*Folder, error) { - folder := Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, id), "/job/")} - status, err := folder.Poll() - if err != nil { - return nil, fmt.Errorf("trouble polling folder: %v", err) - } - if status == 200 { - return &folder, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Jenkins) GetAllNodes() ([]*Node, error) { - computers := new(Computers) - - qr := map[string]string{ - "depth": "1", - } - - _, err := j.Requester.GetJSON("/computer", computers, qr) - if err != nil { - return nil, err - } - - nodes := make([]*Node, len(computers.Computers)) - for i, node := range computers.Computers { - nodes[i] = &Node{Jenkins: j, Raw: node, Base: "/computer/" + node.DisplayName} - } - - return nodes, nil -} - -// Get all builds Numbers and URLS for a specific job. -// There are only build IDs here, -// To get all the other info of the build use jenkins.GetBuild(job,buildNumber) -// or job.GetBuild(buildNumber) -func (j *Jenkins) GetAllBuildIds(job string) ([]JobBuild, error) { - jobObj, err := j.GetJob(job) - if err != nil { - return nil, err - } - return jobObj.GetAllBuildIds() -} - -func (j *Jenkins) GetAllBuildStatus(jobId string) ([]JobBuildStatus, error) { - job, err := j.GetJob(jobId) - if err != nil { - return nil, err - } - return job.GetAllBuildStatus() -} - -// Get Only Array of Job Names, Color, URL -// Does not query each single Job. -func (j *Jenkins) GetAllJobNames() ([]InnerJob, error) { - exec := Executor{Raw: new(ExecutorResponse), Jenkins: j} - _, err := j.Requester.GetJSON("/", exec.Raw, nil) - - if err != nil { - return nil, err - } - - return exec.Raw.Jobs, nil -} - -// Get All Possible Job Objects. -// Each job will be queried. -func (j *Jenkins) GetAllJobs() ([]*Job, error) { - exec := Executor{Raw: new(ExecutorResponse), Jenkins: j} - _, err := j.Requester.GetJSON("/", exec.Raw, nil) - - if err != nil { - return nil, err - } - - jobs := make([]*Job, len(exec.Raw.Jobs)) - for i, job := range exec.Raw.Jobs { - ji, err := j.GetJob(job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -// Returns a Queue -func (j *Jenkins) GetQueue() (*Queue, error) { - q := &Queue{Jenkins: j, Raw: new(queueResponse), Base: j.GetQueueUrl()} - _, err := q.Poll() - if err != nil { - return nil, err - } - return q, nil -} - -func (j *Jenkins) GetQueueUrl() string { - return "/queue" -} - -// Get Artifact data by Hash -func (j *Jenkins) GetArtifactData(id string) (*FingerPrintResponse, error) { - fp := FingerPrint{Jenkins: j, Base: "/fingerprint/", Id: id, Raw: new(FingerPrintResponse)} - return fp.GetInfo() -} - -// Returns the list of all plugins installed on the Jenkins server. -// You can supply depth parameter, to limit how much data is returned. -func (j *Jenkins) GetPlugins(depth int) (*Plugins, error) { - p := Plugins{Jenkins: j, Raw: new(PluginResponse), Base: "/pluginManager", Depth: depth} - _, err := p.Poll() - if err != nil { - return nil, err - } - return &p, nil -} - -// Check if the plugin is installed on the server. -// Depth level 1 is used. If you need to go deeper, you can use GetPlugins, and iterate through them. -func (j *Jenkins) HasPlugin(name string) (*Plugin, error) { - p, err := j.GetPlugins(1) - - if err != nil { - return nil, err - } - return p.Contains(name), nil -} - -// Verify FingerPrint -func (j *Jenkins) ValidateFingerPrint(id string) (bool, error) { - fp := FingerPrint{Jenkins: j, Base: "/fingerprint/", Id: id, Raw: new(FingerPrintResponse)} - valid, err := fp.Valid() - if err != nil { - return false, err - } - if valid { - return true, nil - } - return false, nil -} - -func (j *Jenkins) GetView(name string) (*View, error) { - url := "/view/" + name - view := View{Jenkins: j, Raw: new(ViewResponse), Base: url} - _, err := view.Poll() - if err != nil { - return nil, err - } - return &view, nil -} - -func (j *Jenkins) GetAllViews() ([]*View, error) { - _, err := j.Poll() - if err != nil { - return nil, err - } - views := make([]*View, len(j.Raw.Views)) - for i, v := range j.Raw.Views { - views[i], _ = j.GetView(v.Name) - } - return views, nil -} - -// Create View -// First Parameter - name of the View -// Second parameter - Type -// Possible Types: -// gojenkins.LIST_VIEW -// gojenkins.NESTED_VIEW -// gojenkins.MY_VIEW -// gojenkins.DASHBOARD_VIEW -// gojenkins.PIPELINE_VIEW -// Example: jenkins.CreateView("newView",gojenkins.LIST_VIEW) -func (j *Jenkins) CreateView(name string, viewType string) (*View, error) { - view := &View{Jenkins: j, Raw: new(ViewResponse), Base: "/view/" + name} - endpoint := "/createView" - data := map[string]string{ - "name": name, - "mode": viewType, - "Submit": "OK", - "json": makeJson(map[string]string{ - "name": name, - "mode": viewType, - }), - } - r, err := j.Requester.Post(endpoint, nil, view.Raw, data) - - if err != nil { - return nil, err - } - - if r.StatusCode == 200 { - return j.GetView(name) - } - return nil, errors.New(strconv.Itoa(r.StatusCode)) -} - -func (j *Jenkins) Poll() (int, error) { - resp, err := j.Requester.GetJSON("/", j.Raw, nil) - if err != nil { - return 0, err - } - return resp.StatusCode, nil -} - -// Create a ssh credentials -// return credentials id -func (j *Jenkins) CreateSshCredential(id, username, passphrase, privateKey, description string) (*string, error) { - requestStruct := NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - response, err := j.Requester.Post("/credentials/store/system/domain/_/createCredentials", - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateUsernamePasswordCredential(id, username, password, description string) (*string, error) { - requestStruct := NewCreateUsernamePasswordRequest(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - response, err := j.Requester.Post("/credentials/store/system/domain/_/createCredentials", - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateSshCredentialInFolder(domain, id, username, passphrase, privateKey, description string, folders ...string) (*string, error) { - requestStruct := NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateUsernamePasswordCredentialInFolder(domain, id, username, password, description string, folders ...string) (*string, error) { - requestStruct := NewCreateUsernamePasswordRequest(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateSecretTextCredentialInFolder(domain, id, secret, description string, folders ...string) (*string, error) { - requestStruct := NewCreateSecretTextCredentialRequest(id, secret, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateKubeconfigCredentialInFolder(domain, id, content, description string, folders ...string) (*string, error) { - requestStruct := NewCreateKubeconfigCredentialRequest(id, content, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) UpdateSshCredentialInFolder(domain, id, username, passphrase, privateKey, description string, folders ...string) (*string, error) { - requestStruct := NewSshCredential(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateUsernamePasswordCredentialInFolder(domain, id, username, password, description string, folders ...string) (*string, error) { - requestStruct := NewUsernamePasswordCredential(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateSecretTextCredentialInFolder(domain, id, secret, description string, folders ...string) (*string, error) { - requestStruct := NewSecretTextCredential(id, secret, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateKubeconfigCredentialInFolder(domain, id, content, description string, folders ...string) (*string, error) { - requestStruct := NewKubeconfigCredential(id, content, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) GetCredentialInFolder(domain, id string, folders ...string) (*CredentialResponse, error) { - responseStruct := &CredentialResponse{} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.GetJSON(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s", domain, id), - responseStruct, map[string]string{ - "depth": "2", - }) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - responseStruct.Domain = domain - return responseStruct, nil -} - -func (j *Jenkins) GetCredentialContentInFolder(domain, id string, folders ...string) (string, error) { - responseStruct := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return "", fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.GetHtml(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/update", domain, id), - &responseStruct, nil) - if err != nil { - return "", err - } - if response.StatusCode != http.StatusOK { - return "", errors.New(strconv.Itoa(response.StatusCode)) - } - return responseStruct, nil -} - -func (j *Jenkins) GetCredentialsInFolder(domain string, folders ...string) ([]*CredentialResponse, error) { - prePath := "" - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - - if domain == "" { - var responseStruct = &struct { - Domains map[string]struct { - Credentials []*CredentialResponse `json:"credentials"` - } `json:"domains"` - }{} - response, err := j.Requester.GetJSON(prePath+ - "/credentials/store/folder/", - responseStruct, map[string]string{ - "depth": "2", - }) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - responseArray := make([]*CredentialResponse, 0) - for domainName, domain := range responseStruct.Domains { - for _, credential := range domain.Credentials { - credential.Domain = domainName - responseArray = append(responseArray, credential) - } - } - return responseArray, nil - } - - var responseStruct = &struct { - Credentials []*CredentialResponse `json:"credentials"` - }{} - response, err := j.Requester.GetJSON(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s", domain), - responseStruct, map[string]string{ - "depth": "2", - }) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - for _, credential := range responseStruct.Credentials { - credential.Domain = domain - } - return responseStruct.Credentials, nil - -} - -func (j *Jenkins) DeleteCredentialInFolder(domain, id string, folders ...string) (*string, error) { - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/doDelete", domain, id), - nil, nil, nil) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) GetGlobalRole(roleName string) (*GlobalRole, error) { - roleResponse := &GlobalRoleResponse{ - RoleName: roleName, - } - stringResponse := "" - response, err := j.Requester.Get("/role-strategy/strategy/getRole", - &stringResponse, - map[string]string{ - "roleName": roleName, - "type": GLOBAL_ROLE, - }) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - if stringResponse == "{}" { - return nil, nil - } - err = json.Unmarshal([]byte(stringResponse), roleResponse) - if err != nil { - return nil, err - } - return &GlobalRole{ - Jenkins: j, - Raw: *roleResponse, - }, nil -} - -func (j *Jenkins) GetProjectRole(roleName string) (*ProjectRole, error) { - roleResponse := &ProjectRoleResponse{ - RoleName: roleName, - } - stringResponse := "" - response, err := j.Requester.Get("/role-strategy/strategy/getRole", - &stringResponse, - map[string]string{ - "roleName": roleName, - "type": PROJECT_ROLE, - }) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - if stringResponse == "{}" { - return nil, nil - } - err = json.Unmarshal([]byte(stringResponse), roleResponse) - if err != nil { - return nil, err - } - return &ProjectRole{ - Jenkins: j, - Raw: *roleResponse, - }, nil -} - -func (j *Jenkins) AddGlobalRole(roleName string, ids GlobalPermissionIds, overwrite bool) (*GlobalRole, error) { - responseRole := &GlobalRole{ - Jenkins: j, - Raw: GlobalRoleResponse{ - RoleName: roleName, - PermissionIds: ids, - }} - var idArray []string - values := reflect.ValueOf(ids) - for i := 0; i < values.NumField(); i++ { - field := values.Field(i) - if field.Bool() { - idArray = append(idArray, values.Type().Field(i).Tag.Get("json")) - } - } - param := map[string]string{ - "roleName": roleName, - "type": GLOBAL_ROLE, - "permissionIds": strings.Join(idArray, ","), - "overwrite": strconv.FormatBool(overwrite), - } - responseString := "" - response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return responseRole, nil -} - -func (j *Jenkins) DeleteProjectRoles(roleName ...string) error { - responseString := "" - - response, err := j.Requester.Post("/role-strategy/strategy/removeRoles", nil, &responseString, map[string]string{ - "type": PROJECT_ROLE, - "roleNames": strings.Join(roleName, ","), - }) - if err != nil { - return err - } - if response.StatusCode != http.StatusOK { - fmt.Println(responseString) - return errors.New(strconv.Itoa(response.StatusCode)) - } - return nil -} - -func (j *Jenkins) AddProjectRole(roleName string, pattern string, ids ProjectPermissionIds, overwrite bool) (*ProjectRole, error) { - responseRole := &ProjectRole{ - Jenkins: j, - Raw: ProjectRoleResponse{ - RoleName: roleName, - PermissionIds: ids, - Pattern: pattern, - }} - var idArray []string - values := reflect.ValueOf(ids) - for i := 0; i < values.NumField(); i++ { - field := values.Field(i) - if field.Bool() { - idArray = append(idArray, values.Type().Field(i).Tag.Get("json")) - } - } - param := map[string]string{ - "roleName": roleName, - "type": PROJECT_ROLE, - "permissionIds": strings.Join(idArray, ","), - "overwrite": strconv.FormatBool(overwrite), - "pattern": pattern, - } - responseString := "" - response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return responseRole, nil -} - -func (j *Jenkins) DeleteUserInProject(username string) error { - param := map[string]string{ - "type": PROJECT_ROLE, - "sid": username, - } - responseString := "" - response, err := j.Requester.Post("/role-strategy/strategy/deleteSid", nil, &responseString, param) - if err != nil { - return err - } - if response.StatusCode != http.StatusOK { - return errors.New(strconv.Itoa(response.StatusCode)) - } - return nil -} - -func (j *Jenkins) GetQueueItem(number int64) (*QueueItemResponse, error) { - responseItem := &QueueItemResponse{} - response, err := j.Requester.GetJSON(fmt.Sprintf("/queue/item/%s", strconv.FormatInt(number, 10)), - responseItem, nil) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return responseItem, nil -} - -// Creates a new Jenkins Instance -// Optional parameters are: client, username, password -// After creating an instance call init method. -func CreateJenkins(client *http.Client, base string, maxConnection int, auth ...interface{}) *Jenkins { - j := &Jenkins{} - if strings.HasSuffix(base, "/") { - base = base[:len(base)-1] - } - j.Server = base - j.Requester = &Requester{Base: base, SslVerify: true, Client: client, connControl: make(chan struct{}, maxConnection)} - if j.Requester.Client == nil { - j.Requester.Client = http.DefaultClient - } - if len(auth) == 2 { - j.Requester.BasicAuth = &BasicAuth{Username: auth[0].(string), Password: auth[1].(string)} - } - return j -} diff --git a/pkg/gojenkins/job.go b/pkg/gojenkins/job.go deleted file mode 100644 index c075a42e5..000000000 --- a/pkg/gojenkins/job.go +++ /dev/null @@ -1,529 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "net/url" - "path" - "strconv" - "strings" -) - -type Job struct { - Raw *JobResponse - Jenkins *Jenkins - Base string -} - -type JobBuild struct { - Number int64 - URL string -} - -type JobBuildStatus struct { - Number int64 - Building bool - Result string -} - -type InnerJob struct { - Name string `json:"name"` - Url string `json:"url"` - Color string `json:"color"` -} - -type ParameterDefinition struct { - DefaultParameterValue struct { - Name string `json:"name"` - Value interface{} `json:"value"` - } `json:"defaultParameterValue"` - Description string `json:"description"` - Name string `json:"name"` - Type string `json:"type"` -} - -type JobResponse struct { - Class string `json:"_class"` - Actions []GeneralObj - Buildable bool `json:"buildable"` - Builds []JobBuild - Color string `json:"color"` - ConcurrentBuild bool `json:"concurrentBuild"` - Description string `json:"description"` - DisplayName string `json:"displayName"` - DisplayNameOrNull interface{} `json:"displayNameOrNull"` - DownstreamProjects []InnerJob `json:"downstreamProjects"` - FirstBuild JobBuild - HealthReport []struct { - Description string `json:"description"` - IconClassName string `json:"iconClassName"` - IconUrl string `json:"iconUrl"` - Score int64 `json:"score"` - } `json:"healthReport"` - InQueue bool `json:"inQueue"` - KeepDependencies bool `json:"keepDependencies"` - LastBuild JobBuild `json:"lastBuild"` - LastCompletedBuild JobBuild `json:"lastCompletedBuild"` - LastFailedBuild JobBuild `json:"lastFailedBuild"` - LastStableBuild JobBuild `json:"lastStableBuild"` - LastSuccessfulBuild JobBuild `json:"lastSuccessfulBuild"` - LastUnstableBuild JobBuild `json:"lastUnstableBuild"` - LastUnsuccessfulBuild JobBuild `json:"lastUnsuccessfulBuild"` - Name string `json:"name"` - SubJobs []InnerJob `json:"subJobs"` - NextBuildNumber int64 `json:"nextBuildNumber"` - Property []struct { - ParameterDefinitions []ParameterDefinition `json:"parameterDefinitions"` - } `json:"property"` - QueueItem interface{} `json:"queueItem"` - Scm struct{} `json:"scm"` - UpstreamProjects []InnerJob `json:"upstreamProjects"` - URL string `json:"url"` - Jobs []InnerJob `json:"jobs"` - PrimaryView *ViewData `json:"primaryView"` - Views []ViewData `json:"views"` -} - -func (j *Job) parentBase() string { - return j.Base[:strings.LastIndex(j.Base, "/job/")] -} - -type History struct { - BuildNumber int - BuildStatus string - BuildTimestamp int64 -} - -func (j *Job) GetName() string { - return j.Raw.Name -} - -func (j *Job) GetDescription() string { - return j.Raw.Description -} - -func (j *Job) GetDetails() *JobResponse { - return j.Raw -} - -func (j *Job) GetBuild(id int64) (*Build, error) { - build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(BuildResponse), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)} - status, err := build.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &build, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Job) getBuildByType(buildType string) (*Build, error) { - allowed := map[string]JobBuild{ - "lastStableBuild": j.Raw.LastStableBuild, - "lastSuccessfulBuild": j.Raw.LastSuccessfulBuild, - "lastBuild": j.Raw.LastBuild, - "lastCompletedBuild": j.Raw.LastCompletedBuild, - "firstBuild": j.Raw.FirstBuild, - "lastFailedBuild": j.Raw.LastFailedBuild, - } - number := "" - if val, ok := allowed[buildType]; ok { - number = strconv.FormatInt(val.Number, 10) - } else { - panic("No Such Build") - } - build := Build{ - Jenkins: j.Jenkins, - Depth: 1, - Job: j, - Raw: new(BuildResponse), - Base: j.Base + "/" + number} - status, err := build.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &build, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Job) GetLastSuccessfulBuild() (*Build, error) { - return j.getBuildByType("lastSuccessfulBuild") -} - -func (j *Job) GetFirstBuild() (*Build, error) { - return j.getBuildByType("firstBuild") -} - -func (j *Job) GetLastBuild() (*Build, error) { - return j.getBuildByType("lastBuild") -} - -func (j *Job) GetLastStableBuild() (*Build, error) { - return j.getBuildByType("lastStableBuild") -} - -func (j *Job) GetLastFailedBuild() (*Build, error) { - return j.getBuildByType("lastFailedBuild") -} - -func (j *Job) GetLastCompletedBuild() (*Build, error) { - return j.getBuildByType("lastCompletedBuild") -} - -// Returns All Builds with Number and URL -func (j *Job) GetAllBuildIds() ([]JobBuild, error) { - var buildsResp struct { - Builds []JobBuild `json:"allBuilds"` - } - _, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,url]"}) - if err != nil { - return nil, err - } - return buildsResp.Builds, nil -} - -func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) { - var buildsResp struct { - Builds []JobBuildStatus `json:"allBuilds"` - } - _, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,building,result]"}) - if err != nil { - return nil, err - } - return buildsResp.Builds, nil -} - -func (j *Job) GetSubJobsMetadata() []InnerJob { - return j.Raw.SubJobs -} - -func (j *Job) GetUpstreamJobsMetadata() []InnerJob { - return j.Raw.UpstreamProjects -} - -func (j *Job) GetDownstreamJobsMetadata() []InnerJob { - return j.Raw.DownstreamProjects -} - -func (j *Job) GetSubJobs() ([]*Job, error) { - jobs := make([]*Job, len(j.Raw.SubJobs)) - for i, job := range j.Raw.SubJobs { - ji, err := j.Jenkins.GetSubJob(j.GetName(), job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -func (j *Job) GetInnerJobsMetadata() []InnerJob { - return j.Raw.Jobs -} - -func (j *Job) GetUpstreamJobs() ([]*Job, error) { - jobs := make([]*Job, len(j.Raw.UpstreamProjects)) - for i, job := range j.Raw.UpstreamProjects { - ji, err := j.Jenkins.GetJob(job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -func (j *Job) GetDownstreamJobs() ([]*Job, error) { - jobs := make([]*Job, len(j.Raw.DownstreamProjects)) - for i, job := range j.Raw.DownstreamProjects { - ji, err := j.Jenkins.GetJob(job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -func (j *Job) GetInnerJob(id string) (*Job, error) { - job := Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: j.Base + "/job/" + id} - status, err := job.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &job, nil - } - return nil, errors.New(strconv.Itoa(status)) -} - -func (j *Job) GetInnerJobs() ([]*Job, error) { - jobs := make([]*Job, len(j.Raw.Jobs)) - for i, job := range j.Raw.Jobs { - ji, err := j.GetInnerJob(job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -func (j *Job) Enable() (bool, error) { - resp, err := j.Jenkins.Requester.Post(j.Base+"/enable", nil, nil, nil) - if err != nil { - return false, err - } - if resp.StatusCode != 200 { - return false, errors.New(strconv.Itoa(resp.StatusCode)) - } - return true, nil -} - -func (j *Job) Disable() (bool, error) { - resp, err := j.Jenkins.Requester.Post(j.Base+"/disable", nil, nil, nil) - if err != nil { - return false, err - } - if resp.StatusCode != 200 { - return false, errors.New(strconv.Itoa(resp.StatusCode)) - } - return true, nil -} - -func (j *Job) Delete() (bool, error) { - resp, err := j.Jenkins.Requester.Post(j.Base+"/doDelete", nil, nil, nil) - if err != nil { - return false, err - } - if resp.StatusCode != 200 { - return false, errors.New(strconv.Itoa(resp.StatusCode)) - } - return true, nil -} - -func (j *Job) Rename(name string) (bool, error) { - data := url.Values{} - data.Set("newName", name) - _, err := j.Jenkins.Requester.Post(j.Base+"/doRename", bytes.NewBufferString(data.Encode()), nil, nil) - if err != nil { - return false, err - } - j.Base = "/job/" + name - j.Poll() - return true, nil -} - -func (j *Job) Create(config string, qr ...interface{}) (*Job, error) { - var querystring map[string]string - if len(qr) > 0 { - querystring = qr[0].(map[string]string) - } - resp, err := j.Jenkins.Requester.PostXML(j.parentBase()+"/createItem", config, j.Raw, querystring) - if err != nil { - return nil, err - } - if resp.StatusCode == 200 { - j.Poll() - return j, nil - } - return nil, errors.New(strconv.Itoa(resp.StatusCode)) -} - -func (j *Job) Copy(destinationName string) (*Job, error) { - qr := map[string]string{"name": destinationName, "from": j.GetName(), "mode": "copy"} - resp, err := j.Jenkins.Requester.Post(j.parentBase()+"/createItem", nil, nil, qr) - if err != nil { - return nil, err - } - if resp.StatusCode == 200 { - newJob := &Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: "/job/" + destinationName} - _, err := newJob.Poll() - if err != nil { - return nil, err - } - return newJob, nil - } - return nil, errors.New(strconv.Itoa(resp.StatusCode)) -} - -func (j *Job) UpdateConfig(config string) error { - - var querystring map[string]string - - resp, err := j.Jenkins.Requester.PostXML(j.Base+"/config.xml", config, nil, querystring) - if err != nil { - return err - } - if resp.StatusCode == 200 { - j.Poll() - return nil - } - return errors.New(strconv.Itoa(resp.StatusCode)) - -} - -func (j *Job) GetConfig() (string, error) { - var data string - _, err := j.Jenkins.Requester.GetXML(j.Base+"/config.xml", &data, nil) - if err != nil { - return "", err - } - return data, nil -} - -func (j *Job) GetParameters() ([]ParameterDefinition, error) { - _, err := j.Poll() - if err != nil { - return nil, err - } - var parameters []ParameterDefinition - for _, property := range j.Raw.Property { - parameters = append(parameters, property.ParameterDefinitions...) - } - return parameters, nil -} - -func (j *Job) IsQueued() (bool, error) { - if _, err := j.Poll(); err != nil { - return false, err - } - return j.Raw.InQueue, nil -} - -func (j *Job) IsRunning() (bool, error) { - if _, err := j.Poll(); err != nil { - return false, err - } - lastBuild, err := j.GetLastBuild() - if err != nil { - return false, err - } - return lastBuild.IsRunning(), nil -} - -func (j *Job) IsEnabled() (bool, error) { - if _, err := j.Poll(); err != nil { - return false, err - } - return j.Raw.Color != "disabled", nil -} - -func (j *Job) HasQueuedBuild() { - panic("Not Implemented yet") -} - -func (j *Job) InvokeSimple(params map[string]string) (int64, error) { - endpoint := "/build" - parameters, err := j.GetParameters() - if err != nil { - return 0, err - } - if len(parameters) > 0 { - endpoint = "/buildWithParameters" - } - data := url.Values{} - for k, v := range params { - data.Set(k, v) - } - resp, err := j.Jenkins.Requester.Post(j.Base+endpoint, bytes.NewBufferString(data.Encode()), nil, nil) - if err != nil { - return 0, err - } - - if resp.StatusCode != 200 && resp.StatusCode != 201 { - return 0, errors.New("Could not invoke job " + j.GetName()) - } - - location := resp.Header.Get("Location") - if location == "" { - return 0, errors.New("Don't have key \"Location\" in response of header") - } - - u, err := url.Parse(location) - if err != nil { - return 0, err - } - - number, err := strconv.ParseInt(path.Base(u.Path), 10, 64) - if err != nil { - return 0, err - } - - return number, nil -} - -func (j *Job) Invoke(files []string, skipIfRunning bool, params map[string]string, cause string, securityToken string) (bool, error) { - isRunning, err := j.IsRunning() - if err != nil { - return false, err - } - if isRunning && skipIfRunning { - return false, fmt.Errorf("Will not request new build because %s is already running", j.GetName()) - } - - base := "/build" - - // If parameters are specified - url is /builWithParameters - if params != nil { - base = "/buildWithParameters" - } else { - params = make(map[string]string) - } - - // If files are specified - url is /build - if files != nil { - base = "/build" - } - reqParams := map[string]string{} - buildParams := map[string]string{} - if securityToken != "" { - reqParams["token"] = securityToken - } - - buildParams["json"] = string(makeJson(params)) - b, _ := json.Marshal(buildParams) - resp, err := j.Jenkins.Requester.PostFiles(j.Base+base, bytes.NewBuffer(b), nil, reqParams, files) - if err != nil { - return false, err - } - if resp.StatusCode == 200 || resp.StatusCode == 201 { - return true, nil - } - return false, errors.New(strconv.Itoa(resp.StatusCode)) -} - -func (j *Job) Poll() (int, error) { - response, err := j.Jenkins.Requester.GetJSON(j.Base, j.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} - -func (j *Job) History() ([]*History, error) { - resp, err := j.Jenkins.Requester.Get(j.Base+"/buildHistory/ajax", nil, nil) - if err != nil { - return nil, err - } - return parseBuildHistory(resp.Body), nil -} diff --git a/pkg/gojenkins/label.go b/pkg/gojenkins/label.go deleted file mode 100644 index a757b630f..000000000 --- a/pkg/gojenkins/label.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -type Label struct { - Raw *LabelResponse - Jenkins *Jenkins - Base string -} - -type MODE string - -const ( - NORMAL MODE = "NORMAL" - EXCLUSIVE = "EXCLUSIVE" -) - -type LabelNode struct { - NodeName string `json:"nodeName"` - NodeDescription string `json:"nodeDescription"` - NumExecutors int64 `json:"numExecutors"` - Mode string `json:"mode"` - Class string `json:"_class"` -} - -type LabelResponse struct { - Name string `json:"name"` - Description string `json:"description"` - Nodes []LabelNode `json:"nodes"` - Offline bool `json:"offline"` - IdleExecutors int64 `json:"idleExecutors"` - BusyExecutors int64 `json:"busyExecutors"` - TotalExecutors int64 `json:"totalExecutors"` -} - -func (l *Label) GetName() string { - return l.Raw.Name -} - -func (l *Label) GetNodes() []LabelNode { - return l.Raw.Nodes -} - -func (l *Label) Poll() (int, error) { - response, err := l.Jenkins.Requester.GetJSON(l.Base, l.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/gojenkins/node.go b/pkg/gojenkins/node.go deleted file mode 100644 index 14a6face8..000000000 --- a/pkg/gojenkins/node.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import "errors" - -// Nodes - -type Computers struct { - BusyExecutors int `json:"busyExecutors"` - Computers []*NodeResponse `json:"computer"` - DisplayName string `json:"displayName"` - TotalExecutors int `json:"totalExecutors"` -} - -type Node struct { - Raw *NodeResponse - Jenkins *Jenkins - Base string -} - -type NodeResponse struct { - Actions []interface{} `json:"actions"` - DisplayName string `json:"displayName"` - Executors []struct { - CurrentExecutable struct { - Number int `json:"number"` - URL string `json:"url"` - SubBuilds []struct { - Abort bool `json:"abort"` - Build interface{} `json:"build"` - BuildNumber int `json:"buildNumber"` - Duration string `json:"duration"` - Icon string `json:"icon"` - JobName string `json:"jobName"` - ParentBuildNumber int `json:"parentBuildNumber"` - ParentJobName string `json:"parentJobName"` - PhaseName string `json:"phaseName"` - Result string `json:"result"` - Retry bool `json:"retry"` - URL string `json:"url"` - } `json:"subBuilds"` - } `json:"currentExecutable"` - } `json:"executors"` - Icon string `json:"icon"` - IconClassName string `json:"iconClassName"` - Idle bool `json:"idle"` - JnlpAgent bool `json:"jnlpAgent"` - LaunchSupported bool `json:"launchSupported"` - LoadStatistics struct{} `json:"loadStatistics"` - ManualLaunchAllowed bool `json:"manualLaunchAllowed"` - MonitorData struct { - Hudson_NodeMonitors_ArchitectureMonitor interface{} `json:"hudson.node_monitors.ArchitectureMonitor"` - Hudson_NodeMonitors_ClockMonitor interface{} `json:"hudson.node_monitors.ClockMonitor"` - Hudson_NodeMonitors_DiskSpaceMonitor interface{} `json:"hudson.node_monitors.DiskSpaceMonitor"` - Hudson_NodeMonitors_ResponseTimeMonitor struct { - Average int64 `json:"average"` - } `json:"hudson.node_monitors.ResponseTimeMonitor"` - Hudson_NodeMonitors_SwapSpaceMonitor interface{} `json:"hudson.node_monitors.SwapSpaceMonitor"` - Hudson_NodeMonitors_TemporarySpaceMonitor interface{} `json:"hudson.node_monitors.TemporarySpaceMonitor"` - } `json:"monitorData"` - NumExecutors int64 `json:"numExecutors"` - Offline bool `json:"offline"` - OfflineCause struct{} `json:"offlineCause"` - OfflineCauseReason string `json:"offlineCauseReason"` - OneOffExecutors []interface{} `json:"oneOffExecutors"` - TemporarilyOffline bool `json:"temporarilyOffline"` -} - -func (n *Node) Info() (*NodeResponse, error) { - _, err := n.Poll() - if err != nil { - return nil, err - } - return n.Raw, nil -} - -func (n *Node) GetName() string { - return n.Raw.DisplayName -} - -func (n *Node) Delete() (bool, error) { - resp, err := n.Jenkins.Requester.Post(n.Base+"/doDelete", nil, nil, nil) - if err != nil { - return false, err - } - return resp.StatusCode == 200, nil -} - -func (n *Node) IsOnline() (bool, error) { - _, err := n.Poll() - if err != nil { - return false, err - } - return !n.Raw.Offline, nil -} - -func (n *Node) IsTemporarilyOffline() (bool, error) { - _, err := n.Poll() - if err != nil { - return false, err - } - return n.Raw.TemporarilyOffline, nil -} - -func (n *Node) IsIdle() (bool, error) { - _, err := n.Poll() - if err != nil { - return false, err - } - return n.Raw.Idle, nil -} - -func (n *Node) IsJnlpAgent() (bool, error) { - _, err := n.Poll() - if err != nil { - return false, err - } - return n.Raw.JnlpAgent, nil -} - -func (n *Node) SetOnline() (bool, error) { - _, err := n.Poll() - - if err != nil { - return false, err - } - - if n.Raw.Offline && !n.Raw.TemporarilyOffline { - return false, errors.New("Node is Permanently offline, can't bring it up") - } - - if n.Raw.Offline && n.Raw.TemporarilyOffline { - return n.ToggleTemporarilyOffline() - } - - return true, nil -} - -func (n *Node) SetOffline(options ...interface{}) (bool, error) { - if !n.Raw.Offline { - return n.ToggleTemporarilyOffline(options...) - } - return false, errors.New("Node already Offline") -} - -func (n *Node) ToggleTemporarilyOffline(options ...interface{}) (bool, error) { - state_before, err := n.IsTemporarilyOffline() - if err != nil { - return false, err - } - qr := map[string]string{"offlineMessage": "requested from gojenkins"} - if len(options) > 0 { - qr["offlineMessage"] = options[0].(string) - } - _, err = n.Jenkins.Requester.Post(n.Base+"/toggleOffline", nil, nil, qr) - if err != nil { - return false, err - } - new_state, err := n.IsTemporarilyOffline() - if err != nil { - return false, err - } - if state_before == new_state { - return false, errors.New("Node state not changed") - } - return true, nil -} - -func (n *Node) Poll() (int, error) { - response, err := n.Jenkins.Requester.GetJSON(n.Base, n.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} - -func (n *Node) LaunchNodeBySSH() (int, error) { - qr := map[string]string{ - "json": "", - "Submit": "Launch slave agent", - } - response, err := n.Jenkins.Requester.Post(n.Base+"/launchSlaveAgent", nil, nil, qr) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} - -func (n *Node) Disconnect() (int, error) { - qr := map[string]string{ - "offlineMessage": "", - "json": makeJson(map[string]string{"offlineMessage": ""}), - "Submit": "Yes", - } - response, err := n.Jenkins.Requester.Post(n.Base+"/doDisconnect", nil, nil, qr) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} - -func (n *Node) GetLogText() (string, error) { - var log string - - _, err := n.Jenkins.Requester.Post(n.Base+"/log", nil, nil, nil) - if err != nil { - return "", err - } - - qr := map[string]string{"start": "0"} - _, err = n.Jenkins.Requester.GetJSON(n.Base+"/logText/progressiveHtml/", &log, qr) - if err != nil { - return "", nil - } - - return log, nil -} diff --git a/pkg/gojenkins/plugin.go b/pkg/gojenkins/plugin.go deleted file mode 100644 index 5e44717db..000000000 --- a/pkg/gojenkins/plugin.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "strconv" -) - -type Plugins struct { - Jenkins *Jenkins - Raw *PluginResponse - Base string - Depth int -} - -type PluginResponse struct { - Plugins []Plugin `json:"plugins"` -} - -type Plugin struct { - Active bool `json:"active"` - BackupVersion interface{} `json:"backupVersion"` - Bundled bool `json:"bundled"` - Deleted bool `json:"deleted"` - Dependencies []struct { - Optional string `json:"optional"` - ShortName string `json:"shortname"` - Version string `json:"version"` - } `json:"dependencies"` - Downgradable bool `json:"downgradable"` - Enabled bool `json:"enabled"` - HasUpdate bool `json:"hasUpdate"` - LongName string `json:"longName"` - Pinned bool `json:"pinned"` - ShortName string `json:"shortName"` - SupportsDynamicLoad string `json:"supportsDynamicLoad"` - URL string `json:"url"` - Version string `json:"version"` -} - -func (p *Plugins) Count() int { - return len(p.Raw.Plugins) -} - -func (p *Plugins) Contains(name string) *Plugin { - for _, p := range p.Raw.Plugins { - if p.LongName == name || p.ShortName == name { - return &p - } - } - return nil -} - -func (p *Plugins) Poll() (int, error) { - qr := map[string]string{ - "depth": strconv.Itoa(p.Depth), - } - response, err := p.Jenkins.Requester.GetJSON(p.Base, p.Raw, qr) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/gojenkins/queue.go b/pkg/gojenkins/queue.go deleted file mode 100644 index e73c939b5..000000000 --- a/pkg/gojenkins/queue.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "strconv" -) - -type Queue struct { - Jenkins *Jenkins - Raw *queueResponse - Base string -} - -type queueResponse struct { - Items []taskResponse -} - -type Task struct { - Raw *taskResponse - Jenkins *Jenkins - Queue *Queue -} - -type taskResponse struct { - Actions []generalAction `json:"actions"` - Blocked bool `json:"blocked"` - Buildable bool `json:"buildable"` - BuildableStartMilliseconds int64 `json:"buildableStartMilliseconds"` - ID int64 `json:"id"` - InQueueSince int64 `json:"inQueueSince"` - Params string `json:"params"` - Pending bool `json:"pending"` - Stuck bool `json:"stuck"` - Task struct { - Color string `json:"color"` - Name string `json:"name"` - URL string `json:"url"` - } `json:"task"` - URL string `json:"url"` - Why string `json:"why"` -} - -type generalAction struct { - Causes []map[string]interface{} - Parameters []parameter -} - -type QueueItemResponse struct { - Actions []generalAction `json:"actions"` - Blocked bool `json:"blocked"` - Buildable bool `json:"buildable"` - ID int64 `json:"id"` - InQueueSince int64 `json:"inQueueSince"` - Params string `json:"params"` - Stuck bool `json:"stuck"` - Task struct { - Color string `json:"color"` - Name string `json:"name"` - URL string `json:"url"` - } `json:"task"` - URL string `json:"url"` - Cancelled bool `json:"cancelled"` - Why string `json:"why"` - Executable struct { - Number int64 `json:"number"` - Url string `json:"url"` - } `json:"executable"` -} - -func (q *Queue) Tasks() []*Task { - tasks := make([]*Task, len(q.Raw.Items)) - for i, t := range q.Raw.Items { - tasks[i] = &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t} - } - return tasks -} - -func (q *Queue) GetTaskById(id int64) *Task { - for _, t := range q.Raw.Items { - if t.ID == id { - return &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t} - } - } - return nil -} - -func (q *Queue) GetTasksForJob(name string) []*Task { - tasks := make([]*Task, 0) - for _, t := range q.Raw.Items { - if t.Task.Name == name { - tasks = append(tasks, &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t}) - } - } - return tasks -} - -func (q *Queue) CancelTask(id int64) (bool, error) { - task := q.GetTaskById(id) - return task.Cancel() -} - -func (t *Task) Cancel() (bool, error) { - qr := map[string]string{ - "id": strconv.FormatInt(t.Raw.ID, 10), - } - response, err := t.Jenkins.Requester.Post(t.Jenkins.GetQueueUrl()+"/cancelItem", nil, t.Raw, qr) - if err != nil { - return false, err - } - return response.StatusCode == 200, nil -} - -func (t *Task) GetJob() (*Job, error) { - return t.Jenkins.GetJob(t.Raw.Task.Name) -} - -func (t *Task) GetWhy() string { - return t.Raw.Why -} - -func (t *Task) GetParameters() []parameter { - for _, a := range t.Raw.Actions { - if a.Parameters != nil { - return a.Parameters - } - } - return nil -} - -func (t *Task) GetCauses() []map[string]interface{} { - for _, a := range t.Raw.Actions { - if a.Causes != nil { - return a.Causes - } - } - return nil -} - -func (q *Queue) Poll() (int, error) { - response, err := q.Jenkins.Requester.GetJSON(q.Base, q.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/gojenkins/request.go b/pkg/gojenkins/request.go deleted file mode 100644 index 25cc3e064..000000000 --- a/pkg/gojenkins/request.go +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" -) - -// Request Methods - -type APIRequest struct { - Method string - Endpoint string - Payload io.Reader - Headers http.Header - Suffix string -} - -func (ar *APIRequest) SetHeader(key string, value string) *APIRequest { - ar.Headers.Set(key, value) - return ar -} - -func NewAPIRequest(method string, endpoint string, payload io.Reader) *APIRequest { - var headers = http.Header{} - var suffix string - ar := &APIRequest{method, endpoint, payload, headers, suffix} - return ar -} - -type Requester struct { - Base string - BasicAuth *BasicAuth - Client *http.Client - CACert []byte - SslVerify bool - connControl chan struct{} -} - -func (r *Requester) SetCrumb(ar *APIRequest) error { - crumbData := map[string]string{} - response, err := r.GetJSON("/crumbIssuer/api/json", &crumbData, nil) - if err != nil { - jenkinsError, ok := err.(*ErrorResponse) - if ok && jenkinsError.Response.StatusCode == http.StatusNotFound { - return nil - } - return err - } - if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" { - ar.SetHeader(crumbData["crumbRequestField"], crumbData["crumb"]) - } - - return nil -} - -func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { - ar := NewAPIRequest("POST", endpoint, payload) - if err := r.SetCrumb(ar); err != nil { - return nil, err - } - ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") - ar.Suffix = "api/json" - return r.Do(ar, responseStruct, querystring) -} - -func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { - ar := NewAPIRequest("POST", endpoint, payload) - if err := r.SetCrumb(ar); err != nil { - return nil, err - } - ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") - ar.Suffix = "" - return r.Do(ar, responseStruct, querystring) -} -func (r *Requester) PostForm(endpoint string, payload io.Reader, responseStruct interface{}, formString map[string]string) (*http.Response, error) { - ar := NewAPIRequest("POST", endpoint, payload) - if err := r.SetCrumb(ar); err != nil { - return nil, err - } - ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") - ar.Suffix = "" - return r.DoPostForm(ar, responseStruct, formString) -} - -func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) { - ar := NewAPIRequest("POST", endpoint, payload) - if err := r.SetCrumb(ar); err != nil { - return nil, err - } - return r.Do(ar, responseStruct, querystring, files) -} - -func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { - payload := bytes.NewBuffer([]byte(xml)) - ar := NewAPIRequest("POST", endpoint, payload) - if err := r.SetCrumb(ar); err != nil { - return nil, err - } - ar.SetHeader("Content-Type", "application/xml;charset=utf-8") - ar.Suffix = "" - return r.Do(ar, responseStruct, querystring) -} - -func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) { - ar := NewAPIRequest("GET", endpoint, nil) - ar.SetHeader("Content-Type", "application/json") - ar.Suffix = "api/json" - return r.Do(ar, responseStruct, query) -} - -func (r *Requester) GetXML(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) { - ar := NewAPIRequest("GET", endpoint, nil) - ar.SetHeader("Content-Type", "application/xml") - ar.Suffix = "" - return r.Do(ar, responseStruct, query) -} - -func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { - ar := NewAPIRequest("GET", endpoint, nil) - ar.Suffix = "" - return r.Do(ar, responseStruct, querystring) -} - -func (r *Requester) GetHtml(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { - ar := NewAPIRequest("GET", endpoint, nil) - ar.Suffix = "" - return r.DoGet(ar, responseStruct, querystring) -} - -func (r *Requester) SetClient(client *http.Client) *Requester { - r.Client = client - return r -} - -//Add auth on redirect if required. -func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error { - if r.BasicAuth != nil { - req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) - } - return nil -} - -func (r *Requester) DoGet(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) { - fileUpload := false - var files []string - URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) - - if err != nil { - return nil, err - } - - for _, o := range options { - switch v := o.(type) { - case map[string]string: - - querystring := make(url.Values) - for key, val := range v { - querystring.Set(key, val) - } - - URL.RawQuery = querystring.Encode() - case []string: - fileUpload = true - files = v - } - } - var req *http.Request - if fileUpload { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - for _, file := range files { - fileData, err := os.Open(file) - if err != nil { - Error.Println(err.Error()) - return nil, err - } - - part, err := writer.CreateFormFile("file", filepath.Base(file)) - if err != nil { - Error.Println(err.Error()) - return nil, err - } - if _, err = io.Copy(part, fileData); err != nil { - return nil, err - } - defer fileData.Close() - } - var params map[string]string - json.NewDecoder(ar.Payload).Decode(¶ms) - for key, val := range params { - if err = writer.WriteField(key, val); err != nil { - return nil, err - } - } - if err = writer.Close(); err != nil { - return nil, err - } - req, err = http.NewRequest(ar.Method, URL.String(), body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", writer.FormDataContentType()) - } else { - req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload) - if err != nil { - return nil, err - } - } - - if r.BasicAuth != nil { - req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) - } - req.Close = true - req.Header.Add("Accept", "*/*") - for k := range ar.Headers { - req.Header.Add(k, ar.Headers.Get(k)) - } - r.connControl <- struct{}{} - if response, err := r.Client.Do(req); err != nil { - <-r.connControl - return nil, err - } else { - <-r.connControl - errorText := response.Header.Get("X-Error") - if errorText != "" { - return nil, errors.New(errorText) - } - err := CheckResponse(response) - if err != nil { - return nil, err - } - switch responseStruct.(type) { - case *string: - return r.ReadRawResponse(response, responseStruct) - default: - return r.ReadJSONResponse(response, responseStruct) - } - - } - -} - -func (r *Requester) Do(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) { - if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" { - ar.Endpoint += "/" - } - - fileUpload := false - var files []string - URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) - - if err != nil { - return nil, err - } - - for _, o := range options { - switch v := o.(type) { - case map[string]string: - - querystring := make(url.Values) - for key, val := range v { - querystring.Set(key, val) - } - - URL.RawQuery = querystring.Encode() - case []string: - fileUpload = true - files = v - } - } - var req *http.Request - if fileUpload { - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - for _, file := range files { - fileData, err := os.Open(file) - if err != nil { - Error.Println(err.Error()) - return nil, err - } - - part, err := writer.CreateFormFile("file", filepath.Base(file)) - if err != nil { - Error.Println(err.Error()) - return nil, err - } - if _, err = io.Copy(part, fileData); err != nil { - return nil, err - } - defer fileData.Close() - } - var params map[string]string - json.NewDecoder(ar.Payload).Decode(¶ms) - for key, val := range params { - if err = writer.WriteField(key, val); err != nil { - return nil, err - } - } - if err = writer.Close(); err != nil { - return nil, err - } - req, err = http.NewRequest(ar.Method, URL.String(), body) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", writer.FormDataContentType()) - } else { - req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload) - if err != nil { - return nil, err - } - } - - if r.BasicAuth != nil { - req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) - } - req.Close = true - req.Header.Add("Accept", "*/*") - for k := range ar.Headers { - req.Header.Add(k, ar.Headers.Get(k)) - } - r.connControl <- struct{}{} - if response, err := r.Client.Do(req); err != nil { - <-r.connControl - return nil, err - } else { - <-r.connControl - errorText := response.Header.Get("X-Error") - if errorText != "" { - return nil, errors.New(errorText) - } - err := CheckResponse(response) - if err != nil { - return nil, err - } - switch responseStruct.(type) { - case *string: - return r.ReadRawResponse(response, responseStruct) - default: - return r.ReadJSONResponse(response, responseStruct) - } - - } - -} - -func (r *Requester) DoPostForm(ar *APIRequest, responseStruct interface{}, form map[string]string) (*http.Response, error) { - - if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" { - ar.Endpoint += "/" - } - URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) - - if err != nil { - return nil, err - } - formValue := make(url.Values) - for k, v := range form { - formValue.Set(k, v) - } - req, err := http.NewRequest("POST", URL.String(), strings.NewReader(formValue.Encode())) - if r.BasicAuth != nil { - req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) - } - req.Close = true - req.Header.Add("Accept", "*/*") - for k := range ar.Headers { - req.Header.Add(k, ar.Headers.Get(k)) - } - r.connControl <- struct{}{} - if response, err := r.Client.Do(req); err != nil { - <-r.connControl - return nil, err - } else { - <-r.connControl - errorText := response.Header.Get("X-Error") - if errorText != "" { - return nil, errors.New(errorText) - } - err := CheckResponse(response) - if err != nil { - return nil, err - } - switch responseStruct.(type) { - case *string: - return r.ReadRawResponse(response, responseStruct) - default: - return r.ReadJSONResponse(response, responseStruct) - } - - } -} - -func (r *Requester) ReadRawResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) { - defer response.Body.Close() - - content, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - if str, ok := responseStruct.(*string); ok { - *str = string(content) - } else { - return nil, fmt.Errorf("Could not cast responseStruct to *string") - } - - return response, nil -} - -func (r *Requester) ReadJSONResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) { - defer response.Body.Close() - err := json.NewDecoder(response.Body).Decode(responseStruct) - if err != nil && err.Error() == "EOF" { - return response, nil - } - return response, nil -} - -type ErrorResponse struct { - Body []byte - Response *http.Response - Message string -} - -func (e *ErrorResponse) Error() string { - u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, e.Response.Request.URL.RequestURI()) - return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message) -} -func CheckResponse(r *http.Response) error { - - switch r.StatusCode { - case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent, http.StatusFound, http.StatusNotModified: - return nil - } - defer r.Body.Close() - errorResponse := &ErrorResponse{Response: r} - data, err := ioutil.ReadAll(r.Body) - if err == nil && data != nil { - errorResponse.Body = data - errorResponse.Message = string(data) - } - - return errorResponse -} diff --git a/pkg/gojenkins/utils.go b/pkg/gojenkins/utils.go deleted file mode 100644 index 92b714492..000000000 --- a/pkg/gojenkins/utils.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "encoding/json" - "strings" - "time" - "unicode/utf8" -) - -func makeJson(data interface{}) string { - str, err := json.Marshal(data) - if err != nil { - return "" - } - return string(json.RawMessage(str)) -} - -func Reverse(s string) string { - size := len(s) - buf := make([]byte, size) - for start := 0; start < size; { - r, n := utf8.DecodeRuneInString(s[start:]) - start += n - utf8.EncodeRune(buf[size-start:], r) - } - return string(buf) -} - -type JenkinsBlueTime time.Time - -func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error { - if b == nil || strings.Trim(string(b), "\"") == "null" { - *t = JenkinsBlueTime(time.Time{}) - return nil - } - j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\"")) - - if err != nil { - return err - } - *t = JenkinsBlueTime(j) - return nil -} - -func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) { - return json.Marshal(time.Time(t)) -} diff --git a/pkg/gojenkins/utils/utils.go b/pkg/gojenkins/utils/utils.go deleted file mode 100644 index 93ad30b3b..000000000 --- a/pkg/gojenkins/utils/utils.go +++ /dev/null @@ -1,21 +0,0 @@ -package utils - -import ( - "github.com/asaskevich/govalidator" - "kubesphere.io/kubesphere/pkg/gojenkins" - "net/http" - "strconv" -) - -func GetJenkinsStatusCode(jenkinsErr error) int { - if code, err := strconv.Atoi(jenkinsErr.Error()); err == nil { - message := http.StatusText(code) - if !govalidator.IsNull(message) { - return code - } - } - if jErr, ok := jenkinsErr.(*gojenkins.ErrorResponse); ok { - return jErr.Response.StatusCode - } - return http.StatusInternalServerError -} diff --git a/pkg/gojenkins/views.go b/pkg/gojenkins/views.go deleted file mode 100644 index 99b802643..000000000 --- a/pkg/gojenkins/views.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2015 Vadim Kravcenko -// -// 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 gojenkins - -import ( - "errors" - "strconv" -) - -type View struct { - Raw *ViewResponse - Jenkins *Jenkins - Base string -} - -type ViewResponse struct { - Description string `json:"description"` - Jobs []InnerJob `json:"jobs"` - Name string `json:"name"` - Property []interface{} `json:"property"` - URL string `json:"url"` -} - -var ( - LIST_VIEW = "hudson.model.ListView" - NESTED_VIEW = "hudson.plugins.nested_view.NestedView" - MY_VIEW = "hudson.model.MyView" - DASHBOARD_VIEW = "hudson.plugins.view.dashboard.Dashboard" - PIPELINE_VIEW = "au.com.centrumsystems.hudson.plugin.buildpipeline.BuildPipelineView" -) - -// Returns True if successfully added Job, otherwise false -func (v *View) AddJob(name string) (bool, error) { - url := "/addJobToView" - qr := map[string]string{"name": name} - resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr) - if err != nil { - return false, err - } - if resp.StatusCode == 200 { - return true, nil - } - return false, errors.New(strconv.Itoa(resp.StatusCode)) -} - -// Returns True if successfully deleted Job, otherwise false -func (v *View) DeleteJob(name string) (bool, error) { - url := "/removeJobFromView" - qr := map[string]string{"name": name} - resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr) - if err != nil { - return false, err - } - if resp.StatusCode == 200 { - return true, nil - } - return false, errors.New(strconv.Itoa(resp.StatusCode)) -} - -func (v *View) GetDescription() string { - return v.Raw.Description -} - -func (v *View) GetJobs() []InnerJob { - return v.Raw.Jobs -} - -func (v *View) GetName() string { - return v.Raw.Name -} - -func (v *View) GetUrl() string { - return v.Raw.URL -} - -func (v *View) Poll() (int, error) { - response, err := v.Jenkins.Requester.GetJSON(v.Base, v.Raw, nil) - if err != nil { - return 0, err - } - return response.StatusCode, nil -} diff --git a/pkg/informers/informers.go b/pkg/informers/informers.go index f8db835b1..36a3bd6a7 100644 --- a/pkg/informers/informers.go +++ b/pkg/informers/informers.go @@ -18,56 +18,91 @@ package informers import ( + applicationclient "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned" applicationinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" - s2iinformers "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions" + istioclient "istio.io/client-go/pkg/clientset/versioned" + istioinformers "istio.io/client-go/pkg/informers/externalversions" k8sinformers "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned" ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/simple/client" - "sync" "time" ) +// default re-sync period for all informer factories const defaultResync = 600 * time.Second -var ( - k8sOnce sync.Once - s2iOnce sync.Once - ksOnce sync.Once - appOnce sync.Once - informerFactory k8sinformers.SharedInformerFactory - s2iInformerFactory s2iinformers.SharedInformerFactory - ksInformerFactory ksinformers.SharedInformerFactory - appInformerFactory applicationinformers.SharedInformerFactory -) +// InformerFactory is a group all shared informer factories which kubesphere needed +// callers should check if the return value is nil +type InformerFactory interface { + KubernetesSharedInformerFactory() k8sinformers.SharedInformerFactory + KubeSphereSharedInformerFactory() ksinformers.SharedInformerFactory + IstioSharedInformerFactory() istioinformers.SharedInformerFactory + ApplicationSharedInformerFactory() applicationinformers.SharedInformerFactory -func SharedInformerFactory() k8sinformers.SharedInformerFactory { - k8sOnce.Do(func() { - k8sClient := client.ClientSets().K8s().Kubernetes() - informerFactory = k8sinformers.NewSharedInformerFactory(k8sClient, defaultResync) - }) - return informerFactory + // Start shared informer factory one by one if they are not nil + Start(stopCh <-chan struct{}) } -func S2iSharedInformerFactory() s2iinformers.SharedInformerFactory { - s2iOnce.Do(func() { - k8sClient := client.ClientSets().K8s().S2i() - s2iInformerFactory = s2iinformers.NewSharedInformerFactory(k8sClient, defaultResync) - }) - return s2iInformerFactory +type informerFactories struct { + informerFactory k8sinformers.SharedInformerFactory + ksInformerFactory ksinformers.SharedInformerFactory + istioInformerFactory istioinformers.SharedInformerFactory + appInformerFactory applicationinformers.SharedInformerFactory } -func KsSharedInformerFactory() ksinformers.SharedInformerFactory { - ksOnce.Do(func() { - k8sClient := client.ClientSets().K8s().KubeSphere() - ksInformerFactory = ksinformers.NewSharedInformerFactory(k8sClient, defaultResync) - }) - return ksInformerFactory +func NewInformerFactories(client kubernetes.Interface, ksClient versioned.Interface, istioClient istioclient.Interface, appClient applicationclient.Interface) InformerFactory { + factory := &informerFactories{} + + if client != nil { + factory.informerFactory = k8sinformers.NewSharedInformerFactory(client, defaultResync) + } + + if ksClient != nil { + factory.ksInformerFactory = ksinformers.NewSharedInformerFactory(ksClient, defaultResync) + } + + if appClient != nil { + factory.appInformerFactory = applicationinformers.NewSharedInformerFactory(appClient, defaultResync) + } + + if istioClient != nil { + factory.istioInformerFactory = istioinformers.NewSharedInformerFactory(istioClient, defaultResync) + } + + return factory } -func AppSharedInformerFactory() applicationinformers.SharedInformerFactory { - appOnce.Do(func() { - appClient := client.ClientSets().K8s().Application() - appInformerFactory = applicationinformers.NewSharedInformerFactory(appClient, defaultResync) - }) - return appInformerFactory +func (f *informerFactories) KubernetesSharedInformerFactory() k8sinformers.SharedInformerFactory { + return f.informerFactory +} + +func (f *informerFactories) KubeSphereSharedInformerFactory() ksinformers.SharedInformerFactory { + return f.ksInformerFactory +} + +func (f *informerFactories) ApplicationSharedInformerFactory() applicationinformers.SharedInformerFactory { + return f.appInformerFactory +} + +func (f *informerFactories) IstioSharedInformerFactory() istioinformers.SharedInformerFactory { + return f.istioInformerFactory +} + +func (f *informerFactories) Start(stopCh <-chan struct{}) { + if f.informerFactory != nil { + f.informerFactory.Start(stopCh) + } + + if f.ksInformerFactory != nil { + f.ksInformerFactory.Start(stopCh) + } + + if f.istioInformerFactory != nil { + f.istioInformerFactory.Start(stopCh) + } + + if f.appInformerFactory != nil { + f.appInformerFactory.Start(stopCh) + } } diff --git a/pkg/informers/null_informers.go b/pkg/informers/null_informers.go new file mode 100644 index 000000000..b6957584c --- /dev/null +++ b/pkg/informers/null_informers.go @@ -0,0 +1,34 @@ +package informers + +import ( + appinformers "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + istioinformers "istio.io/client-go/pkg/informers/externalversions" + "k8s.io/client-go/informers" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" +) + +type nullInformerFactory struct { +} + +func NewNullInformerFactory() InformerFactory { + return &nullInformerFactory{} +} + +func (n nullInformerFactory) KubernetesSharedInformerFactory() informers.SharedInformerFactory { + return nil +} + +func (n nullInformerFactory) KubeSphereSharedInformerFactory() ksinformers.SharedInformerFactory { + return nil +} + +func (n nullInformerFactory) IstioSharedInformerFactory() istioinformers.SharedInformerFactory { + return nil +} + +func (n nullInformerFactory) ApplicationSharedInformerFactory() appinformers.SharedInformerFactory { + return nil +} + +func (n nullInformerFactory) Start(stopCh <-chan struct{}) { +} diff --git a/pkg/kapis/config/v1alpha2/register.go b/pkg/kapis/config/v1alpha2/register.go new file mode 100644 index 000000000..785d9c5a1 --- /dev/null +++ b/pkg/kapis/config/v1alpha2/register.go @@ -0,0 +1,51 @@ +/* + * + * Copyright 2020 The KubeSphere 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 v1alpha2 + +import ( + "github.com/emicklei/go-restful" + "k8s.io/apimachinery/pkg/runtime/schema" + apiserverconfig "kubesphere.io/kubesphere/pkg/apiserver/config" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" +) + +const ( + GroupName = "config.kubesphere.io" +) + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} + +func AddToContainer(c *restful.Container, config *apiserverconfig.Config) error { + webservice := runtime.NewWebService(GroupVersion) + + webservice.Route(webservice.GET("/configs/oauth"). + Doc("Information about the authorization server are published."). + To(func(request *restful.Request, response *restful.Response) { + response.WriteEntity(config.AuthenticationOptions.OAuthOptions) + })) + + webservice.Route(webservice.GET("/configs/configz"). + Doc("Information about the server configuration"). + To(func(request *restful.Request, response *restful.Response) { + response.WriteAsJson(config.ToMap()) + })) + + c.Add(webservice) + return nil +} diff --git a/pkg/kapis/devops/install/install.go b/pkg/kapis/devops/install/install.go deleted file mode 100644 index 8174c03f0..000000000 --- a/pkg/kapis/devops/install/install.go +++ /dev/null @@ -1,34 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/apiserver/devops/OWNERS b/pkg/kapis/devops/v1alpha2/OWNERS similarity index 100% rename from pkg/apiserver/devops/OWNERS rename to pkg/kapis/devops/v1alpha2/OWNERS diff --git a/pkg/kapis/devops/v1alpha2/devops.go b/pkg/kapis/devops/v1alpha2/devops.go new file mode 100644 index 000000000..c9a8d5006 --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/devops.go @@ -0,0 +1,648 @@ +/* + + Copyright 2019 The KubeSphere 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 v1alpha2 + +import ( + "github.com/emicklei/go-restful" + log "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/models/devops" + "net/http" + "strings" +) + +const jenkinsHeaderPre = "X-" + +func (h *ProjectPipelineHandler) GetPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.GetPipeline(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ListPipelines(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ListPipelines(req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetPipelineRun(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetPipelineRun(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ListPipelineRuns(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.ListPipelineRuns(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) StopPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.StopPipeline(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ReplayPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.ReplayPipeline(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) RunPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.RunPipeline(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetArtifacts(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetArtifacts(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetRunLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetRunLog(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetStepLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") + + res, header, err := h.devopsOperator.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + for k, v := range header { + if strings.HasPrefix(k, jenkinsHeaderPre) { + resp.AddHeader(k, v[0]) + } + } + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetNodeSteps(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + + res, err := h.devopsOperator.GetNodeSteps(projectName, pipelineName, runId, nodeId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetPipelineRunNodes(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetPipelineRunNodes(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") + + res, err := h.devopsOperator.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetNodesDetail(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetNodesDetail(projectName, pipelineName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetBranchPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + + res, err := h.devopsOperator.GetBranchPipeline(projectName, pipelineName, branchName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetBranchPipelineRun(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) StopBranchPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.StopBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ReplayBranchPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) RunBranchPipeline(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + + res, err := h.devopsOperator.RunBranchPipeline(projectName, pipelineName, branchName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetBranchArtifacts(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetBranchArtifacts(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetBranchRunLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetBranchRunLog(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetBranchStepLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") + + res, header, err := h.devopsOperator.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) + + if err != nil { + parseErr(err, resp) + return + } + for k, v := range header { + if strings.HasPrefix(k, jenkinsHeaderPre) { + resp.AddHeader(k, v[0]) + } + } + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetBranchNodeSteps(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + + res, err := h.devopsOperator.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetBranchPipelineRunNodes(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) SubmitBranchInputStep(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") + + res, err := h.devopsOperator.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetBranchNodesDetail(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + + res, err := h.devopsOperator.GetBranchNodesDetail(projectName, pipelineName, branchName, runId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetPipelineBranch(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.GetPipelineBranch(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ScanBranch(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.ScanBranch(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetConsoleLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.GetConsoleLog(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GetCrumb(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetCrumb(req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetSCMServers(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + + res, err := h.devopsOperator.GetSCMServers(scmId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetSCMOrg(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + + res, err := h.devopsOperator.GetSCMOrg(scmId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetOrgRepo(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + organizationId := req.PathParameter("organization") + + res, err := h.devopsOperator.GetOrgRepo(scmId, organizationId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) CreateSCMServers(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + + res, err := h.devopsOperator.CreateSCMServers(scmId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) Validate(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + + res, err := h.devopsOperator.Validate(scmId, req.Request) + if err != nil { + log.Error(err) + if jErr, ok := err.(*devops.JkError); ok { + if jErr.Code != http.StatusUnauthorized { + resp.WriteError(jErr.Code, err) + } else { + resp.WriteHeader(http.StatusPreconditionRequired) + } + } else { + resp.WriteError(http.StatusInternalServerError, err) + } + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetNotifyCommit(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetNotifyCommit(req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Write(res) +} + +func (h *ProjectPipelineHandler) PostNotifyCommit(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetNotifyCommit(req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Write(res) +} + +func (h *ProjectPipelineHandler) GithubWebhook(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GithubWebhook(req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Write(res) +} + +func (h *ProjectPipelineHandler) CheckScriptCompile(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + resBody, err := h.devopsOperator.CheckScriptCompile(projectName, pipelineName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.WriteAsJson(resBody) +} + +func (h *ProjectPipelineHandler) CheckCron(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + + res, err := h.devopsOperator.CheckCron(projectName, req.Request) + if err != nil { + parseErr(err, resp) + return + } + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ToJenkinsfile(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ToJenkinsfile(req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) ToJson(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ToJson(req.Request) + if err != nil { + parseErr(err, resp) + return + } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} + +func (h *ProjectPipelineHandler) GetProjectCredentialUsage(req *restful.Request, resp *restful.Response) { + projectId := req.PathParameter("devops") + credentialId := req.PathParameter("credential") + response, err := h.projectCredentialGetter.GetProjectCredentialUsage(projectId, credentialId) + if err != nil { + log.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + resp.WriteAsJson(response) + return + +} + +func parseErr(err error, resp *restful.Response) { + log.Error(err) + if jErr, ok := err.(*devops.JkError); ok { + resp.WriteError(jErr.Code, err) + } else { + resp.WriteError(http.StatusInternalServerError, err) + } + return +} diff --git a/pkg/kapis/devops/v1alpha2/handler.go b/pkg/kapis/devops/v1alpha2/handler.go new file mode 100644 index 000000000..39ebd4acb --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/handler.go @@ -0,0 +1,36 @@ +package v1alpha2 + +import ( + "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/devops" + devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" +) + +type ProjectPipelineHandler struct { + devopsOperator devops.DevopsOperator + projectCredentialGetter devops.ProjectCredentialGetter +} + +type PipelineSonarHandler struct { + pipelineSonarGetter devops.PipelineSonarGetter +} + +func NewProjectPipelineHandler(devopsClient devopsClient.Interface) ProjectPipelineHandler { + return ProjectPipelineHandler{ + devopsOperator: devops.NewDevopsOperator(devopsClient), + projectCredentialGetter: devops.NewProjectCredentialOperator(devopsClient), + } +} + +func NewPipelineSonarHandler(devopsClient devopsClient.Interface, sonarClient sonarqube.SonarInterface) PipelineSonarHandler { + return PipelineSonarHandler{ + pipelineSonarGetter: devops.NewPipelineSonarGetter(devopsClient, sonarClient), + } +} + +func NewS2iBinaryHandler(client versioned.Interface, informers externalversions.SharedInformerFactory, s3Client s3.Interface) S2iBinaryHandler { + return S2iBinaryHandler{devops.NewS2iBinaryUploader(client, informers, s3Client)} +} diff --git a/pkg/kapis/devops/v1alpha2/pipeline_sonar.go b/pkg/kapis/devops/v1alpha2/pipeline_sonar.go new file mode 100644 index 000000000..163162fa4 --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/pipeline_sonar.go @@ -0,0 +1,32 @@ +package v1alpha2 + +import ( + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" +) + +func (h PipelineSonarHandler) GetPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) { + projectId := request.PathParameter("devops") + pipelineId := request.PathParameter("pipeline") + sonarStatus, err := h.pipelineSonarGetter.GetPipelineSonar(projectId, pipelineId) + if err != nil { + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + resp.WriteAsJson(sonarStatus) +} + +func (h PipelineSonarHandler) GetMultiBranchesPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) { + projectId := request.PathParameter("devops") + pipelineId := request.PathParameter("pipeline") + branchId := request.PathParameter("branch") + sonarStatus, err := h.pipelineSonarGetter.GetMultiBranchPipelineSonar(projectId, pipelineId, branchId) + if err != nil { + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + resp.WriteAsJson(sonarStatus) +} diff --git a/pkg/kapis/devops/v1alpha2/register.go b/pkg/kapis/devops/v1alpha2/register.go index ea7668192..f2c7fdb98 100644 --- a/pkg/kapis/devops/v1alpha2/register.go +++ b/pkg/kapis/devops/v1alpha2/register.go @@ -22,14 +22,17 @@ import ( "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" - devopsapi "kubesphere.io/kubesphere/pkg/apiserver/devops" "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/simple/client/s3" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + + //"kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/simple/client/devops" - "kubesphere.io/kubesphere/pkg/server/params" "net/http" ) @@ -40,777 +43,647 @@ const ( var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) +func AddToContainer(container *restful.Container, ksInformers externalversions.SharedInformerFactory, devopsClient devops.Interface, sonarqubeClient sonarqube.SonarInterface, ksClient versioned.Interface, s3Client s3.Interface) error { + ws := runtime.NewWebService(GroupVersion) -func addWebService(c *restful.Container) error { + err := AddPipelineToWebService(ws, devopsClient) + if err != nil { + return err + } - webservice := runtime.NewWebService(GroupVersion) + err = AddSonarToWebService(ws, devopsClient, sonarqubeClient) + if err != nil { + return err + } - webservice.Route(webservice.GET("/devops/{devops}"). - To(devopsapi.GetDevOpsProjectHandler). - Doc("Get the specified DevOps Project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Returns(http.StatusOK, RespOK, v1alpha2.DevOpsProject{}). - Writes(v1alpha2.DevOpsProject{})) + err = AddS2IToWebService(ws, ksClient, ksInformers, s3Client) + if err != nil { + return err + } - webservice.Route(webservice.PATCH("/devops/{devops}"). - To(devopsapi.UpdateProjectHandler). - Doc("Update the specified DevOps Project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Reads(v1alpha2.DevOpsProject{}). - Returns(http.StatusOK, RespOK, v1alpha2.DevOpsProject{}). - Writes(v1alpha2.DevOpsProject{})) - - webservice.Route(webservice.GET("/devops/{devops}/defaultroles"). - To(devopsapi.GetDevOpsProjectDefaultRoles). - Doc("Get the build-in roles info of the specified DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Returns(http.StatusOK, RespOK, []devops.Role{}). - Writes([]devops.Role{})) - - webservice.Route(webservice.GET("/devops/{devops}/members"). - To(devopsapi.GetDevOpsProjectMembersHandler). - Doc("Get the members of the specified DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.QueryParameter(params.PagingParam, "page"). - Required(false). - DataFormat("limit=%d,page=%d"). - DefaultValue("limit=10,page=1")). - Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, support using key-value pairs separated by comma to search, like 'conditions:somekey=somevalue,anotherkey=anothervalue'"). - Required(false). - DataFormat("key=%s,key~%s")). - Returns(http.StatusOK, RespOK, []devops.DevOpsProjectMembership{}). - Writes([]devops.DevOpsProjectMembership{})) - - webservice.Route(webservice.GET("/devops/{devops}/members/{member}"). - To(devopsapi.GetDevOpsProjectMemberHandler). - Doc("Get the specified member of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("member", "member's username, e.g. admin")). - Returns(http.StatusOK, RespOK, devops.DevOpsProjectMembership{}). - Writes(devops.DevOpsProjectMembership{})) - - webservice.Route(webservice.POST("/devops/{devops}/members"). - To(devopsapi.AddDevOpsProjectMemberHandler). - Doc("Add a member to the specified DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Returns(http.StatusOK, RespOK, devops.DevOpsProjectMembership{}). - Writes(devops.DevOpsProjectMembership{}). - Reads(devops.DevOpsProjectMembership{})) - - webservice.Route(webservice.PATCH("/devops/{devops}/members/{member}"). - To(devopsapi.UpdateDevOpsProjectMemberHandler). - Doc("Update the specified member of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("member", "member's username, e.g. admin")). - Returns(http.StatusOK, RespOK, devops.DevOpsProjectMembership{}). - Reads(devops.DevOpsProjectMembership{}). - Writes(devops.DevOpsProjectMembership{})) - - webservice.Route(webservice.DELETE("/devops/{devops}/members/{member}"). - To(devopsapi.DeleteDevOpsProjectMemberHandler). - Doc("Delete the specified member of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("member", "member's username, e.g. admin")). - Writes(devops.DevOpsProjectMembership{})) - - webservice.Route(webservice.POST("/devops/{devops}/pipelines"). - To(devopsapi.CreateDevOpsProjectPipelineHandler). - Doc("Create a DevOps project pipeline"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Returns(http.StatusOK, RespOK, devops.ProjectPipeline{}). - Writes(devops.ProjectPipeline{}). - Reads(devops.ProjectPipeline{})) - - webservice.Route(webservice.PUT("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.UpdateDevOpsProjectPipelineHandler). - Doc("Update the specified pipeline of the DevOps project"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Writes(devops.ProjectPipeline{}). - Reads(devops.ProjectPipeline{})) - - webservice.Route(webservice.DELETE("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.DeleteDevOpsProjectPipelineHandler). - Doc("Delete the specified pipeline of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline"))) - - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/config"). - To(devopsapi.GetDevOpsProjectPipelineHandler). - Doc("Get the configuration information of the specified pipeline of the DevOps Project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). - Returns(http.StatusOK, RespOK, devops.ProjectPipeline{}). - Writes(devops.ProjectPipeline{})) - - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/sonarstatus"). - To(devopsapi.GetPipelineSonarStatusHandler). - Doc("Get the sonar quality information for the specified pipeline of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). - Returns(http.StatusOK, RespOK, []devops.SonarStatus{}). - Writes([]devops.SonarStatus{})) - - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/sonarstatus"). - To(devopsapi.GetMultiBranchesPipelineSonarStatusHandler). - Doc("Get the sonar quality check information for the specified pipeline branch of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). - Param(webservice.PathParameter("branch", "branch name, e.g. master")). - Returns(http.StatusOK, RespOK, []devops.SonarStatus{}). - Writes([]devops.SonarStatus{})) - - webservice.Route(webservice.POST("/devops/{devops}/credentials"). - To(devopsapi.CreateDevOpsProjectCredentialHandler). - Doc("Create a credential in the specified DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Reads(devops.JenkinsCredential{})) - - webservice.Route(webservice.PUT("/devops/{devops}/credentials/{credential}"). - To(devopsapi.UpdateDevOpsProjectCredentialHandler). - Doc("Update the specified credential of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id")). - Reads(devops.JenkinsCredential{})) - - webservice.Route(webservice.DELETE("/devops/{devops}/credentials/{credential}"). - To(devopsapi.DeleteDevOpsProjectCredentialHandler). - Doc("Delete the specified credential of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id"))) - - webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}"). - To(devopsapi.GetDevOpsProjectCredentialHandler). - Doc("Get the specified credential of the DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id")). - Param(webservice.QueryParameter("content", ` -Get extra credential content if this query parameter is set. -Specifically, there are three types of info in a credential. One is the basic info that must be returned for each query such as name, id, etc. -The second one is non-encrypted info such as the username of the username-password type of credential, which returns when the "content" parameter is set to non-empty. -The last one is encrypted info, such as the password of the username-password type of credential, which never returns. -`)). - Returns(http.StatusOK, RespOK, devops.JenkinsCredential{})) - - webservice.Route(webservice.GET("/devops/{devops}/credentials"). - To(devopsapi.GetDevOpsProjectCredentialsHandler). - Doc("Get all credentials of the specified DevOps project"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Returns(http.StatusOK, RespOK, []devops.JenkinsCredential{})) - - // match Jenkisn api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.GetPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get the specified pipeline of the DevOps project"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Returns(http.StatusOK, RespOK, devops.Pipeline{}). - Writes(devops.Pipeline{})) - - // match Jenkisn api: "jenkins_api/blue/rest/search" - webservice.Route(webservice.GET("/search"). - To(devopsapi.SearchPipelines). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Search DevOps resource. More info: https://github.com/jenkinsci/blueocean-plugin/tree/master/blueocean-rest#get-pipelines-across-organization"). - Param(webservice.QueryParameter("q", "query pipelines, condition for filtering."). - Required(true). - DataFormat("q=%s")). - Param(webservice.QueryParameter("filter", "Filter some types of jobs. e.g. no-folder,will not get a job of type folder"). - Required(false). - DataFormat("filter=%s")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d")). - Param(webservice.QueryParameter("limit", "the limit item count of the search."). - Required(false). - DataFormat("limit=%d")). - Returns(http.StatusOK, RespOK, struct { - Items []devops.Pipeline `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.Pipeline `json:"items"` - Total int `json:"total_count"` - }{})) - - // match Jenkisn api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs"). - To(devopsapi.SearchPipelineRuns). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all runs of the specified pipeline"). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.QueryParameter("start", "the item number that the search starts from"). - Required(false). - DataFormat("start=%d")). - Param(webservice.QueryParameter("limit", "the limit item count of the search"). - Required(false). - DataFormat("limit=%d")). - Param(webservice.QueryParameter("branch", "the name of branch, same as repository branch, will be filtered by branch."). - Required(false). - DataFormat("branch=%s")). - Returns(http.StatusOK, RespOK, struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{})) - - // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}"). - To(devopsapi.GetBranchPipelineRun). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all runs in the specified branch"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.BranchPipelineRun{}). - Writes(devops.BranchPipelineRun{})) - - // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes"). - To(devopsapi.GetPipelineRunNodesbyBranch). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get run nodes."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). - Param(webservice.QueryParameter("limit", "the limit item count of the search."). - Required(false). - DataFormat("limit=%d"). - DefaultValue("limit=10000")). - Returns(http.StatusOK, RespOK, []devops.BranchPipelineRunNodes{}). - Writes([]devops.BranchPipelineRunNodes{})) - - // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log"). - To(devopsapi.GetBranchStepLog). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get the step logs in the specified pipeline activity."). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node id, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step id, the step in pipeline.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d"). - DefaultValue("start=0"))) - - // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log"). - To(devopsapi.GetStepLog). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get pipelines step log."). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step ID, the step in pipeline.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d"). - DefaultValue("start=0"))) - - // match "/blue/rest/organizations/jenkins/scm/github/validate/" - webservice.Route(webservice.POST("/scms/{scm}/verify"). - To(devopsapi.Validate). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("Validate the access token of the specified source configuration management (SCM) such as Github"). - Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). - Returns(http.StatusOK, RespOK, devops.Validates{}). - Writes(devops.Validates{})) - - // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/?credentialId=github" - webservice.Route(webservice.GET("/scms/{scm}/organizations"). - To(devopsapi.GetSCMOrg). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all organizations of the specified source configuration management (SCM) such as Github."). - Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). - Param(webservice.QueryParameter("credentialId", "credential ID for source configuration management (SCM)."). - Required(true). - DataFormat("credentialId=%s")). - Returns(http.StatusOK, RespOK, []devops.SCMOrg{}). - Writes([]devops.SCMOrg{})) - - // match "/blue/rest/organizations/jenkins/scm/%s/servers/" - webservice.Route(webservice.GET("/scms/{scm}/servers"). - To(devopsapi.GetSCMServers). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all servers in the jenkins."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Returns(http.StatusOK, RespOK, []devops.SCMServer{}). - Writes([]devops.SCMServer{})) - - // match "/blue/rest/organizations/jenkins/scm/%s/servers/" - webservice.Route(webservice.POST("/scms/{scm}/servers"). - To(devopsapi.CreateSCMServers). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("Create scm server in the jenkins."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Reads(devops.CreateScmServerReq{}). - Returns(http.StatusOK, RespOK, devops.SCMServer{}). - Writes(devops.SCMServer{})) - - // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/{organization}/repositories/?credentialId=&pageNumber&pageSize=" - webservice.Route(webservice.GET("/scms/{scm}/organizations/{organization}/repositories"). - To(devopsapi.GetOrgRepo). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all repositories in the specified organization."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Param(webservice.PathParameter("organization", "organization ID, such as github username.")). - Param(webservice.QueryParameter("credentialId", "credential ID for SCM."). - Required(true). - DataFormat("credentialId=%s")). - Param(webservice.QueryParameter("pageNumber", "page number."). - Required(true). - DataFormat("pageNumber=%d")). - Param(webservice.QueryParameter("pageSize", "the item count of one page."). - Required(true). - DataFormat("pageSize=%d")). - Returns(http.StatusOK, RespOK, []devops.OrgRepo{}). - Writes([]devops.OrgRepo{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop"). - To(devopsapi.StopBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Stop the specified pipeline of the DevOps project."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). - Required(false). - DataFormat("blocking=%t"). - DefaultValue("blocking=false")). - Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). - Required(false). - DataFormat("timeOutInSecs=%d"). - DefaultValue("timeOutInSecs=10")). - Returns(http.StatusOK, RespOK, devops.StopPipe{}). - Writes(devops.StopPipe{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/stop/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/stop"). - To(devopsapi.StopPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Stop pipeline"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). - Required(false). - DataFormat("blocking=%t"). - DefaultValue("blocking=false")). - Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). - Required(false). - DataFormat("timeOutInSecs=%d"). - DefaultValue("timeOutInSecs=10")). - Returns(http.StatusOK, RespOK, devops.StopPipe{}). - Writes(devops.StopPipe{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/Replay/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/replay"). - To(devopsapi.ReplayBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Replay the specified pipeline of the DevOps project"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.ReplayPipe{}). - Writes(devops.ReplayPipe{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/Replay/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/replay"). - To(devopsapi.ReplayPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Replay pipeline"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.ReplayPipe{}). - Writes(devops.ReplayPipe{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/log/?start=0 - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/log"). - To(devopsapi.GetBranchRunLog). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get run logs of the specified pipeline activity."). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d"). - DefaultValue("start=0"))) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/log/?start=0 - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/log"). - To(devopsapi.GetRunLog). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get run logs of the specified pipeline activity."). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d"). - DefaultValue("start=0"))) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/artifacts - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/artifacts"). - To(devopsapi.GetBranchArtifacts). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all artifacts generated from the specified run of the pipeline branch."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d")). - Param(webservice.QueryParameter("limit", "the limit item count of the search."). - Required(false). - DataFormat("limit=%d")). - Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). - Writes([]devops.Artifacts{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/artifacts - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/artifacts"). - To(devopsapi.GetArtifacts). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all artifacts in the specified pipeline."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d")). - Param(webservice.QueryParameter("limit", "the limit item count of the search."). - Required(false). - DataFormat("limit=%d")). - Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). - Writes([]devops.Artifacts{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/?filter=&start&limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches"). - To(devopsapi.GetPipeBranch). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all branches in the specified pipeline."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.QueryParameter("filter", "filter remote scm. e.g. origin"). - Required(false). - DataFormat("filter=%s")). - Param(webservice.QueryParameter("start", "the count of branches start."). - Required(false). - DataFormat("start=%d").DefaultValue("start=0")). - Param(webservice.QueryParameter("limit", "the count of branches limit."). - Required(false). - DataFormat("limit=%d").DefaultValue("limit=100")). - Returns(http.StatusOK, RespOK, []devops.PipeBranch{}). - Writes([]devops.PipeBranch{})) - - // /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step} - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}"). - To(devopsapi.SubmitBranchInputStep). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Proceed or Break the paused pipeline which waiting for user input."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step ID, the step in pipeline.")). - Reads(devops.CheckPlayload{}). - Produces("text/plain; charset=utf-8")) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step} - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}"). - To(devopsapi.SubmitInputStep). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Proceed or Break the paused pipeline which is waiting for user input."). - Reads(devops.CheckPlayload{}). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step ID"))) - - // match /job/project-8QnvykoJw4wZ/job/test-1/indexing/consoleText - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/consolelog"). - To(devopsapi.GetConsoleLog). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get scan reponsitory logs in the specified pipeline."). - Produces("text/plain; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline"))) - - // match /job/{devops}/job/{pipeline}/build?delay=0 - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/scan"). - To(devopsapi.ScanBranch). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Scan remote Repository, Start a build if have new branch."). - Produces("text/html; charset=utf-8"). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.QueryParameter("delay", "the delay time to scan"). - Required(false). - DataFormat("delay=%d"))) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{}/runs/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs"). - To(devopsapi.RunBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Run the specified pipeline of the DevOps project."). - Reads(devops.RunPayload{}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Returns(http.StatusOK, RespOK, devops.QueuedBlueRun{}). - Writes(devops.QueuedBlueRun{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs"). - To(devopsapi.RunPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Run pipeline."). - Reads(devops.RunPayload{}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Returns(http.StatusOK, RespOK, devops.QueuedBlueRun{}). - Writes(devops.QueuedBlueRun{})) - - // match /crumbIssuer/api/json/ - webservice.Route(webservice.GET("/crumbissuer"). - To(devopsapi.GetCrumb). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get crumb issuer. A CrumbIssuer represents an algorithm to generate a nonce value, known as a crumb, to counter cross site request forgery exploits. Crumbs are typically hashes incorporating information that uniquely identifies an agent that sends a request, along with a guarded secret so that the crumb value cannot be forged by a third party."). - Returns(http.StatusOK, RespOK, devops.Crumb{}). - Writes(devops.Crumb{})) - - webservice.Route(webservice.PUT("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file"). - To(devopsapi.UploadS2iBinary). - Consumes("multipart/form-data"). - Produces(restful.MIME_JSON). - Doc("Upload S2iBinary file"). - Param(webservice.PathParameter("namespace", "the name of namespaces")). - Param(webservice.PathParameter("s2ibinary", "the name of s2ibinary")). - Param(webservice.FormParameter("s2ibinary", "file to upload")). - Param(webservice.FormParameter("md5", "md5 of file")). - Returns(http.StatusOK, RespOK, devopsv1alpha1.S2iBinary{})) - - webservice.Route(webservice.GET("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file/{file}"). - To(devopsapi.DownloadS2iBinary). - Produces(restful.MIME_OCTET). - Doc("Download S2iBinary file"). - Param(webservice.PathParameter("namespace", "the name of namespaces")). - Param(webservice.PathParameter("s2ibinary", "the name of s2ibinary")). - Param(webservice.PathParameter("file", "the name of binary file")). - Returns(http.StatusOK, RespOK, nil)) - - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/checkScriptCompile"). - To(devopsapi.CheckScriptCompile). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.QueryParameter("pipeline", "the name of the CI/CD pipeline"). - Required(false). - DataFormat("pipeline=%s")). - Consumes("application/x-www-form-urlencoded", "charset=utf-8"). - Produces("application/json", "charset=utf-8"). - Doc("Check pipeline script compile."). - Reads(devops.ReqScript{}). - Returns(http.StatusOK, RespOK, devops.CheckScript{}). - Writes(devops.CheckScript{})) - - webservice.Route(webservice.POST("/devops/{devops}/checkCron"). - To(devopsapi.CheckCron). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Produces("application/json", "charset=utf-8"). - Doc("Check cron script compile."). - Reads(devops.CronData{}). - Returns(http.StatusOK, RespOK, devops.CheckCronRes{}). - Writes(devops.CheckCronRes{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/ - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}"). - To(devopsapi.GetPipelineRun). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all activities in the specified pipeline."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.PipelineRun{}). - Writes(devops.PipelineRun{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch} - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}"). - To(devopsapi.GetBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all activities in the specified pipeline."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch")). - Returns(http.StatusOK, RespOK, devops.BranchPipeline{}). - Writes(devops.BranchPipeline{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/?limit=10000 - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes"). - To(devopsapi.GetPipelineRunNodes). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all nodes in the specified activity. node is the stage in the pipeline task"). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). - Returns(http.StatusOK, RespOK, []devops.PipelineRunNodes{}). - Writes([]devops.PipelineRunNodes{})) - - // match /blue/rest/organizations/jenkins/pipelines/%s/%s/branches/%s/runs/%s/nodes/%s/steps/?limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps"). - To(devopsapi.GetBranchNodeSteps). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all steps in the specified node."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). - Writes([]devops.NodeSteps{})) - - // match /blue/rest/organizations/jenkins/pipelines/%s/%s/runs/%s/nodes/%s/steps/?limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps"). - To(devopsapi.GetNodeSteps). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all steps in the specified node."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). - Writes([]devops.NodeSteps{})) - - // match /pipeline-model-converter/toJenkinsfile - webservice.Route(webservice.POST("/tojenkinsfile"). - To(devopsapi.ToJenkinsfile). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). - Consumes("application/x-www-form-urlencoded"). - Produces("application/json", "charset=utf-8"). - Doc("Convert json to jenkinsfile format."). - Reads(devops.ReqJson{}). - Returns(http.StatusOK, RespOK, devops.ResJenkinsfile{}). - Writes(devops.ResJenkinsfile{})) - - // match /pipeline-model-converter/toJson - webservice.Route(webservice.POST("/tojson"). - To(devopsapi.ToJson). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). - Consumes("application/x-www-form-urlencoded"). - Produces("application/json", "charset=utf-8"). - Doc("Convert jenkinsfile to json format. Usually the frontend uses json to show or edit pipeline"). - Reads(devops.ReqJenkinsfile{}). - Returns(http.StatusOK, RespOK, devops.ResJson{}). - Writes(devops.ResJson{})) - - // match /git/notifyCommit/?url= - webservice.Route(webservice.GET("/webhook/git"). - To(devopsapi.GetNotifyCommit). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification by HTTP GET method. Git webhook will request here."). - Produces("text/plain; charset=utf-8"). - Param(webservice.QueryParameter("url", "Git url"). - Required(true). - DataFormat("url=%s"))) - - // Gitlab or some other scm managers can only use HTTP method. match /git/notifyCommit/?url= - webservice.Route(webservice.POST("/webhook/git"). - To(devopsapi.PostNotifyCommit). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification by HTTP POST method. Git webhook will request here."). - Consumes("application/json"). - Produces("text/plain; charset=utf-8"). - Param(webservice.QueryParameter("url", "Git url"). - Required(true). - DataFormat("url=%s"))) - - webservice.Route(webservice.POST("/webhook/github"). - To(devopsapi.GithubWebhook). - Consumes("application/x-www-form-urlencoded", "application/json"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification. Github webhook will request here.")) - - // in scm get all steps in nodes. - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodesdetail"). - To(devopsapi.GetBranchNodesDetail). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get steps details in an activity node. For a node, the steps which is defined inside the node."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). - Writes(devops.NodesDetail{})) - - // out of scm get all steps in nodes. - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodesdetail"). - To(devopsapi.GetNodesDetail). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get steps details inside a activity node. For a node, the steps which defined inside the node."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). - Writes(devops.NodesDetail{})) - - c.Add(webservice) + container.Add(ws) return nil } + +func AddPipelineToWebService(webservice *restful.WebService, devopsClient devops.Interface) error { + + projectPipelineEnable := devopsClient != nil + + if projectPipelineEnable { + projectPipelineHandler := NewProjectPipelineHandler(devopsClient) + + webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}/usage"). + To(projectPipelineHandler.GetProjectCredentialUsage). + Doc("Get the specified credential usage of the DevOps project"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id")). + Returns(http.StatusOK, RespOK, devops.Credential{})) + + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}"). + To(projectPipelineHandler.GetPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get the specified pipeline of the DevOps project"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Returns(http.StatusOK, RespOK, devops.Pipeline{}). + Writes(devops.Pipeline{})) + + // match Jenkins api: "jenkins_api/blue/rest/search" + webservice.Route(webservice.GET("/search"). + To(projectPipelineHandler.ListPipelines). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Search DevOps resource. More info: https://github.com/jenkinsci/blueocean-plugin/tree/master/blueocean-rest#get-pipelines-across-organization"). + Param(webservice.QueryParameter("q", "query pipelines, condition for filtering."). + Required(true). + DataFormat("q=%s")). + Param(webservice.QueryParameter("filter", "Filter some types of jobs. e.g. no-folder,will not get a job of type folder"). + Required(false). + DataFormat("filter=%s")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d")). + Param(webservice.QueryParameter("limit", "the limit item count of the search."). + Required(false). + DataFormat("limit=%d")). + Returns(http.StatusOK, RespOK, devops.PipelineList{}). + Writes(devops.PipelineList{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/ + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}"). + To(projectPipelineHandler.GetPipelineRun). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get details in the specified pipeline activity."). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.PipelineRun{}). + Writes(devops.PipelineRun{})) + + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs"). + To(projectPipelineHandler.ListPipelineRuns). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all runs of the specified pipeline"). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.QueryParameter("start", "the item number that the search starts from"). + Required(false). + DataFormat("start=%d")). + Param(webservice.QueryParameter("limit", "the limit item count of the search"). + Required(false). + DataFormat("limit=%d")). + Param(webservice.QueryParameter("branch", "the name of branch, same as repository branch, will be filtered by branch."). + Required(false). + DataFormat("branch=%s")). + Returns(http.StatusOK, RespOK, devops.PipelineRunList{}). + Writes(devops.PipelineRunList{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/stop/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/stop"). + To(projectPipelineHandler.StopPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Stop pipeline"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). + Required(false). + DataFormat("blocking=%t"). + DefaultValue("blocking=false")). + Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). + Required(false). + DataFormat("timeOutInSecs=%d"). + DefaultValue("timeOutInSecs=10")). + Returns(http.StatusOK, RespOK, devops.StopPipeline{}). + Writes(devops.StopPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/Replay/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/replay"). + To(projectPipelineHandler.ReplayPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Replay pipeline"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.ReplayPipeline{}). + Writes(devops.ReplayPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs"). + To(projectPipelineHandler.RunPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Run pipeline."). + Reads(devops.RunPayload{}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Returns(http.StatusOK, RespOK, devops.RunPipeline{}). + Writes(devops.RunPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/artifacts + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/artifacts"). + To(projectPipelineHandler.GetArtifacts). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all artifacts in the specified pipeline."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d")). + Param(webservice.QueryParameter("limit", "the limit item count of the search."). + Required(false). + DataFormat("limit=%d")). + Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). + Writes([]devops.Artifacts{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/log/?start=0 + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/log"). + To(projectPipelineHandler.GetRunLog). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get run logs of the specified pipeline activity."). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d"). + DefaultValue("start=0"))) + + // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log"). + To(projectPipelineHandler.GetStepLog). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get pipelines step log."). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step ID, the step in pipeline.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d"). + DefaultValue("start=0"))) + + // match /blue/rest/organizations/jenkins/pipelines/%s/%s/runs/%s/nodes/%s/steps/?limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps"). + To(projectPipelineHandler.GetNodeSteps). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all steps in the specified node."). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). + Writes([]devops.NodeSteps{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/?limit=10000 + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes"). + To(projectPipelineHandler.GetPipelineRunNodes). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all nodes in the specified activity. node is the stage in the pipeline task"). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). + Returns(http.StatusOK, RespOK, []devops.PipelineRunNodes{}). + Writes([]devops.PipelineRunNodes{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step} + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}"). + To(projectPipelineHandler.SubmitInputStep). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Proceed or Break the paused pipeline which is waiting for user input."). + Reads(devops.CheckPlayload{}). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step ID"))) + + // out of scm get all steps in nodes. + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodesdetail"). + To(projectPipelineHandler.GetNodesDetail). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get steps details inside a activity node. For a node, the steps which defined inside the node."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). + Writes(devops.NodesDetail{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch} + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}"). + To(projectPipelineHandler.GetBranchPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get the specified branch pipeline of the DevOps project"). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch")). + Returns(http.StatusOK, RespOK, devops.BranchPipeline{}). + Writes(devops.BranchPipeline{})) + + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}"). + To(projectPipelineHandler.GetBranchPipelineRun). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get details in the specified pipeline activity."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.PipelineRun{}). + Writes(devops.PipelineRun{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop"). + To(projectPipelineHandler.StopBranchPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Stop the specified pipeline of the DevOps project."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). + Required(false). + DataFormat("blocking=%t"). + DefaultValue("blocking=false")). + Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). + Required(false). + DataFormat("timeOutInSecs=%d"). + DefaultValue("timeOutInSecs=10")). + Returns(http.StatusOK, RespOK, devops.StopPipeline{}). + Writes(devops.StopPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/Replay/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/replay"). + To(projectPipelineHandler.ReplayBranchPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Replay the specified pipeline of the DevOps project"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.ReplayPipeline{}). + Writes(devops.ReplayPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{}/runs/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs"). + To(projectPipelineHandler.RunBranchPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Run the specified pipeline of the DevOps project."). + Reads(devops.RunPayload{}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Returns(http.StatusOK, RespOK, devops.RunPipeline{}). + Writes(devops.RunPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/artifacts + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/artifacts"). + To(projectPipelineHandler.GetBranchArtifacts). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get all artifacts generated from the specified run of the pipeline branch."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d")). + Param(webservice.QueryParameter("limit", "the limit item count of the search."). + Required(false). + DataFormat("limit=%d")). + Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). + Writes([]devops.Artifacts{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/log/?start=0 + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/log"). + To(projectPipelineHandler.GetBranchRunLog). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get run logs of the specified pipeline activity."). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d"). + DefaultValue("start=0"))) + + // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log"). + To(projectPipelineHandler.GetBranchStepLog). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get the step logs in the specified pipeline activity."). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node id, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step id, the step in pipeline.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d"). + DefaultValue("start=0"))) + + // match /blue/rest/organizations/jenkins/pipelines/%s/%s/branches/%s/runs/%s/nodes/%s/steps/?limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps"). + To(projectPipelineHandler.GetBranchNodeSteps). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get all steps in the specified node."). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). + Writes([]devops.NodeSteps{})) + + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes"). + To(projectPipelineHandler.GetBranchPipelineRunNodes). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get run nodes."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Param(webservice.QueryParameter("limit", "the limit item count of the search."). + Required(false). + DataFormat("limit=%d"). + DefaultValue("limit=10000")). + Returns(http.StatusOK, RespOK, []devops.BranchPipelineRunNodes{}). + Writes([]devops.BranchPipelineRunNodes{})) + + // /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step} + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}"). + To(projectPipelineHandler.SubmitBranchInputStep). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Proceed or Break the paused pipeline which waiting for user input."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step ID, the step in pipeline.")). + Reads(devops.CheckPlayload{}). + Produces("text/plain; charset=utf-8")) + + // in scm get all steps in nodes. + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodesdetail"). + To(projectPipelineHandler.GetBranchNodesDetail). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get steps details in an activity node. For a node, the steps which is defined inside the node."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). + Writes(devops.NodesDetail{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/?filter=&start&limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches"). + To(projectPipelineHandler.GetPipelineBranch). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get all branches in the specified pipeline."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.QueryParameter("filter", "filter remote scm. e.g. origin"). + Required(false). + DataFormat("filter=%s")). + Param(webservice.QueryParameter("start", "the count of branches start."). + Required(false). + DataFormat("start=%d").DefaultValue("start=0")). + Param(webservice.QueryParameter("limit", "the count of branches limit."). + Required(false). + DataFormat("limit=%d").DefaultValue("limit=100")). + Returns(http.StatusOK, RespOK, []devops.PipelineBranch{}). + Writes([]devops.PipelineBranch{})) + + // match /job/{devops}/job/{pipeline}/build?delay=0 + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/scan"). + To(projectPipelineHandler.ScanBranch). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Scan remote Repository, Start a build if have new branch."). + Produces("text/html; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.QueryParameter("delay", "the delay time to scan"). + Required(false). + DataFormat("delay=%d"))) + + // match /job/project-8QnvykoJw4wZ/job/test-1/indexing/consoleText + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/consolelog"). + To(projectPipelineHandler.GetConsoleLog). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get scan reponsitory logs in the specified pipeline."). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline"))) + + // match /crumbIssuer/api/json/ + webservice.Route(webservice.GET("/crumbissuer"). + To(projectPipelineHandler.GetCrumb). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get crumb issuer. A CrumbIssuer represents an algorithm to generate a nonce value, known as a crumb, to counter cross site request forgery exploits. Crumbs are typically hashes incorporating information that uniquely identifies an agent that sends a request, along with a guarded secret so that the crumb value cannot be forged by a third party."). + Returns(http.StatusOK, RespOK, devops.Crumb{}). + Writes(devops.Crumb{})) + + // match "/blue/rest/organizations/jenkins/scm/%s/servers/" + webservice.Route(webservice.GET("/scms/{scm}/servers"). + To(projectPipelineHandler.GetSCMServers). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all servers in the jenkins."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Returns(http.StatusOK, RespOK, []devops.SCMServer{}). + Writes([]devops.SCMServer{})) + + // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/?credentialId=github" + webservice.Route(webservice.GET("/scms/{scm}/organizations"). + To(projectPipelineHandler.GetSCMOrg). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all organizations of the specified source configuration management (SCM) such as Github."). + Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). + Param(webservice.QueryParameter("credentialId", "credential ID for source configuration management (SCM)."). + Required(true). + DataFormat("credentialId=%s")). + Returns(http.StatusOK, RespOK, []devops.SCMOrg{}). + Writes([]devops.SCMOrg{})) + + // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/{organization}/repositories/?credentialId=&pageNumber&pageSize=" + webservice.Route(webservice.GET("/scms/{scm}/organizations/{organization}/repositories"). + To(projectPipelineHandler.GetOrgRepo). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all repositories in the specified organization."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Param(webservice.PathParameter("organization", "organization ID, such as github username.")). + Param(webservice.QueryParameter("credentialId", "credential ID for SCM."). + Required(true). + DataFormat("credentialId=%s")). + Param(webservice.QueryParameter("pageNumber", "page number."). + Required(true). + DataFormat("pageNumber=%d")). + Param(webservice.QueryParameter("pageSize", "the item count of one page."). + Required(true). + DataFormat("pageSize=%d")). + Returns(http.StatusOK, RespOK, []devops.OrgRepo{}). + Writes([]devops.OrgRepo{})) + + // match "/blue/rest/organizations/jenkins/scm/%s/servers/" create bitbucket server + webservice.Route(webservice.POST("/scms/{scm}/servers"). + To(projectPipelineHandler.CreateSCMServers). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("Create scm server in the jenkins."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Reads(devops.CreateScmServerReq{}). + Returns(http.StatusOK, RespOK, devops.SCMServer{}). + Writes(devops.SCMServer{})) + + // match "/blue/rest/organizations/jenkins/scm/github/validate/" + webservice.Route(webservice.POST("/scms/{scm}/verify"). + To(projectPipelineHandler.Validate). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("Validate the access token of the specified source configuration management (SCM) such as Github"). + Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). + Returns(http.StatusOK, RespOK, devops.Validates{}). + Writes(devops.Validates{})) + + // match /git/notifyCommit/?url= + webservice.Route(webservice.GET("/webhook/git"). + To(projectPipelineHandler.GetNotifyCommit). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification by HTTP GET method. Git webhook will request here."). + Produces("text/plain; charset=utf-8"). + Param(webservice.QueryParameter("url", "Git url"). + Required(true). + DataFormat("url=%s"))) + + // Gitlab or some other scm managers can only use HTTP method. match /git/notifyCommit/?url= + webservice.Route(webservice.POST("/webhook/git"). + To(projectPipelineHandler.PostNotifyCommit). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification by HTTP POST method. Git webhook will request here."). + Consumes("application/json"). + Produces("text/plain; charset=utf-8"). + Param(webservice.QueryParameter("url", "Git url"). + Required(true). + DataFormat("url=%s"))) + + webservice.Route(webservice.POST("/webhook/github"). + To(projectPipelineHandler.GithubWebhook). + Consumes("application/x-www-form-urlencoded", "application/json"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification. Github webhook will request here.")) + + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/checkScriptCompile"). + To(projectPipelineHandler.CheckScriptCompile). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.QueryParameter("pipeline", "the name of the CI/CD pipeline"). + Required(false). + DataFormat("pipeline=%s")). + Consumes("application/x-www-form-urlencoded", "charset=utf-8"). + Produces("application/json", "charset=utf-8"). + Doc("Check pipeline script compile."). + Reads(devops.ReqScript{}). + Returns(http.StatusOK, RespOK, devops.CheckScript{}). + Writes(devops.CheckScript{})) + + webservice.Route(webservice.POST("/devops/{devops}/checkCron"). + To(projectPipelineHandler.CheckCron). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Produces("application/json", "charset=utf-8"). + Doc("Check cron script compile."). + Reads(devops.CronData{}). + Returns(http.StatusOK, RespOK, devops.CheckCronRes{}). + Writes(devops.CheckCronRes{})) + + // match /pipeline-model-converter/toJenkinsfile + webservice.Route(webservice.POST("/tojenkinsfile"). + To(projectPipelineHandler.ToJenkinsfile). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). + Consumes("application/x-www-form-urlencoded"). + Produces("application/json", "charset=utf-8"). + Doc("Convert json to jenkinsfile format."). + Reads(devops.ReqJson{}). + Returns(http.StatusOK, RespOK, devops.ResJenkinsfile{}). + Writes(devops.ResJenkinsfile{})) + + // match /pipeline-model-converter/toJson + webservice.Route(webservice.POST("/tojson"). + To(projectPipelineHandler.ToJson). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). + Consumes("application/x-www-form-urlencoded"). + Produces("application/json", "charset=utf-8"). + Doc("Convert jenkinsfile to json format. Usually the frontend uses json to show or edit pipeline"). + Reads(devops.ReqJenkinsfile{}). + Returns(http.StatusOK, RespOK, devops.ResJson{}). + Writes(devops.ResJson{})) + } + return nil +} + +func AddSonarToWebService(webservice *restful.WebService, devopsClient devops.Interface, sonarClient sonarqube.SonarInterface) error { + sonarEnable := devopsClient != nil && sonarClient != nil + if sonarEnable { + sonarHandler := NewPipelineSonarHandler(devopsClient, sonarClient) + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/sonarstatus"). + To(sonarHandler.GetPipelineSonarStatusHandler). + Doc("Get the sonar quality information for the specified pipeline of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). + Returns(http.StatusOK, RespOK, []sonarqube.SonarStatus{}). + Writes([]sonarqube.SonarStatus{})) + + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/sonarstatus"). + To(sonarHandler.GetMultiBranchesPipelineSonarStatusHandler). + Doc("Get the sonar quality check information for the specified pipeline branch of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). + Param(webservice.PathParameter("branch", "branch name, e.g. master")). + Returns(http.StatusOK, RespOK, []sonarqube.SonarStatus{}). + Writes([]sonarqube.SonarStatus{})) + } + return nil +} + +func AddS2IToWebService(webservice *restful.WebService, ksClient versioned.Interface, + ksInformer externalversions.SharedInformerFactory, s3Client s3.Interface) error { + s2iEnable := ksClient != nil && ksInformer != nil && s3Client != nil + + if s2iEnable { + s2iHandler := NewS2iBinaryHandler(ksClient, ksInformer, s3Client) + webservice.Route(webservice.PUT("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file"). + To(s2iHandler.UploadS2iBinaryHandler). + Consumes("multipart/form-data"). + Produces(restful.MIME_JSON). + Doc("Upload S2iBinary file"). + Param(webservice.PathParameter("namespace", "the name of namespaces")). + Param(webservice.PathParameter("s2ibinary", "the name of s2ibinary")). + Param(webservice.FormParameter("s2ibinary", "file to upload")). + Param(webservice.FormParameter("md5", "md5 of file")). + Returns(http.StatusOK, RespOK, devopsv1alpha1.S2iBinary{})) + + webservice.Route(webservice.GET("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file/{file}"). + To(s2iHandler.DownloadS2iBinaryHandler). + Produces(restful.MIME_OCTET). + Doc("Download S2iBinary file"). + Param(webservice.PathParameter("namespace", "the name of namespaces")). + Param(webservice.PathParameter("s2ibinary", "the name of s2ibinary")). + Param(webservice.PathParameter("file", "the name of binary file")). + Returns(http.StatusOK, RespOK, nil)) + } + return nil +} diff --git a/pkg/kapis/devops/v1alpha2/s2ibinary.go b/pkg/kapis/devops/v1alpha2/s2ibinary.go new file mode 100644 index 000000000..1deb9cdb5 --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/s2ibinary.go @@ -0,0 +1,91 @@ +package v1alpha2 + +import ( + "code.cloudfoundry.org/bytefmt" + "fmt" + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/utils/hashutil" + "net/http" +) + +type S2iBinaryHandler struct { + s2iUploader devops.S2iBinaryUploader +} + +func (h S2iBinaryHandler) UploadS2iBinaryHandler(req *restful.Request, resp *restful.Response) { + ns := req.PathParameter("namespace") + name := req.PathParameter("s2ibinary") + + err := req.Request.ParseMultipartForm(bytefmt.MEGABYTE * 20) + if err != nil { + klog.Errorf("%+v", err) + api.HandleBadRequest(resp, nil, err) + return + } + if len(req.Request.MultipartForm.File) == 0 { + err := restful.NewError(http.StatusBadRequest, "could not get file from form") + klog.Errorf("%+v", err) + api.HandleBadRequest(resp, nil, err) + return + } + if len(req.Request.MultipartForm.File["s2ibinary"]) == 0 { + err := restful.NewError(http.StatusBadRequest, "could not get file from form") + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + if len(req.Request.MultipartForm.File["s2ibinary"]) > 1 { + err := restful.NewError(http.StatusBadRequest, "s2ibinary should only have one file") + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + defer req.Request.MultipartForm.RemoveAll() + file, err := req.Request.MultipartForm.File["s2ibinary"][0].Open() + if err != nil { + klog.Error(err) + api.HandleInternalError(resp, nil, err) + return + } + filemd5, err := hashutil.GetMD5(file) + if err != nil { + klog.Error(err) + api.HandleInternalError(resp, nil, err) + return + } + md5, ok := req.Request.MultipartForm.Value["md5"] + if ok && len(req.Request.MultipartForm.Value["md5"]) > 0 { + if md5[0] != filemd5 { + err := restful.NewError(http.StatusBadRequest, fmt.Sprintf("md5 not match, origin: %+v, calculate: %+v", md5[0], filemd5)) + klog.Error(err) + api.HandleInternalError(resp, nil, err) + return + } + } + + s2ibin, err := h.s2iUploader.UploadS2iBinary(ns, name, filemd5, req.Request.MultipartForm.File["s2ibinary"][0]) + if err != nil { + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + resp.WriteAsJson(s2ibin) + +} + +func (h S2iBinaryHandler) DownloadS2iBinaryHandler(req *restful.Request, resp *restful.Response) { + ns := req.PathParameter("namespace") + name := req.PathParameter("s2ibinary") + fileName := req.PathParameter("file") + url, err := h.s2iUploader.DownloadS2iBinary(ns, name, fileName) + if err != nil { + klog.Errorf("%+v", err) + api.HandleInternalError(resp, nil, err) + return + } + http.Redirect(resp.ResponseWriter, req.Request, url, http.StatusFound) + return +} diff --git a/pkg/kapis/iam/install/install.go b/pkg/kapis/iam/install/install.go deleted file mode 100644 index 4e4df52af..000000000 --- a/pkg/kapis/iam/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/iam/v1alpha2/handler.go b/pkg/kapis/iam/v1alpha2/handler.go new file mode 100644 index 000000000..7a1a0cb9a --- /dev/null +++ b/pkg/kapis/iam/v1alpha2/handler.go @@ -0,0 +1,232 @@ +package v1alpha2 + +import ( + "fmt" + "github.com/emicklei/go-restful" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/models/iam/im" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type iamHandler struct { + am am.AccessManagementInterface + im im.IdentityManagementInterface +} + +func newIAMHandler(im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) *iamHandler { + return &iamHandler{ + am: am, + im: im, + } +} + +func (h *iamHandler) DescribeUser(req *restful.Request, resp *restful.Response) { + username := req.PathParameter("user") + user, err := h.im.DescribeUser(username) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + + globalRole, err := h.am.GetGlobalRoleOfUser(username) + + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + + result := iamv1alpha2.UserDetail{User: user, GlobalRole: globalRole} + + resp.WriteEntity(result) +} + +func (h *iamHandler) ListUsers(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.im.ListUsers(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + for i, item := range result.Items { + user := item.(*iamv1alpha2.User) + user = user.DeepCopy() + role, err := h.am.GetGlobalRoleOfUser(user.Name) + if err != nil && !errors.IsNotFound(err) { + klog.Error(err) + api.HandleInternalError(resp, req, err) + return + } + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + if role != nil { + user.Annotations["iam.kubesphere.io/global-role"] = role.Name + } else { + user.Annotations["iam.kubesphere.io/global-role"] = "" + } + + result.Items[i] = user + } + + resp.WriteEntity(result) + +} + +func (h *iamHandler) ListRoles(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListRoles(namespace, queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) +} + +func (h *iamHandler) ListClusterRoles(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListClusterRoles(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) +} + +func (h *iamHandler) ListGlobalRoles(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + result, err := h.am.ListGlobalRoles(queryParam) + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + resp.WriteEntity(result) +} + +func (h *iamHandler) ListRoleUsers(req *restful.Request, resp *restful.Response) { + panic("implement me") +} + +func (h *iamHandler) ListNamespaceUsers(req *restful.Request, resp *restful.Response) { + queryParam := query.ParseQueryParameter(req) + namespace := req.PathParameter("namespace") + + roleBindings, err := h.am.ListRoleBindings("", namespace) + + if err != nil { + api.HandleInternalError(resp, req, err) + return + } + + users := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) + + if err != nil { + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + api.HandleInternalError(resp, req, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + resp.WriteEntity(result) +} + +func (h *iamHandler) ListWorkspaceRoles(request *restful.Request, response *restful.Response) { + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s:%s", tenantv1alpha1.WorkspaceLabel, workspace)) + + result, err := h.am.ListWorkspaceRoles(queryParam) + if err != nil { + api.HandleInternalError(response, request, err) + return + } + response.WriteEntity(result) +} + +func (h *iamHandler) ListWorkspaceUsers(request *restful.Request, response *restful.Response) { + queryParam := query.ParseQueryParameter(request) + workspace := request.PathParameter("workspace") + + roleBindings, err := h.am.ListWorkspaceRoleBindings("", workspace) + + if err != nil { + api.HandleInternalError(response, request, err) + return + } + + users := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + for _, subject := range roleBinding.Subjects { + if subject.Kind == iamv1alpha2.ResourceKindUser { + user, err := h.im.DescribeUser(subject.Name) + + if err != nil { + if errors.IsNotFound(err) { + klog.Errorf("orphan subject: %+v", subject) + continue + } + api.HandleInternalError(response, request, err) + return + } + + user = user.DeepCopy() + + if user.Annotations == nil { + user.Annotations = make(map[string]string, 0) + } + + user.Annotations["iam.kubesphere.io/workspace-role"] = roleBinding.RoleRef.Name + + users = append(users, user) + } + } + } + + result := resources.DefaultList(users, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + user := object.(*iamv1alpha2.User).ObjectMeta + return resources.DefaultObjectMetaFilter(user, filter) + }) + + response.WriteEntity(result) +} diff --git a/pkg/kapis/iam/v1alpha2/register.go b/pkg/kapis/iam/v1alpha2/register.go index 3bbd13af8..3687b2f37 100644 --- a/pkg/kapis/iam/v1alpha2/register.go +++ b/pkg/kapis/iam/v1alpha2/register.go @@ -20,273 +20,80 @@ package v1alpha2 import ( "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" - rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/iam" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam/policy" - "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/models/iam/im" "net/http" - "time" ) -const GroupName = "iam.kubesphere.io" +const ( + GroupName = "iam.kubesphere.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -type UserUpdateRequest struct { - Username string `json:"username" description:"username"` - Email string `json:"email" description:"email address"` - Lang string `json:"lang" description:"user's language setting, default is zh-CN"` - Description string `json:"description" description:"user's description"` - Password string `json:"password,omitempty" description:"this is necessary if you need to change your password"` - CurrentPassword string `json:"current_password,omitempty" description:"this is necessary if you need to change your password"` - ClusterRole string `json:"cluster_role" description:"user's cluster role"` -} - -type CreateUserRequest struct { - Username string `json:"username" description:"username"` - Email string `json:"email" description:"email address"` - Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"` - Description string `json:"description" description:"user's description"` - Password string `json:"password" description:"password'"` - ClusterRole string `json:"cluster_role" description:"user's cluster role"` -} - -type UserList struct { - Items []struct { - Username string `json:"username" description:"username"` - Email string `json:"email" description:"email address"` - Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"` - Description string `json:"description" description:"user's description"` - ClusterRole string `json:"cluster_role" description:"user's cluster role"` - CreateTime time.Time `json:"create_time" description:"user creation time"` - LastLoginTime time.Time `json:"last_login_time" description:"last login time"` - } `json:"items" description:"paging data"` - TotalCount int `json:"total_count" description:"total count"` -} -type NamespacedUser struct { - Username string `json:"username" description:"username"` - Email string `json:"email" description:"email address"` - Lang string `json:"lang,omitempty" description:"user's language setting, default is zh-CN"` - Description string `json:"description" description:"user's description"` - Role string `json:"role" description:"user's role in the specified namespace"` - RoleBinding string `json:"role_binding" description:"user's role binding name in the specified namespace"` - RoleBindTime string `json:"role_bind_time" description:"user's role binding time"` - CreateTime time.Time `json:"create_time" description:"user creation time"` - LastLoginTime time.Time `json:"last_login_time" description:"last login time"` -} - -type ClusterRoleList struct { - Items []rbacv1.ClusterRole `json:"items" description:"paging data"` - TotalCount int `json:"total_count" description:"total count"` -} - -type LoginLog struct { - LoginTime string `json:"login_time" description:"last login time"` - LoginIP string `json:"login_ip" description:"last login ip"` -} - -type RoleList struct { - Items []rbacv1.Role `json:"items" description:"paging data"` - TotalCount int `json:"total_count" description:"total count"` -} - -type InviteUserRequest struct { - Username string `json:"username" description:"username"` - WorkspaceRole string `json:"workspace_role" description:"user's workspace role'"` -} - -type DescribeWorkspaceUserResponse struct { - Username string `json:"username" description:"username"` - Email string `json:"email" description:"email address"` - Lang string `json:"lang" description:"user's language setting, default is zh-CN"` - Description string `json:"description" description:"user's description"` - ClusterRole string `json:"cluster_role" description:"user's cluster role"` - WorkspaceRole string `json:"workspace_role" description:"user's workspace role"` - CreateTime time.Time `json:"create_time" description:"user creation time"` - LastLoginTime time.Time `json:"last_login_time" description:"last login time"` -} - -func addWebService(c *restful.Container) error { +func AddToContainer(container *restful.Container, im im.IdentityManagementInterface, am am.AccessManagementInterface, options *authoptions.AuthenticationOptions) error { ws := runtime.NewWebService(GroupVersion) - ok := "ok" + handler := newIAMHandler(im, am, options) - ws.Route(ws.POST("/authenticate"). - To(iam.TokenReviewHandler). - Doc("TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be cached by the webhook token authenticator plugin in the kube-apiserver."). - Reads(iam.TokenReview{}). - Returns(http.StatusOK, ok, iam.TokenReview{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.POST("/login"). - To(iam.Login). - Doc("KubeSphere APIs support token-based authentication via the Authtoken request header. The POST Login API is used to retrieve the authentication token. After the authentication token is obtained, it must be inserted into the Authtoken header for all requests."). - Reads(iam.LoginRequest{}). - Returns(http.StatusOK, ok, models.AuthGrantResponse{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.POST("/token"). - To(iam.OAuth). - Doc("OAuth API,only support resource owner password credentials grant"). - Reads(iam.LoginRequest{}). - Returns(http.StatusOK, ok, models.AuthGrantResponse{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.GET("/users/{user}"). - To(iam.DescribeUser). - Doc("Describe the specified user."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, models.User{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.POST("/users"). - To(iam.CreateUser). - Doc("Create a user account."). - Reads(CreateUserRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.DELETE("/users/{user}"). - To(iam.DeleteUser). - Doc("Delete the specified user."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, errors.Error{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.PUT("/users/{user}"). - To(iam.UpdateUser). - Doc("Update information about the specified user."). - Param(ws.PathParameter("user", "username")). - Reads(UserUpdateRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.GET("/users/{user}/logs"). - To(iam.UserLoginLogs). - Doc("Retrieve the \"login logs\" for the specified user."). - Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, LoginLog{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) + // global resource ws.Route(ws.GET("/users"). - To(iam.ListUsers). + To(handler.ListUsers). Doc("List all users."). - Returns(http.StatusOK, ok, UserList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) - ws.Route(ws.GET("/users/{user}/roles"). - To(iam.ListUserRoles). - Doc("Retrieve all the roles that are assigned to the specified user."). + Returns(http.StatusOK, api.StatusOK, api.ListResult{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) + // global resource + ws.Route(ws.GET("/users/{user}"). + To(handler.DescribeUser). + Doc("Retrieve user details."). Param(ws.PathParameter("user", "username")). - Returns(http.StatusOK, ok, iam.RoleList{}). + Returns(http.StatusOK, api.StatusOK, iamv1alpha2.UserDetail{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/namespaces/{namespace}/roles"). - To(iam.ListRoles). - Doc("Retrieve the roles that are assigned to the user in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Returns(http.StatusOK, ok, RoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/clusterroles"). - To(iam.ListClusterRoles). + // global resource + ws.Route(ws.GET("/globalroles"). + To(handler.ListGlobalRoles). Doc("List all cluster roles."). - Returns(http.StatusOK, ok, ClusterRoleList{}). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/users"). - To(iam.ListRoleUsers). - Doc("Retrieve the users that are bound to the role in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Param(ws.PathParameter("role", "role name")). - Returns(http.StatusOK, ok, []NamespacedUser{}). + + ws.Route(ws.GET("/clusterroles"). + To(handler.ListClusterRoles). + Doc("List cluster roles."). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/namespaces/{namespace}/users"). - To(iam.ListNamespaceUsers). - Doc("List all users in the specified namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Returns(http.StatusOK, ok, []NamespacedUser{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/clusterroles/{clusterrole}/users"). - To(iam.ListClusterRoleUsers). - Doc("List all users that are bound to the specified cluster role."). - Param(ws.PathParameter("clusterrole", "cluster role name")). - Returns(http.StatusOK, ok, UserList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/clusterroles/{clusterrole}/rules"). - To(iam.ListClusterRoleRules). - Doc("List all policy rules of the specified cluster role."). - Param(ws.PathParameter("clusterrole", "cluster role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/namespaces/{namespace}/roles/{role}/rules"). - To(iam.ListRoleRules). - Doc("List all policy rules of the specified role in the given namespace."). - Param(ws.PathParameter("namespace", "kubernetes namespace")). - Param(ws.PathParameter("role", "role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/devops/{devops}/roles/{role}/rules"). - To(iam.ListDevopsRoleRules). - Doc("List all policy rules of the specified role in the given devops project."). - Param(ws.PathParameter("devops", "devops project ID")). - Param(ws.PathParameter("role", "devops role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/rulesmapping/clusterroles"). - To(iam.ClusterRulesMapping). - Doc("Get the mapping relationships between cluster roles and policy rules."). - Returns(http.StatusOK, ok, policy.ClusterRoleRuleMapping). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/rulesmapping/roles"). - To(iam.RulesMapping). - Doc("Get the mapping relationships between namespaced roles and policy rules."). - Returns(http.StatusOK, ok, policy.RoleRuleMapping). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/roles"). - To(iam.ListWorkspaceRoles). - Doc("List all workspace roles."). - Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, ClusterRoleList{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}"). - To(iam.DescribeWorkspaceRole). - Doc("Describe the workspace role."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("role", "workspace role name")). - Returns(http.StatusOK, ok, rbacv1.ClusterRole{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/roles/{role}/rules"). - To(iam.ListWorkspaceRoleRules). - Doc("List all policy rules of the specified workspace role."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("role", "workspace role name")). - Returns(http.StatusOK, ok, []models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/members"). - To(iam.ListWorkspaceUsers). + + ws.Route(ws.GET("/workspaces/{workspace}/users"). + To(handler.ListWorkspaceUsers). Doc("List all members in the specified workspace."). Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, UserList{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.POST("/workspaces/{workspace}/members"). - To(iam.InviteUser). - Doc("Invite a member to the specified workspace."). + + ws.Route(ws.GET("/workspaces/{workspace}/workspaceroles"). + To(handler.ListWorkspaceRoles). + Doc("List all workspace roles."). Param(ws.PathParameter("workspace", "workspace name")). - Reads(InviteUserRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.DELETE("/workspaces/{workspace}/members/{member}"). - To(iam.RemoveUser). - Doc("Remove the specified member from the workspace."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, ok, errors.Error{}). + + ws.Route(ws.GET("/namespaces/{namespace}/users"). + To(handler.ListNamespaceUsers). + Doc("List all users in the specified namespace."). + Param(ws.PathParameter("namespace", "kubernetes namespace")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - ws.Route(ws.GET("/workspaces/{workspace}/members/{member}"). - To(iam.DescribeWorkspaceUser). - Doc("Describe the specified user in the given workspace."). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "username")). - Returns(http.StatusOK, ok, DescribeWorkspaceUserResponse{}). + + ws.Route(ws.GET("/namespaces/{namespace}/roles"). + To(handler.ListRoles). + Doc("List all roles in the specified namespace."). + Param(ws.PathParameter("namespace", "namespace")). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.AccessManagementTag})) - c.Add(ws) + + container.Add(ws) return nil } diff --git a/pkg/kapis/kapis.go b/pkg/kapis/kapis.go deleted file mode 100644 index 1e8d81ca5..000000000 --- a/pkg/kapis/kapis.go +++ /dev/null @@ -1,32 +0,0 @@ -package kapis - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" - iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" - loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" - monitoringv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2" - openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" - operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" - resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" - servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2" - tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" - terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" -) - -func InstallAPIs(container *restful.Container) { - urlruntime.Must(servicemeshv1alpha2.AddToContainer(container)) - urlruntime.Must(devopsv1alpha2.AddToContainer(container)) - urlruntime.Must(loggingv1alpha2.AddToContainer(container)) - urlruntime.Must(monitoringv1alpha2.AddToContainer(container)) - urlruntime.Must(openpitrixv1.AddToContainer(container)) - urlruntime.Must(operationsv1alpha2.AddToContainer(container)) - urlruntime.Must(resourcesv1alpha2.AddToContainer(container)) - urlruntime.Must(tenantv1alpha2.AddToContainer(container)) - urlruntime.Must(terminalv1alpha2.AddToContainer(container)) -} - -func InstallAuthorizationAPIs(container *restful.Container) { - urlruntime.Must(iamv1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/logging/install/install.go b/pkg/kapis/logging/install/install.go deleted file mode 100644 index 999daa8e5..000000000 --- a/pkg/kapis/logging/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/logging/v1alpha2/handler.go b/pkg/kapis/logging/v1alpha2/handler.go new file mode 100644 index 000000000..585099466 --- /dev/null +++ b/pkg/kapis/logging/v1alpha2/handler.go @@ -0,0 +1,156 @@ +package v1alpha2 + +import ( + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/models/logging" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + loggingclient "kubesphere.io/kubesphere/pkg/simple/client/logging" + util "kubesphere.io/kubesphere/pkg/utils/stringutils" + "strconv" + "strings" + "time" +) + +const ( + LevelCluster = iota + LevelContainer + + // query type, default to `query` + TypeStat = "statistics" + TypeHist = "histogram" + TypeExport = "export" + + Ascending = "asc" + Descending = "desc" +) + +type handler struct { + k k8s.Client + lo logging.LoggingOperator +} + +func newHandler(k k8s.Client, l loggingclient.Interface) *handler { + return &handler{k, logging.NewLoggingOperator(l)} +} + +func (h handler) handleClusterQuery(req *restful.Request, resp *restful.Response) { + h.get(req, LevelCluster, resp) +} + +func (h handler) handleContainerQuery(req *restful.Request, resp *restful.Response) { + h.get(req, LevelContainer, resp) +} + +func (h handler) get(req *restful.Request, lvl int, resp *restful.Response) { + typ := req.QueryParameter("type") + + noHit, sf, err := h.newSearchFilter(req, lvl) + if err != nil { + api.HandleBadRequest(resp, nil, err) + } + if noHit { + handleNoHit(typ, resp) + return + } + + switch typ { + case TypeStat: + res, err := h.lo.GetCurrentStats(sf) + if err != nil { + api.HandleInternalError(resp, nil, err) + } + resp.WriteAsJson(res) + case TypeHist: + interval := req.QueryParameter("interval") + res, err := h.lo.CountLogsByInterval(sf, interval) + if err != nil { + api.HandleInternalError(resp, nil, err) + } + resp.WriteAsJson(res) + case TypeExport: + resp.Header().Set(restful.HEADER_ContentType, "text/plain") + resp.Header().Set("Content-Disposition", "attachment") + err := h.lo.ExportLogs(sf, resp.ResponseWriter) + if err != nil { + api.HandleInternalError(resp, nil, err) + } + default: + from, _ := strconv.ParseInt(req.QueryParameter("from"), 10, 64) + size, err := strconv.ParseInt(req.QueryParameter("size"), 10, 64) + if err != nil { + size = 10 + } + order := req.QueryParameter("sort") + if order != Ascending { + order = Descending + } + res, err := h.lo.SearchLogs(sf, from, size, order) + if err != nil { + api.HandleInternalError(resp, nil, err) + } + resp.WriteAsJson(res) + } +} + +func (h handler) newSearchFilter(req *restful.Request, level int) (bool, loggingclient.SearchFilter, error) { + var sf loggingclient.SearchFilter + + switch level { + case LevelCluster: + sf.NamespaceFilter = h.intersect( + util.Split(req.QueryParameter("namespaces"), ","), + util.Split(strings.ToLower(req.QueryParameter("namespace_query")), ","), + util.Split(req.QueryParameter("workspaces"), ","), + util.Split(strings.ToLower(req.QueryParameter("workspace_query")), ",")) + sf.WorkloadFilter = util.Split(req.QueryParameter("workloads"), ",") + sf.WorkloadSearch = util.Split(req.QueryParameter("workload_query"), ",") + sf.PodFilter = util.Split(req.QueryParameter("pods"), ",") + sf.PodSearch = util.Split(req.QueryParameter("pod_query"), ",") + sf.ContainerFilter = util.Split(req.QueryParameter("containers"), ",") + sf.ContainerSearch = util.Split(req.QueryParameter("container_query"), ",") + case LevelContainer: + sf.NamespaceFilter = h.withCreationTime(req.PathParameter("namespace")) + sf.PodFilter = []string{req.PathParameter("pod")} + sf.ContainerFilter = []string{req.PathParameter("container")} + } + + sf.LogSearch = util.Split(req.QueryParameter("log_query"), ",") + + var err error + now := time.Now() + // If time is not given, set it to now. + if req.QueryParameter("start_time") == "" { + sf.Starttime = now + } else { + sf.Starttime, err = time.Parse(time.RFC3339, req.QueryParameter("start_time")) + if err != nil { + return false, sf, err + } + } + if req.QueryParameter("end_time") == "" { + sf.Endtime = now + } else { + sf.Endtime, err = time.Parse(time.RFC3339, req.QueryParameter("end_time")) + if err != nil { + return false, sf, err + } + } + + return len(sf.NamespaceFilter) == 0, sf, nil +} + +func handleNoHit(typ string, resp *restful.Response) { + switch typ { + case TypeStat: + resp.WriteAsJson(new(loggingclient.Statistics)) + case TypeHist: + resp.WriteAsJson(new(loggingclient.Histogram)) + case TypeExport: + resp.Header().Set(restful.HEADER_ContentType, "text/plain") + resp.Header().Set("Content-Disposition", "attachment") + resp.Write(nil) + default: + resp.WriteAsJson(new(loggingclient.Logs)) + } +} diff --git a/pkg/kapis/logging/v1alpha2/helper.go b/pkg/kapis/logging/v1alpha2/helper.go new file mode 100644 index 000000000..1acb22a9e --- /dev/null +++ b/pkg/kapis/logging/v1alpha2/helper.go @@ -0,0 +1,56 @@ +package v1alpha2 + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/utils/stringutils" + "strings" + "time" +) + +func (h handler) intersect(nsFilter []string, nsSearch []string, wsFilter []string, wsSearch []string) map[string]time.Time { + nsList, err := h.k.Kubernetes().CoreV1().Namespaces().List(v1.ListOptions{}) + if err != nil { + klog.Errorf("failed to list namespace, error: %s", err) + return nil + } + + inner := make(map[string]time.Time) + + // if no search condition is set on both namespace and workspace, + // then return all namespaces + if nsSearch == nil && nsFilter == nil && wsSearch == nil && wsFilter == nil { + for _, ns := range nsList.Items { + inner[ns.Name] = ns.CreationTimestamp.Time + } + } else { + for _, ns := range nsList.Items { + if stringutils.StringIn(ns.Name, nsFilter) || + stringutils.StringIn(ns.Annotations[constants.WorkspaceLabelKey], wsFilter) || + containsIn(ns.Name, nsSearch) || + containsIn(ns.Annotations[constants.WorkspaceLabelKey], wsSearch) { + inner[ns.Name] = ns.CreationTimestamp.Time + } + } + } + + return inner +} + +func containsIn(str string, subStrs []string) bool { + for _, sub := range subStrs { + if strings.Contains(str, sub) { + return true + } + } + return false +} + +func (h handler) withCreationTime(name string) map[string]time.Time { + ns, err := h.k.Kubernetes().CoreV1().Namespaces().Get(name, v1.GetOptions{}) + if err == nil { + return map[string]time.Time{name: ns.CreationTimestamp.Time} + } + return nil +} diff --git a/pkg/kapis/logging/v1alpha2/register.go b/pkg/kapis/logging/v1alpha2/register.go index fa4b59d84..efd268d70 100644 --- a/pkg/kapis/logging/v1alpha2/register.go +++ b/pkg/kapis/logging/v1alpha2/register.go @@ -22,11 +22,10 @@ import ( "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/apiserver/logging" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/log" - fluentbitclient "kubesphere.io/kubesphere/pkg/simple/client/fluentbit" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + "kubesphere.io/kubesphere/pkg/simple/client/logging" "net/http" ) @@ -37,15 +36,13 @@ const ( var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { +func AddToContainer(c *restful.Container, k8sClient k8s.Client, loggingClient logging.Interface) error { ws := runtime.NewWebService(GroupVersion) - ws.Route(ws.GET("/cluster").To(logging.LoggingQueryCluster). + h := newHandler(k8sClient, loggingClient) + + ws.Route(ws.GET("/cluster"). + To(h.handleClusterQuery). Doc("Query logs against the cluster."). Param(ws.QueryParameter("operation", "Operation type. This can be one of four types: query (for querying logs), statistics (for retrieving statistical data), histogram (for displaying log count by time interval) and export (for exporting logs). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). Param(ws.QueryParameter("workspaces", "A comma-separated list of workspaces. This field restricts the query to specified workspaces. For example, the following filter matches the workspace my-ws and demo-ws: `my-ws,demo-ws`").DataType("string").Required(false)). @@ -66,102 +63,13 @@ func addWebService(c *restful.Container) error { Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). + Writes(v1alpha2.APIResponse{}). + Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON, "text/plain") - ws.Route(ws.GET("/workspaces/{workspace}").To(logging.LoggingQueryWorkspace). - Doc("Query logs against the specific workspace."). - Param(ws.PathParameter("workspace", "The name of the workspace.").DataType("string").Required(true)). - Param(ws.QueryParameter("operation", "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). - Param(ws.QueryParameter("namespaces", "A comma-separated list of namespaces. This field restricts the query to specified namespaces. For example, the following filter matches the namespace my-ns and demo-ns: `my-ns,demo-ns`").DataType("string").Required(false)). - Param(ws.QueryParameter("namespace_query", "A comma-separated list of keywords. Differing from **namespaces**, this field performs fuzzy matching on namespaces. For example, the following value limits the query to namespaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("workloads", "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`").DataType("string").Required(false)). - Param(ws.QueryParameter("workload_query", "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("pods", "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`").DataType("string").Required(false)). - Param(ws.QueryParameter("pod_query", "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("containers", "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`").DataType("string").Required(false)). - Param(ws.QueryParameter("container_query", "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("log_query", "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).").DataType("string").Required(false)). - Param(ws.QueryParameter("interval", "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).").DefaultValue("15m").DataType("string").Required(false)). - Param(ws.QueryParameter("start_time", "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("end_time", "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort", "Sort order. One of acs, desc. This field sorts logs by timestamp.").DataType("string").DefaultValue("desc").Required(false)). - Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). - Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}").To(logging.LoggingQueryNamespace). - Doc("Query logs against the specific namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.QueryParameter("operation", "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). - Param(ws.QueryParameter("workloads", "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`").DataType("string").Required(false)). - Param(ws.QueryParameter("workload_query", "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("pods", "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`").DataType("string").Required(false)). - Param(ws.QueryParameter("pod_query", "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("containers", "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`").DataType("string").Required(false)). - Param(ws.QueryParameter("container_query", "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("log_query", "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).").DataType("string").Required(false)). - Param(ws.QueryParameter("interval", "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).").DefaultValue("15m").DataType("string").Required(false)). - Param(ws.QueryParameter("start_time", "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("end_time", "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort", "Sort order. One of acs, desc. This field sorts logs by timestamp.").DataType("string").DefaultValue("desc").Required(false)). - Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). - Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/workloads/{workload}").To(logging.LoggingQueryWorkload). - Doc("Query logs against the specific workload."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("workload", "The name of the workload.").DataType("string").Required(true)). - Param(ws.QueryParameter("operation", "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). - Param(ws.QueryParameter("pods", "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`").DataType("string").Required(false)). - Param(ws.QueryParameter("pod_query", "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("containers", "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`").DataType("string").Required(false)). - Param(ws.QueryParameter("container_query", "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("log_query", "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).").DataType("string").Required(false)). - Param(ws.QueryParameter("interval", "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).").DefaultValue("15m").DataType("string").Required(false)). - Param(ws.QueryParameter("start_time", "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("end_time", "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort", "Sort order. One of acs, desc. This field sorts logs by timestamp.").DataType("string").DefaultValue("desc").Required(false)). - Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). - Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}").To(logging.LoggingQueryPod). - Doc("Query logs against the specific pod."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). - Param(ws.QueryParameter("operation", "Query type. This can be one of three types: query (for querying logs), statistics (for retrieving statistical data), and histogram (for displaying log count by time interval). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). - Param(ws.QueryParameter("containers", "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`").DataType("string").Required(false)). - Param(ws.QueryParameter("container_query", "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("log_query", "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).").DataType("string").Required(false)). - Param(ws.QueryParameter("interval", "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).").DefaultValue("15m").DataType("string").Required(false)). - Param(ws.QueryParameter("start_time", "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("end_time", "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort", "Sort order. One of acs, desc. This field sorts logs by timestamp.").DataType("string").DefaultValue("desc").Required(false)). - Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). - Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}").To(logging.LoggingQueryContainer). + ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}"). + To(h.handleContainerQuery). Doc("Query logs against the specific container."). Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). @@ -175,47 +83,11 @@ func addWebService(c *restful.Container) error { Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). Metadata(restfulspec.KeyOpenAPITags, []string{constants.LogQueryTag}). - Writes(v1alpha2.QueryResult{}). - Returns(http.StatusOK, RespOK, v1alpha2.QueryResult{})). + Writes(v1alpha2.APIResponse{}). + Returns(http.StatusOK, RespOK, v1alpha2.APIResponse{})). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON, restful.MIME_OCTET) - ws.Route(ws.GET("/fluentbit/outputs").To(logging.LoggingQueryFluentbitOutputs). - Doc("List all Fluent bit output plugins."). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.FluentBitSetting}). - Writes(log.FluentbitOutputsResult{}). - Returns(http.StatusOK, RespOK, log.FluentbitOutputsResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.POST("/fluentbit/outputs").To(logging.LoggingInsertFluentbitOutput). - Doc("Add a new Fluent bit output plugin."). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.FluentBitSetting}). - Reads(fluentbitclient.OutputPlugin{}). - Writes(log.FluentbitOutputsResult{}). - Returns(http.StatusOK, RespOK, log.FluentbitOutputsResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.PUT("/fluentbit/outputs/{output}").To(logging.LoggingUpdateFluentbitOutput). - Doc("Update the specific Fluent bit output plugin."). - Param(ws.PathParameter("output", "ID of the output.").DataType("string").Required(true)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.FluentBitSetting}). - Reads(fluentbitclient.OutputPlugin{}). - Writes(log.FluentbitOutputsResult{}). - Returns(http.StatusOK, RespOK, log.FluentbitOutputsResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.DELETE("/fluentbit/outputs/{output}").To(logging.LoggingDeleteFluentbitOutput). - Doc("Delete the specific Fluent bit output plugin."). - Param(ws.PathParameter("output", "ID of the output.").DataType("string").Required(true)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.FluentBitSetting}). - Writes(log.FluentbitOutputsResult{}). - Returns(http.StatusOK, RespOK, log.FluentbitOutputsResult{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - c.Add(ws) return nil } diff --git a/pkg/kapis/monitoring/install/install.go b/pkg/kapis/monitoring/install/install.go deleted file mode 100644 index c1c15e8ae..000000000 --- a/pkg/kapis/monitoring/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/monitoring/v1alpha2/register.go b/pkg/kapis/monitoring/v1alpha2/register.go deleted file mode 100644 index ebc8a1fb1..000000000 --- a/pkg/kapis/monitoring/v1alpha2/register.go +++ /dev/null @@ -1,409 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 v1alpha2 - -import ( - "github.com/emicklei/go-restful" - "github.com/emicklei/go-restful-openapi" - "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/monitoring" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/metrics" - "net/http" -) - -const ( - GroupName = "monitoring.kubesphere.io" - RespOK = "ok" -) - -var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} - -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { - ws := runtime.NewWebService(GroupVersion) - - ws.Route(ws.GET("/cluster").To(monitoring.MonitorCluster). - Doc("Get cluster-level metric data."). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both cluster CPU usage and disk usage: `cluster_cpu_usage|cluster_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("type", "Additional operations. Currently available types is statistics. It retrieves the total number of workspaces, devops projects, namespaces, accounts in the cluster at the moment.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/nodes").To(monitoring.MonitorNode). - Doc("Get node-level metric data of all nodes."). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both node CPU usage and disk usage: `node_cpu_usage|node_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The node filter consists of a regexp pattern. It specifies which node data to return. For example, the following filter matches both node i-caojnter and i-cmu82ogj: `i-caojnter|i-cmu82ogj`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort nodes by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/nodes/{node}").To(monitoring.MonitorNode). - Doc("Get node-level metric data of the specific node."). - Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both node CPU usage and disk usage: `node_cpu_usage|node_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/workspaces").To(monitoring.MonitorWorkspace). - Doc("Get workspace-level metric data of all workspaces."). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workspace CPU usage and memory usage: `workspace_cpu_usage|workspace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The workspace filter consists of a regexp pattern. It specifies which workspace data to return.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort workspaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/workspaces/{workspace}").To(monitoring.MonitorWorkspace). - Doc("Get workspace-level metric data of a specific workspace."). - Param(ws.PathParameter("workspace", "Workspace name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workspace CPU usage and memory usage: `workspace_cpu_usage|workspace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("type", "Additional operations. Currently available types is statistics. It retrieves the total number of namespaces, devops projects, members and roles in this workspace at the moment.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/workspaces/{workspace}/namespaces").To(monitoring.MonitorNamespace). - Doc("Get namespace-level metric data of a specific workspace."). - Param(ws.PathParameter("workspace", "Workspace name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The namespace filter consists of a regexp pattern. It specifies which namespace data to return. For example, the following filter matches both namespace test and kube-system: `test|kube-system`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort namespaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces").To(monitoring.MonitorNamespace). - Doc("Get namespace-level metric data of all namespaces."). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The namespace filter consists of a regexp pattern. It specifies which namespace data to return. For example, the following filter matches both namespace test and kube-system: `test|kube-system`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort namespaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}").To(monitoring.MonitorNamespace). - Doc("Get namespace-level metric data of the specific namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/workloads").To(monitoring.MonitorWorkload). - Doc("Get workload-level metric data of a specific namespace's workloads."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workload CPU usage and memory usage: `workload_cpu_usage|workload_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The workload filter consists of a regexp pattern. It specifies which workload data to return. For example, the following filter matches any workload whose name begins with prometheus: `prometheus.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort workloads by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}").To(monitoring.MonitorWorkload). - Doc("Get workload-level metric data of all workloads which belongs to a specific kind."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("kind", "Workload kind. One of deployment, daemonset, statefulset.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workload CPU usage and memory usage: `workload_cpu_usage|workload_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The workload filter consists of a regexp pattern. It specifies which workload data to return. For example, the following filter matches any workload whose name begins with prometheus: `prometheus.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort workloads by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods").To(monitoring.MonitorPod). - Doc("Get pod-level metric data of the specific namespace's pods."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}").To(monitoring.MonitorPod). - Doc("Get pod-level metric data of a specific pod. Navigate to the pod by the pod's namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}/{workload}/pods").To(monitoring.MonitorPod). - Doc("Get pod-level metric data of a specific workload's pods. Navigate to the workload by the namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("kind", "Workload kind. One of deployment, daemonset, statefulset.").DataType("string").Required(true)). - Param(ws.PathParameter("workload", "Workload name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/nodes/{node}/pods").To(monitoring.MonitorPod). - Doc("Get pod-level metric data of all pods on a specific node."). - Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/nodes/{node}/pods/{pod}").To(monitoring.MonitorPod). - Doc("Get pod-level metric data of a specific pod. Navigate to the pod by the node where it is scheduled."). - Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). - Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers").To(monitoring.MonitorContainer). - Doc("Get container-level metric data of a specific pod's containers. Navigate to the pod by the pod's namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both container CPU usage and memory usage: `container_cpu_usage|container_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The container filter consists of a regexp pattern. It specifies which container data to return. For example, the following filter matches container prometheus and prometheus-config-reloader: `prometheus|prometheus-config-reloader`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort containers by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}").To(monitoring.MonitorContainer). - Doc("Get container-level metric data of a specific container. Navigate to the container by the pod name and the namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). - Param(ws.PathParameter("container", "Container name.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both container CPU usage and memory usage: `container_cpu_usage|container_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/storageclasses/{storageclass}/persistentvolumeclaims").To(monitoring.MonitorPVC). - Doc("Get PVC-level metric data of the specific storageclass's PVCs."). - Param(ws.PathParameter("storageclass", "The name of the storageclass.").DataType("string").Required(true)). - Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The PVC filter consists of a regexp pattern. It specifies which PVC data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort PVCs by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims").To(monitoring.MonitorPVC). - Doc("Get PVC-level metric data of the specific namespace's PVCs."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("resources_filter", "The PVC filter consists of a regexp pattern. It specifies which PVC data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_metric", "Sort PVCs by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). - Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). - Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}").To(monitoring.MonitorPVC). - Doc("Get PVC-level metric data of a specific PVC. Navigate to the PVC by the PVC's namespace."). - Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). - Param(ws.PathParameter("pvc", "PVC name.").DataType("string").Required(true)). - Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - ws.Route(ws.GET("/components/{component}").To(monitoring.MonitorComponent). - Doc("Get component-level metric data of the specific system component."). - Param(ws.PathParameter("component", "system component to monitor. One of etcd, apiserver, scheduler, controller_manager, coredns, prometheus.").DataType("string").Required(true)). - Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both etcd server list and total size of the underlying database: `etcd_server_list|etcd_mvcc_db_size`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). - Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). - Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). - Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentMetricsTag}). - Writes(metrics.Response{}). - Returns(http.StatusOK, RespOK, metrics.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON) - - c.Add(ws) - return nil -} diff --git a/pkg/kapis/monitoring/v1alpha3/handler.go b/pkg/kapis/monitoring/v1alpha3/handler.go new file mode 100644 index 000000000..8bf4916cc --- /dev/null +++ b/pkg/kapis/monitoring/v1alpha3/handler.go @@ -0,0 +1,227 @@ +/* + + Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "github.com/emicklei/go-restful" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/api" + model "kubesphere.io/kubesphere/pkg/models/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "regexp" +) + +type handler struct { + k kubernetes.Interface + mo model.MonitoringOperator +} + +func newHandler(k kubernetes.Interface, m monitoring.Interface) *handler { + return &handler{k, model.NewMonitoringOperator(m)} +} + +func (h handler) handleClusterMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelCluster) + if err != nil { + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleNodeMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelNode) + if err != nil { + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleWorkspaceMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelWorkspace) + if err != nil { + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleNamespaceMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelNamespace) + if err != nil { + if err.Error() == ErrNoHit { + res := handleNoHit(opt.namedMetrics) + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleWorkloadMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelWorkload) + if err != nil { + if err.Error() == ErrNoHit { + res := handleNoHit(opt.namedMetrics) + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handlePodMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelPod) + if err != nil { + if err.Error() == ErrNoHit { + res := handleNoHit(opt.namedMetrics) + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleContainerMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelContainer) + if err != nil { + if err.Error() == ErrNoHit { + res := handleNoHit(opt.namedMetrics) + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handlePVCMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelPVC) + if err != nil { + if err.Error() == ErrNoHit { + res := handleNoHit(opt.namedMetrics) + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func (h handler) handleComponentMetricsQuery(req *restful.Request, resp *restful.Response) { + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, monitoring.LevelComponent) + if err != nil { + api.HandleBadRequest(resp, nil, err) + return + } + h.handleNamedMetricsQuery(resp, opt) +} + +func handleNoHit(namedMetrics []string) model.Metrics { + var res model.Metrics + for _, metic := range namedMetrics { + res.Results = append(res.Results, monitoring.Metric{ + MetricName: metic, + MetricData: monitoring.MetricData{}, + }) + } + return res +} + +func (h handler) handleNamedMetricsQuery(resp *restful.Response, q queryOptions) { + var res model.Metrics + + var metrics []string + for _, metric := range q.namedMetrics { + ok, _ := regexp.MatchString(q.metricFilter, metric) + if ok { + metrics = append(metrics, metric) + } + } + if len(metrics) == 0 { + resp.WriteAsJson(res) + return + } + + if q.isRangeQuery() { + res = h.mo.GetNamedMetricsOverTime(metrics, q.start, q.end, q.step, q.option) + } else { + res = h.mo.GetNamedMetrics(metrics, q.time, q.option) + if q.shouldSort() { + res = *res.Sort(q.target, q.order, q.identifier).Page(q.page, q.limit) + } + } + resp.WriteAsJson(res) +} + +func (h handler) handleMetadataQuery(req *restful.Request, resp *restful.Response) { + res := h.mo.GetMetadata(req.PathParameter("namespace")) + resp.WriteAsJson(res) +} + +func (h handler) handleAdhocQuery(req *restful.Request, resp *restful.Response) { + var res monitoring.Metric + + params := parseRequestParams(req) + opt, err := h.makeQueryOptions(params, 0) + if err != nil { + if err.Error() == ErrNoHit { + resp.WriteAsJson(res) + return + } + + api.HandleBadRequest(resp, nil, err) + return + } + + if opt.isRangeQuery() { + res, err = h.mo.GetMetricOverTime(params.expression, params.namespaceName, opt.start, opt.end, opt.step) + } else { + res, err = h.mo.GetMetric(params.expression, params.namespaceName, opt.time) + } + + if err != nil { + api.HandleBadRequest(resp, nil, err) + } else { + resp.WriteAsJson(res) + } +} diff --git a/pkg/kapis/monitoring/v1alpha3/helper.go b/pkg/kapis/monitoring/v1alpha3/helper.go new file mode 100644 index 000000000..9bbb2e2f8 --- /dev/null +++ b/pkg/kapis/monitoring/v1alpha3/helper.go @@ -0,0 +1,281 @@ +package v1alpha3 + +import ( + "github.com/emicklei/go-restful" + "github.com/pkg/errors" + corev1 "k8s.io/apimachinery/pkg/apis/meta/v1" + model "kubesphere.io/kubesphere/pkg/models/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "strconv" + "time" +) + +const ( + DefaultStep = 10 * time.Minute + DefaultFilter = ".*" + DefaultOrder = model.OrderDescending + DefaultPage = 1 + DefaultLimit = 5 + + ComponentEtcd = "etcd" + ComponentAPIServer = "apiserver" + ComponentScheduler = "scheduler" + + ErrNoHit = "'end' must be after the namespace creation time." + ErrParamConflict = "'time' and the combination of 'start' and 'end' are mutually exclusive." + ErrInvalidStartEnd = "'start' must be before 'end'." + ErrInvalidPage = "Invalid parameter 'page'." + ErrInvalidLimit = "Invalid parameter 'limit'." +) + +type reqParams struct { + time string + start string + end string + step string + target string + order string + page string + limit string + metricFilter string + resourceFilter string + nodeName string + workspaceName string + namespaceName string + workloadKind string + workloadName string + podName string + containerName string + pvcName string + storageClassName string + componentType string + expression string +} + +type queryOptions struct { + metricFilter string + namedMetrics []string + + start time.Time + end time.Time + time time.Time + step time.Duration + + target string + identifier string + order string + page int + limit int + + option monitoring.QueryOption +} + +func (q queryOptions) isRangeQuery() bool { + return q.time.IsZero() +} + +func (q queryOptions) shouldSort() bool { + return q.target != "" && q.identifier != "" +} + +func parseRequestParams(req *restful.Request) reqParams { + var r reqParams + r.time = req.QueryParameter("time") + r.start = req.QueryParameter("start") + r.end = req.QueryParameter("end") + r.step = req.QueryParameter("step") + r.target = req.QueryParameter("sort_metric") + r.order = req.QueryParameter("sort_type") + r.page = req.QueryParameter("page") + r.limit = req.QueryParameter("limit") + r.metricFilter = req.QueryParameter("metrics_filter") + r.resourceFilter = req.QueryParameter("resources_filter") + r.nodeName = req.PathParameter("node") + r.workspaceName = req.PathParameter("workspace") + r.namespaceName = req.PathParameter("namespace") + r.workloadKind = req.PathParameter("kind") + r.workloadName = req.PathParameter("workload") + r.podName = req.PathParameter("pod") + r.containerName = req.PathParameter("container") + r.pvcName = req.PathParameter("pvc") + r.storageClassName = req.PathParameter("storageclass") + r.componentType = req.PathParameter("component") + r.expression = req.QueryParameter("expr") + return r +} + +func (h handler) makeQueryOptions(r reqParams, lvl monitoring.Level) (q queryOptions, err error) { + if r.resourceFilter == "" { + r.resourceFilter = DefaultFilter + } + + q.metricFilter = r.metricFilter + if r.metricFilter == "" { + q.metricFilter = DefaultFilter + } + + switch lvl { + case monitoring.LevelCluster: + q.option = monitoring.ClusterOption{} + q.namedMetrics = model.ClusterMetrics + case monitoring.LevelNode: + q.identifier = model.IdentifierNode + q.namedMetrics = model.NodeMetrics + q.option = monitoring.NodeOption{ + ResourceFilter: r.resourceFilter, + NodeName: r.nodeName, + } + case monitoring.LevelWorkspace: + q.identifier = model.IdentifierWorkspace + q.namedMetrics = model.WorkspaceMetrics + q.option = monitoring.WorkspaceOption{ + ResourceFilter: r.resourceFilter, + WorkspaceName: r.workspaceName, + } + case monitoring.LevelNamespace: + q.identifier = model.IdentifierNamespace + q.namedMetrics = model.NamespaceMetrics + q.option = monitoring.NamespaceOption{ + ResourceFilter: r.resourceFilter, + WorkspaceName: r.workspaceName, + NamespaceName: r.namespaceName, + } + case monitoring.LevelWorkload: + q.identifier = model.IdentifierWorkload + q.namedMetrics = model.WorkloadMetrics + q.option = monitoring.WorkloadOption{ + ResourceFilter: r.resourceFilter, + NamespaceName: r.namespaceName, + WorkloadKind: r.workloadKind, + } + case monitoring.LevelPod: + q.identifier = model.IdentifierPod + q.namedMetrics = model.PodMetrics + q.option = monitoring.PodOption{ + ResourceFilter: r.resourceFilter, + NodeName: r.nodeName, + NamespaceName: r.namespaceName, + WorkloadKind: r.workloadKind, + WorkloadName: r.workloadName, + PodName: r.podName, + } + case monitoring.LevelContainer: + q.identifier = model.IdentifierContainer + q.namedMetrics = model.ContainerMetrics + q.option = monitoring.ContainerOption{ + ResourceFilter: r.resourceFilter, + NamespaceName: r.namespaceName, + PodName: r.podName, + ContainerName: r.containerName, + } + case monitoring.LevelPVC: + q.identifier = model.IdentifierPVC + q.namedMetrics = model.PVCMetrics + q.option = monitoring.PVCOption{ + ResourceFilter: r.resourceFilter, + NamespaceName: r.namespaceName, + StorageClassName: r.storageClassName, + PersistentVolumeClaimName: r.pvcName, + } + case monitoring.LevelComponent: + q.option = monitoring.ComponentOption{} + switch r.componentType { + case ComponentEtcd: + q.namedMetrics = model.EtcdMetrics + case ComponentAPIServer: + q.namedMetrics = model.APIServerMetrics + case ComponentScheduler: + q.namedMetrics = model.SchedulerMetrics + } + } + + // Parse time params + if r.start != "" && r.end != "" { + startInt, err := strconv.ParseInt(r.start, 10, 64) + if err != nil { + return q, err + } + q.start = time.Unix(startInt, 0) + + endInt, err := strconv.ParseInt(r.end, 10, 64) + if err != nil { + return q, err + } + q.end = time.Unix(endInt, 0) + + if r.step == "" { + q.step = DefaultStep + } else { + q.step, err = time.ParseDuration(r.step) + if err != nil { + return q, err + } + } + + if q.start.After(q.end) { + return q, errors.New(ErrInvalidStartEnd) + } + } else if r.start == "" && r.end == "" { + if r.time == "" { + q.time = time.Now() + } else { + timeInt, err := strconv.ParseInt(r.time, 10, 64) + if err != nil { + return q, err + } + q.time = time.Unix(timeInt, 0) + } + } else { + return q, errors.Errorf(ErrParamConflict) + } + + // Ensure query start time to be after the namespace creation time + if r.namespaceName != "" { + ns, err := h.k.CoreV1().Namespaces().Get(r.namespaceName, corev1.GetOptions{}) + if err != nil { + return q, err + } + cts := ns.CreationTimestamp.Time + + // Query should happen no earlier than namespace's creation time. + // For range query, check and mutate `start`. For instant query, check and mutate `time`. + // In range query, if `start` and `end` are both before namespace's creation time, it causes no hit. + if !q.isRangeQuery() { + if q.time.Before(cts) { + q.time = cts + } + } else { + if q.start.Before(cts) { + q.start = cts + } + if q.end.Before(cts) { + return q, errors.New(ErrNoHit) + } + } + + } + + // Parse sorting and paging params + if r.target != "" { + q.target = r.target + q.page = DefaultPage + q.limit = DefaultLimit + if r.order != model.OrderAscending { + q.order = DefaultOrder + } + if r.page != "" { + q.page, err = strconv.Atoi(r.page) + if err != nil || q.page <= 0 { + return q, errors.New(ErrInvalidPage) + } + } + if r.limit != "" { + q.limit, err = strconv.Atoi(r.limit) + if err != nil || q.limit <= 0 { + return q, errors.New(ErrInvalidLimit) + } + } + } + + return q, nil +} diff --git a/pkg/kapis/monitoring/v1alpha3/helper_test.go b/pkg/kapis/monitoring/v1alpha3/helper_test.go new file mode 100644 index 000000000..b7aed1b23 --- /dev/null +++ b/pkg/kapis/monitoring/v1alpha3/helper_test.go @@ -0,0 +1,231 @@ +package v1alpha3 + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + model "kubesphere.io/kubesphere/pkg/models/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "testing" + "time" +) + +func TestIsRangeQuery(t *testing.T) { + tests := []struct { + opt queryOptions + expected bool + }{ + { + opt: queryOptions{ + time: time.Now(), + }, + expected: false, + }, + { + opt: queryOptions{ + start: time.Now().Add(-time.Hour), + end: time.Now(), + }, + expected: true, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + b := tt.opt.isRangeQuery() + if b != tt.expected { + t.Fatalf("expected %v, but got %v", tt.expected, b) + } + }) + } +} + +func TestParseRequestParams(t *testing.T) { + tests := []struct { + params reqParams + lvl monitoring.Level + namespace corev1.Namespace + expected queryOptions + expectedErr bool + }{ + { + params: reqParams{ + time: "abcdef", + }, + lvl: monitoring.LevelCluster, + expectedErr: true, + }, + { + params: reqParams{ + time: "1585831995", + }, + lvl: monitoring.LevelCluster, + expected: queryOptions{ + time: time.Unix(1585831995, 0), + metricFilter: ".*", + namedMetrics: model.ClusterMetrics, + option: monitoring.ClusterOption{}, + }, + expectedErr: false, + }, + { + params: reqParams{ + start: "1585830000", + end: "1585839999", + step: "1m", + namespaceName: "default", + }, + lvl: monitoring.LevelNamespace, + namespace: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + CreationTimestamp: metav1.Time{ + Time: time.Unix(1585836666, 0), + }, + }, + }, + expected: queryOptions{ + start: time.Unix(1585836666, 0), + end: time.Unix(1585839999, 0), + step: time.Minute, + identifier: model.IdentifierNamespace, + metricFilter: ".*", + namedMetrics: model.NamespaceMetrics, + option: monitoring.NamespaceOption{ + ResourceFilter: ".*", + NamespaceName: "default", + }, + }, + expectedErr: false, + }, + { + params: reqParams{ + time: "1585830000", + namespaceName: "default", + }, + lvl: monitoring.LevelNamespace, + namespace: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + CreationTimestamp: metav1.Time{ + Time: time.Unix(1585836666, 0), + }, + }, + }, + expected: queryOptions{ + time: time.Unix(1585836666, 0), + identifier: model.IdentifierNamespace, + metricFilter: ".*", + namedMetrics: model.NamespaceMetrics, + option: monitoring.NamespaceOption{ + ResourceFilter: ".*", + NamespaceName: "default", + }, + }, + expectedErr: false, + }, + { + params: reqParams{ + start: "1585830000", + end: "1585839999", + step: "1m", + namespaceName: "default", + }, + lvl: monitoring.LevelNamespace, + namespace: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + CreationTimestamp: metav1.Time{ + Time: time.Unix(1589999999, 0), + }, + }, + }, + expectedErr: true, + }, + { + params: reqParams{ + start: "1585830000", + end: "1585839999", + step: "1m", + namespaceName: "non-exist", + }, + lvl: monitoring.LevelNamespace, + namespace: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + CreationTimestamp: metav1.Time{ + Time: time.Unix(1589999999, 0), + }, + }, + }, + expectedErr: true, + }, + { + params: reqParams{ + time: "1585830000", + componentType: "etcd", + metricFilter: "etcd_server_list", + }, + lvl: monitoring.LevelComponent, + expected: queryOptions{ + time: time.Unix(1585830000, 0), + metricFilter: "etcd_server_list", + namedMetrics: model.EtcdMetrics, + option: monitoring.ComponentOption{}, + }, + expectedErr: false, + }, + { + params: reqParams{ + time: "1585830000", + workspaceName: "system-workspace", + metricFilter: "namespace_memory_usage_wo_cache|namespace_memory_limit_hard|namespace_cpu_usage", + page: "1", + limit: "10", + order: "desc", + target: "namespace_cpu_usage", + }, + lvl: monitoring.LevelNamespace, + expected: queryOptions{ + time: time.Unix(1585830000, 0), + metricFilter: "namespace_memory_usage_wo_cache|namespace_memory_limit_hard|namespace_cpu_usage", + namedMetrics: model.NamespaceMetrics, + option: monitoring.NamespaceOption{ + ResourceFilter: ".*", + WorkspaceName: "system-workspace", + }, + target: "namespace_cpu_usage", + identifier: "namespace", + order: "desc", + page: 1, + limit: 10, + }, + expectedErr: false, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + client := fake.NewSimpleClientset(&tt.namespace) + handler := newHandler(client, nil) + + result, err := handler.makeQueryOptions(tt.params, tt.lvl) + if err != nil { + if !tt.expectedErr { + t.Fatalf("unexpected err: %s.", err.Error()) + } + return + } + + if tt.expectedErr { + t.Fatalf("failed to catch error.") + } + + if diff := cmp.Diff(result, tt.expected, cmp.AllowUnexported(result, tt.expected)); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", tt.expected, diff) + } + }) + } +} diff --git a/pkg/kapis/monitoring/v1alpha3/register.go b/pkg/kapis/monitoring/v1alpha3/register.go new file mode 100644 index 000000000..f7eb2f33a --- /dev/null +++ b/pkg/kapis/monitoring/v1alpha3/register.go @@ -0,0 +1,428 @@ +/* + + Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/constants" + model "kubesphere.io/kubesphere/pkg/models/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "net/http" +) + +const ( + GroupName = "monitoring.kubesphere.io" + RespOK = "ok" +) + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"} + +func AddToContainer(c *restful.Container, k8sClient kubernetes.Interface, monitoringClient monitoring.Interface) error { + ws := runtime.NewWebService(GroupVersion) + + h := newHandler(k8sClient, monitoringClient) + + ws.Route(ws.GET("/cluster"). + To(h.handleClusterMetricsQuery). + Doc("Get cluster-level metric data."). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both cluster CPU usage and disk usage: `cluster_cpu_usage|cluster_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/nodes"). + To(h.handleNodeMetricsQuery). + Doc("Get node-level metric data of all nodes."). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both node CPU usage and disk usage: `node_cpu_usage|node_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The node filter consists of a regexp pattern. It specifies which node data to return. For example, the following filter matches both node i-caojnter and i-cmu82ogj: `i-caojnter|i-cmu82ogj`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort nodes by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/nodes/{node}"). + To(h.handleNodeMetricsQuery). + Doc("Get node-level metric data of the specific node."). + Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both node CPU usage and disk usage: `node_cpu_usage|node_disk_size_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NodeMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/workspaces"). + To(h.handleWorkspaceMetricsQuery). + Doc("Get workspace-level metric data of all workspaces."). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workspace CPU usage and memory usage: `workspace_cpu_usage|workspace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The workspace filter consists of a regexp pattern. It specifies which workspace data to return.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort workspaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/workspaces/{workspace}"). + To(h.handleWorkspaceMetricsQuery). + Doc("Get workspace-level metric data of a specific workspace."). + Param(ws.PathParameter("workspace", "Workspace name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workspace CPU usage and memory usage: `workspace_cpu_usage|workspace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkspaceMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/workspaces/{workspace}/namespaces"). + To(h.handleNamespaceMetricsQuery). + Doc("Get namespace-level metric data of a specific workspace."). + Param(ws.PathParameter("workspace", "Workspace name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The namespace filter consists of a regexp pattern. It specifies which namespace data to return. For example, the following filter matches both namespace test and kube-system: `test|kube-system`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort namespaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces"). + To(h.handleNamespaceMetricsQuery). + Doc("Get namespace-level metric data of all namespaces."). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The namespace filter consists of a regexp pattern. It specifies which namespace data to return. For example, the following filter matches both namespace test and kube-system: `test|kube-system`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort namespaces by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}"). + To(h.handleNamespaceMetricsQuery). + Doc("Get namespace-level metric data of the specific namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both namespace CPU usage and memory usage: `namespace_cpu_usage|namespace_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/workloads"). + To(h.handleWorkloadMetricsQuery). + Doc("Get workload-level metric data of a specific namespace's workloads."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workload CPU usage and memory usage: `workload_cpu_usage|workload_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The workload filter consists of a regexp pattern. It specifies which workload data to return. For example, the following filter matches any workload whose name begins with prometheus: `prometheus.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort workloads by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}"). + To(h.handleWorkloadMetricsQuery). + Doc("Get workload-level metric data of all workloads which belongs to a specific kind."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("kind", "Workload kind. One of deployment, daemonset, statefulset.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both workload CPU usage and memory usage: `workload_cpu_usage|workload_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The workload filter consists of a regexp pattern. It specifies which workload data to return. For example, the following filter matches any workload whose name begins with prometheus: `prometheus.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort workloads by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.WorkloadMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/pods"). + To(h.handlePodMetricsQuery). + Doc("Get pod-level metric data of the specific namespace's pods."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}"). + To(h.handlePodMetricsQuery). + Doc("Get pod-level metric data of a specific pod. Navigate to the pod by the pod's namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/workloads/{kind}/{workload}/pods"). + To(h.handlePodMetricsQuery). + Doc("Get pod-level metric data of a specific workload's pods. Navigate to the workload by the namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("kind", "Workload kind. One of deployment, daemonset, statefulset.").DataType("string").Required(true)). + Param(ws.PathParameter("workload", "Workload name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/nodes/{node}/pods"). + To(h.handlePodMetricsQuery). + Doc("Get pod-level metric data of all pods on a specific node."). + Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The pod filter consists of a regexp pattern. It specifies which pod data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort pods by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/nodes/{node}/pods/{pod}"). + To(h.handlePodMetricsQuery). + Doc("Get pod-level metric data of a specific pod. Navigate to the pod by the node where it is scheduled."). + Param(ws.PathParameter("node", "Node name.").DataType("string").Required(true)). + Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both pod CPU usage and memory usage: `pod_cpu_usage|pod_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PodMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers"). + To(h.handleContainerMetricsQuery). + Doc("Get container-level metric data of a specific pod's containers. Navigate to the pod by the pod's namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both container CPU usage and memory usage: `container_cpu_usage|container_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The container filter consists of a regexp pattern. It specifies which container data to return. For example, the following filter matches container prometheus and prometheus-config-reloader: `prometheus|prometheus-config-reloader`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort containers by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/pods/{pod}/containers/{container}"). + To(h.handleContainerMetricsQuery). + Doc("Get container-level metric data of a specific container. Navigate to the container by the pod name and the namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("pod", "Pod name.").DataType("string").Required(true)). + Param(ws.PathParameter("container", "Container name.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both container CPU usage and memory usage: `container_cpu_usage|container_memory_usage`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.ContainerMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/storageclasses/{storageclass}/persistentvolumeclaims"). + To(h.handlePVCMetricsQuery). + Doc("Get PVC-level metric data of the specific storageclass's PVCs."). + Param(ws.PathParameter("storageclass", "The name of the storageclass.").DataType("string").Required(true)). + Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The PVC filter consists of a regexp pattern. It specifies which PVC data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort PVCs by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims"). + To(h.handlePVCMetricsQuery). + Doc("Get PVC-level metric data of the specific namespace's PVCs."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("resources_filter", "The PVC filter consists of a regexp pattern. It specifies which PVC data to return. For example, the following filter matches any pod whose name begins with redis: `redis.*`.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_metric", "Sort PVCs by the specified metric. Not applicable if **start** and **end** are provided.").DataType("string").Required(false)). + Param(ws.QueryParameter("sort_type", "Sort order. One of asc, desc.").DefaultValue("desc.").DataType("string").Required(false)). + Param(ws.QueryParameter("page", "The page number. This field paginates result data of each metric, then returns a specific page. For example, setting **page** to 2 returns the second page. It only applies to sorted metric data.").DataType("integer").Required(false)). + Param(ws.QueryParameter("limit", "Page size, the maximum number of results in a single page. Defaults to 5.").DataType("integer").Required(false).DefaultValue("5")). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/persistentvolumeclaims/{pvc}"). + To(h.handlePVCMetricsQuery). + Doc("Get PVC-level metric data of a specific PVC. Navigate to the PVC by the PVC's namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.PathParameter("pvc", "PVC name.").DataType("string").Required(true)). + Param(ws.PathParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both PVC available and used inodes: `pvc_inodes_available|pvc_inodes_used`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.PVCMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/components/{component}"). + To(h.handleComponentMetricsQuery). + Doc("Get component-level metric data of the specific system component."). + Param(ws.PathParameter("component", "system component to monitor. One of etcd, apiserver, scheduler, controller_manager, coredns, prometheus.").DataType("string").Required(true)). + Param(ws.QueryParameter("metrics_filter", "The metric name filter consists of a regexp pattern. It specifies which metric data to return. For example, the following filter matches both etcd server list and total size of the underlying database: `etcd_server_list|etcd_mvcc_db_size`. View available metrics at [kubesphere.io](https://docs.kubesphere.io/advanced-v2.0/zh-CN/api-reference/monitoring-metrics/).").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentMetricsTag}). + Writes(model.Metrics{}). + Returns(http.StatusOK, RespOK, model.Metrics{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/targets/metadata"). + To(h.handleMetadataQuery). + Doc("Get metadata of metrics for the specific namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.CustomMetricsTag}). + Writes(model.Metadata{}). + Returns(http.StatusOK, RespOK, model.Metadata{})). + Produces(restful.MIME_JSON) + + ws.Route(ws.GET("/namespaces/{namespace}/targets/query"). + To(h.handleAdhocQuery). + Doc("Make an ad-hoc query in the specific namespace."). + Param(ws.PathParameter("namespace", "The name of the namespace.").DataType("string").Required(true)). + Param(ws.QueryParameter("expr", "The expression to be evaluated.").DataType("string").Required(false)). + Param(ws.QueryParameter("start", "Start time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1559347200. ").DataType("string").Required(true)). + Param(ws.QueryParameter("end", "End time of query. Use **start** and **end** to retrieve metric data over a time span. It is a string with Unix time format, eg. 1561939200. ").DataType("string").Required(false)). + Param(ws.QueryParameter("step", "Time interval. Retrieve metric data at a fixed interval within the time range of start and end. It requires both **start** and **end** are provided. The format is [0-9]+[smhdwy]. Defaults to 10m (i.e. 10 min).").DataType("string").DefaultValue("10m").Required(false)). + Param(ws.QueryParameter("time", "A timestamp in Unix time format. Retrieve metric data at a single point in time. Defaults to now. Time and the combination of start, end, step are mutually exclusive.").DataType("string").Required(false)). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.CustomMetricsTag}). + Writes(monitoring.Metric{}). + Returns(http.StatusOK, RespOK, monitoring.Metric{})). + Produces(restful.MIME_JSON) + + c.Add(ws) + return nil +} diff --git a/pkg/kapis/network/group.go b/pkg/kapis/network/group.go new file mode 100644 index 000000000..71d348680 --- /dev/null +++ b/pkg/kapis/network/group.go @@ -0,0 +1,18 @@ +/* +Copyright 2020 The KubeSphere 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 network contains network API versions +package network diff --git a/pkg/kapis/network/v1alpha2/handler.go b/pkg/kapis/network/v1alpha2/handler.go new file mode 100644 index 000000000..b7c5a3cc9 --- /dev/null +++ b/pkg/kapis/network/v1alpha2/handler.go @@ -0,0 +1,87 @@ +package v1alpha2 + +import ( + "fmt" + "io/ioutil" + "net/http" + "net/url" + + "github.com/emicklei/go-restful" + "k8s.io/klog" + + "kubesphere.io/kubesphere/pkg/api" +) + +const ScopeQueryUrl = "http://%s/api/topology/services" + +type handler struct { + weaveScopeHost string +} + +func (h *handler) getScopeUrl() string { + return fmt.Sprintf(ScopeQueryUrl, h.weaveScopeHost) +} + +func (h *handler) getNamespaceTopology(request *restful.Request, response *restful.Response) { + var query = url.Values{ + "namespace": []string{request.PathParameter("namespace")}, + "timestamp": request.QueryParameters("timestamp"), + } + var u = fmt.Sprintf("%s?%s", h.getScopeUrl(), query.Encode()) + + resp, err := http.Get(u) + + if err != nil { + klog.Errorf("query scope faile with err %v", err) + api.HandleInternalError(response, nil, err) + return + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + klog.Errorf("read response error : %v", err) + api.HandleInternalError(response, nil, err) + return + } + + // need to set header for proper response + response.Header().Set("Content-Type", "application/json") + _, err = response.Write(body) + + if err != nil { + klog.Errorf("write response failed %v", err) + } +} + +func (h *handler) getNamespaceNodeTopology(request *restful.Request, response *restful.Response) { + var query = url.Values{ + "namespace": []string{request.PathParameter("namespace")}, + "timestamp": request.QueryParameters("timestamp"), + } + var u = fmt.Sprintf("%s/%s?%s", h.getScopeUrl(), request.PathParameter("node_id"), query.Encode()) + + resp, err := http.Get(u) + + if err != nil { + klog.Errorf("query scope faile with err %v", err) + api.HandleInternalError(response, nil, err) + return + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + klog.Errorf("read response error : %v", err) + api.HandleInternalError(response, nil, err) + return + } + + // need to set header for proper response + response.Header().Set("Content-Type", "application/json") + _, err = response.Write(body) + + if err != nil { + klog.Errorf("write response failed %v", err) + } +} diff --git a/pkg/kapis/network/v1alpha2/register.go b/pkg/kapis/network/v1alpha2/register.go new file mode 100644 index 000000000..300480308 --- /dev/null +++ b/pkg/kapis/network/v1alpha2/register.go @@ -0,0 +1,59 @@ +/* +Copyright 2020 The KubeSphere 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 v1alpha2 + +import ( + "net/http" + + restful "github.com/emicklei/go-restful" + restfulspec "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/constants" +) + +const GroupName = "network.kubesphere.io" + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} + +func AddToContainer(c *restful.Container, weaveScopeHost string) error { + webservice := runtime.NewWebService(GroupVersion) + h := handler{weaveScopeHost: weaveScopeHost} + + webservice.Route(webservice.GET("/namespaces/{namespace}/topology"). + To(h.getNamespaceTopology). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). + Doc("Get the topology with specifying a namespace"). + Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). + Returns(http.StatusOK, "ok", TopologyResponse{}). + Writes(TopologyResponse{})). + Produces(restful.MIME_JSON) + + webservice.Route(webservice.GET("/namespaces/{namespace}/topology/{node_id}"). + To(h.getNamespaceNodeTopology). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.NetworkTopologyTag}). + Doc("Get the topology with specifying a node id in the whole topology and specifying a namespace"). + Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). + Param(webservice.PathParameter("node_id", "id of the node in the whole topology").Required(true)). + Returns(http.StatusOK, "ok", NodeResponse{}). + Writes(NodeResponse{})). + Produces(restful.MIME_JSON) + + c.Add(webservice) + + return nil +} diff --git a/pkg/kapis/network/v1alpha2/swagger-doc.go b/pkg/kapis/network/v1alpha2/swagger-doc.go new file mode 100644 index 000000000..d8cdd4c0a --- /dev/null +++ b/pkg/kapis/network/v1alpha2/swagger-doc.go @@ -0,0 +1,218 @@ +/* +Copyright 2020 The KubeSphere 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 v1alpha2 + +import ( + "time" +) + +///////////////////// +// SWAGGER RESPONSES +///////////////////// + +// NoContent: the response is empty +type NoContent struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// BadRequestError: the client request is incorrect +type BadRequestError struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// NotFoundError is the error message that is generated when server could not find +// what was requested +type NotFoundError struct { + Status int32 `json:"status"` + Reason error `json:"reason"` +} + +// copy from github.com/weaveworks/scope v1.12.0 + +// MetadataRow is a row for the metadata table. +type MetadataRow struct { + ID string `json:"id"` + Label string `json:"label"` + Value string `json:"value"` + Priority float64 `json:"priority,omitempty"` + Datatype string `json:"dataType,omitempty"` + Truncate int `json:"truncate,omitempty"` +} + +// BasicNodeSummary is basic summary information about a Node, +// sufficient for rendering links to the node. +type BasicNodeSummary struct { + ID string `json:"id"` + Label string `json:"label"` + LabelMinor string `json:"labelMinor"` + Rank string `json:"rank"` + Shape string `json:"shape,omitempty"` + Tag string `json:"tag,omitempty"` + Stack bool `json:"stack,omitempty"` + Pseudo bool `json:"pseudo,omitempty"` +} + +// Parent is the information needed to build a link to the parent of a Node. +type Parent struct { + ID string `json:"id"` + Label string `json:"label"` + TopologyID string `json:"topologyId"` +} + +// Metric is a list of timeseries data with some metadata. Clients must use the +// Add method to add values. Metrics are immutable. +type Metric struct { + Samples []Sample `json:"samples,omitempty"` + Min float64 `json:"min"` + Max float64 `json:"max"` +} + +func (m Metric) first() time.Time { return m.Samples[0].Timestamp } +func (m Metric) last() time.Time { return m.Samples[len(m.Samples)-1].Timestamp } + +// Sample is a single datapoint of a metric. +type Sample struct { + Timestamp time.Time `json:"date"` + Value float64 `json:"value"` +} + +// MetricRow is a tuple of data used to render a metric as a sparkline and +// accoutrements. +type MetricRow struct { + ID string + Label string + Format string + Group string + Value float64 + ValueEmpty bool + Priority float64 + URL string + Metric *Metric +} + +// NodeSummaryGroup is a topology-typed group of children for a Node. +type NodeSummaryGroup struct { + ID string `json:"id"` + Label string `json:"label"` + Nodes []NodeSummary `json:"nodes"` + TopologyID string `json:"topologyId"` + Columns []Column `json:"columns"` +} + +// Connection is a row in the connections table. +type Connection struct { + ID string `json:"id"` // ID of this element in the UI. Must be unique for a given ConnectionsSummary. + NodeID string `json:"nodeId"` // ID of a node in the topology. Optional, must be set if linkable is true. + Label string `json:"label"` + LabelMinor string `json:"labelMinor,omitempty"` + Metadata []MetadataRow `json:"metadata,omitempty"` +} + +// ConnectionsSummary is the table of connection to/form a node +type ConnectionsSummary struct { + ID string `json:"id"` + TopologyID string `json:"topologyId"` + Label string `json:"label"` + Columns []Column `json:"columns"` + Connections []Connection `json:"connections"` +} + +// Column is the type for multi-column tables in the UI. +type Column struct { + ID string `json:"id"` + Label string `json:"label"` + DataType string `json:"dataType"` +} + +// Row is the type that holds the table data for the UI. Entries map from column ID to cell value. +type Row struct { + ID string `json:"id"` + Entries map[string]string `json:"entries"` +} + +// Table is the type for a table in the UI. +type Table struct { + ID string `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + Columns []Column `json:"columns"` + Rows []Row `json:"rows"` + TruncationCount int `json:"truncationCount,omitempty"` +} + +// StringSet is a sorted set of unique strings. Clients must use the Add +// method to add strings. +type StringSet []string + +// IDList is a list of string IDs, which are always sorted and unique. +type IDList StringSet + +// NodeSummary is summary information about a Node. +type NodeSummary struct { + BasicNodeSummary + Metadata []MetadataRow `json:"metadata,omitempty"` + Parents []Parent `json:"parents,omitempty"` + Metrics []MetricRow `json:"metrics,omitempty"` + Tables []Table `json:"tables,omitempty"` + Adjacency IDList `json:"adjacency,omitempty"` +} + +type NodeSummaries map[string]NodeSummary + +type APITopology struct { + Nodes NodeSummaries `json:"nodes"` +} + +// A Control basically describes an RPC +type Control struct { + ID string `json:"id"` + Human string `json:"human"` + Icon string `json:"icon"` // from https://fortawesome.github.io/Font-Awesome/cheatsheet/ please + Confirmation string `json:"confirmation,omitempty"` + Rank int `json:"rank"` +} + +// ControlInstance contains a control description, and all the info +// needed to execute it. +type ControlInstance struct { + ProbeID string + NodeID string + Control Control +} + +// Node is the data type that's yielded to the JavaScript layer when +// we want deep information about an individual node. +type Node struct { + NodeSummary + Controls []ControlInstance `json:"controls"` + Children []NodeSummaryGroup `json:"children,omitempty"` + Connections []ConnectionsSummary `json:"connections,omitempty"` +} + +type APINode struct { + Node Node `json:"node"` +} + +type TopologyResponse struct { + APITopology +} + +type NodeResponse struct { + APINode +} diff --git a/pkg/kapis/oauth/handler.go b/pkg/kapis/oauth/handler.go new file mode 100644 index 000000000..bab48cd0f --- /dev/null +++ b/pkg/kapis/oauth/handler.go @@ -0,0 +1,190 @@ +/* + * + * Copyright 2020 The KubeSphere 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 oauth + +import ( + "fmt" + "github.com/emicklei/go-restful" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/auth" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "net/http" +) + +type oauthHandler struct { + issuer token.Issuer + options *authoptions.AuthenticationOptions +} + +func newOAUTHHandler(issuer token.Issuer, options *authoptions.AuthenticationOptions) *oauthHandler { + return &oauthHandler{issuer: issuer, options: options} +} + +// Implement webhook authentication interface +// https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication +func (h *oauthHandler) TokenReviewHandler(req *restful.Request, resp *restful.Response) { + var tokenReview auth.TokenReview + + err := req.ReadEntity(&tokenReview) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(resp, req, err) + return + } + + if err = tokenReview.Validate(); err != nil { + klog.Error(err) + api.HandleBadRequest(resp, req, err) + return + } + + user, err := h.issuer.Verify(tokenReview.Spec.Token) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, req, err) + return + } + + success := auth.TokenReview{APIVersion: tokenReview.APIVersion, + Kind: auth.KindTokenReview, + Status: &auth.Status{ + Authenticated: true, + User: map[string]interface{}{"username": user.GetName(), "uid": user.GetUID()}, + }, + } + + resp.WriteEntity(success) +} + +func (h *oauthHandler) AuthorizeHandler(req *restful.Request, resp *restful.Response) { + user, ok := request.UserFrom(req.Request.Context()) + clientId := req.QueryParameter("client_id") + responseType := req.QueryParameter("response_type") + redirectURI := req.QueryParameter("redirect_uri") + + conf, err := h.options.OAuthOptions.OAuthClient(clientId) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + + if responseType != "token" { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: response type %s is not supported", responseType)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + + if !ok { + err := apierrors.NewUnauthorized("Unauthorized") + resp.WriteError(http.StatusUnauthorized, err) + return + } + + expiresIn := h.options.OAuthOptions.AccessTokenMaxAge + + if conf.AccessTokenMaxAge != nil { + expiresIn = *conf.AccessTokenMaxAge + } + + accessToken, err := h.issuer.IssueTo(user, expiresIn) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + + redirectURL, err := conf.ResolveRedirectURL(redirectURI) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + + redirectURL = fmt.Sprintf("%s#access_token=%s&token_type=Bearer", redirectURL, accessToken) + + if expiresIn > 0 { + redirectURL = fmt.Sprintf("%s&expires_in=%v", redirectURL, expiresIn.Seconds()) + } + + resp.Header().Set("Content-Type", "text/plain") + http.Redirect(resp, req.Request, redirectURL, http.StatusFound) +} + +func (h *oauthHandler) OAuthCallBackHandler(req *restful.Request, resp *restful.Response) { + + code := req.QueryParameter("code") + name := req.PathParameter("callback") + + if code == "" { + err := apierrors.NewUnauthorized("Unauthorized: missing code") + resp.WriteError(http.StatusUnauthorized, err) + } + + idP, err := h.options.OAuthOptions.IdentityProviderOptions(name) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + } + + oauthIdentityProvider, err := identityprovider.ResolveOAuthProvider(idP.Type, idP.Provider) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + } + + user, err := oauthIdentityProvider.IdentityExchange(code) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + } + + expiresIn := h.options.OAuthOptions.AccessTokenMaxAge + + accessToken, err := h.issuer.IssueTo(user, expiresIn) + + if err != nil { + err := apierrors.NewUnauthorized(fmt.Sprintf("Unauthorized: %s", err)) + resp.WriteError(http.StatusUnauthorized, err) + return + } + + result := oauth.Token{ + AccessToken: accessToken, + TokenType: "Bearer", + ExpiresIn: int(expiresIn.Seconds()), + } + + resp.WriteEntity(result) + return +} diff --git a/pkg/kapis/oauth/register.go b/pkg/kapis/oauth/register.go new file mode 100644 index 000000000..81d2bb426 --- /dev/null +++ b/pkg/kapis/oauth/register.go @@ -0,0 +1,95 @@ +/* + * + * Copyright 2020 The KubeSphere 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 oauth + +import ( + "github.com/emicklei/go-restful" + restfulspec "github.com/emicklei/go-restful-openapi" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/auth" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" + "kubesphere.io/kubesphere/pkg/apiserver/authentication/token" + "kubesphere.io/kubesphere/pkg/constants" + "net/http" +) + +// ks-apiserver includes a built-in OAuth server. Users obtain OAuth access tokens to authenticate themselves to the API. +// The OAuth server supports standard authorization code grant and the implicit grant OAuth authorization flows. +// All requests for OAuth tokens involve a request to /oauth/authorize. +// Most authentication integrations place an authenticating proxy in front of this endpoint, or configure ks-apiserver +// to validate credentials against a backing identity provider. +// Requests to /oauth/authorize can come from user-agents that cannot display interactive login pages, such as the CLI. +func AddToContainer(c *restful.Container, issuer token.Issuer, options *authoptions.AuthenticationOptions) error { + ws := &restful.WebService{} + ws.Path("/oauth"). + Consumes(restful.MIME_JSON). + Produces(restful.MIME_JSON) + + handler := newOAUTHHandler(issuer, options) + + // Implement webhook authentication interface + // https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication + ws.Route(ws.POST("/authenticate"). + Doc("TokenReview attempts to authenticate a token to a known user. Note: TokenReview requests may be "+ + "cached by the webhook token authenticator plugin in the kube-apiserver."). + Reads(auth.TokenReview{}). + To(handler.TokenReviewHandler). + Returns(http.StatusOK, api.StatusOK, auth.TokenReview{}). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.IdentityManagementTag})) + + // Only support implicit grant flow + // https://tools.ietf.org/html/rfc6749#section-4.2 + ws.Route(ws.GET("/authorize"). + Doc("All requests for OAuth tokens involve a request to /oauth/authorize."). + Param(ws.QueryParameter("response_type", "The value MUST be one of \"code\" for requesting an "+ + "authorization code as described by [RFC6749] Section 4.1.1, \"token\" for requesting an access token (implicit grant)"+ + " as described by [RFC6749] Section 4.2.2.").Required(true)). + Param(ws.QueryParameter("client_id", "The client identifier issued to the client during the "+ + "registration process described by [RFC6749] Section 2.2.").Required(true)). + Param(ws.QueryParameter("redirect_uri", "After completing its interaction with the resource owner, "+ + "the authorization server directs the resource owner's user-agent back to the client.The redirection endpoint "+ + "URI MUST be an absolute URI as defined by [RFC3986] Section 4.3.").Required(false)). + To(handler.AuthorizeHandler)) + //ws.Route(ws.POST("/token")) + + // Authorization callback URL, where the end of the URL contains the identity provider name. + // The provider name is also used to build the callback URL. + ws.Route(ws.GET("/callback/{callback}"). + Doc("OAuth callback API, the path param callback is config by identity provider"). + Param(ws.QueryParameter("access_token", "The access token issued by the authorization server."). + Required(true)). + Param(ws.QueryParameter("token_type", "The type of the token issued as described in [RFC6479] Section 7.1. "+ + "Value is case insensitive.").Required(true)). + Param(ws.QueryParameter("expires_in", "The lifetime in seconds of the access token. For "+ + "example, the value \"3600\" denotes that the access token will "+ + "expire in one hour from the time the response was generated."+ + "If omitted, the authorization server SHOULD provide the "+ + "expiration time via other means or document the default value.")). + Param(ws.QueryParameter("scope", "if identical to the scope requested by the client;"+ + "otherwise, REQUIRED. The scope of the access token as described by [RFC6479] Section 3.3.").Required(false)). + Param(ws.QueryParameter("state", "if the \"state\" parameter was present in the client authorization request."+ + "The exact value received from the client.").Required(true)). + To(handler.OAuthCallBackHandler). + Returns(http.StatusOK, api.StatusOK, oauth.Token{})) + + c.Add(ws) + + return nil +} diff --git a/pkg/kapis/openpitrix/group.go b/pkg/kapis/openpitrix/group.go deleted file mode 100644 index f6b0abd4a..000000000 --- a/pkg/kapis/openpitrix/group.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 operations contains operations API versions -package openpitrix diff --git a/pkg/kapis/openpitrix/install/install.go b/pkg/kapis/openpitrix/install/install.go deleted file mode 100644 index 6193817d8..000000000 --- a/pkg/kapis/openpitrix/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1.AddToContainer(container)) -} diff --git a/pkg/kapis/openpitrix/v1/handler.go b/pkg/kapis/openpitrix/v1/handler.go new file mode 100644 index 000000000..09ca9c3e8 --- /dev/null +++ b/pkg/kapis/openpitrix/v1/handler.go @@ -0,0 +1,894 @@ +package v1 + +import ( + "fmt" + "github.com/emicklei/go-restful" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + k8sinformers "k8s.io/client-go/informers" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/openpitrix" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + op "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "strconv" + "strings" +) + +type openpitrixHandler struct { + openpitrix openpitrix.Interface + informers k8sinformers.SharedInformerFactory +} + +func newOpenpitrixHandler(factory informers.InformerFactory, opClient op.Client) *openpitrixHandler { + + return &openpitrixHandler{ + openpitrix: openpitrix.NewOpenpitrixOperator(factory.KubernetesSharedInformerFactory(), opClient), + informers: factory.KubernetesSharedInformerFactory(), + } +} + +func (h *openpitrixHandler) ListApplications(request *restful.Request, response *restful.Response) { + limit, offset := params.ParsePaging(request) + namespace := request.PathParameter("namespace") + orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, false) + conditions, err := params.ParseConditions(request) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(response, nil, err) + return + } + + // filter namespaced applications by runtime_id + if namespace != "" { + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(response, nil, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId == "" { + // runtime id not exist,return empty response + response.WriteAsJson(models.PageableResponse{Items: []interface{}{}, TotalCount: 0}) + return + } else { + // filter by runtime id + conditions.Match[openpitrix.RuntimeId] = runtimeId + } + } + + result, err := h.openpitrix.ListApplications(conditions, limit, offset, orderBy, reverse) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(result) +} + +func (h *openpitrixHandler) DescribeApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Infoln(err) + api.HandleForbidden(resp, nil, err) + return + } + + resp.WriteEntity(app) + return +} + +func (h *openpitrixHandler) CreateApplication(req *restful.Request, resp *restful.Response) { + namespace := req.PathParameter("namespace") + var createClusterRequest openpitrix.CreateClusterRequest + err := req.ReadEntity(&createClusterRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + createClusterRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + err = h.openpitrix.CreateApplication(namespace, createClusterRequest) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) ModifyApplication(req *restful.Request, resp *restful.Response) { + var modifyClusterAttributesRequest openpitrix.ModifyClusterAttributesRequest + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + err := req.ReadEntity(&modifyClusterAttributesRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Infoln(err) + api.HandleForbidden(resp, nil, err) + return + } + + err = h.openpitrix.ModifyApplication(modifyClusterAttributesRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteApplication(req *restful.Request, resp *restful.Response) { + clusterId := req.PathParameter("application") + namespace := req.PathParameter("namespace") + app, err := h.openpitrix.DescribeApplication(namespace, clusterId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + ns, err := h.informers.Core().V1().Namespaces().Lister().Get(namespace) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey] + + if runtimeId != app.Cluster.RuntimeId { + err = fmt.Errorf("rumtime not match %s,%s", app.Cluster.RuntimeId, runtimeId) + klog.V(4).Infoln(err) + api.HandleForbidden(resp, nil, err) + return + } + + err = h.openpitrix.DeleteApplication(clusterId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) GetAppVersionPackage(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + + result, err := h.openpitrix.GetAppVersionPackage(appId, versionId) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DoAppAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + appId := req.PathParameter("app") + + err = h.openpitrix.DoAppAction(appId, &doActionRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DoAppVersionAction(req *restful.Request, resp *restful.Response) { + var doActionRequest openpitrix.ActionRequest + err := req.ReadEntity(&doActionRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + doActionRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + versionId := req.PathParameter("version") + + err = h.openpitrix.DoAppVersionAction(versionId, &doActionRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) GetAppVersionFiles(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + getAppVersionFilesRequest := &openpitrix.GetAppVersionFilesRequest{} + if f := req.QueryParameter("files"); f != "" { + getAppVersionFilesRequest.Files = strings.Split(f, ",") + } + + result, err := h.openpitrix.GetAppVersionFiles(versionId, getAppVersionFilesRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListAppVersionAudits(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + appId := req.PathParameter("app") + versionId := req.PathParameter("version") + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + conditions.Match[openpitrix.AppId] = appId + if versionId != "" { + conditions.Match[openpitrix.VersionId] = versionId + } + + result, err := h.openpitrix.ListAppVersionAudits(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListReviews(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.StatusTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.ListAppVersionReviews(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListAppVersions(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + appId := req.PathParameter("app") + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + conditions.Match[openpitrix.AppId] = appId + + result, err := h.openpitrix.ListAppVersions(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + + if statistics { + for _, item := range result.Items { + if version, ok := item.(*openpitrix.AppVersion); ok { + statisticsResult, err := h.openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{"app_id": version.AppId, "version_id": version.VersionId}}, 0, 0, "", false) + if err != nil { + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) + return + } + version.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListApps(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.ListApps(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + if statistics { + for _, item := range result.Items { + if app, ok := item.(*openpitrix.App); ok { + statuses := "active|used|enabled|stopped|pending|creating|upgrading|updating|rollbacking|stopping|starting|recovering|resizing|scaling|deleting" + statisticsResult, err := h.openpitrix.ListApplications(¶ms.Conditions{Match: map[string]string{openpitrix.AppId: app.AppId, openpitrix.Status: statuses}}, 0, 0, "", false) + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + app.ClusterTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ModifyApp(req *restful.Request, resp *restful.Response) { + + var patchAppRequest openpitrix.ModifyAppRequest + err := req.ReadEntity(&patchAppRequest) + appId := req.PathParameter("app") + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + err = h.openpitrix.ModifyApp(appId, &patchAppRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + result, err := h.openpitrix.DescribeApp(appId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DeleteApp(req *restful.Request, resp *restful.Response) { + appId := req.PathParameter("app") + + err := h.openpitrix.DeleteApp(appId) + + if err != nil { + if status.Code(err) == codes.NotFound { + api.HandleNotFound(resp, nil, err) + return + } + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) CreateApp(req *restful.Request, resp *restful.Response) { + createAppRequest := &openpitrix.CreateAppRequest{} + err := req.ReadEntity(createAppRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + createAppRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppRequest.VersionPackage, + VersionType: createAppRequest.VersionType, + } + result, err = h.openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = h.openpitrix.CreateApp(createAppRequest) + } + + if err != nil { + if status.Code(err) == codes.InvalidArgument { + api.HandleBadRequest(resp, nil, err) + return + } + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateAppVersion(req *restful.Request, resp *restful.Response) { + var createAppVersionRequest openpitrix.CreateAppVersionRequest + err := req.ReadEntity(&createAppVersionRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + // override app id + createAppVersionRequest.AppId = req.PathParameter("app") + createAppVersionRequest.Username = req.HeaderParameter(constants.UserNameHeader) + + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validatePackageRequest := &openpitrix.ValidatePackageRequest{ + VersionPackage: createAppVersionRequest.Package, + VersionType: createAppVersionRequest.Type, + } + result, err = h.openpitrix.ValidatePackage(validatePackageRequest) + } else { + result, err = h.openpitrix.CreateAppVersion(&createAppVersionRequest) + } + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ModifyAppVersion(req *restful.Request, resp *restful.Response) { + + var patchAppVersionRequest openpitrix.ModifyAppVersionRequest + err := req.ReadEntity(&patchAppVersionRequest) + versionId := req.PathParameter("version") + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + err = h.openpitrix.ModifyAppVersion(versionId, &patchAppVersionRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + err := h.openpitrix.DeleteAppVersion(versionId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeAppVersion(req *restful.Request, resp *restful.Response) { + versionId := req.PathParameter("version") + + result, err := h.openpitrix.DescribeAppVersion(versionId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DescribeAttachment(req *restful.Request, resp *restful.Response) { + attachmentId := req.PathParameter("attachment") + fileName := req.QueryParameter("filename") + result, err := h.openpitrix.DescribeAttachment(attachmentId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + // file raw + if fileName != "" { + data := result.AttachmentContent[fileName] + resp.Write(data) + resp.Header().Set("Content-Type", "text/plain") + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateCategory(req *restful.Request, resp *restful.Response) { + createCategoryRequest := &openpitrix.CreateCategoryRequest{} + err := req.ReadEntity(createCategoryRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.CreateCategory(createCategoryRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) DeleteCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + err := h.openpitrix.DeleteCategory(categoryId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} +func (h *openpitrixHandler) ModifyCategory(req *restful.Request, resp *restful.Response) { + var modifyCategoryRequest openpitrix.ModifyCategoryRequest + categoryId := req.PathParameter("category") + err := req.ReadEntity(&modifyCategoryRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + err = h.openpitrix.ModifyCategory(categoryId, &modifyCategoryRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} +func (h *openpitrixHandler) DescribeCategory(req *restful.Request, resp *restful.Response) { + categoryId := req.PathParameter("category") + + result, err := h.openpitrix.DescribeCategory(categoryId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) ListCategories(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + statistics := params.GetBoolValueWithDefault(req, "statistics", false) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.ListCategories(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + if statistics { + for _, item := range result.Items { + if category, ok := item.(*openpitrix.Category); ok { + statisticsResult, err := h.openpitrix.ListApps(¶ms.Conditions{Match: map[string]string{"category_id": category.CategoryID, "status": openpitrix.StatusActive, "repo": openpitrix.BuiltinRepoId}}, "", false, 0, 0) + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + category.AppTotal = &statisticsResult.TotalCount + } + } + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) CreateRepo(req *restful.Request, resp *restful.Response) { + createRepoRequest := &openpitrix.CreateRepoRequest{} + err := req.ReadEntity(createRepoRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + validate, _ := strconv.ParseBool(req.QueryParameter("validate")) + + var result interface{} + + if validate { + validateRepoRequest := &openpitrix.ValidateRepoRequest{ + Type: createRepoRequest.Type, + Url: createRepoRequest.URL, + Credential: createRepoRequest.Credential, + } + result, err = h.openpitrix.ValidateRepo(validateRepoRequest) + } else { + result, err = h.openpitrix.CreateRepo(createRepoRequest) + } + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) DoRepoAction(req *restful.Request, resp *restful.Response) { + repoActionRequest := &openpitrix.RepoActionRequest{} + repoId := req.PathParameter("repo") + err := req.ReadEntity(repoActionRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + err = h.openpitrix.DoRepoAction(repoId, repoActionRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DeleteRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + err := h.openpitrix.DeleteRepo(repoId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) ModifyRepo(req *restful.Request, resp *restful.Response) { + var updateRepoRequest openpitrix.ModifyRepoRequest + repoId := req.PathParameter("repo") + err := req.ReadEntity(&updateRepoRequest) + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + err = h.openpitrix.ModifyRepo(repoId, &updateRepoRequest) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(errors.None) +} + +func (h *openpitrixHandler) DescribeRepo(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + + result, err := h.openpitrix.DescribeRepo(repoId) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} +func (h *openpitrixHandler) ListRepos(req *restful.Request, resp *restful.Response) { + limit, offset := params.ParsePaging(req) + orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, openpitrix.CreateTime) + reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.ListRepos(conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func (h *openpitrixHandler) ListRepoEvents(req *restful.Request, resp *restful.Response) { + repoId := req.PathParameter("repo") + limit, offset := params.ParsePaging(req) + conditions, err := params.ParseConditions(req) + + if err != nil { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + + result, err := h.openpitrix.ListRepoEvents(repoId, conditions, limit, offset) + + if err != nil { + klog.Errorln(err) + handleOpenpitrixError(resp, err) + return + } + + resp.WriteEntity(result) +} + +func handleOpenpitrixError(resp *restful.Response, err error) { + if status.Code(err) == codes.NotFound { + klog.V(4).Infoln(err) + api.HandleNotFound(resp, nil, err) + return + } + if status.Code(err) == codes.InvalidArgument { + klog.V(4).Infoln(err) + api.HandleBadRequest(resp, nil, err) + return + } + klog.Errorln(err) + api.HandleInternalError(resp, nil, err) +} diff --git a/pkg/kapis/openpitrix/v1/register.go b/pkg/kapis/openpitrix/v1/register.go index 6464b746e..7a7e290fd 100644 --- a/pkg/kapis/openpitrix/v1/register.go +++ b/pkg/kapis/openpitrix/v1/register.go @@ -21,34 +21,33 @@ import ( "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/openpitrix" + "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - opmodels "kubesphere.io/kubesphere/pkg/models/openpitrix" + openpitrix2 "kubesphere.io/kubesphere/pkg/models/openpitrix" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" + op "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "net/http" ) -const GroupName = "openpitrix.io" +const ( + GroupName = "openpitrix.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) +func AddToContainer(c *restful.Container, factory informers.InformerFactory, op op.Client) error { -func addWebService(c *restful.Container) error { - - ok := "ok" mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson} webservice := runtime.NewWebService(GroupVersion) + handler := newOpenpitrixHandler(factory, op) webservice.Route(webservice.GET("/applications"). - To(openpitrix.ListApplications). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListApplications). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("List all applications"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). @@ -62,8 +61,8 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1"))) webservice.Route(webservice.GET("/namespaces/{namespace}/applications"). - To(openpitrix.ListApplications). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListApplications). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("List all applications within the specified namespace"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). @@ -77,72 +76,72 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1"))) webservice.Route(webservice.GET("/namespaces/{namespace}/applications/{application}"). - To(openpitrix.DescribeApplication). - Returns(http.StatusOK, ok, opmodels.Application{}). + To(handler.DescribeApplication). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Application{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Describe the specified application of the namespace"). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "application ID"))) webservice.Route(webservice.POST("/namespaces/{namespace}/applications"). - To(openpitrix.CreateApplication). + To(handler.CreateApplication). Doc("Deploy a new application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Reads(opmodels.CreateClusterRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.CreateClusterRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.PATCH("/namespaces/{namespace}/applications/{application}"). Consumes(mimePatch...). - To(openpitrix.ModifyApplication). + To(handler.ModifyApplication). Doc("Modify application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Reads(opmodels.ModifyClusterAttributesRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyClusterAttributesRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "the id of the application cluster"))) webservice.Route(webservice.DELETE("/namespaces/{namespace}/applications/{application}"). - To(openpitrix.DeleteApplication). + To(handler.DeleteApplication). Doc("Delete the specified application"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("namespace", "the name of the project")). Param(webservice.PathParameter("application", "the id of the application cluster"))) webservice.Route(webservice.POST("/apps/{app}/versions"). - To(openpitrix.CreateAppVersion). + To(handler.CreateAppVersion). Doc("Create a new app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.CreateAppVersionRequest{}). + Reads(openpitrix2.CreateAppVersionRequest{}). Param(webservice.QueryParameter("validate", "Validate format of package(pack by op tool)")). - Returns(http.StatusOK, ok, opmodels.CreateAppVersionResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateAppVersionResponse{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/apps/{app}/versions/{version}"). - To(openpitrix.DeleteAppVersion). + To(handler.DeleteAppVersion). Doc("Delete the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.PATCH("/apps/{app}/versions/{version}"). Consumes(mimePatch...). - To(openpitrix.ModifyAppVersion). + To(handler.ModifyAppVersion). Doc("Patch the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.ModifyAppVersionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyAppVersionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}"). - To(openpitrix.DescribeAppVersion). + To(handler.DescribeAppVersion). Doc("Describe the specified app template version"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, opmodels.AppVersion{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersion{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions"). - To(openpitrix.ListAppVersions). + To(handler.ListAppVersions). Doc("Get active versions of app, can filter with these fields(version_id, app_id, name, owner, description, package_name, status, type), default return all active app versions"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -154,33 +153,33 @@ func addWebService(c *restful.Container) error { Param(webservice.PathParameter("app", "app template id")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/audits"). - To(openpitrix.ListAppVersionAudits). + To(handler.ListAppVersionAudits). Doc("List audits information of version-specific app template"). - Returns(http.StatusOK, ok, opmodels.AppVersionAudit{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionAudit{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/package"). - To(openpitrix.GetAppVersionPackage). + To(handler.GetAppVersionPackage). Doc("Get packages of version-specific app"). - Returns(http.StatusOK, ok, opmodels.GetAppVersionPackageResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.GetAppVersionPackageResponse{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.POST("/apps/{app}/versions/{version}/action"). - To(openpitrix.DoAppVersionAction). + To(handler.DoAppVersionAction). Doc("Perform submit or other operations on app"). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}/versions/{version}/files"). - To(openpitrix.GetAppVersionFiles). + To(handler.GetAppVersionFiles). Doc("Get app template package files"). - Returns(http.StatusOK, ok, opmodels.GetAppVersionPackageFilesResponse{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.GetAppVersionPackageFilesResponse{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/reviews"). - To(openpitrix.ListReviews). + To(handler.ListReviews). Doc("Get reviews of version-specific app"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -189,47 +188,47 @@ func addWebService(c *restful.Container) error { Required(false). DataFormat("limit=%d,page=%d"). DefaultValue("limit=10,page=1")). - Returns(http.StatusOK, ok, opmodels.AppVersionReview{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionReview{})) webservice.Route(webservice.GET("/apps/{app}/audits"). - To(openpitrix.ListAppVersionAudits). + To(handler.ListAppVersionAudits). Doc("List audits information of the specific app template"). Param(webservice.PathParameter("app", "app template id")). - Returns(http.StatusOK, ok, opmodels.AppVersionAudit{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersionAudit{})) webservice.Route(webservice.POST("/apps"). - To(openpitrix.CreateApp). + To(handler.CreateApp). Doc("Create a new app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, opmodels.CreateAppResponse{}). - Reads(opmodels.CreateAppRequest{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateAppResponse{}). + Reads(openpitrix2.CreateAppRequest{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/apps/{app}"). - To(openpitrix.DeleteApp). + To(handler.DeleteApp). Doc("Delete the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.PATCH("/apps/{app}"). Consumes(mimePatch...). - To(openpitrix.ModifyApp). + To(handler.ModifyApp). Doc("Patch the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.ModifyAppVersionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyAppVersionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps/{app}"). - To(openpitrix.DescribeApp). + To(handler.DescribeApp). Doc("Describe the specified app template"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, opmodels.AppVersion{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.AppVersion{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.POST("/apps/{app}/action"). - To(openpitrix.DoAppAction). + To(handler.DoAppAction). Doc("Perform recover or suspend operation on app"). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("version", "app template version id")). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.GET("/apps"). - To(openpitrix.ListApps). + To(handler.ListApps). Doc("List app templates"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -240,36 +239,36 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.POST("/categories"). - To(openpitrix.CreateCategory). + To(handler.CreateCategory). Doc("Create app template category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.CreateCategoryRequest{}). - Returns(http.StatusOK, ok, opmodels.CreateCategoryResponse{}). + Reads(openpitrix2.CreateCategoryRequest{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateCategoryResponse{}). Param(webservice.PathParameter("app", "app template id"))) webservice.Route(webservice.DELETE("/categories/{category}"). - To(openpitrix.DeleteCategory). + To(handler.DeleteCategory). Doc("Delete the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.PATCH("/categories/{category}"). Consumes(mimePatch...). - To(openpitrix.ModifyCategory). + To(handler.ModifyCategory). Doc("Patch the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.ModifyCategoryRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyCategoryRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.GET("/categories/{category}"). - To(openpitrix.DescribeCategory). + To(handler.DescribeCategory). Doc("Describe the specified category"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, opmodels.Category{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Category{}). Param(webservice.PathParameter("category", "category id"))) webservice.Route(webservice.GET("/categories"). - To(openpitrix.ListCategories). + To(handler.ListCategories). Doc("List categories"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -280,43 +279,43 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.GET("/attachments/{attachment}"). - To(openpitrix.DescribeAttachment). + To(handler.DescribeAttachment). Doc("Get attachment by attachment id"). Param(webservice.PathParameter("attachment", "attachment id")). - Returns(http.StatusOK, ok, opmodels.Attachment{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.Attachment{})) webservice.Route(webservice.POST("/repos"). - To(openpitrix.CreateRepo). + To(handler.CreateRepo). Doc("Create repository, repository used to store package of app"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). Param(webservice.QueryParameter("validate", "Validate repository")). - Returns(http.StatusOK, ok, opmodels.CreateRepoResponse{}). - Reads(opmodels.CreateRepoRequest{})) + Returns(http.StatusOK, api.StatusOK, openpitrix2.CreateRepoResponse{}). + Reads(openpitrix2.CreateRepoRequest{})) webservice.Route(webservice.DELETE("/repos/{repo}"). - To(openpitrix.DeleteRepo). + To(handler.DeleteRepo). Doc("Delete the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, errors.Error{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.PATCH("/repos/{repo}"). Consumes(mimePatch...). - To(openpitrix.ModifyRepo). + To(handler.ModifyRepo). Doc("Patch the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Reads(opmodels.ModifyRepoRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.ModifyRepoRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos/{repo}"). - To(openpitrix.DescribeRepo). + To(handler.DescribeRepo). Doc("Describe the specified repository"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.OpenpitrixTag}). - Returns(http.StatusOK, ok, opmodels.Repo{}). + Returns(http.StatusOK, api.StatusOK, openpitrix2.Repo{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos"). - To(openpitrix.ListRepos). + To(handler.ListRepos). Doc("List repositories"). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions,connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). @@ -327,17 +326,17 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.POST("/repos/{repo}/action"). - To(openpitrix.DoRepoAction). + To(handler.DoRepoAction). Doc("Start index repository event"). - Reads(opmodels.RepoActionRequest{}). - Returns(http.StatusOK, ok, errors.Error{}). + Reads(openpitrix2.RepoActionRequest{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{}). Param(webservice.PathParameter("repo", "repo id"))) webservice.Route(webservice.GET("/repos/{repo}/events"). - To(openpitrix.ListRepoEvents). + To(handler.ListRepoEvents). Doc("Get repository events"). - Returns(http.StatusOK, ok, models.PageableResponse{}). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Param(webservice.PathParameter("repo", "repo id"))) c.Add(webservice) diff --git a/pkg/kapis/operations/group.go b/pkg/kapis/operations/group.go deleted file mode 100644 index 12ee8cbf8..000000000 --- a/pkg/kapis/operations/group.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 operations contains operations API versions -package operations diff --git a/pkg/kapis/operations/install/install.go b/pkg/kapis/operations/install/install.go deleted file mode 100644 index ca8bcfde4..000000000 --- a/pkg/kapis/operations/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/operations/v1alpha2/handler.go b/pkg/kapis/operations/v1alpha2/handler.go new file mode 100644 index 000000000..bffac8096 --- /dev/null +++ b/pkg/kapis/operations/v1alpha2/handler.go @@ -0,0 +1,48 @@ +package v1alpha2 + +import ( + "fmt" + "github.com/emicklei/go-restful" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/models/workloads" + "kubesphere.io/kubesphere/pkg/server/errors" + "net/http" +) + +type operationHandler struct { + jobRunner workloads.JobRunner +} + +func newOperationHandler(client kubernetes.Interface) *operationHandler { + return &operationHandler{ + jobRunner: workloads.NewJobRunner(client), + } +} + +func (r *operationHandler) handleJobReRun(request *restful.Request, response *restful.Response) { + var err error + + job := request.PathParameter("job") + namespace := request.PathParameter("namespace") + action := request.QueryParameter("action") + resourceVersion := request.QueryParameter("resourceVersion") + + switch action { + case "rerun": + err = r.jobRunner.JobReRun(namespace, job, resourceVersion) + default: + response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("invalid operation %s", action))) + return + } + if err != nil { + if k8serr.IsConflict(err) { + response.WriteHeaderAndEntity(http.StatusConflict, errors.Wrap(err)) + return + } + response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + response.WriteAsJson(errors.None) +} diff --git a/pkg/kapis/operations/v1alpha2/register.go b/pkg/kapis/operations/v1alpha2/register.go index c4240d908..d6f60805b 100644 --- a/pkg/kapis/operations/v1alpha2/register.go +++ b/pkg/kapis/operations/v1alpha2/register.go @@ -20,42 +20,34 @@ package v1alpha2 import ( "github.com/emicklei/go-restful" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/operations" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/server/errors" "net/http" ) -const GroupName = "operations.kubesphere.io" +const ( + GroupName = "operations.kubesphere.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) +func AddToContainer(c *restful.Container, client kubernetes.Interface) error { -func addWebService(c *restful.Container) error { - - ok := "ok" webservice := runtime.NewWebService(GroupVersion) - webservice.Route(webservice.POST("/nodes/{node}/drainage"). - To(operations.DrainNode). - Deprecate(). - Doc("remove a node from service, safely evict all of your pods from a node and you can power down the node. More info: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/"). - Param(webservice.PathParameter("node", "node name")). - Returns(http.StatusOK, ok, errors.Error{})) + handler := newOperationHandler(client) webservice.Route(webservice.POST("/namespaces/{namespace}/jobs/{job}"). - To(operations.RerunJob). + To(handler.handleJobReRun). Doc("Rerun job whether the job is complete or not"). Deprecate(). Param(webservice.PathParameter("job", "job name")). Param(webservice.PathParameter("namespace", "the name of the namespace where the job runs in")). Param(webservice.QueryParameter("action", "action must be \"rerun\"")). Param(webservice.QueryParameter("resourceVersion", "version of job, rerun when the version matches").Required(true)). - Returns(http.StatusOK, ok, errors.Error{})) + Returns(http.StatusOK, api.StatusOK, errors.Error{})) c.Add(webservice) diff --git a/pkg/kapis/resources/install/install.go b/pkg/kapis/resources/install/install.go deleted file mode 100644 index 85400efb9..000000000 --- a/pkg/kapis/resources/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(c *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(c)) -} diff --git a/pkg/kapis/resources/v1alpha2/handler.go b/pkg/kapis/resources/v1alpha2/handler.go new file mode 100644 index 000000000..793ae3918 --- /dev/null +++ b/pkg/kapis/resources/v1alpha2/handler.go @@ -0,0 +1,381 @@ +package v1alpha2 + +import ( + "fmt" + "github.com/emicklei/go-restful" + v1 "k8s.io/api/core/v1" + k8serr "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/client-go/kubernetes" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/components" + "kubesphere.io/kubesphere/pkg/models/git" + "kubesphere.io/kubesphere/pkg/models/kubeconfig" + "kubesphere.io/kubesphere/pkg/models/kubectl" + "kubesphere.io/kubesphere/pkg/models/quotas" + "kubesphere.io/kubesphere/pkg/models/registries" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" + "kubesphere.io/kubesphere/pkg/models/revisions" + "kubesphere.io/kubesphere/pkg/models/routers" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/server/params" + "net/http" + "strconv" + "strings" +) + +type resourceHandler struct { + resourcesGetter *resource.ResourceGetter + componentsGetter components.ComponentsGetter + resourceQuotaGetter quotas.ResourceQuotaGetter + revisionGetter revisions.RevisionGetter + routerOperator routers.RouterOperator + gitVerifier git.GitVerifier + registryGetter registries.RegistryGetter + kubeconfigOperator kubeconfig.Interface + kubectlOperator kubectl.Interface +} + +func newResourceHandler(client kubernetes.Interface, factory informers.InformerFactory) *resourceHandler { + + return &resourceHandler{ + resourcesGetter: resource.NewResourceGetter(factory), + componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), + resourceQuotaGetter: quotas.NewResourceQuotaGetter(factory.KubernetesSharedInformerFactory()), + revisionGetter: revisions.NewRevisionGetter(factory.KubernetesSharedInformerFactory()), + routerOperator: routers.NewRouterOperator(client, factory.KubernetesSharedInformerFactory()), + gitVerifier: git.NewGitVerifier(factory.KubernetesSharedInformerFactory()), + registryGetter: registries.NewRegistryGetter(factory.KubernetesSharedInformerFactory()), + kubeconfigOperator: kubeconfig.NewKubeconfigOperator(), + kubectlOperator: kubectl.NewKubectlOperator(client, factory.KubernetesSharedInformerFactory()), + } +} + +func (r *resourceHandler) handleGetNamespacedResources(request *restful.Request, response *restful.Response) { + r.handleListNamespaceResources(request, response) +} + +func (r *resourceHandler) handleListNamespaceResources(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + resource := request.PathParameter("resources") + orderBy := params.GetStringValueWithDefault(request, params.OrderByParam, v1alpha2.CreateTime) + limit, offset := params.ParsePaging(request) + reverse := params.GetBoolValueWithDefault(request, params.ReverseParam, false) + conditions, err := params.ParseConditions(request) + + if err != nil { + klog.Error(err) + api.HandleBadRequest(response, request, err) + return + } + + result, err := r.resourcesGetter.ListResources(namespace, resource, conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteEntity(result) +} + +func (r *resourceHandler) handleGetSystemHealthStatus(_ *restful.Request, response *restful.Response) { + result, err := r.componentsGetter.GetSystemHealthStatus() + + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetComponentStatus(request *restful.Request, response *restful.Response) { + component := request.PathParameter("component") + result, err := r.componentsGetter.GetComponentStatus(component) + + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetComponents(_ *restful.Request, response *restful.Response) { + result, err := r.componentsGetter.GetAllComponentsStatus() + + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetClusterQuotas(_ *restful.Request, response *restful.Response) { + result, err := r.resourceQuotaGetter.GetClusterQuota() + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetNamespaceQuotas(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + quota, err := r.resourceQuotaGetter.GetNamespaceQuota(namespace) + + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(quota) +} + +func (r *resourceHandler) handleGetDaemonSetRevision(request *restful.Request, response *restful.Response) { + daemonset := request.PathParameter("daemonset") + namespace := request.PathParameter("namespace") + revision, err := strconv.Atoi(request.PathParameter("revision")) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := r.revisionGetter.GetDaemonSetRevision(namespace, daemonset, revision) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetDeploymentRevision(request *restful.Request, response *restful.Response) { + deploy := request.PathParameter("deployment") + namespace := request.PathParameter("namespace") + revision := request.PathParameter("revision") + + result, err := r.revisionGetter.GetDeploymentRevision(namespace, deploy, revision) + + if err != nil { + response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + response.WriteAsJson(result) +} + +func (r *resourceHandler) handleGetStatefulSetRevision(request *restful.Request, response *restful.Response) { + statefulset := request.PathParameter("statefulset") + namespace := request.PathParameter("namespace") + revision, err := strconv.Atoi(request.PathParameter("revision")) + if err != nil { + response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + result, err := r.revisionGetter.GetStatefulSetRevision(namespace, statefulset, revision) + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + response.WriteAsJson(result) +} + +// Get ingress controller service for specified namespace +func (r *resourceHandler) handleGetRouter(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + router, err := r.routerOperator.GetRouter(namespace) + if err != nil { + if k8serr.IsNotFound(err) { + response.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err)) + } else { + api.HandleInternalError(response, nil, err) + } + return + } + + response.WriteAsJson(router) +} + +// Create ingress controller and related services +func (r *resourceHandler) handleCreateRouter(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + newRouter := api.Router{} + err := request.ReadEntity(&newRouter) + if err != nil { + response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(fmt.Errorf("wrong annotations, missing key or value"))) + return + } + + routerType := v1.ServiceTypeNodePort + if strings.Compare(strings.ToLower(newRouter.RouterType), "loadbalancer") == 0 { + routerType = v1.ServiceTypeLoadBalancer + } + + router, err := r.routerOperator.CreateRouter(namespace, routerType, newRouter.Annotations) + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(router) +} + +// Delete ingress controller and services +func (r *resourceHandler) handleDeleteRouter(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + + router, err := r.routerOperator.DeleteRouter(namespace) + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(router) +} + +func (r *resourceHandler) handleUpdateRouter(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + newRouter := api.Router{} + err := request.ReadEntity(&newRouter) + if err != nil { + response.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err)) + return + } + + var routerType = v1.ServiceTypeNodePort + if strings.Compare(strings.ToLower(newRouter.RouterType), "loadbalancer") == 0 { + routerType = v1.ServiceTypeLoadBalancer + } + router, err := r.routerOperator.UpdateRouter(namespace, routerType, newRouter.Annotations) + + if err != nil { + api.HandleInternalError(response, nil, err) + return + } + + response.WriteAsJson(router) +} + +func (r *resourceHandler) handleVerifyGitCredential(request *restful.Request, response *restful.Response) { + + var credential api.GitCredential + err := request.ReadEntity(&credential) + if err != nil { + api.HandleBadRequest(response, nil, err) + return + } + + err = r.gitVerifier.VerifyGitCredential(credential.RemoteUrl, credential.SecretRef.Namespace, credential.SecretRef.Name) + if err != nil { + api.HandleBadRequest(response, nil, err) + return + } + + response.WriteHeader(http.StatusOK) +} + +func (r *resourceHandler) handleVerifyRegistryCredential(request *restful.Request, response *restful.Response) { + var credential api.RegistryCredential + err := request.ReadEntity(&credential) + if err != nil { + api.HandleBadRequest(response, nil, err) + return + } + + err = r.registryGetter.VerifyRegistryCredential(credential) + if err != nil { + api.HandleBadRequest(response, nil, err) + return + } + + response.WriteHeader(http.StatusOK) +} + +func (r *resourceHandler) handleGetRegistryEntry(request *restful.Request, response *restful.Response) { + imageName := request.QueryParameter("image") + namespace := request.QueryParameter("namespace") + secretName := request.QueryParameter("secret") + + detail, err := r.registryGetter.GetEntry(namespace, secretName, imageName) + if err != nil { + api.HandleBadRequest(response, nil, err) + return + } + + response.WriteAsJson(detail) +} + +func (r *resourceHandler) handleGetNamespacedAbnormalWorkloads(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + + result := api.Workloads{ + Namespace: namespace, + Count: make(map[string]int), + } + + for _, workloadType := range []string{api.ResourceKindDeployment, api.ResourceKindStatefulSet, api.ResourceKindDaemonSet, api.ResourceKindJob, api.ResourceKindPersistentVolumeClaim} { + var notReadyStatus string + + switch workloadType { + case api.ResourceKindPersistentVolumeClaim: + notReadyStatus = strings.Join([]string{v1alpha2.StatusPending, v1alpha2.StatusLost}, "|") + case api.ResourceKindJob: + notReadyStatus = v1alpha2.StatusFailed + default: + notReadyStatus = v1alpha2.StatusUpdating + } + + res, err := r.resourcesGetter.ListResources(namespace, workloadType, ¶ms.Conditions{Match: map[string]string{v1alpha2.Status: notReadyStatus}}, "", false, -1, 0) + if err != nil { + api.HandleInternalError(response, nil, err) + } + + result.Count[workloadType] = len(res.Items) + } + + response.WriteAsJson(result) + +} + +func (r *resourceHandler) GetKubectlPod(request *restful.Request, response *restful.Response) { + user := request.PathParameter("user") + + kubectlPod, err := r.kubectlOperator.GetKubectlPod(user) + + if err != nil { + klog.Errorln(err) + response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err)) + return + } + + response.WriteEntity(kubectlPod) +} + +func (r *resourceHandler) GetKubeconfig(request *restful.Request, response *restful.Response) { + user := request.PathParameter("user") + + kubectlConfig, err := r.kubeconfigOperator.GetKubeConfig(user) + + if err != nil { + klog.Error(err) + if k8serr.IsNotFound(err) { + // recreate + response.WriteHeaderAndJson(http.StatusNotFound, errors.Wrap(err), restful.MIME_JSON) + } else { + response.WriteHeaderAndJson(http.StatusInternalServerError, errors.Wrap(err), restful.MIME_JSON) + } + return + } + + response.Write([]byte(kubectlConfig)) +} diff --git a/pkg/kapis/resources/v1alpha2/register.go b/pkg/kapis/resources/v1alpha2/register.go index 10652eda0..e635e0a88 100644 --- a/pkg/kapis/resources/v1alpha2/register.go +++ b/pkg/kapis/resources/v1alpha2/register.go @@ -22,46 +22,34 @@ import ( "github.com/emicklei/go-restful-openapi" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "kubesphere.io/kubesphere/pkg/apiserver/components" - "kubesphere.io/kubesphere/pkg/apiserver/git" - "kubesphere.io/kubesphere/pkg/apiserver/operations" - "kubesphere.io/kubesphere/pkg/apiserver/quotas" - "kubesphere.io/kubesphere/pkg/apiserver/registries" - "kubesphere.io/kubesphere/pkg/apiserver/resources" - "kubesphere.io/kubesphere/pkg/apiserver/revisions" - "kubesphere.io/kubesphere/pkg/apiserver/routers" + "k8s.io/client-go/kubernetes" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/resource/v1alpha2" "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/workloadstatuses" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" gitmodel "kubesphere.io/kubesphere/pkg/models/git" registriesmodel "kubesphere.io/kubesphere/pkg/models/registries" - "kubesphere.io/kubesphere/pkg/models/status" "kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/params" "net/http" ) -const GroupName = "resources.kubesphere.io" +const ( + GroupName = "resources.kubesphere.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { - +func AddToContainer(c *restful.Container, client kubernetes.Interface, factory informers.InformerFactory) error { webservice := runtime.NewWebService(GroupVersion) - - ok := "ok" - mimePatch := []string{restful.MIME_JSON, runtime.MimeMergePatchJson, runtime.MimeJsonPatchJson} + handler := newResourceHandler(client, factory) webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}"). - To(resources.ListNamespacedResources). + To(handler.handleListNamespaceResources). + Deprecate(). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Namespace level resource query"). Param(webservice.PathParameter("namespace", "the name of the project")). @@ -75,24 +63,15 @@ func addWebService(c *restful.Container) error { DefaultValue("limit=10,page=1")). Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime")). - Returns(http.StatusOK, ok, models.PageableResponse{})) - - webservice.Route(webservice.POST("/namespaces/{namespace}/jobs/{job}"). - To(operations.RerunJob). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Doc("Rerun job whether the job is complete or not"). - Param(webservice.PathParameter("job", "job name")). - Param(webservice.PathParameter("namespace", "the name of the namespace where the job runs in")). - Param(webservice.QueryParameter("action", "action must be \"rerun\"")). - Param(webservice.QueryParameter("resourceVersion", "version of job, rerun when the version matches")). - Returns(http.StatusOK, ok, errors.Error{})) + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{})) webservice.Route(webservice.GET("/{resources}"). - To(resources.ListResources). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.handleListNamespaceResources). + Deprecate(). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Doc("Cluster level resource query"). - Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterroles.")). + Doc("Cluster level resources"). + Param(webservice.PathParameter("resources", "cluster level resource type, e.g. nodes,workspaces,storageclasses,clusterrole.")). Param(webservice.QueryParameter(params.ConditionsParam, "query conditions, connect multiple conditions with commas, equal symbol for exact query, wave symbol for fuzzy query e.g. name~a"). Required(false). DataFormat("key=value,key~value"). @@ -104,67 +83,60 @@ func addWebService(c *restful.Container) error { Param(webservice.QueryParameter(params.ReverseParam, "sort parameters, e.g. reverse=true")). Param(webservice.QueryParameter(params.OrderByParam, "sort parameters, e.g. orderBy=createTime"))) - webservice.Route(webservice.POST("/nodes/{node}/drainage"). - To(operations.DrainNode). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Doc("remove a node from service, safely evict all of your pods from a node and you can power down the node. More info: https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/"). - Param(webservice.PathParameter("node", "node name")). - Returns(http.StatusOK, ok, errors.Error{})) - webservice.Route(webservice.GET("/users/{user}/kubectl"). - To(resources.GetKubectl). + To(handler.GetKubectlPod). Doc("get user's kubectl pod"). Param(webservice.PathParameter("user", "username")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserResourcesTag}). - Returns(http.StatusOK, ok, models.PodInfo{})) + Returns(http.StatusOK, api.StatusOK, models.PodInfo{})) webservice.Route(webservice.GET("/users/{user}/kubeconfig"). Produces("text/plain", restful.MIME_JSON). - To(resources.GetKubeconfig). + To(handler.GetKubeconfig). Doc("get users' kubeconfig"). Param(webservice.PathParameter("user", "username")). - Returns(http.StatusOK, ok, ""). + Returns(http.StatusOK, api.StatusOK, ""). Metadata(restfulspec.KeyOpenAPITags, []string{constants.UserResourcesTag})) webservice.Route(webservice.GET("/components"). - To(components.GetComponents). + To(handler.handleGetComponents). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentStatusTag}). Doc("List the system components."). - Returns(http.StatusOK, ok, []models.ComponentStatus{})) + Returns(http.StatusOK, api.StatusOK, []v1alpha2.ComponentStatus{})) + webservice.Route(webservice.GET("/components/{component}"). - To(components.GetComponentStatus). + To(handler.handleGetComponentStatus). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentStatusTag}). Doc("Describe the specified system component."). Param(webservice.PathParameter("component", "component name")). - Returns(http.StatusOK, ok, models.ComponentStatus{})) + Returns(http.StatusOK, api.StatusOK, v1alpha2.ComponentStatus{})) webservice.Route(webservice.GET("/componenthealth"). - To(components.GetSystemHealthStatus). + To(handler.handleGetSystemHealthStatus). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ComponentStatusTag}). Doc("Get the health status of system components."). - Returns(http.StatusOK, ok, models.HealthStatus{})) + Returns(http.StatusOK, api.StatusOK, v1alpha2.HealthStatus{})) webservice.Route(webservice.GET("/quotas"). - To(quotas.GetClusterQuotas). + To(handler.handleGetClusterQuotas). Doc("get whole cluster's resource usage"). - Returns(http.StatusOK, ok, models.ResourceQuota{}). + Returns(http.StatusOK, api.StatusOK, api.ResourceQuota{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag})) webservice.Route(webservice.GET("/namespaces/{namespace}/quotas"). Doc("get specified namespace's resource quota and usage"). Param(webservice.PathParameter("namespace", "the name of the project")). - Returns(http.StatusOK, ok, models.ResourceQuota{}). + Returns(http.StatusOK, api.StatusOK, api.ResourceQuota{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - To(quotas.GetNamespaceQuotas)) + To(handler.handleGetNamespaceQuotas)) webservice.Route(webservice.POST("registry/verify"). - To(registries.RegistryVerify). + To(handler.handleVerifyRegistryCredential). Metadata(restfulspec.KeyOpenAPITags, []string{constants.VerificationTag}). Doc("verify if a user has access to the docker registry"). - Reads(registriesmodel.AuthInfo{}). - Returns(http.StatusOK, ok, errors.Error{})) - + Reads(api.RegistryCredential{}). + Returns(http.StatusOK, api.StatusOK, errors.Error{})) webservice.Route(webservice.GET("/registry/blob"). - To(registries.RegistryImageBlob). + To(handler.handleGetRegistryEntry). Param(webservice.QueryParameter("image", "query image, condition for filtering."). Required(true). DataFormat("image=%s")). @@ -177,97 +149,82 @@ func addWebService(c *restful.Container) error { Metadata(restfulspec.KeyOpenAPITags, []string{constants.RegistryTag}). Doc("Retrieve the blob from the registry identified"). Writes(registriesmodel.ImageDetails{}). - Returns(http.StatusOK, ok, registriesmodel.ImageDetails{}), + Returns(http.StatusOK, api.StatusOK, registriesmodel.ImageDetails{}), ) - webservice.Route(webservice.POST("git/verify"). - To(git.GitReadVerify). + To(handler.handleVerifyGitCredential). Metadata(restfulspec.KeyOpenAPITags, []string{constants.VerificationTag}). Doc("Verify if the kubernetes secret has read access to the git repository"). Reads(gitmodel.AuthInfo{}). - Returns(http.StatusOK, ok, errors.Error{}), + Returns(http.StatusOK, api.StatusOK, errors.Error{}), ) webservice.Route(webservice.GET("/namespaces/{namespace}/daemonsets/{daemonset}/revisions/{revision}"). - To(revisions.GetDaemonSetRevision). + To(handler.handleGetDaemonSetRevision). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Get the specified daemonset revision"). Param(webservice.PathParameter("daemonset", "the name of the daemonset")). Param(webservice.PathParameter("namespace", "the namespace of the daemonset")). Param(webservice.PathParameter("revision", "the revision of the daemonset")). - Returns(http.StatusOK, ok, appsv1.DaemonSet{})) + Returns(http.StatusOK, api.StatusOK, appsv1.DaemonSet{})) webservice.Route(webservice.GET("/namespaces/{namespace}/deployments/{deployment}/revisions/{revision}"). - To(revisions.GetDeployRevision). + To(handler.handleGetDeploymentRevision). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Get the specified deployment revision"). Param(webservice.PathParameter("deployment", "the name of deployment")). Param(webservice.PathParameter("namespace", "the namespace of the deployment")). Param(webservice.PathParameter("revision", "the revision of the deployment")). - Returns(http.StatusOK, ok, appsv1.ReplicaSet{})) + Returns(http.StatusOK, api.StatusOK, appsv1.ReplicaSet{})) webservice.Route(webservice.GET("/namespaces/{namespace}/statefulsets/{statefulset}/revisions/{revision}"). - To(revisions.GetStatefulSetRevision). + To(handler.handleGetStatefulSetRevision). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). Doc("Get the specified statefulset revision"). Param(webservice.PathParameter("statefulset", "the name of the statefulset")). Param(webservice.PathParameter("namespace", "the namespace of the statefulset")). Param(webservice.PathParameter("revision", "the revision of the statefulset")). - Returns(http.StatusOK, ok, appsv1.StatefulSet{})) - - webservice.Route(webservice.GET("/routers"). - To(routers.GetAllRouters). - Doc("List all routers of all projects"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Returns(http.StatusOK, ok, corev1.ServiceList{})) + Returns(http.StatusOK, api.StatusOK, appsv1.StatefulSet{})) webservice.Route(webservice.GET("/namespaces/{namespace}/router"). - To(routers.GetRouter). + To(handler.handleGetRouter). Doc("List router of a specified project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, corev1.Service{}). + Returns(http.StatusOK, api.StatusOK, corev1.Service{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.DELETE("/namespaces/{namespace}/router"). - To(routers.DeleteRouter). + To(handler.handleDeleteRouter). Doc("List router of a specified project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, corev1.Service{}). + Returns(http.StatusOK, api.StatusOK, corev1.Service{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.POST("/namespaces/{namespace}/router"). - To(routers.CreateRouter). + To(handler.handleCreateRouter). Doc("Create a router for a specified project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, corev1.Service{}). + Returns(http.StatusOK, api.StatusOK, corev1.Service{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.PUT("/namespaces/{namespace}/router"). - To(routers.UpdateRouter). + To(handler.handleUpdateRouter). Doc("Update a router for a specified project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, corev1.Service{}). + Returns(http.StatusOK, api.StatusOK, corev1.Service{}). Param(webservice.PathParameter("namespace", "the name of the project"))) webservice.Route(webservice.GET("/abnormalworkloads"). Doc("get abnormal workloads' count of whole cluster"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Returns(http.StatusOK, ok, status.WorkLoadStatus{}). - To(workloadstatuses.GetClusterAbnormalWorkloads)) + Returns(http.StatusOK, api.StatusOK, api.Workloads{}). + To(handler.handleGetNamespacedAbnormalWorkloads)) webservice.Route(webservice.GET("/namespaces/{namespace}/abnormalworkloads"). Doc("get abnormal workloads' count of specified namespace"). Param(webservice.PathParameter("namespace", "the name of the project")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.NamespaceResourcesTag}). - Returns(http.StatusOK, ok, status.WorkLoadStatus{}). - To(workloadstatuses.GetNamespacedAbnormalWorkloads)) - - webservice.Route(webservice.PATCH("/storageclasses/{storageclass}"). - Consumes(mimePatch...). - To(resources.PatchStorageClass). - Doc("patch storage class"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.ClusterResourcesTag}). - Returns(http.StatusOK, ok, storagev1.StorageClass{}). - Writes(storagev1.StorageClass{}). - Param(webservice.PathParameter("storageclass", "the name of storage class"))) + Returns(http.StatusOK, api.StatusOK, api.Workloads{}). + To(handler.handleGetNamespacedAbnormalWorkloads)) c.Add(webservice) + return nil } diff --git a/pkg/kapis/resources/v1alpha3/handler.go b/pkg/kapis/resources/v1alpha3/handler.go new file mode 100644 index 000000000..75a8f1f67 --- /dev/null +++ b/pkg/kapis/resources/v1alpha3/handler.go @@ -0,0 +1,156 @@ +package v1alpha3 + +import ( + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/components" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + resourcev1alpha2 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "kubesphere.io/kubesphere/pkg/server/params" + "strings" +) + +type Handler struct { + resourceGetterV1alpha3 *resource.ResourceGetter + resourcesGetterV1alpha2 *resourcev1alpha2.ResourceGetter + componentsGetter components.ComponentsGetter +} + +func New(factory informers.InformerFactory) *Handler { + return &Handler{ + resourceGetterV1alpha3: resource.NewResourceGetter(factory), + resourcesGetterV1alpha2: resourcev1alpha2.NewResourceGetter(factory), + componentsGetter: components.NewComponentsGetter(factory.KubernetesSharedInformerFactory()), + } +} + +// handleListResources retrieves resources +func (h *Handler) handleListResources(request *restful.Request, response *restful.Response) { + query := query.ParseQueryParameter(request) + resourceType := request.PathParameter("resources") + namespace := request.PathParameter("namespace") + + result, err := h.resourceGetterV1alpha3.List(resourceType, namespace, query) + + if err == nil { + response.WriteEntity(result) + return + } + + if err != resource.ErrResourceNotSupported { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + // fallback to v1alpha2 + result, err = h.fallback(resourceType, namespace, query) + + if err != nil { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteEntity(result) +} + +func (h *Handler) fallback(resourceType string, namespace string, q *query.Query) (*api.ListResult, error) { + orderBy := string(q.SortBy) + limit, offset := q.Pagination.Limit, q.Pagination.Offset + reverse := !q.Ascending + conditions := ¶ms.Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)} + for field, value := range q.Filters { + switch field { + case query.FieldName: + conditions.Match[v1alpha2.Name] = string(value) + break + case query.FieldCreationTimeStamp: + conditions.Match[v1alpha2.CreateTime] = string(value) + break + case query.FieldLastUpdateTimestamp: + conditions.Match[v1alpha2.UpdateTime] = string(value) + break + case query.FieldLabel: + values := strings.SplitN(string(value), ":", 2) + if len(values) == 2 { + conditions.Match[values[0]] = values[1] + } else { + conditions.Match[v1alpha2.Label] = values[0] + } + break + case query.FieldAnnotation: + values := strings.SplitN(string(value), ":", 2) + if len(values) == 2 { + conditions.Match[v1alpha2.Annotation] = values[1] + } else { + conditions.Match[v1alpha2.Annotation] = values[0] + } + break + case query.FieldStatus: + conditions.Match[v1alpha2.Status] = string(value) + break + case query.FieldOwnerReference: + conditions.Match[v1alpha2.Owner] = string(value) + break + default: + conditions.Match[string(field)] = string(value) + break + } + } + + result, err := h.resourcesGetterV1alpha2.ListResources(namespace, resourceType, conditions, orderBy, reverse, limit, offset) + + if err != nil { + klog.Error(err) + return nil, err + } + + return &api.ListResult{ + Items: result.Items, + TotalItems: result.TotalCount, + }, nil +} + +func (h *Handler) handleGetComponentStatus(request *restful.Request, response *restful.Response) { + component := request.PathParameter("component") + result, err := h.componentsGetter.GetComponentStatus(component) + + if err != nil { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteEntity(result) +} + +func (h *Handler) handleGetSystemHealthStatus(request *restful.Request, response *restful.Response) { + result, err := h.componentsGetter.GetSystemHealthStatus() + + if err != nil { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteEntity(result) +} + +// get all componentsHandler +func (h *Handler) handleGetComponents(request *restful.Request, response *restful.Response) { + + result, err := h.componentsGetter.GetAllComponentsStatus() + + if err != nil { + klog.Error(err) + api.HandleInternalError(response, nil, err) + return + } + + response.WriteEntity(result) +} diff --git a/pkg/kapis/resources/v1alpha3/handler_test.go b/pkg/kapis/resources/v1alpha3/handler_test.go new file mode 100644 index 000000000..4fa8b2f2a --- /dev/null +++ b/pkg/kapis/resources/v1alpha3/handler_test.go @@ -0,0 +1,191 @@ +package v1alpha3 + +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "testing" +) + +func TestResourceV1alpha2Fallback(t *testing.T) { + tests := []struct { + description string + namespace string + resource string + query *query.Query + expectedError error + expected *api.ListResult + }{ + { + description: "list namespaces", + namespace: "default", + resource: "namespaces", + query: &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: "name", + Ascending: false, + Filters: nil, + }, + expectedError: nil, + expected: &api.ListResult{ + Items: []interface{}{kubesphereNamespace, defaultNamespace}, + TotalItems: 2, + }, + }, + { + description: "list secrets fallback", + namespace: "default", + resource: "secrets", + query: &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: "name", + Ascending: false, + Filters: nil, + }, + expectedError: nil, + expected: &api.ListResult{ + Items: []interface{}{secretFoo2, secretFoo1}, + TotalItems: 2, + }, + }, + } + + factory, err := prepare() + if err != nil { + t.Fatal(err) + } + + handler := New(factory) + + for _, test := range tests { + got, err := listResources(test.namespace, test.resource, test.query, handler) + + if err != test.expectedError { + t.Fatalf("expected error: %s, got: %s", test.expectedError, err) + } + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +func listResources(namespace, resourceType string, query *query.Query, h *Handler) (*api.ListResult, error) { + + result, err := h.resourceGetterV1alpha3.List(resourceType, namespace, query) + + if err == nil { + return result, nil + } + + if err != resource.ErrResourceNotSupported { + return nil, err + } + + // fallback to v1alpha2 + return h.fallback(resourceType, namespace, query) +} + +var ( + defaultNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + kubesphereNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kubesphere-system", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + secretFoo1 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + secretFoo2 = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + + replicas = int32(1) + + nginxDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 1, + }, + } + redisDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "redis", + Namespace: "default", + Labels: map[string]string{"kubesphere.io/creator": "admin"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 0, + }, + } + deployments = []interface{}{redisDeployment, nginxDeployment} + namespaces = []interface{}{defaultNamespace, kubesphereNamespace} + secrets = []interface{}{secretFoo1, secretFoo2} +) + +func prepare() (informers.InformerFactory, error) { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, namespace := range namespaces { + err := k8sInformerFactory.Core().V1().Namespaces().Informer().GetIndexer().Add(namespace) + if err != nil { + return nil, err + } + } + for _, deployment := range deployments { + err := k8sInformerFactory.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + if err != nil { + return nil, err + } + } + for _, secret := range secrets { + err := k8sInformerFactory.Core().V1().Secrets().Informer().GetIndexer().Add(secret) + if err != nil { + return nil, err + } + } + + return fakeInformerFactory, nil +} diff --git a/pkg/kapis/resources/v1alpha3/register.go b/pkg/kapis/resources/v1alpha3/register.go new file mode 100644 index 000000000..7d462bdfa --- /dev/null +++ b/pkg/kapis/resources/v1alpha3/register.go @@ -0,0 +1,93 @@ +/* + + Copyright 2019 The KubeSphere 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 v1alpha3 + +import ( + "github.com/emicklei/go-restful" + "github.com/emicklei/go-restful-openapi" + "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/api/resource/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/apiserver/runtime" + "kubesphere.io/kubesphere/pkg/informers" + "net/http" +) + +const ( + GroupName = "resources.kubesphere.io" + + tagComponentStatus = "Component Status" + tagNamespacedResource = "Namespaced Resource" + + ok = "OK" +) + +var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"} + +func AddToContainer(c *restful.Container, informerFactory informers.InformerFactory) error { + + webservice := runtime.NewWebService(GroupVersion) + handler := New(informerFactory) + + webservice.Route(webservice.GET("/{resources}"). + To(handler.handleListResources). + Metadata(restfulspec.KeyOpenAPITags, []string{tagNamespacedResource}). + Doc("Cluster level resources"). + Param(webservice.PathParameter("resources", "cluster level resource type, e.g. pods,jobs,configmaps,services.")). + Param(webservice.QueryParameter(query.ParameterName, "name used to do filtering").Required(false)). + Param(webservice.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")). + Param(webservice.QueryParameter(query.ParameterLimit, "limit").Required(false)). + Param(webservice.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")). + Param(webservice.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, api.ListResult{})) + + webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}"). + To(handler.handleListResources). + Metadata(restfulspec.KeyOpenAPITags, []string{tagNamespacedResource}). + Doc("Namespace level resource query"). + Param(webservice.PathParameter("namespace", "the name of the project")). + Param(webservice.PathParameter("resources", "namespace level resource type, e.g. pods,jobs,configmaps,services.")). + Param(webservice.QueryParameter(query.ParameterName, "name used to do filtering").Required(false)). + Param(webservice.QueryParameter(query.ParameterPage, "page").Required(false).DataFormat("page=%d").DefaultValue("page=1")). + Param(webservice.QueryParameter(query.ParameterLimit, "limit").Required(false)). + Param(webservice.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")). + Param(webservice.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")). + Returns(http.StatusOK, ok, api.ListResult{})) + + webservice.Route(webservice.GET("/components"). + To(handler.handleGetComponents). + Metadata(restfulspec.KeyOpenAPITags, []string{tagComponentStatus}). + Doc("List the system components."). + Returns(http.StatusOK, ok, []v1alpha2.ComponentStatus{})) + webservice.Route(webservice.GET("/components/{component}"). + To(handler.handleGetComponentStatus). + Metadata(restfulspec.KeyOpenAPITags, []string{tagComponentStatus}). + Doc("Describe the specified system component."). + Param(webservice.PathParameter("component", "component name")). + Returns(http.StatusOK, ok, v1alpha2.ComponentStatus{})) + webservice.Route(webservice.GET("/componenthealth"). + To(handler.handleGetSystemHealthStatus). + Metadata(restfulspec.KeyOpenAPITags, []string{tagComponentStatus}). + Doc("Get the health status of system components."). + Returns(http.StatusOK, ok, v1alpha2.HealthStatus{})) + + c.Add(webservice) + + return nil +} diff --git a/pkg/kapis/servicemesh/metrics/v1alpha2/handler.go b/pkg/kapis/servicemesh/metrics/v1alpha2/handler.go new file mode 100644 index 000000000..06338a20b --- /dev/null +++ b/pkg/kapis/servicemesh/metrics/v1alpha2/handler.go @@ -0,0 +1,110 @@ +package v1alpha2 + +import ( + "fmt" + "github.com/emicklei/go-restful" + "github.com/kiali/kiali/handlers" + "io/ioutil" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "net/http" +) + +var JaegerQueryUrl = "http://jaeger-query.istio-system.svc:16686/jaeger" + +// Get app metrics +func getAppMetrics(request *restful.Request, response *restful.Response) { + handlers.AppMetrics(request, response) +} + +// Get workload metrics +func getWorkloadMetrics(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + workload := request.PathParameter("workload") + + if len(namespace) > 0 && len(workload) > 0 { + request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s&workload=%s", request.Request.URL.RawQuery, namespace, workload) + } + + handlers.WorkloadMetrics(request, response) +} + +// Get service metrics +func getServiceMetrics(request *restful.Request, response *restful.Response) { + handlers.ServiceMetrics(request, response) +} + +// Get namespace metrics +func getNamespaceMetrics(request *restful.Request, response *restful.Response) { + handlers.NamespaceMetrics(request, response) +} + +// Get service graph for namespace +func getNamespaceGraph(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + + if len(namespace) > 0 { + request.Request.URL.RawQuery = fmt.Sprintf("%s&namespaces=%s", request.Request.URL.RawQuery, namespace) + } + + handlers.GetNamespaceGraph(request, response) +} + +// Get service graph for namespaces +func getNamespacesGraph(request *restful.Request, response *restful.Response) { + handlers.GraphNamespaces(request, response) +} + +// Get namespace health +func getNamespaceHealth(request *restful.Request, response *restful.Response) { + handlers.NamespaceHealth(request, response) +} + +// Get workload health +func getWorkloadHealth(request *restful.Request, response *restful.Response) { + handlers.WorkloadHealth(request, response) +} + +// Get app health +func getAppHealth(request *restful.Request, response *restful.Response) { + handlers.AppHealth(request, response) +} + +// Get service health +func getServiceHealth(request *restful.Request, response *restful.Response) { + handlers.ServiceHealth(request, response) +} + +func getServiceTracing(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + service := request.PathParameter("service") + + serviceName := fmt.Sprintf("%s.%s", service, namespace) + + url := fmt.Sprintf("%s/api/traces?%s&service=%s", JaegerQueryUrl, request.Request.URL.RawQuery, serviceName) + + resp, err := http.Get(url) + + if err != nil { + klog.Errorf("query jaeger faile with err %v", err) + api.HandleInternalError(response, nil, err) + return + } + + body, err := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + + if err != nil { + klog.Errorf("read response error : %v", err) + api.HandleInternalError(response, nil, err) + return + } + + // need to set header for proper response + response.Header().Set("Content-Type", "application/json") + _, err = response.Write(body) + + if err != nil { + klog.Errorf("write response failed %v", err) + } +} diff --git a/pkg/kapis/servicemesh/metrics/v1alpha2/register.go b/pkg/kapis/servicemesh/metrics/v1alpha2/register.go index 40d9b7dce..b20a7af53 100644 --- a/pkg/kapis/servicemesh/metrics/v1alpha2/register.go +++ b/pkg/kapis/servicemesh/metrics/v1alpha2/register.go @@ -5,8 +5,6 @@ import ( "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/servicemesh/metrics" - "kubesphere.io/kubesphere/pkg/apiserver/servicemesh/tracing" "net/http" ) @@ -14,12 +12,7 @@ const GroupName = "servicemesh.kubesphere.io" var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { +func AddToContainer(c *restful.Container) error { tags := []string{"ServiceMesh"} @@ -28,7 +21,7 @@ func addWebService(c *restful.Container) error { // Get service metrics // GET /namespaces/{namespace}/services/{service}/metrics webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/metrics"). - To(metrics.GetServiceMetrics). + To(getServiceMetrics). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get service metrics from a specific namespace"). Param(webservice.PathParameter("namespace", "name of the namespace")). @@ -49,7 +42,7 @@ func addWebService(c *restful.Container) error { // Get app metrics // Get /namespaces/{namespace}/apps/{app}/metrics webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/metrics"). - To(metrics.GetAppMetrics). + To(getAppMetrics). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get app metrics from a specific namespace"). Param(webservice.PathParameter("namespace", "name of the namespace")). @@ -71,7 +64,7 @@ func addWebService(c *restful.Container) error { // Get workload metrics // Get /namespaces/{namespace}/workloads/{workload}/metrics webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/metrics"). - To(metrics.GetWorkloadMetrics). + To(getWorkloadMetrics). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get workload metrics from a specific namespace"). Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). @@ -94,7 +87,7 @@ func addWebService(c *restful.Container) error { // Get namespace metrics // Get /namespaces/{namespace}/metrics webservice.Route(webservice.GET("/namespaces/{namespace}/metrics"). - To(metrics.GetNamespaceMetrics). + To(getNamespaceMetrics). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get metrics from a specific namespace"). Param(webservice.PathParameter("namespace", "name of the namespace").Required(true)). @@ -115,7 +108,7 @@ func addWebService(c *restful.Container) error { // Get namespace graph // Get /namespaces/{namespace}/graph webservice.Route(webservice.GET("/namespaces/{namespace}/graph"). - To(metrics.GetNamespaceGraph). + To(getNamespaceGraph). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get service graph for a specific namespace"). Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)). @@ -132,7 +125,7 @@ func addWebService(c *restful.Container) error { // Get namespaces graph, for multiple namespaces // Get /namespaces/graph webservice.Route(webservice.GET("/namespaces/graph"). - To(metrics.GetNamespacesGraph). + To(getNamespacesGraph). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get graph from all namespaces"). Param(webservice.QueryParameter("duration", "duration of the query period, in seconds").DefaultValue("10m")). @@ -147,7 +140,7 @@ func addWebService(c *restful.Container) error { // Get namespace health webservice.Route(webservice.GET("/namespaces/{namespace}/health"). - To(metrics.GetNamespaceHealth). + To(getNamespaceHealth). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get app/service/workload health of a namespace"). Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)). @@ -161,7 +154,7 @@ func addWebService(c *restful.Container) error { // Get workloads health webservice.Route(webservice.GET("/namespaces/{namespace}/workloads/{workload}/health"). - To(metrics.GetWorkloadHealth). + To(getWorkloadHealth). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get workload health"). Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)). @@ -173,7 +166,7 @@ func addWebService(c *restful.Container) error { // Get app health webservice.Route(webservice.GET("/namespaces/{namespace}/apps/{app}/health"). - To(metrics.GetAppHealth). + To(getAppHealth). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get app health"). Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)). @@ -185,7 +178,7 @@ func addWebService(c *restful.Container) error { // Get service health webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/health"). - To(metrics.GetServiceHealth). + To(getServiceHealth). Metadata(restfulspec.KeyOpenAPITags, tags). Doc("Get service health"). Param(webservice.PathParameter("namespace", "name of a namespace").Required(true)). @@ -197,7 +190,7 @@ func addWebService(c *restful.Container) error { // Get service tracing webservice.Route(webservice.GET("/namespaces/{namespace}/services/{service}/traces"). - To(tracing.GetServiceTracing). + To(getServiceTracing). Doc("Get tracing of a service, should have servicemesh enabled first"). Metadata(restfulspec.KeyOpenAPITags, tags). Param(webservice.PathParameter("namespace", "namespace of service").Required(true)). diff --git a/pkg/kapis/tenant/group.go b/pkg/kapis/tenant/group.go deleted file mode 100644 index 2f29dcfcc..000000000 --- a/pkg/kapis/tenant/group.go +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 tenant diff --git a/pkg/kapis/tenant/install/install.go b/pkg/kapis/tenant/install/install.go deleted file mode 100644 index d164f7ca2..000000000 --- a/pkg/kapis/tenant/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(container *restful.Container) { - urlruntime.Must(tenantv1alpha2.AddToContainer(container)) -} diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go new file mode 100644 index 000000000..04d1f9df6 --- /dev/null +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -0,0 +1,67 @@ +package v1alpha2 + +import ( + "errors" + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/apiserver/request" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/tenant" +) + +type tenantHandler struct { + tenant tenant.Interface +} + +func newTenantHandler(factory informers.InformerFactory) *tenantHandler { + + return &tenantHandler{ + tenant: tenant.New(factory), + } +} + +func (h *tenantHandler) ListWorkspaces(req *restful.Request, resp *restful.Response) { + user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) + + if !ok { + err := errors.New("cannot obtain user info") + klog.Errorln(err) + api.HandleForbidden(resp, nil, err) + return + } + + result, err := h.tenant.ListWorkspaces(user, queryParam) + + if err != nil { + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(result) +} + +func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Response) { + user, ok := request.UserFrom(req.Request.Context()) + queryParam := query.ParseQueryParameter(req) + + if !ok { + err := errors.New("cannot obtain user info") + klog.Errorln(err) + api.HandleForbidden(resp, nil, err) + return + } + + workspace := req.PathParameter("workspace") + + result, err := h.tenant.ListNamespaces(user, workspace, queryParam) + + if err != nil { + api.HandleInternalError(resp, nil, err) + return + } + + resp.WriteEntity(result) +} diff --git a/pkg/kapis/tenant/v1alpha2/register.go b/pkg/kapis/tenant/v1alpha2/register.go index f4a916e17..b0d38d53d 100644 --- a/pkg/kapis/tenant/v1alpha2/register.go +++ b/pkg/kapis/tenant/v1alpha2/register.go @@ -22,164 +22,35 @@ import ( "github.com/emicklei/go-restful-openapi" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/schema" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/tenant" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/server/errors" - "kubesphere.io/kubesphere/pkg/server/params" - "net/http" ) const ( GroupName = "tenant.kubesphere.io" - RespOK = "ok" ) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { - ok := "ok" +func AddToContainer(c *restful.Container, factory informers.InformerFactory) error { ws := runtime.NewWebService(GroupVersion) + handler := newTenantHandler(factory) ws.Route(ws.GET("/workspaces"). - To(tenant.ListWorkspaces). - Returns(http.StatusOK, ok, models.PageableResponse{}). + To(handler.ListWorkspaces). + Returns(http.StatusOK, api.StatusOK, models.PageableResponse{}). Doc("List all workspaces that belongs to the current user"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/workspaces/{workspace}"). - To(tenant.DescribeWorkspace). - Doc("Describe the specified workspace"). - Param(ws.PathParameter("workspace", "workspace name")). - Returns(http.StatusOK, ok, v1alpha1.Workspace{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/workspaces/{workspace}/rules"). - To(tenant.ListWorkspaceRules). - Param(ws.PathParameter("workspace", "workspace name")). - Doc("List the rules of the specified workspace for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/namespaces/{namespace}/rules"). - To(tenant.ListNamespaceRules). - Param(ws.PathParameter("namespace", "the name of the namespace")). - Doc("List the rules of the specified namespace for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/devops/{devops}/rules"). - To(tenant.ListDevopsRules). - Param(ws.PathParameter("devops", "devops project ID")). - Doc("List the rules of the specified DevOps project for the current user"). - Returns(http.StatusOK, ok, models.SimpleRule{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/namespaces"). - To(tenant.ListNamespaces). + To(handler.ListNamespaces). Param(ws.PathParameter("workspace", "workspace name")). Doc("List the namespaces of the specified workspace for the current user"). - Returns(http.StatusOK, ok, []v1.Namespace{}). + Returns(http.StatusOK, api.StatusOK, []v1.Namespace{}). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/namespaces"). - To(tenant.ListNamespacesByUsername). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "workspace member's username")). - Doc("List the namespaces for the workspace member"). - Returns(http.StatusOK, ok, []v1.Namespace{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.POST("/workspaces/{workspace}/namespaces"). - To(tenant.CreateNamespace). - Param(ws.PathParameter("workspace", "workspace name")). - Doc("Create a namespace in the specified workspace"). - Returns(http.StatusOK, ok, []v1.Namespace{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}"). - To(tenant.DeleteNamespace). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("namespace", "the name of the namespace")). - Doc("Delete the specified namespace from the workspace"). - Returns(http.StatusOK, ok, errors.Error{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - - ws.Route(ws.GET("/workspaces/{workspace}/devops"). - To(tenant.ListDevopsProjects). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.QueryParameter(params.PagingParam, "page"). - Required(false). - DataFormat("limit=%d,page=%d"). - DefaultValue("limit=10,page=1")). - Param(ws.QueryParameter(params.ConditionsParam, "query conditions"). - Required(false). - DataFormat("key=%s,key~%s")). - Doc("List devops projects for the current user"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). - To(tenant.ListDevopsProjectsByUsername). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("member", "workspace member's username")). - Param(ws.QueryParameter(params.PagingParam, "page"). - Required(false). - DataFormat("limit=%d,page=%d"). - DefaultValue("limit=10,page=1")). - Param(ws.QueryParameter(params.ConditionsParam, "query conditions"). - Required(false). - DataFormat("key=%s,key~%s")). - Returns(http.StatusOK, ok, models.PageableResponse{}). - Doc("List the devops projects for the workspace member"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/devopscount"). - To(tenant.GetDevOpsProjectsCount). - Returns(http.StatusOK, ok, struct { - Count uint32 `json:"count"` - }{}). - Doc("Get the devops projects count for the member"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.POST("/workspaces/{workspace}/devops"). - To(tenant.CreateDevopsProject). - Param(ws.PathParameter("workspace", "workspace name")). - Doc("Create a devops project in the specified workspace"). - Reads(devopsv1alpha2.DevOpsProject{}). - Returns(http.StatusOK, RespOK, devopsv1alpha2.DevOpsProject{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.DELETE("/workspaces/{workspace}/devops/{devops}"). - To(tenant.DeleteDevopsProject). - Param(ws.PathParameter("workspace", "workspace name")). - Param(ws.PathParameter("devops", "devops project ID")). - Doc("Delete the specified devops project from the workspace"). - Returns(http.StatusOK, RespOK, devopsv1alpha2.DevOpsProject{}). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) - ws.Route(ws.GET("/logs"). - To(tenant.LogQuery). - Doc("Query cluster-level logs in a multi-tenants environment"). - Param(ws.QueryParameter("operation", "Operation type. This can be one of four types: query (for querying logs), statistics (for retrieving statistical data), histogram (for displaying log count by time interval) and export (for exporting logs). Defaults to query.").DefaultValue("query").DataType("string").Required(false)). - Param(ws.QueryParameter("workspaces", "A comma-separated list of workspaces. This field restricts the query to specified workspaces. For example, the following filter matches the workspace my-ws and demo-ws: `my-ws,demo-ws`").DataType("string").Required(false)). - Param(ws.QueryParameter("workspace_query", "A comma-separated list of keywords. Differing from **workspaces**, this field performs fuzzy matching on workspaces. For example, the following value limits the query to workspaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("namespaces", "A comma-separated list of namespaces. This field restricts the query to specified namespaces. For example, the following filter matches the namespace my-ns and demo-ns: `my-ns,demo-ns`").DataType("string").Required(false)). - Param(ws.QueryParameter("namespace_query", "A comma-separated list of keywords. Differing from **namespaces**, this field performs fuzzy matching on namespaces. For example, the following value limits the query to namespaces whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("workloads", "A comma-separated list of workloads. This field restricts the query to specified workloads. For example, the following filter matches the workload my-wl and demo-wl: `my-wl,demo-wl`").DataType("string").Required(false)). - Param(ws.QueryParameter("workload_query", "A comma-separated list of keywords. Differing from **workloads**, this field performs fuzzy matching on workloads. For example, the following value limits the query to workloads whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("pods", "A comma-separated list of pods. This field restricts the query to specified pods. For example, the following filter matches the pod my-po and demo-po: `my-po,demo-po`").DataType("string").Required(false)). - Param(ws.QueryParameter("pod_query", "A comma-separated list of keywords. Differing from **pods**, this field performs fuzzy matching on pods. For example, the following value limits the query to pods whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("containers", "A comma-separated list of containers. This field restricts the query to specified containers. For example, the following filter matches the container my-cont and demo-cont: `my-cont,demo-cont`").DataType("string").Required(false)). - Param(ws.QueryParameter("container_query", "A comma-separated list of keywords. Differing from **containers**, this field performs fuzzy matching on containers. For example, the following value limits the query to containers whose name contains the word my(My,MY,...) *OR* demo(Demo,DemO,...): `my,demo`.").DataType("string").Required(false)). - Param(ws.QueryParameter("log_query", "A comma-separated list of keywords. The query returns logs which contain at least one keyword. Case-insensitive matching. For example, if the field is set to `err,INFO`, the query returns any log containing err(ERR,Err,...) *OR* INFO(info,InFo,...).").DataType("string").Required(false)). - Param(ws.QueryParameter("interval", "Time interval. It requires **operation** is set to histogram. The format is [0-9]+[smhdwMqy]. Defaults to 15m (i.e. 15 min).").DefaultValue("15m").DataType("string").Required(false)). - Param(ws.QueryParameter("start_time", "Start time of query. Default to 0. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("end_time", "End time of query. Default to now. The format is a string representing milliseconds since the epoch, eg. 1559664000000.").DataType("string").Required(false)). - Param(ws.QueryParameter("sort", "Sort order. One of acs, desc. This field sorts logs by timestamp.").DataType("string").DefaultValue("desc").Required(false)). - Param(ws.QueryParameter("from", "The offset from the result set. This field returns query results from the specified offset. It requires **operation** is set to query. Defaults to 0 (i.e. from the beginning of the result set).").DataType("integer").DefaultValue("0").Required(false)). - Param(ws.QueryParameter("size", "Size of result to return. It requires **operation** is set to query. Defaults to 10 (i.e. 10 log records).").DataType("integer").DefaultValue("10").Required(false)). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}). - Writes(v1alpha2.Response{}). - Returns(http.StatusOK, RespOK, v1alpha2.Response{})). - Consumes(restful.MIME_JSON, restful.MIME_XML). - Produces(restful.MIME_JSON, "text/plain") c.Add(ws) return nil diff --git a/pkg/kapis/terminal/group.go b/pkg/kapis/terminal/group.go deleted file mode 100644 index ced53f44b..000000000 --- a/pkg/kapis/terminal/group.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 terminal contains terminal API versions -package terminal diff --git a/pkg/kapis/terminal/install/install.go b/pkg/kapis/terminal/install/install.go deleted file mode 100644 index 2f5e9efcd..000000000 --- a/pkg/kapis/terminal/install/install.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 install - -import ( - "github.com/emicklei/go-restful" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" -) - -func init() { - Install(runtime.Container) -} - -func Install(c *restful.Container) { - urlruntime.Must(v1alpha2.AddToContainer(c)) -} diff --git a/pkg/kapis/terminal/v1alpha2/handler.go b/pkg/kapis/terminal/v1alpha2/handler.go new file mode 100644 index 000000000..c8e6ea553 --- /dev/null +++ b/pkg/kapis/terminal/v1alpha2/handler.go @@ -0,0 +1,43 @@ +package v1alpha2 + +import ( + "github.com/emicklei/go-restful" + "github.com/gorilla/websocket" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models/terminal" + "net/http" +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + // Allow connections from any Origin + CheckOrigin: func(r *http.Request) bool { return true }, +} + +type terminalHandler struct { + terminaler terminal.Interface +} + +func newTerminalHandler(client kubernetes.Interface, config *rest.Config) *terminalHandler { + return &terminalHandler{ + terminaler: terminal.NewTerminaler(client, config), + } +} + +func (t *terminalHandler) handleTerminalSession(request *restful.Request, response *restful.Response) { + namespace := request.PathParameter("namespace") + podName := request.PathParameter("pod") + containerName := request.QueryParameter("container") + shell := request.QueryParameter("shell") + + conn, err := upgrader.Upgrade(response.ResponseWriter, request.Request, nil) + if err != nil { + klog.Warning(err) + return + } + + t.terminaler.HandleSession(shell, namespace, podName, containerName, conn) +} diff --git a/pkg/kapis/terminal/v1alpha2/register.go b/pkg/kapis/terminal/v1alpha2/register.go index bda42753f..7997ccfae 100644 --- a/pkg/kapis/terminal/v1alpha2/register.go +++ b/pkg/kapis/terminal/v1alpha2/register.go @@ -21,30 +21,29 @@ import ( "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/apiserver/terminal" + "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" ) -const GroupName = "terminal.kubesphere.io" +const ( + GroupName = "terminal.kubesphere.io" +) var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -var ( - WebServiceBuilder = runtime.NewContainerBuilder(addWebService) - AddToContainer = WebServiceBuilder.AddToContainer -) - -func addWebService(c *restful.Container) error { +func AddToContainer(c *restful.Container, client kubernetes.Interface, config *rest.Config) error { webservice := runtime.NewWebService(GroupVersion) - tags := []string{"Terminal"} + handler := newTerminalHandler(client, config) webservice.Route(webservice.GET("/namespaces/{namespace}/pods/{pod}"). - To(terminal.HandleTerminalSession). + To(handler.handleTerminalSession). Doc("create terminal session"). - Metadata(restfulspec.KeyOpenAPITags, tags). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.TerminalTag}). Writes(models.PodInfo{})) c.Add(webservice) diff --git a/pkg/models/components/components.go b/pkg/models/components/components.go index f24f670c5..bd27a6f20 100644 --- a/pkg/models/components/components.go +++ b/pkg/models/components/components.go @@ -19,47 +19,56 @@ package components import ( "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api/resource/v1alpha2" "kubesphere.io/kubesphere/pkg/constants" ) -func GetComponentStatus(name string) (interface{}, error) { +type ComponentsGetter interface { + GetComponentStatus(name string) (v1alpha2.ComponentStatus, error) + GetSystemHealthStatus() (v1alpha2.HealthStatus, error) + GetAllComponentsStatus() ([]v1alpha2.ComponentStatus, error) +} + +type componentsGetter struct { + informers informers.SharedInformerFactory +} + +func NewComponentsGetter(informers informers.SharedInformerFactory) ComponentsGetter { + return &componentsGetter{informers: informers} +} + +func (c *componentsGetter) GetComponentStatus(name string) (v1alpha2.ComponentStatus, error) { var service *corev1.Service var err error - serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister() - for _, ns := range constants.SystemNamespaces { - service, err = serviceLister.Services(ns).Get(name) + service, err = c.informers.Core().V1().Services().Lister().Services(ns).Get(name) if err == nil { break } } if err != nil { - return nil, err + return v1alpha2.ComponentStatus{}, err } if len(service.Spec.Selector) == 0 { - return nil, fmt.Errorf("component %s has no selector", name) + return v1alpha2.ComponentStatus{}, fmt.Errorf("component %s has no selector", name) } - podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister() - - pods, err := podLister.Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector)) + pods, err := c.informers.Core().V1().Pods().Lister().Pods(service.Namespace).List(labels.SelectorFromValidatedSet(service.Spec.Selector)) if err != nil { - return nil, err + return v1alpha2.ComponentStatus{}, err } - component := models.ComponentStatus{ + component := v1alpha2.ComponentStatus{ Name: service.Name, Namespace: service.Namespace, SelfLink: service.SelfLink, @@ -86,21 +95,20 @@ func isAllContainersReady(pod *corev1.Pod) bool { return true } -func GetSystemHealthStatus() (*models.HealthStatus, error) { +func (c *componentsGetter) GetSystemHealthStatus() (v1alpha2.HealthStatus, error) { - status := &models.HealthStatus{} + status := v1alpha2.HealthStatus{} // get kubesphere-system components - components, err := GetAllComponentsStatus() + components, err := c.GetAllComponentsStatus() if err != nil { klog.Errorln(err) } status.KubeSphereComponents = components - nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister() // get node status - nodes, err := nodeLister.List(labels.Everything()) + nodes, err := c.informers.Core().V1().Nodes().Lister().List(labels.Everything()) if err != nil { klog.Errorln(err) return status, nil @@ -116,7 +124,7 @@ func GetSystemHealthStatus() (*models.HealthStatus, error) { } } } - nodeStatus := models.NodeStatus{TotalNodes: totalNodes, HealthyNodes: healthyNodes} + nodeStatus := v1alpha2.NodeStatus{TotalNodes: totalNodes, HealthyNodes: healthyNodes} status.NodeStatus = nodeStatus @@ -124,16 +132,14 @@ func GetSystemHealthStatus() (*models.HealthStatus, error) { } -func GetAllComponentsStatus() ([]models.ComponentStatus, error) { - serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister() - podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister() +func (c *componentsGetter) GetAllComponentsStatus() ([]v1alpha2.ComponentStatus, error) { - components := make([]models.ComponentStatus, 0) + components := make([]v1alpha2.ComponentStatus, 0) var err error for _, ns := range constants.SystemNamespaces { - services, err := serviceLister.Services(ns).List(labels.Everything()) + services, err := c.informers.Core().V1().Services().Lister().Services(ns).List(labels.Everything()) if err != nil { klog.Error(err) @@ -147,7 +153,7 @@ func GetAllComponentsStatus() ([]models.ComponentStatus, error) { continue } - component := models.ComponentStatus{ + component := v1alpha2.ComponentStatus{ Name: service.Name, Namespace: service.Namespace, SelfLink: service.SelfLink, @@ -157,7 +163,7 @@ func GetAllComponentsStatus() ([]models.ComponentStatus, error) { TotalBackends: 0, } - pods, err := podLister.Pods(ns).List(labels.SelectorFromValidatedSet(service.Spec.Selector)) + pods, err := c.informers.Core().V1().Pods().Lister().Pods(ns).List(labels.SelectorFromValidatedSet(service.Spec.Selector)) if err != nil { klog.Errorln(err) diff --git a/pkg/models/components/components_test.go b/pkg/models/components/components_test.go new file mode 100644 index 000000000..95d66076e --- /dev/null +++ b/pkg/models/components/components_test.go @@ -0,0 +1,356 @@ +package components + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api/resource/v1alpha2" + "testing" + "time" +) + +func service(name, namespace string, selector map[string]string) runtime.Object { + return &v1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1.ServiceSpec{ + Selector: selector, + }, + } +} + +func pods(name, namespace string, labels map[string]string, healthPods, totalPods int) []runtime.Object { + var ps []runtime.Object + + for index := 0; index < totalPods; index++ { + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%d", name, index), + Namespace: namespace, + Labels: labels, + }, + Status: v1.PodStatus{ + Phase: v1.PodRunning, + ContainerStatuses: []v1.ContainerStatus{ + { + Name: fmt.Sprintf("%s-%d", name, index), + Ready: true, + }, + }, + }, + } + + if index >= healthPods { + pod.Status.Phase = v1.PodPending + pod.Status.ContainerStatuses[0].Ready = false + } + + ps = append(ps, pod) + } + + return ps +} + +func nodes(name string, healthNodes, totalNodes int) []runtime.Object { + var ns []runtime.Object + + for index := 0; index < totalNodes; index++ { + node := &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-%d", name, index), + }, + Status: v1.NodeStatus{ + Phase: v1.NodeRunning, + Conditions: []v1.NodeCondition{ + { + Type: v1.NodeReady, + Status: v1.ConditionTrue, + }, + }, + }, + } + + if index >= healthNodes { + node.Status.Phase = v1.NodePending + node.Status.Conditions[0].Status = v1.ConditionFalse + } + + ns = append(ns, node) + } + + return ns +} + +func TestGetSystemHealthStatus(t *testing.T) { + var tests = []struct { + description string + labels map[string]string + namespace string + name string + healthPods int + totalPods int + healthNodes int + totalNodes int + expected v1alpha2.HealthStatus + }{ + { + "no backends", + map[string]string{"app": "foo"}, + "kube-system", + "", + 0, + 0, + 0, + 0, + v1alpha2.HealthStatus{ + KubeSphereComponents: []v1alpha2.ComponentStatus{ + { + Namespace: "kube-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 0, + HealthyBackends: 0, + }, + }, + NodeStatus: v1alpha2.NodeStatus{}, + }, + }, + { + "all healthy", + map[string]string{"app": "foo"}, + "kubesphere-system", + "ks-apiserver", + 2, + 2, + 2, + 2, + v1alpha2.HealthStatus{ + KubeSphereComponents: []v1alpha2.ComponentStatus{ + { + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 2, + HealthyBackends: 2, + }, + }, + NodeStatus: v1alpha2.NodeStatus{ + TotalNodes: 2, + HealthyNodes: 2, + }, + }, + }, + { + "all unhealthy", + map[string]string{"app": "foo"}, + "kubesphere-system", + "ks-apiserver", + 0, + 2, + 0, + 2, + v1alpha2.HealthStatus{ + KubeSphereComponents: []v1alpha2.ComponentStatus{ + { + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 2, + HealthyBackends: 0, + }, + }, + NodeStatus: v1alpha2.NodeStatus{ + TotalNodes: 2, + HealthyNodes: 0, + }, + }, + }, + { + "half healthy", + map[string]string{"app": "foo"}, + "kubesphere-system", + "ks-apiserver", + 2, + 4, + 2, + 4, + v1alpha2.HealthStatus{ + KubeSphereComponents: []v1alpha2.ComponentStatus{ + { + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 4, + HealthyBackends: 2, + }, + }, + NodeStatus: v1alpha2.NodeStatus{ + TotalNodes: 4, + HealthyNodes: 2, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + ps := pods(test.name, test.namespace, test.labels, test.healthPods, test.totalPods) + svc := service(test.name, test.namespace, test.labels) + ns := nodes(test.name, test.healthNodes, test.totalNodes) + + var objs []runtime.Object + objs = append(objs, ps...) + objs = append(objs, svc) + objs = append(objs, ns...) + + client := fake.NewSimpleClientset(objs...) + + informer := informers.NewSharedInformerFactory(client, time.Minute*10) + + informer.Core().V1().Services().Informer().GetIndexer().Add(svc) + + for _, obj := range ps { + informer.Core().V1().Pods().Informer().GetIndexer().Add(obj) + } + + for _, obj := range ns { + informer.Core().V1().Nodes().Informer().GetIndexer().Add(obj) + } + + c := NewComponentsGetter(informer) + healthStatus, err := c.GetSystemHealthStatus() + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(healthStatus, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + return + } + }) + } + +} + +func TestGetComponentStatus(t *testing.T) { + var tests = []struct { + description string + name string + namespace string + labels map[string]string + healthPods int + totalPods int + expected v1alpha2.ComponentStatus + expectedError bool + }{ + { + "no component", + "random", + "foo", + map[string]string{"app": "foo"}, + 2, + 4, + v1alpha2.ComponentStatus{ + Name: "", + Namespace: "", + SelfLink: "", + Label: nil, + StartedAt: time.Time{}, + TotalBackends: 0, + HealthyBackends: 0, + }, + true, + }, + { + "all healthy", + "ks-apiserver", + "kubesphere-system", + map[string]string{"app": "foo"}, + 2, + 4, + v1alpha2.ComponentStatus{ + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 4, + HealthyBackends: 2, + }, + false, + }, + { + "all unhealthy", + "ks-apiserver", + "kubesphere-system", + map[string]string{"app": "foo"}, + 0, + 4, + v1alpha2.ComponentStatus{ + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 4, + HealthyBackends: 0, + }, + false, + }, + { + "half healthy", + "ks-apiserver", + "kubesphere-system", + map[string]string{"app": "foo"}, + 2, + 4, + v1alpha2.ComponentStatus{ + Name: "ks-apiserver", + Namespace: "kubesphere-system", + Label: map[string]string{"app": "foo"}, + TotalBackends: 4, + HealthyBackends: 2, + }, + false, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + ps := pods(test.name, test.namespace, test.labels, test.healthPods, test.totalPods) + svc := service(test.name, test.namespace, test.labels) + + var objs []runtime.Object + objs = append(objs, ps...) + objs = append(objs, svc) + + client := fake.NewSimpleClientset(objs...) + + informer := informers.NewSharedInformerFactory(client, time.Minute*10) + + informer.Core().V1().Services().Informer().GetIndexer().Add(svc) + + for _, obj := range ps { + informer.Core().V1().Pods().Informer().GetIndexer().Add(obj) + } + + c := NewComponentsGetter(informer) + healthStatus, err := c.GetComponentStatus(test.name) + if err == nil && test.expectedError { + t.Fatalf("expected error while got nothing") + } else if err != nil && !test.expectedError { + t.Fatal(err) + } + + if diff := cmp.Diff(healthStatus, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + return + } + }) + } +} diff --git a/pkg/models/devops/common.go b/pkg/models/devops/common.go index 7d0af8385..c23288c49 100644 --- a/pkg/models/devops/common.go +++ b/pkg/models/devops/common.go @@ -14,13 +14,7 @@ limitations under the License. package devops import ( - "fmt" "github.com/fatih/structs" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" "kubesphere.io/kubesphere/pkg/utils/stringutils" ) @@ -67,298 +61,3 @@ const ( const ( KS_ADMIN = "admin" ) - -const ( - ProjectOwner = "owner" - ProjectMaintainer = "maintainer" - ProjectDeveloper = "developer" - ProjectReporter = "reporter" -) - -const ( - JenkinsAllUserRoleName = "kubesphere-user" -) - -type Role struct { - Name string `json:"name" description:"role's name e.g. owner'"` - Description string `json:"description" description:"role 's description'"` -} - -var DefaultRoles = []*Role{ - { - Name: ProjectOwner, - Description: "Owner have access to do all the operations of a DevOps project and own the highest permissions as well.", - }, - { - Name: ProjectMaintainer, - Description: "Maintainer have access to manage pipeline and credential configuration in a DevOps project.", - }, - { - Name: ProjectDeveloper, - Description: "Developer is able to view and trigger the pipeline.", - }, - { - Name: ProjectReporter, - Description: "Reporter is only allowed to view the status of the pipeline.", - }, -} - -var AllRoleSlice = []string{ProjectDeveloper, ProjectReporter, ProjectMaintainer, ProjectOwner} - -var JenkinsOwnerProjectPermissionIds = &gojenkins.ProjectPermissionIds{ - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, -} - -var JenkinsProjectPermissionMap = map[string]gojenkins.ProjectPermissionIds{ - ProjectOwner: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectMaintainer: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: true, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectDeveloper: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: false, - }, - ProjectReporter: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: false, - ItemCancel: false, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: false, - RunDelete: false, - RunReplay: false, - RunUpdate: false, - SCMTag: false, - }, -} - -var JenkinsPipelinePermissionMap = map[string]gojenkins.ProjectPermissionIds{ - ProjectOwner: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectMaintainer: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectDeveloper: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: false, - }, - ProjectReporter: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: false, - ItemCancel: false, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: false, - RunDelete: false, - RunReplay: false, - RunUpdate: false, - SCMTag: false, - }, -} - -func GetProjectRoleName(projectId, role string) string { - return fmt.Sprintf("%s-%s-project", projectId, role) -} - -func GetPipelineRoleName(projectId, role string) string { - return fmt.Sprintf("%s-%s-pipeline", projectId, role) -} - -func GetProjectRolePattern(projectId string) string { - return fmt.Sprintf("^%s$", projectId) -} - -func GetPipelineRolePattern(projectId string) string { - return fmt.Sprintf("^%s/.*", projectId) -} - -func CheckProjectUserInRole(username, projectId string, roles []string) error { - if username == KS_ADMIN { - return nil - } - dbconn, err := client.ClientSets().MySQL() - if err != nil { - if _, ok := err.(client.ClientSetNotEnabledError); ok { - klog.Error("mysql is not enabled") - } else { - klog.Error("error creating mysql client", err) - } - return nil - } - - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) - if err != nil { - return err - } - if !reflectutils.In(membership.Role, roles) { - return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles) - } - return nil -} - -func GetProjectUserRole(username, projectId string) (string, error) { - if username == KS_ADMIN { - return ProjectOwner, nil - } - dbconn, err := client.ClientSets().MySQL() - if err != nil { - if _, ok := err.(client.ClientSetNotEnabledError); ok { - klog.Error("mysql is not enabled") - } else { - klog.Error("error creating mysql client", err) - } - return "", err - } - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) - if err != nil { - return "", err - } - - return membership.Role, nil -} diff --git a/pkg/models/devops/devops.go b/pkg/models/devops/devops.go index 87c22bbe4..94298915b 100644 --- a/pkg/models/devops/devops.go +++ b/pkg/models/devops/devops.go @@ -19,38 +19,131 @@ package devops import ( "bytes" - "compress/gzip" "encoding/json" "fmt" - "github.com/PuerkitoBio/goquery" - "github.com/emicklei/go-restful" "io" "io/ioutil" - "kubesphere.io/kubesphere/pkg/models" - "k8s.io/klog" - cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" - "net/url" - "strings" "sync" - "time" ) const ( channelMaxCapacity = 100 - cronJobLayout = "Monday, January 2, 2006 15:04:05 PM" ) -func GetPipeline(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) +type DevopsOperator interface { + GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) + ListPipelines(req *http.Request) (*devops.PipelineList, error) + GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) (*devops.PipelineRun, error) + ListPipelineRuns(projectName, pipelineName string, req *http.Request) (*devops.PipelineRunList, error) + StopPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.StopPipeline, error) + ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.ReplayPipeline, error) + RunPipeline(projectName, pipelineName string, req *http.Request) (*devops.RunPipeline, error) + GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]devops.Artifacts, error) + GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) + GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) + GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) + GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) + SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) + GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]devops.NodesDetail, error) + + GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.BranchPipeline, error) + GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.PipelineRun, error) + StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.StopPipeline, error) + ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.ReplayPipeline, error) + RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.RunPipeline, error) + GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.Artifacts, error) + GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) + GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) + GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) + GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.BranchPipelineRunNodes, error) + SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) + GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.NodesDetail, error) + GetPipelineBranch(projectName, pipelineName string, req *http.Request) (*devops.PipelineBranch, error) + ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) + + GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) + GetCrumb(req *http.Request) (*devops.Crumb, error) + + GetSCMServers(scmId string, req *http.Request) ([]devops.SCMServer, error) + GetSCMOrg(scmId string, req *http.Request) ([]devops.SCMOrg, error) + GetOrgRepo(scmId, organizationId string, req *http.Request) ([]devops.OrgRepo, error) + CreateSCMServers(scmId string, req *http.Request) (*devops.SCMServer, error) + Validate(scmId string, req *http.Request) (*devops.Validates, error) + + GetNotifyCommit(req *http.Request) ([]byte, error) + GithubWebhook(req *http.Request) ([]byte, error) + + CheckScriptCompile(projectName, pipelineName string, req *http.Request) (*devops.CheckScript, error) + CheckCron(projectName string, req *http.Request) (*devops.CheckCronRes, error) + + ToJenkinsfile(req *http.Request) (*devops.ResJenkinsfile, error) + ToJson(req *http.Request) (*devops.ResJson, error) +} + +type devopsOperator struct { + devopsClient devops.Interface +} + +func NewDevopsOperator(client devops.Interface) DevopsOperator { + return &devopsOperator{devopsClient: client} +} + +func convertToHttpParameters(req *http.Request) *devops.HttpParameters { + httpParameters := devops.HttpParameters{ + Method: req.Method, + Header: req.Header, + Body: req.Body, + Form: req.Form, + PostForm: req.PostForm, + Url: req.URL, } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipelineUrl, projectName, pipelineName) + return &httpParameters +} - res, err := sendJenkinsRequest(baseUrl, req) +func (d devopsOperator) GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) { + + res, err := d.devopsClient.GetPipeline(projectName, pipelineName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + } + return res, err +} + +func (d devopsOperator) ListPipelines(req *http.Request) (*devops.PipelineList, error) { + + res, err := d.devopsClient.ListPipelines(convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + } + return res, err +} + +func (d devopsOperator) GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) (*devops.PipelineRun, error) { + + res, err := d.devopsClient.GetPipelineRun(projectName, pipelineName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + } + return res, err +} + +func (d devopsOperator) ListPipelineRuns(projectName, pipelineName string, req *http.Request) (*devops.PipelineRunList, error) { + + res, err := d.devopsClient.ListPipelineRuns(projectName, pipelineName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + } + return res, err +} + +func (d devopsOperator) StopPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.StopPipeline, error) { + + req.Method = http.MethodPut + res, err := d.devopsClient.StopPipeline(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -59,136 +152,9 @@ func GetPipeline(projectName, pipelineName string, req *http.Request) ([]byte, e return res, err } -func SearchPipelines(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.ReplayPipeline, error) { - baseUrl := devops.Jenkins().Server + SearchPipelineUrl + req.URL.RawQuery - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - count, err := searchPipelineCount(req) - if err != nil { - klog.Error(err) - return nil, err - } - responseStruct := models.PageableResponse{TotalCount: count} - err = json.Unmarshal(res, &responseStruct.Items) - if err != nil { - klog.Error(err) - return nil, err - } - res, err = json.Marshal(responseStruct) - if err != nil { - klog.Error(err) - return nil, err - } - return res, err -} - -func searchPipelineCount(req *http.Request) (int, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return 0, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - query, _ := parseJenkinsQuery(req.URL.RawQuery) - query.Set("start", "0") - query.Set("limit", "1000") - query.Set("depth", "-1") - - baseUrl := devops.Jenkins().Server + SearchPipelineUrl + query.Encode() - klog.V(4).Info("Jenkins-url: " + baseUrl) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return 0, err - } - var pipelines []Pipeline - err = json.Unmarshal(res, &pipelines) - if err != nil { - klog.Error(err) - return 0, err - } - return len(pipelines), nil -} - -func searchPipelineRunsCount(projectName, pipelineName string, req *http.Request) (int, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return 0, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - query, _ := parseJenkinsQuery(req.URL.RawQuery) - query.Set("start", "0") - query.Set("limit", "1000") - query.Set("depth", "-1") - baseUrl := fmt.Sprintf(devops.Jenkins().Server+SearchPipelineRunUrl, projectName, pipelineName) - - klog.V(4).Info("Jenkins-url: " + baseUrl) - - res, err := sendJenkinsRequest(baseUrl+query.Encode(), req) - if err != nil { - klog.Error(err) - return 0, err - } - var runs []PipelineRun - err = json.Unmarshal(res, &runs) - if err != nil { - klog.Error(err) - return 0, err - } - return len(runs), nil -} - -func SearchPipelineRuns(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+SearchPipelineRunUrl, projectName, pipelineName) - - klog.V(4).Info("Jenkins-url: " + baseUrl) - - res, err := sendJenkinsRequest(baseUrl+req.URL.RawQuery, req) - if err != nil { - klog.Error(err) - return nil, err - } - count, err := searchPipelineRunsCount(projectName, pipelineName, req) - if err != nil { - klog.Error(err) - return nil, err - } - responseStruct := models.PageableResponse{TotalCount: count} - err = json.Unmarshal(res, &responseStruct.Items) - if err != nil { - klog.Error(err) - return nil, err - } - res, err = json.Marshal(responseStruct) - if err != nil { - klog.Error(err) - return nil, err - } - return res, err -} - -func GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeBranchRunUrl, projectName, pipelineName, branchName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.ReplayPipeline(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -197,16 +163,9 @@ func GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, r return res, err } -func GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) RunPipeline(projectName, pipelineName string, req *http.Request) (*devops.RunPipeline, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchPipeRunNodesUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - klog.V(4).Info("Jenkins-url: " + baseUrl) - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.RunPipeline(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -215,15 +174,31 @@ func GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId st return res, err } -func GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - devops, err := cs.ClientSets().Devops() +func (d devopsOperator) GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]devops.Artifacts, error) { + + res, err := d.devopsClient.GetArtifacts(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { - return nil, nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + klog.Error(err) + return nil, err } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchStepLogUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId) + return res, err +} - resBody, header, err := jenkinsClient(baseUrl, req) +func (d devopsOperator) GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { + + res, err := d.devopsClient.GetRunLog(projectName, pipelineName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { + + resBody, header, err := d.devopsClient.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, nil, err @@ -232,15 +207,173 @@ func GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, step return resBody, header, err } -func GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - devops, err := cs.ClientSets().Devops() +func (d devopsOperator) GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) { + res, err := d.devopsClient.GetNodeSteps(projectName, pipelineName, runId, nodeId, convertToHttpParameters(req)) if err != nil { - return nil, nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + klog.Error(err) + return nil, err } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetStepLogUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId, stepId) + return res, err +} - resBody, header, err := jenkinsClient(baseUrl, req) +func (d devopsOperator) GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) { + + res, err := d.devopsClient.GetPipelineRunNodes(projectName, pipelineName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + fmt.Println() + + return res, err +} + +func (d devopsOperator) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { + + newBody, err := getInputReqBody(req.Body) + if err != nil { + klog.Error(err) + return nil, err + } + req.Body = newBody + + resBody, err := d.devopsClient.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return resBody, err +} + +func (d devopsOperator) GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]devops.NodesDetail, error) { + var wg sync.WaitGroup + var nodesDetails []devops.NodesDetail + stepChan := make(chan *devops.NodesStepsIndex, channelMaxCapacity) + + respNodes, err := d.GetPipelineRunNodes(projectName, pipelineName, runId, req) + if err != nil { + klog.Error(err) + return nil, err + } + + Nodes, err := json.Marshal(respNodes) + err = json.Unmarshal(Nodes, &nodesDetails) + if err != nil { + klog.Error(err) + return nil, err + } + + // get all steps in nodes. + for i, v := range respNodes { + wg.Add(1) + go func(nodeId string, index int) { + Steps, err := d.GetNodeSteps(projectName, pipelineName, runId, nodeId, req) + if err != nil { + klog.Error(err) + return + } + + stepChan <- &devops.NodesStepsIndex{Id: index, Steps: Steps} + wg.Done() + }(v.ID, i) + } + + wg.Wait() + close(stepChan) + + for oneNodeSteps := range stepChan { + if oneNodeSteps != nil { + nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) + } + } + + return nodesDetails, err +} + +func (d devopsOperator) GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.BranchPipeline, error) { + + res, err := d.devopsClient.GetBranchPipeline(projectName, pipelineName, branchName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.PipelineRun, error) { + + res, err := d.devopsClient.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.StopPipeline, error) { + + req.Method = http.MethodPut + res, err := d.devopsClient.StopBranchPipeline(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.ReplayPipeline, error) { + + res, err := d.devopsClient.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.RunPipeline, error) { + + res, err := d.devopsClient.RunBranchPipeline(projectName, pipelineName, branchName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.Artifacts, error) { + + res, err := d.devopsClient.GetBranchArtifacts(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { + + res, err := d.devopsClient.GetBranchRunLog(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { + + resBody, header, err := d.devopsClient.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, nil, err @@ -249,59 +382,195 @@ func GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *ht return resBody, header, err } -func GetSCMServers(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() +func (d devopsOperator) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) { + + res, err := d.devopsClient.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, convertToHttpParameters(req)) if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + klog.Error(err) + return nil, err } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetSCMServersUrl, scmId) + return res, err +} + +func (d devopsOperator) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.BranchPipelineRunNodes, error) { + + res, err := d.devopsClient.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { + + newBody, err := getInputReqBody(req.Body) + if err != nil { + klog.Error(err) + return nil, err + } + req.Body = newBody + resBody, err := d.devopsClient.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return resBody, err +} + +func (d devopsOperator) GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.NodesDetail, error) { + var wg sync.WaitGroup + var nodesDetails []devops.NodesDetail + stepChan := make(chan *devops.NodesStepsIndex, channelMaxCapacity) + + respNodes, err := d.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, req) + if err != nil { + klog.Error(err) + return nil, err + } + Nodes, err := json.Marshal(respNodes) + err = json.Unmarshal(Nodes, &nodesDetails) + if err != nil { + klog.Error(err) + return nil, err + } + + // get all steps in nodes. + for i, v := range nodesDetails { + wg.Add(1) + go func(nodeId string, index int) { + Steps, err := d.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req) + if err != nil { + klog.Error(err) + return + } + + stepChan <- &devops.NodesStepsIndex{Id: index, Steps: Steps} + wg.Done() + }(v.ID, i) + } + + wg.Wait() + close(stepChan) + + for oneNodeSteps := range stepChan { + if oneNodeSteps != nil { + nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) + } + } + + return nodesDetails, err +} + +func (d devopsOperator) GetPipelineBranch(projectName, pipelineName string, req *http.Request) (*devops.PipelineBranch, error) { + + res, err := d.devopsClient.GetPipelineBranch(projectName, pipelineName, convertToHttpParameters(req)) + //baseUrl+req.URL.RawQuery, req) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { + + resBody, err := d.devopsClient.ScanBranch(projectName, pipelineName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return resBody, err +} + +func (d devopsOperator) GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) { + + resBody, err := d.devopsClient.GetConsoleLog(projectName, pipelineName, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return resBody, err +} + +func (d devopsOperator) GetCrumb(req *http.Request) (*devops.Crumb, error) { + + res, err := d.devopsClient.GetCrumb(convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) GetSCMServers(scmId string, req *http.Request) ([]devops.SCMServer, error) { + req.Method = http.MethodGet - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.GetSCMServers(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, err } return resBody, err } -func CreateSCMServers(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() +func (d devopsOperator) GetSCMOrg(scmId string, req *http.Request) ([]devops.SCMOrg, error) { + + res, err := d.devopsClient.GetSCMOrg(scmId, convertToHttpParameters(req)) if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + klog.Error(err) + return nil, err } + return res, err +} + +func (d devopsOperator) GetOrgRepo(scmId, organizationId string, req *http.Request) ([]devops.OrgRepo, error) { + + res, err := d.devopsClient.GetOrgRepo(scmId, organizationId, convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) CreateSCMServers(scmId string, req *http.Request) (*devops.SCMServer, error) { + requestBody, err := ioutil.ReadAll(req.Body) if err != nil { klog.Error(err) return nil, err } - createReq := &CreateScmServerReq{} + createReq := &devops.CreateScmServerReq{} err = json.Unmarshal(requestBody, createReq) if err != nil { klog.Error(err) return nil, err } req.Body = nil - byteServers, err := GetSCMServers(scmId, req) + servers, err := d.GetSCMServers(scmId, req) if err != nil { klog.Error(err) return nil, err } - var servers []*SCMServer - _ = json.Unmarshal(byteServers, &servers) for _, server := range servers { if server.ApiURL == createReq.ApiURL { - return json.Marshal(server) + return &server, nil } } req.Body = ioutil.NopCloser(bytes.NewReader(requestBody)) - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CreateSCMServersUrl, scmId) - req.Method = http.MethodPost - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.CreateSCMServers(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -309,16 +578,10 @@ func CreateSCMServers(scmId string, req *http.Request) ([]byte, error) { return resBody, err } -func Validate(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ValidateUrl, scmId) +func (d devopsOperator) Validate(scmId string, req *http.Request) (*devops.Validates, error) { req.Method = http.MethodPut - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.Validate(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -327,15 +590,11 @@ func Validate(scmId string, req *http.Request) ([]byte, error) { return resBody, err } -func GetSCMOrg(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GetNotifyCommit(req *http.Request) ([]byte, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetSCMOrgUrl+req.URL.RawQuery, scmId) + req.Method = http.MethodGet - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetNotifyCommit(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -344,15 +603,9 @@ func GetSCMOrg(scmId string, req *http.Request) ([]byte, error) { return res, err } -func GetOrgRepo(scmId, organizationId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GithubWebhook(req *http.Request) ([]byte, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetOrgRepoUrl+req.URL.RawQuery, scmId, organizationId) - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GithubWebhook(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -361,176 +614,9 @@ func GetOrgRepo(scmId, organizationId string, req *http.Request) ([]byte, error) return res, err } -func StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) CheckScriptCompile(projectName, pipelineName string, req *http.Request) (*devops.CheckScript, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+StopBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - - req.Method = http.MethodPut - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func StopPipeline(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+StopPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - req.Method = http.MethodPut - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ReplayBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ReplayPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchRunLogUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetRunLogUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchArtifactsUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetArtifactsUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetPipeBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeBranchUrl, projectName, pipelineName) - - res, err := sendJenkinsRequest(baseUrl+req.URL.RawQuery, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId) - - newBody, err := getInputReqBody(req.Body) - if err != nil { - klog.Error(err) - return nil, err - } - req.Body = newBody - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.CheckScriptCompile(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -539,36 +625,47 @@ func SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, return resBody, err } -func SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) CheckCron(projectName string, req *http.Request) (*devops.CheckCronRes, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId, stepId) + res, err := d.devopsClient.CheckCron(projectName, convertToHttpParameters(req)) - newBody, err := getInputReqBody(req.Body) - if err != nil { - klog.Error(err) - return nil, err - } - req.Body = newBody - resBody, err := sendJenkinsRequest(baseUrl, req) if err != nil { klog.Error(err) return nil, err } - return resBody, err + return res, err +} + +func (d devopsOperator) ToJenkinsfile(req *http.Request) (*devops.ResJenkinsfile, error) { + + res, err := d.devopsClient.ToJenkinsfile(convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err +} + +func (d devopsOperator) ToJson(req *http.Request) (*devops.ResJson, error) { + + res, err := d.devopsClient.ToJson(convertToHttpParameters(req)) + if err != nil { + klog.Error(err) + return nil, err + } + + return res, err } func getInputReqBody(reqBody io.ReadCloser) (newReqBody io.ReadCloser, err error) { - var checkBody CheckPlayload + var checkBody devops.CheckPlayload var jsonBody []byte var workRound struct { - ID string `json:"id,omitempty" description:"id"` - Parameters []CheckPlayloadParameters `json:"parameters"` - Abort bool `json:"abort,omitempty" description:"abort or not"` + ID string `json:"id,omitempty" description:"id"` + Parameters []devops.CheckPlayloadParameters `json:"parameters"` + Abort bool `json:"abort,omitempty" description:"abort or not"` } Body, err := ioutil.ReadAll(reqBody) @@ -580,7 +677,7 @@ func getInputReqBody(reqBody io.ReadCloser) (newReqBody io.ReadCloser, err error err = json.Unmarshal(Body, &checkBody) if checkBody.Abort != true && checkBody.Parameters == nil { - workRound.Parameters = []CheckPlayloadParameters{} + workRound.Parameters = []devops.CheckPlayloadParameters{} workRound.ID = checkBody.ID jsonBody, _ = json.Marshal(workRound) } else { @@ -600,550 +697,3 @@ func parseBody(body io.Reader) (newReqBody io.ReadCloser) { } return rc } - -func GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetConsoleLogUrl+req.URL.RawQuery, projectName, pipelineName) - - resBody, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return resBody, err -} - -func ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ScanBranchUrl+req.URL.RawQuery, projectName, pipelineName) - - resBody, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return resBody, err -} - -func RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+RunBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func RunPipeline(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+RunPipelineUrl+req.URL.RawQuery, projectName, pipelineName) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetCrumb(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server + GetCrumbUrl) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func CheckScriptCompile(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckScriptCompileUrl, projectName, pipelineName) - - resBody, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return resBody, err -} - -func CheckCron(projectName string, req *http.Request) (*CheckCronRes, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkins := devops.Jenkins() - - var res = new(CheckCronRes) - var cron = new(CronData) - var reader io.ReadCloser - var baseUrl string - - reader = req.Body - cronData, err := ioutil.ReadAll(reader) - json.Unmarshal(cronData, cron) - - if cron.PipelineName != "" { - baseUrl = fmt.Sprintf(jenkins.Server+CheckPipelienCronUrl, projectName, cron.PipelineName, cron.Cron) - } else { - baseUrl = fmt.Sprintf(jenkins.Server+CheckCronUrl, projectName, cron.Cron) - } - - newUrl, err := url.Parse(baseUrl) - if err != nil { - klog.Error(err) - return nil, err - } - newUrl.RawQuery = newUrl.Query().Encode() - - reqJenkins := &http.Request{ - Method: http.MethodGet, - URL: newUrl, - Header: req.Header, - } - - client := &http.Client{Timeout: 30 * time.Second} - - resp, err := client.Do(reqJenkins) - - if resp != nil && resp.StatusCode != http.StatusOK { - resBody, _ := getRespBody(resp) - return &CheckCronRes{ - Result: "error", - Message: string(resBody), - }, err - } - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() - - doc, err := goquery.NewDocumentFromReader(resp.Body) - if err != nil { - klog.Error(err) - return nil, err - } - doc.Find("div").Each(func(i int, selection *goquery.Selection) { - res.Message = selection.Text() - res.Result, _ = selection.Attr("class") - }) - if res.Result == "ok" { - res.LastTime, res.NextTime, err = parseCronJobTime(res.Message) - if err != nil { - klog.Error(err) - return nil, err - } - } - - return res, err -} - -func parseCronJobTime(msg string) (string, string, error) { - - times := strings.Split(msg, ";") - - lastTmp := strings.Split(times[0], " ") - lastCount := len(lastTmp) - lastTmp = lastTmp[lastCount-7 : lastCount-1] - lastTime := strings.Join(lastTmp, " ") - lastUinx, err := time.Parse(cronJobLayout, lastTime) - if err != nil { - klog.Error(err) - return "", "", err - } - last := lastUinx.Format(time.RFC3339) - - nextTmp := strings.Split(times[1], " ") - nextCount := len(nextTmp) - nextTmp = nextTmp[nextCount-7 : nextCount-1] - nextTime := strings.Join(nextTmp, " ") - nextUinx, err := time.Parse(cronJobLayout, nextTime) - if err != nil { - klog.Error(err) - return "", "", err - } - next := nextUinx.Format(time.RFC3339) - - return last, next, nil -} - -func GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipelineRunUrl, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchPipeUrl, projectName, pipelineName, branchName) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeRunNodesUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchNodeStepsUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetNodeStepsUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func ToJenkinsfile(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server + ToJenkinsfileUrl) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func ToJson(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server + ToJsonUrl) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetNotifyCommit(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprint(devops.Jenkins().Server, GetNotifyCommitUrl, req.URL.RawQuery) - req.Method = "GET" - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GithubWebhook(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprint(devops.Jenkins().Server, GithubWebhookUrl, req.URL.RawQuery) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} - -func GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]NodesDetail, error) { - var wg sync.WaitGroup - var nodesDetails []NodesDetail - stepChan := make(chan *NodesStepsIndex, channelMaxCapacity) - - respNodes, err := GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId, req) - if err != nil { - klog.Error(err) - return nil, err - } - err = json.Unmarshal(respNodes, &nodesDetails) - if err != nil { - klog.Error(err) - return nil, err - } - - // get all steps in nodes. - for i, v := range nodesDetails { - wg.Add(1) - go func(nodeId string, index int) { - var steps []NodeSteps - respSteps, err := GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req) - if err != nil { - klog.Error(err) - return - } - err = json.Unmarshal(respSteps, &steps) - - stepChan <- &NodesStepsIndex{index, steps} - wg.Done() - }(v.ID, i) - } - - wg.Wait() - close(stepChan) - - for oneNodeSteps := range stepChan { - if oneNodeSteps != nil { - nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) - } - } - - return nodesDetails, err -} - -func GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]NodesDetail, error) { - var wg sync.WaitGroup - var nodesDetails []NodesDetail - stepChan := make(chan *NodesStepsIndex, channelMaxCapacity) - - respNodes, err := GetPipelineRunNodes(projectName, pipelineName, runId, req) - if err != nil { - klog.Error(err) - return nil, err - } - err = json.Unmarshal(respNodes, &nodesDetails) - if err != nil { - klog.Error(err) - return nil, err - } - - // get all steps in nodes. - for i, v := range nodesDetails { - wg.Add(1) - go func(nodeId string, index int) { - var steps []NodeSteps - respSteps, err := GetNodeSteps(projectName, pipelineName, runId, nodeId, req) - if err != nil { - klog.Error(err) - return - } - err = json.Unmarshal(respSteps, &steps) - - stepChan <- &NodesStepsIndex{index, steps} - wg.Done() - }(v.ID, i) - } - - wg.Wait() - close(stepChan) - - for oneNodeSteps := range stepChan { - if oneNodeSteps != nil { - nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) - } - } - - return nodesDetails, err -} - -// create jenkins request -func sendJenkinsRequest(baseUrl string, req *http.Request) ([]byte, error) { - resBody, _, err := jenkinsClient(baseUrl, req) - return resBody, err -} - -func jenkinsClient(baseUrl string, req *http.Request) ([]byte, http.Header, error) { - newReqUrl, err := url.Parse(baseUrl) - if err != nil { - klog.Error(err) - return nil, nil, err - } - - client := &http.Client{Timeout: 30 * time.Second} - - newRequest := &http.Request{ - Method: req.Method, - URL: newReqUrl, - Header: req.Header, - Body: req.Body, - Form: req.Form, - PostForm: req.PostForm, - } - - resp, err := client.Do(newRequest) - if err != nil { - klog.Error(err) - return nil, nil, err - } - - resBody, _ := getRespBody(resp) - defer resp.Body.Close() - - if resp.StatusCode >= http.StatusBadRequest { - klog.Errorf("%+v", string(resBody)) - jkerr := new(JkError) - jkerr.Code = resp.StatusCode - jkerr.Message = string(resBody) - return nil, nil, jkerr - } - - return resBody, resp.Header, nil - -} - -// Decompress response.body of JenkinsAPIResponse -func getRespBody(resp *http.Response) ([]byte, error) { - var reader io.ReadCloser - if resp.Header.Get("Content-Encoding") == "gzip" { - reader, _ = gzip.NewReader(resp.Body) - } else { - reader = resp.Body - } - resBody, err := ioutil.ReadAll(reader) - if err != nil { - klog.Error(err) - return nil, err - } - return resBody, err - -} - -// parseJenkinsQuery Parse the special query of jenkins. -// ParseQuery in the standard library makes the query not re-encode -func parseJenkinsQuery(query string) (url.Values, error) { - m := make(url.Values) - err := error(nil) - for query != "" { - key := query - if i := strings.IndexAny(key, "&"); i >= 0 { - key, query = key[:i], key[i+1:] - } else { - query = "" - } - if key == "" { - continue - } - value := "" - if i := strings.Index(key, "="); i >= 0 { - key, value = key[:i], key[i+1:] - } - key, err1 := url.QueryUnescape(key) - if err1 != nil { - if err == nil { - err = err1 - } - continue - } - value, err1 = url.QueryUnescape(value) - if err1 != nil { - if err == nil { - err = err1 - } - continue - } - m[key] = append(m[key], value) - } - return m, err -} diff --git a/pkg/models/devops/devops_test.go b/pkg/models/devops/devops_test.go index 3749d6041..4d82e5b41 100644 --- a/pkg/models/devops/devops_test.go +++ b/pkg/models/devops/devops_test.go @@ -1,40 +1,113 @@ package devops import ( + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "net/http" "testing" ) -func Test_parseCronJobTime(t *testing.T) { - type Except struct { - Last string - Next string +const baseUrl = "http://127.0.0.1/kapis/devops.kubesphere.io/v1alpha2/" + +func TestGetNodesDetail(t *testing.T) { + fakeData := make(map[string]interface{}) + PipelineRunNodes := []devops.PipelineRunNodes{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "2", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "3", + Result: "SUCCESS", + }, } - Items := []struct { - Input string - Expected Except - }{ - {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 8:59:09 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:14:09 AM UTC.", Except{Last: "2019-09-10T08:59:09Z", Next: "2019-09-10T09:14:09Z"}}, - {"上次è¿è¡Œçš„æ—¶é—´ Thursday, January 3, 2019 11:56:30 PM UTC; 下次è¿è¡Œçš„æ—¶é—´ Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}}, - {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 8:41:34 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, - {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:15:26 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, - {"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, - {"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, + NodeSteps := []devops.NodeSteps{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, } - for _, item := range Items { - last, next, err := parseCronJobTime(item.Input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } + fakeData["project1-pipeline1-run1"] = PipelineRunNodes + fakeData["project1-pipeline1-run1-1"] = NodeSteps + fakeData["project1-pipeline1-run1-2"] = NodeSteps + fakeData["project1-pipeline1-run1-3"] = NodeSteps - if last != item.Expected.Last { - t.Errorf("got %#v, expected %#v", last, item.Expected.Last) - } + devopsClient := fake.NewFakeDevops(fakeData) - if next != item.Expected.Next { - t.Errorf("got %#v, expected %#v", next, item.Expected.Next) - } + devopsOperator := NewDevopsOperator(devopsClient) + httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/runs/run1/nodesdetail/?limit=10000", nil) + + nodesDetails, err := devopsOperator.GetNodesDetail("project1", "pipeline1", "run1", httpReq) + if err != nil || nodesDetails == nil { + t.Fatalf("should not get error %+v", err) + } + + for _, v := range nodesDetails { + if v.Steps[0].ID == "" { + t.Fatalf("Can not get any step.") + } + } +} + +func TestGetBranchNodesDetail(t *testing.T) { + fakeData := make(map[string]interface{}) + + BranchPipelineRunNodes := []devops.BranchPipelineRunNodes{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "2", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "3", + Result: "SUCCESS", + }, + } + + BranchNodeSteps := []devops.NodeSteps{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + } + + fakeData["project1-pipeline1-branch1-run1"] = BranchPipelineRunNodes + fakeData["project1-pipeline1-branch1-run1-1"] = BranchNodeSteps + fakeData["project1-pipeline1-branch1-run1-2"] = BranchNodeSteps + fakeData["project1-pipeline1-branch1-run1-3"] = BranchNodeSteps + + devopsClient := fake.NewFakeDevops(fakeData) + + devopsOperator := NewDevopsOperator(devopsClient) + + httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/branchs/branch1/runs/run1/nodesdetail/?limit=10000", nil) + + nodesDetails, err := devopsOperator.GetBranchNodesDetail("project1", "pipeline1", "branch1", "run1", httpReq) + if err != nil || nodesDetails == nil { + t.Fatalf("should not get error %+v", err) + } + + for _, v := range nodesDetails { + if v.Steps[0].ID == "" { + t.Fatalf("Can not get any step.") + } } } diff --git a/pkg/models/devops/json.go b/pkg/models/devops/json.go deleted file mode 100644 index 05ec9808e..000000000 --- a/pkg/models/devops/json.go +++ /dev/null @@ -1,1147 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 devops - -// GetPipeline & SearchPipelines -type Pipeline struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability." ` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Scm struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"scm,omitempty"` - Branches struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"branches,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Runs struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"runs,omitempty"` - Trends struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"trends,omitempty"` - Queue struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"queue,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource."` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` - Disabled interface{} `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action."` - DisplayName string `json:"displayName,omitempty" description:"display name"` - FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` - FullName string `json:"fullName,omitempty" description:"full name"` - Name string `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Parameters interface{} `json:"parameters,omitempty" description:"parameters of pipeline, a pipeline can define list of parameters pipeline job expects."` - Permissions struct { - Create bool `json:"create,omitempty" description:"create action"` - Configure bool `json:"configure,omitempty" description:"configure action"` - Read bool `json:"read,omitempty" description:"read action"` - Start bool `json:"start,omitempty" description:"start action"` - Stop bool `json:"stop,omitempty" description:"stop action"` - } `json:"permissions,omitempty" description:"permissions"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` - NumberOfFolders int `json:"numberOfFolders,omitempty" description:"number of folders"` - NumberOfPipelines int `json:"numberOfPipelines,omitempty" description:"number of pipelines"` - PipelineFolderNames []interface{} `json:"pipelineFolderNames,omitempty" description:"pipeline folder names"` - WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline activity"` - BranchNames []string `json:"branchNames,omitempty" description:"branch names"` - NumberOfFailingBranches int `json:"numberOfFailingBranches,omitempty" description:"number of failing branches"` - NumberOfFailingPullRequests int `json:"numberOfFailingPullRequests,omitempty" description:"number of failing pull requests"` - NumberOfSuccessfulBranches int `json:"numberOfSuccessfulBranches,omitempty" description:"number of successful pull requests"` - NumberOfSuccessfulPullRequests int `json:"numberOfSuccessfulPullRequests,omitempty" description:"number of successful pull requests"` - ScmSource struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - APIURL interface{} `json:"apiUrl,omitempty" description:"api url"` - ID string `json:"id,omitempty" description:"The id of the source configuration management (SCM)."` - } `json:"scmSource,omitempty"` - TotalNumberOfBranches int `json:"totalNumberOfBranches,omitempty" description:"total number of branches"` - TotalNumberOfPullRequests int `json:"totalNumberOfPullRequests,omitempty" description:"total number of pull requests"` -} - -// GetPipeBranchRun & SearchPipelineRuns -type BranchPipelineRun struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - PrevRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"prevRun,omitempty"` - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Nodes struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nodes,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - NextRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nextRun,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description of resource"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline name"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"source type, such as \"WorkflowRun\""` - Branch struct { - IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` - Issues []interface{} `json:"issues,omitempty" description:"issues"` - URL string `json:"url,omitempty" description:"url"` - } `json:"branch,omitempty"` - CommitID string `json:"commitId,omitempty" description:"commit id"` - CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url "` - PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` -} - -// GetBranchPipeRunNodes -type BranchPipelineRunNodes struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"source type, e.g. \"WorkflowRun\""` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Edges []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ID string `json:"id,omitempty" description:"id"` - Type string `json:"type,omitempty" description:"source type"` - } `json:"edges,omitempty"` - FirstParent interface{} `json:"firstParent,omitempty" description:"first parent resource"` - Restartable bool `json:"restartable,omitempty" description:"restartable or not"` - Steps []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - } `json:"_links,omitempty"` - Actions []struct { - Class string `json:"_class,omitempty"` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty"` - URLName string `json:"urlName,omitempty"` - } `json:"actions,omitempty" description:"references the reachable path to this resource"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"result"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"source type"` - } `json:"steps,omitempty"` -} - -// Validate -type Validates struct { - CredentialID string `json:"credentialId,omitempty" description:"the id of credential"` -} - -// GetSCMOrg -type SCMOrg struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Repositories struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Href string `json:"href,omitempty" description:"url in api"` - } `json:"repositories,omitempty"` - Self struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Href string `json:"href,omitempty" description:"self url in api"` - } `json:"self,omitempty" description:"scm org self info"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Avatar string `json:"avatar,omitempty" description:"the url of organization avatar"` - JenkinsOrganizationPipeline bool `json:"jenkinsOrganizationPipeline,omitempty" description:"weather or not already have jenkins pipeline."` - Name string `json:"name,omitempty" description:"organization name"` -} - -type SCMServer struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Href string `json:"href,omitempty" description:"self url in api"` - } `json:"self,omitempty" description:"scm server self info"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - ID string `json:"id,omitempty" description:"server id of scm server"` - Name string `json:"name,omitempty" description:"name of scm server"` - ApiURL string `json:"apiUrl,omitempty" description:"url of scm server"` -} - -// GetOrgRepo -type OrgRepo struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Repositories struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Items []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - DefaultBranch string `json:"defaultBranch,omitempty" description:"default branch"` - Description string `json:"description,omitempty" description:"description"` - Name string `json:"name,omitempty" description:"name"` - Permissions struct { - Admin bool `json:"admin,omitempty" description:"admin"` - Push bool `json:"push,omitempty" description:"push action"` - Pull bool `json:"pull,omitempty" description:"pull action"` - } `json:"permissions,omitempty"` - Private bool `json:"private,omitempty" description:"private or not"` - FullName string `json:"fullName,omitempty" description:"full name"` - } `json:"items,omitempty"` - LastPage interface{} `json:"lastPage,omitempty" description:"last page"` - NextPage interface{} `json:"nextPage,omitempty" description:"next page"` - PageSize int `json:"pageSize,omitempty" description:"page size"` - } `json:"repositories,omitempty"` -} - -// StopPipeline -type StopPipe struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Nodes struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nodes,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - Branch struct { - IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` - Issues []interface{} `json:"issues,omitempty" description:"issues"` - URL string `json:"url,omitempty" description:"url"` - } `json:"branch,omitempty"` - CommitID string `json:"commitId,omitempty" description:"commit id"` - CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url"` - PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` -} - -// ReplayPipeline -type ReplayPipe struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage string `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis interface{} `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime interface{} `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime interface{} `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis interface{} `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary interface{} `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime interface{} `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - QueueID string `json:"queueId,omitempty" description:"queue id"` -} - -// GetArtifacts -type Artifacts struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Downloadable bool `json:"downloadable,omitempty" description:"downloadable or not"` - ID string `json:"id,omitempty" description:"id"` - Name string `json:"name,omitempty" description:"name"` - Path string `json:"path,omitempty" description:"path"` - Size int `json:"size,omitempty" description:"size"` - URL string `json:"url,omitempty" description:"The url for Download artifacts"` -} - -// GetPipeBranch -type PipeBranch struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Scm struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"scm,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Runs struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"runs,omitempty"` - Trends struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"trends,omitempty"` - Queue struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"queue,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` - Disabled bool `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` - FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` - FullName string `json:"fullName,omitempty" description:"full name"` - LatestRun struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - PrevRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"prevRun,omitempty"` - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile string `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"start run"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - } `json:"latestRun,omitempty"` - Name string `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Parameters []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - DefaultParameterValue struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Name string `json:"name,omitempty" description:"name"` - Value string `json:"value,omitempty" description:"value"` - } `json:"defaultParameterValue,omitempty"` - Description string `json:"description,omitempty" description:"description"` - Name string `json:"name,omitempty" description:"name"` - Type string `json:"type,omitempty" description:"type"` - } `json:"parameters,omitempty"` - Permissions struct { - Create bool `json:"create,omitempty" description:"create action"` - Configure bool `json:"configure,omitempty" description:"configure action"` - Read bool `json:"read,omitempty" description:"read action"` - Start bool `json:"start,omitempty" description:"start action"` - Stop bool `json:"stop,omitempty" description:"stop action"` - } `json:"permissions,omitempty"` - WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline"` - Branch struct { - IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` - Issues []interface{} `json:"issues,omitempty" description:"issues"` - URL string `json:"url,omitempty" description:"url"` - } `json:"branch,omitempty"` -} - -// RunPipeline -type RunPayload struct { - Parameters []struct { - Name string `json:"name,omitempty" description:"name"` - Value string `json:"value,omitempty" description:"value"` - } `json:"parameters,omitempty"` -} - -type QueuedBlueRun struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage string `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis interface{} `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime interface{} `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime interface{} `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis interface{} `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary interface{} `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime interface{} `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - QueueID string `json:"queueId,omitempty" description:"queue id"` -} - -// GetNodeStatus -type NodeStatus struct { - Class string `json:"_class,omitempty" description:""` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Edges []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ID string `json:"id,omitempty" description:"id"` - Type string `json:"type,omitempty" description:"type"` - } `json:"edges,omitempty"` - FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` - Restartable bool `json:"restartable,omitempty" description:"restartable or not"` - Steps []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []struct { - Class string `json:"_class,omitempty" description:"references the reachable path to this resource"` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty" description:""` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - URLName string `json:"urlName,omitempty" description:"url name"` - } `json:"actions,omitempty"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - } `json:"steps,omitempty"` -} - -// CheckPipeline -type CheckPlayload struct { - ID string `json:"id,omitempty" description:"id"` - Parameters []CheckPlayloadParameters `json:"parameters,omitempty"` - Abort bool `json:"abort,omitempty" description:"abort or not"` -} - -type CreateScmServerReq struct { - Name string `json:"name,omitempty" description:"name of scm server"` - ApiURL string `json:"apiUrl,omitempty" description:"url of scm server"` -} - -type CheckPlayloadParameters struct { - Name string `json:"name,omitempty" description:"name"` - Value interface{} `json:"value,omitempty" description:"value"` -} - -// Getcrumb -type Crumb struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Crumb string `json:"crumb,omitempty" description:"crumb data"` - CrumbRequestField string `json:"crumbRequestField,omitempty" description:"crumb request field"` -} - -// CheckScriptCompile -type CheckScript struct { - Column int `json:"column,omitempty" description:"column e.g. 0"` - Line int `json:"line,omitempty" description:"line e.g. 0"` - Message string `json:"message,omitempty" description:"message e.g. unexpected char: '#'"` - Status string `json:"status,omitempty" description:"status e.g. fail"` -} - -// CheckCron -type CronData struct { - PipelineName string `json:"pipelineName,omitempty" description:"Pipeline name, if pipeline haven't created, not required'"` - Cron string `json:"cron" description:"Cron script data."` -} - -type CheckCronRes struct { - Result string `json:"result,omitempty" description:"result e.g. ok, error"` - Message string `json:"message,omitempty" description:"message"` - LastTime string `json:"lastTime,omitempty" description:"last run time."` - NextTime string `json:"nextTime,omitempty" description:"next run time."` -} - -// GetPipelineRun -type PipelineRun struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - PrevRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"prevRun,omitempty"` - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Nodes struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"nodes,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"the name of pipeline"` - Replayable bool `json:"replayable,omitempty" description:"replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - Branch interface{} `json:"branch,omitempty" description:"branch"` - CommitID interface{} `json:"commitId,omitempty" description:"commit id"` - CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url"` - PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` -} - -// GetBranchPipeRun -type BranchPipeline struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Scm struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"scm,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Runs struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"runs,omitempty"` - Trends struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"trends,omitempty"` - Queue struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"queue,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - Disabled bool `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` - FullName string `json:"fullName,omitempty" description:"full name"` - LatestRun struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - PrevRun struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"prevRun,omitempty"` - Parent struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"parent,omitempty"` - Tests struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"tests,omitempty"` - Log struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"log,omitempty"` - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - BlueTestSummary struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"blueTestSummary,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Artifacts struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"artifacts,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - ArtifactsZipFile string `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Causes []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ShortDescription string `json:"shortDescription,omitempty" description:"short description"` - UserID string `json:"userId,omitempty" description:"user id"` - UserName string `json:"userName,omitempty" description:"user name"` - } `json:"causes,omitempty"` - ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` - Description interface{} `json:"description,omitempty" description:"description"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` - EndTime string `json:"endTime,omitempty" description:"the time of end"` - EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Name interface{} `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Pipeline string `json:"pipeline,omitempty" description:"pipeline"` - Replayable bool `json:"replayable,omitempty" description:"Replayable or not"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` - Type string `json:"type,omitempty" description:"type"` - } `json:"latestRun,omitempty"` - Name string `json:"name,omitempty" description:"name"` - Organization string `json:"organization,omitempty" description:"the name of organization"` - Parameters []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - DefaultParameterValue struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Name string `json:"name,omitempty" description:"name"` - Value string `json:"value,omitempty" description:"value"` - } `json:"defaultParameterValue,omitempty" description:""` - Description string `json:"description,omitempty" description:"description"` - Name string `json:"name,omitempty" description:"name"` - Type string `json:"type,omitempty" description:"type"` - } `json:"parameters,omitempty"` - Permissions struct { - Create bool `json:"create,omitempty" description:"create action"` - Configure bool `json:"configure,omitempty" description:"configure action"` - Read bool `json:"read,omitempty" description:"read action"` - Start bool `json:"start,omitempty" description:"start action"` - Stop bool `json:"stop,omitempty" description:"stop action"` - } `json:"permissions,omitempty"` - WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline"` - Branch struct { - IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` - Issues []interface{} `json:"issues,omitempty" description:"issues"` - URL string `json:"url,omitempty" description:"url"` - } `json:"branch,omitempty"` -} - -// GetPipelineRunNodes -type PipelineRunNodes struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. FINISHED"` - Type string `json:"type,omitempty" description:"type"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Edges []interface{} `json:"edges,omitempty" description:"edges"` - FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` - Restartable bool `json:"restartable,omitempty" description:"restartable or not"` -} - -// GetNodeSteps -type NodeSteps struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - URLName string `json:"urlName,omitempty" description:"url name"` - } `json:"actions,omitempty"` - DisplayDescription string `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of starts"` - State string `json:"state,omitempty" description:"run state. e.g. SKIPPED"` - Type string `json:"type,omitempty" description:"type"` -} - -// CheckScriptCompile -type ReqScript struct { - Value string `json:"value,omitempty" description:"Pipeline script data"` -} - -// ToJenkinsfile requests -type ReqJson struct { - Json string `json:"json,omitempty" description:"json data"` -} - -// ToJenkinsfile response -type ResJenkinsfile struct { - Status string `json:"status,omitempty" description:"status e.g. ok"` - Data struct { - Result string `json:"result,omitempty" description:"result e.g. success"` - Jenkinsfile string `json:"jenkinsfile,omitempty" description:"jenkinsfile"` - Errors []struct { - Location []string `json:"location,omitempty" description:"err location"` - Error string `json:"error,omitempty" description:"error message"` - } `json:"errors,omitempty"` - } `json:"data,omitempty"` -} - -type ReqJenkinsfile struct { - Jenkinsfile string `json:"jenkinsfile,omitempty" description:"jenkinsfile"` -} - -type ResJson struct { - Status string `json:"status,omitempty" description:"status e.g. ok"` - Data struct { - Result string `json:"result,omitempty" description:"result e.g. success"` - JSON struct { - Pipeline struct { - Stages []interface{} `json:"stages,omitempty" description:"stages"` - Agent struct { - Type string `json:"type,omitempty" description:"type"` - Arguments []struct { - Key string `json:"key,omitempty" description:"key"` - Value struct { - IsLiteral bool `json:"isLiteral,omitempty" description:"is literal or not"` - Value string `json:"value,omitempty" description:"value"` - } `json:"value,omitempty"` - } `json:"arguments,omitempty"` - } `json:"agent,omitempty"` - } `json:"pipeline,omitempty"` - } `json:"json,omitempty"` - } `json:"data,omitempty"` -} - -type NodesDetail struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links struct { - Self struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - Actions struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"actions,omitempty"` - Steps struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"steps,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` - DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` - DisplayName string `json:"displayName,omitempty" description:"display name"` - DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` - ID string `json:"id,omitempty" description:"id"` - Input *Input `json:"input,omitempty" description:"the action should user input"` - Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` - StartTime string `json:"startTime,omitempty" description:"the time of start"` - State string `json:"state,omitempty" description:"run state. e.g. SKIPPED"` - Type string `json:"type,omitempty" description:"type"` - CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Edges []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ID string `json:"id,omitempty" description:"id"` - Type string `json:"type,omitempty" description:"type"` - } `json:"edges,omitempty"` - FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` - Restartable bool `json:"restartable,omitempty" description:"restartable or not"` - Steps []NodeSteps `json:"steps,omitempty" description:"steps"` -} - -type NodesStepsIndex struct { - Id int `json:"id,omitempty" description:"id"` - Steps []NodeSteps `json:"steps,omitempty" description:"steps"` -} - -type Input struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - Links *struct { - Self *struct { - Class string `json:"_class,omitempty"` - Href string `json:"href,omitempty"` - } `json:"self,omitempty"` - } `json:"_links,omitempty" description:"references the reachable path to this resource"` - ID string `json:"id,omitempty" description:"the id of check action"` - Message string `json:"message,omitempty" description:"the message of check action"` - Ok string `json:"ok,omitempty" description:"check status. e.g. \"Proceed\""` - Parameters []interface{} `json:"parameters,omitempty" description:"the parameters of check action"` - Submitter interface{} `json:"submitter,omitempty" description:"check submitter"` -} diff --git a/pkg/models/devops/membership.go b/pkg/models/devops/membership.go deleted file mode 100644 index 57cf46284..000000000 --- a/pkg/models/devops/membership.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -const ( - DevOpsProjectMembershipTableName = "project_membership" - DevOpsProjectMembershipUsernameColumn = "project_membership.username" - DevOpsProjectMembershipProjectIdColumn = "project_membership.project_id" - DevOpsProjectMembershipRoleColumn = "project_membership.role" -) - -type DevOpsProjectMembership struct { - Username string `json:"username" description:"Member's username,username can uniquely identify a user"` - ProjectId string `json:"project_id" db:"project_id" description:"the DevOps Projects which project membership belongs to"` - Role string `json:"role" description:"DevOps Project membership's role type. e.g. owner '"` - Status string `json:"status" description:"Deprecated, Status of project membership. e.g. active "` - GrantBy string `json:"grand_by,omitempty" description:"Username of the user who assigned the role"` -} - -var DevOpsProjectMembershipColumns = GetColumnsFromStruct(&DevOpsProjectMembership{}) - -func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *DevOpsProjectMembership { - return &DevOpsProjectMembership{ - Username: username, - ProjectId: projectId, - Role: role, - Status: StatusActive, - GrantBy: grantBy, - } -} diff --git a/pkg/models/devops/project_credential.go b/pkg/models/devops/project_credential.go deleted file mode 100644 index 7f64a13dd..000000000 --- a/pkg/models/devops/project_credential.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "github.com/asaskevich/govalidator" - "time" -) - -const ( - CredentialTypeUsernamePassword = "username_password" - CredentialTypeSsh = "ssh" - CredentialTypeSecretText = "secret_text" - CredentialTypeKubeConfig = "kubeconfig" -) - -type JenkinsCredential struct { - Id string `json:"id" description:"Id of Credential, e.g. dockerhub-id"` - Type string `json:"type" description:"Type of Credential, e.g. ssh/kubeconfig"` - DisplayName string `json:"display_name,omitempty" description:"Credential's display name"` - Fingerprint *struct { - FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` - Hash string `json:"hash,omitempty" description:"Credential's hash"` - Usage []*struct { - Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` - Ranges struct { - Ranges []*struct { - Start int `json:"start,omitempty" description:"Start build number"` - End int `json:"end,omitempty" description:"End build number"` - } `json:"ranges,omitempty"` - } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` - } `json:"usage,omitempty" description:"all usage of Credential"` - } `json:"fingerprint,omitempty" description:"usage of the Credential"` - Description string `json:"description,omitempty" description:"Credential's description'"` - Domain string `json:"domain,omitempty" description:"Credential's domain,In ks we only use the default domain, default '_''"` - CreateTime *time.Time `json:"create_time,omitempty" description:"Credential's create_time'"` - Creator string `json:"creator,omitempty" description:"Creator's username"` - UsernamePasswordCredential *UsernamePasswordCredential `json:"username_password,omitempty" description:"username password Credential struct"` - SshCredential *SshCredential `json:"ssh,omitempty" description:"ssh Credential struct"` - SecretTextCredential *SecretTextCredential `json:"secret_text,omitempty" description:"secret_text Credential struct"` - KubeconfigCredential *KubeconfigCredential `json:"kubeconfig,omitempty" description:"kubeconfig Credential struct"` -} - -type UsernamePasswordCredential struct { - Username string `json:"username,omitempty" description:"username of username_password credential"` - Password string `json:"password,omitempty" description:"password of username_password credential"` -} - -type SshCredential struct { - Username string `json:"username,omitempty" description:"username of ssh credential"` - Passphrase string `json:"passphrase,omitempty" description:"passphrase of ssh credential, password of ssh credential"` - PrivateKey string `json:"private_key,omitempty" mapstructure:"private_key" description:"private key of ssh credential"` -} - -type SecretTextCredential struct { - Secret string `json:"secret,omitempty" description:"secret content of credential"` -} - -type KubeconfigCredential struct { - Content string `json:"content,omitempty" description:"content of kubeconfig"` -} - -const ( - ProjectCredentialTableName = "project_credential" - ProjectCredentialIdColumn = "credential_id" - ProjectCredentialDomainColumn = "domain" - ProjectCredentialProjectIdColumn = "project_id" -) - -var CredentialTypeMap = map[string]string{ - "SSH Username with private key": CredentialTypeSsh, - "Username with password": CredentialTypeUsernamePassword, - "Secret text": CredentialTypeSecretText, - "Kubernetes configuration (kubeconfig)": CredentialTypeKubeConfig, -} - -type ProjectCredential struct { - ProjectId string `json:"project_id"` - CredentialId string `json:"credential_id"` - Domain string `json:"domain"` - Creator string `json:"creator"` - CreateTime time.Time `json:"create_time"` -} - -var ProjectCredentialColumns = GetColumnsFromStruct(&ProjectCredential{}) - -func NewProjectCredential(projectId, credentialId, domain, creator string) *ProjectCredential { - if govalidator.IsNull(domain) { - domain = "_" - } - return &ProjectCredential{ - ProjectId: projectId, - CredentialId: credentialId, - Domain: domain, - Creator: creator, - CreateTime: time.Now(), - } -} diff --git a/pkg/models/devops/project_credential_handler.go b/pkg/models/devops/project_credential_handler.go index de0e0f8ef..d2cae36d5 100644 --- a/pkg/models/devops/project_credential_handler.go +++ b/pkg/models/devops/project_credential_handler.go @@ -1,488 +1,29 @@ -/* -Copyright 2019 The KubeSphere 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 devops import ( - "fmt" - "github.com/PuerkitoBio/goquery" - "github.com/asaskevich/govalidator" - "github.com/emicklei/go-restful" - "github.com/gocraft/dbr" "k8s.io/klog" - - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "strings" + "kubesphere.io/kubesphere/pkg/simple/client/devops" ) -func CreateProjectCredential(projectId, username string, credentialRequest *JenkinsCredential) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkinsClient := devops.Jenkins() - - err = checkJenkinsCredentialExists(projectId, credentialRequest.Domain, credentialRequest.Id) - if err != nil { - klog.Errorf("%+v", err) - return "", err - } - - switch credentialRequest.Type { - case CredentialTypeUsernamePassword: - if credentialRequest.UsernamePasswordCredential == nil { - err := fmt.Errorf("usename_password should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.CreateUsernamePasswordCredentialInFolder(credentialRequest.Domain, - credentialRequest.Id, - credentialRequest.UsernamePasswordCredential.Username, - credentialRequest.UsernamePasswordCredential.Password, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username) - if err != nil { - klog.Errorf("%+v", err) - return "", err - } - return *credentialId, nil - case CredentialTypeSsh: - if credentialRequest.SshCredential == nil { - err := fmt.Errorf("ssh should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.CreateSshCredentialInFolder(credentialRequest.Domain, - credentialRequest.Id, - credentialRequest.SshCredential.Username, - credentialRequest.SshCredential.Passphrase, - credentialRequest.SshCredential.PrivateKey, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - return *credentialId, nil - case CredentialTypeSecretText: - if credentialRequest.SecretTextCredential == nil { - err := fmt.Errorf("secret_text should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - - credentialId, err := jenkinsClient.CreateSecretTextCredentialInFolder(credentialRequest.Domain, - credentialRequest.Id, - credentialRequest.SecretTextCredential.Secret, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - return *credentialId, nil - case CredentialTypeKubeConfig: - if credentialRequest.KubeconfigCredential == nil { - err := fmt.Errorf("kubeconfig should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.CreateKubeconfigCredentialInFolder(credentialRequest.Domain, - credentialRequest.Id, - credentialRequest.KubeconfigCredential.Content, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - return *credentialId, nil - default: - err := fmt.Errorf("error unsupport credential type") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - - } - +type ProjectCredentialGetter interface { + GetProjectCredentialUsage(projectId, credentialId string) (*devops.Credential, error) } -func UpdateProjectCredential(projectId, credentialId string, credentialRequest *JenkinsCredential) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - jenkinsCredential, err := jenkinsClient.GetCredentialInFolder(credentialRequest.Domain, - credentialId, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - credentialType := CredentialTypeMap[jenkinsCredential.TypeName] - switch credentialType { - case CredentialTypeUsernamePassword: - if credentialRequest.UsernamePasswordCredential == nil { - err := fmt.Errorf("usename_password should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.UpdateUsernamePasswordCredentialInFolder(credentialRequest.Domain, - credentialId, - credentialRequest.UsernamePasswordCredential.Username, - credentialRequest.UsernamePasswordCredential.Password, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return *credentialId, nil - case CredentialTypeSsh: - if credentialRequest.SshCredential == nil { - err := fmt.Errorf("ssh should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.UpdateSshCredentialInFolder(credentialRequest.Domain, - credentialId, - credentialRequest.SshCredential.Username, - credentialRequest.SshCredential.Passphrase, - credentialRequest.SshCredential.PrivateKey, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return *credentialId, nil - case CredentialTypeSecretText: - if credentialRequest.SecretTextCredential == nil { - err := fmt.Errorf("secret_text should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.UpdateSecretTextCredentialInFolder(credentialRequest.Domain, - credentialId, - credentialRequest.SecretTextCredential.Secret, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return *credentialId, nil - case CredentialTypeKubeConfig: - if credentialRequest.KubeconfigCredential == nil { - err := fmt.Errorf("kubeconfig should not be nil") - klog.Error(err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - credentialId, err := jenkinsClient.UpdateKubeconfigCredentialInFolder(credentialRequest.Domain, - credentialId, - credentialRequest.KubeconfigCredential.Content, - credentialRequest.Description, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return *credentialId, nil - default: - err := fmt.Errorf("error unsupport credential type") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - - } - +type projectCredentialGetter struct { + devopsClient devops.Interface } -func DeleteProjectCredential(projectId, credentialId string, credentialRequest *JenkinsCredential) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - _, err = jenkinsClient.GetCredentialInFolder(credentialRequest.Domain, - credentialId, - projectId) +// GetProjectCredentialUsage get the usage of Credential +func (o *projectCredentialGetter) GetProjectCredentialUsage(projectId, credentialId string) (*devops.Credential, error) { + credential, err := o.devopsClient.GetCredentialInProject(projectId, + credentialId) if err != nil { klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return nil, err } - - id, err := jenkinsClient.DeleteCredentialInFolder(credentialRequest.Domain, credentialId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - deleteConditions := append(make([]dbr.Builder, 0), db.Eq(ProjectCredentialProjectIdColumn, projectId)) - deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialIdColumn, credentialId)) - if !govalidator.IsNull(credentialRequest.Domain) { - deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, credentialRequest.Domain)) - } else { - deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_")) - } - - _, err = dbClient.DeleteFrom(ProjectCredentialTableName). - Where(db.And(deleteConditions...)).Exec() - if err != nil && err != db.ErrNotFound { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - return *id, nil - + return credential, nil } -func GetProjectCredential(projectId, credentialId, domain, getContent string) (*JenkinsCredential, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsResponse, err := jenkinsClient.GetCredentialInFolder(domain, - credentialId, - projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - projectCredential := &ProjectCredential{} - err = dbClient.Select(ProjectCredentialColumns...). - From(ProjectCredentialTableName).Where( - db.And(db.Eq(ProjectCredentialProjectIdColumn, projectId), - db.Eq(ProjectCredentialIdColumn, credentialId), - db.Eq(ProjectCredentialDomainColumn, jenkinsResponse.Domain))).LoadOne(projectCredential) - - if err != nil && err != db.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - - response := formatCredentialResponse(jenkinsResponse, projectCredential) - if getContent != "" { - stringBody, err := jenkinsClient.GetCredentialContentInFolder(jenkinsResponse.Domain, credentialId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - stringReader := strings.NewReader(stringBody) - doc, err := goquery.NewDocumentFromReader(stringReader) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - switch response.Type { - case CredentialTypeKubeConfig: - content := &KubeconfigCredential{} - doc.Find("textarea[name*=content]").Each(func(i int, selection *goquery.Selection) { - value := selection.Text() - content.Content = value - }) - response.KubeconfigCredential = content - case CredentialTypeUsernamePassword: - content := &UsernamePasswordCredential{} - doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) { - value, _ := selection.Attr("value") - content.Username = value - }) - - response.UsernamePasswordCredential = content - case CredentialTypeSsh: - content := &SshCredential{} - doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) { - value, _ := selection.Attr("value") - content.Username = value - }) - - doc.Find("textarea[name*=privateKey]").Each(func(i int, selection *goquery.Selection) { - value := selection.Text() - content.PrivateKey = value - }) - response.SshCredential = content - } - } - return response, nil - -} - -func GetProjectCredentials(projectId, domain string) ([]*JenkinsCredential, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsCredentialResponses, err := jenkinsClient.GetCredentialsInFolder(domain, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - selectCondition := db.Eq(ProjectCredentialProjectIdColumn, projectId) - if !govalidator.IsNull(domain) { - selectCondition = db.And(selectCondition, db.Eq(ProjectCredentialDomainColumn, domain)) - } - projectCredentials := make([]*ProjectCredential, 0) - _, err = dbClient.Select(ProjectCredentialColumns...). - From(ProjectCredentialTableName).Where(selectCondition).Load(&projectCredentials) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - response := formatCredentialsResponse(jenkinsCredentialResponses, projectCredentials) - return response, nil -} - -func insertCredentialToDb(projectId, credentialId, domain, username string) error { - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return err - } - - projectCredential := NewProjectCredential(projectId, credentialId, domain, username) - _, err = dbClient.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...). - Record(projectCredential).Exec() - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(http.StatusInternalServerError, err.Error()) - } - return nil -} - -func checkJenkinsCredentialExists(projectId, domain, credentialId string) error { - devops, err := cs.ClientSets().Devops() - if err != nil { - return restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkinsClient := devops.Jenkins() - - credential, err := jenkinsClient.GetCredentialInFolder(domain, credentialId, projectId) - if credential != nil { - err := fmt.Errorf("credential id [%s] has been used", credential.Id) - klog.Warning(err.Error()) - return restful.NewError(http.StatusConflict, err.Error()) - } - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - - return restful.NewError(http.StatusBadRequest, err.Error()) - } - return nil -} - -func formatCredentialResponse( - jenkinsCredentialResponse *gojenkins.CredentialResponse, - dbCredentialResponse *ProjectCredential) *JenkinsCredential { - response := &JenkinsCredential{} - response.Id = jenkinsCredentialResponse.Id - response.Description = jenkinsCredentialResponse.Description - response.DisplayName = jenkinsCredentialResponse.DisplayName - if jenkinsCredentialResponse.Fingerprint != nil && jenkinsCredentialResponse.Fingerprint.Hash != "" { - response.Fingerprint = &struct { - FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` - Hash string `json:"hash,omitempty" description:"Credential's hash"` - Usage []*struct { - Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` - Ranges struct { - Ranges []*struct { - Start int `json:"start,omitempty" description:"Start build number"` - End int `json:"end,omitempty" description:"End build number"` - } `json:"ranges,omitempty"` - } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` - } `json:"usage,omitempty" description:"all usage of Credential"` - }{} - response.Fingerprint.FileName = jenkinsCredentialResponse.Fingerprint.FileName - response.Fingerprint.Hash = jenkinsCredentialResponse.Fingerprint.Hash - for _, usage := range jenkinsCredentialResponse.Fingerprint.Usage { - response.Fingerprint.Usage = append(response.Fingerprint.Usage, usage) - } - } - response.Domain = jenkinsCredentialResponse.Domain - - if dbCredentialResponse != nil { - response.CreateTime = &dbCredentialResponse.CreateTime - response.Creator = dbCredentialResponse.Creator - } - - credentialType, ok := CredentialTypeMap[jenkinsCredentialResponse.TypeName] - if ok { - response.Type = credentialType - return response - } - response.Type = jenkinsCredentialResponse.TypeName - return response -} - -func formatCredentialsResponse(jenkinsCredentialsResponse []*gojenkins.CredentialResponse, - projectCredentials []*ProjectCredential) []*JenkinsCredential { - responseSlice := make([]*JenkinsCredential, 0) - for _, jenkinsCredential := range jenkinsCredentialsResponse { - var dbCredential *ProjectCredential = nil - for _, projectCredential := range projectCredentials { - if projectCredential.CredentialId == jenkinsCredential.Id && - projectCredential.Domain == jenkinsCredential.Domain { - dbCredential = projectCredential - } - } - responseSlice = append(responseSlice, formatCredentialResponse(jenkinsCredential, dbCredential)) - } - return responseSlice +func NewProjectCredentialOperator(devopsClient devops.Interface) ProjectCredentialGetter { + return &projectCredentialGetter{devopsClient: devopsClient} } diff --git a/pkg/models/devops/project_handler.go b/pkg/models/devops/project_handler.go deleted file mode 100644 index 49537baf0..000000000 --- a/pkg/models/devops/project_handler.go +++ /dev/null @@ -1,85 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "github.com/asaskevich/govalidator" - "github.com/emicklei/go-restful" - "github.com/gocraft/dbr" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - "kubesphere.io/kubesphere/pkg/db" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - project := &v1alpha2.DevOpsProject{} - err = dbconn.Select(DevOpsProjectColumns...). - From(DevOpsProjectTableName). - Where(db.Eq(DevOpsProjectIdColumn, projectId)). - LoadOne(project) - if err != nil && err != dbr.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - if err == dbr.ErrNotFound { - klog.Errorf("%+v", err) - - return nil, restful.NewError(http.StatusNotFound, err.Error()) - } - return project, nil -} - -func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - - query := dbconn.Update(DevOpsProjectTableName) - if !govalidator.IsNull(project.Description) { - query.Set(DevOpsProjectDescriptionColumn, project.Description) - } - if !govalidator.IsNull(project.Extra) { - query.Set(DevOpsProjectExtraColumn, project.Extra) - } - if !govalidator.IsNull(project.Name) { - query.Set(DevOpsProjectNameColumn, project.Name) - } - if len(query.UpdateStmt.Value) > 0 { - _, err := query. - Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)).Exec() - - if err != nil { - klog.Errorf("%+v", err) - - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - } - newProject := &v1alpha2.DevOpsProject{} - err = dbconn.Select(DevOpsProjectColumns...). - From(DevOpsProjectTableName). - Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)). - LoadOne(newProject) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - return newProject, nil -} diff --git a/pkg/models/devops/project_member_handler.go b/pkg/models/devops/project_member_handler.go deleted file mode 100644 index 856f1d0a8..000000000 --- a/pkg/models/devops/project_member_handler.go +++ /dev/null @@ -1,381 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 devops - -import ( - "fmt" - "k8s.io/klog" - "net/http" - - "github.com/emicklei/go-restful" - "github.com/gocraft/dbr" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" -) - -func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - memberships := make([]*DevOpsProjectMembership, 0) - var sqconditions []dbr.Builder - sqconditions = append(sqconditions, db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId)) - if keyword := conditions.Match["keyword"]; keyword != "" { - sqconditions = append(sqconditions, db.Like(DevOpsProjectMembershipUsernameColumn, keyword)) - } - query := dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName) - switch orderBy { - case "name": - if reverse { - query.OrderDesc(DevOpsProjectMembershipUsernameColumn) - } else { - query.OrderAsc(DevOpsProjectMembershipUsernameColumn) - } - default: - if reverse { - query.OrderDesc(DevOpsProjectMembershipRoleColumn) - } else { - query.OrderAsc(DevOpsProjectMembershipRoleColumn) - } - } - query.Limit(uint64(limit)) - query.Offset(uint64(offset)) - if len(sqconditions) > 1 { - query.Where(db.And(sqconditions...)) - } else { - query.Where(sqconditions[0]) - } - _, err = query.Load(&memberships) - if err != nil && err != dbr.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - count, err := query.Count() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - result := make([]interface{}, 0) - for _, v := range memberships { - result = append(result, v) - } - - return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil -} - -func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - - member := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And(db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, username))). - LoadOne(&member) - if err != nil && err != dbr.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - if err == dbr.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusNotFound, err.Error()) - } - return member, nil -} - -func AddProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, err - } - jenkinsClient := devops.Jenkins() - if jenkinsClient == nil { - err := fmt.Errorf("could not connect to jenkins") - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) - // if user could be founded in db, user have been added to project - if err == nil { - err = fmt.Errorf("user [%s] have been added to project", member.Username) - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - - if err != db.ErrNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - - globalRole, err := jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - if globalRole == nil { - _, err := jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{ - GlobalRead: true, - }, true) - if err != nil { - klog.Errorf("failed to create jenkins global role %+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - } - err = globalRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = projectRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = pipelineRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - projectMembership := NewDevOpsProjectMemberShip(member.Username, projectId, member.Role, operator) - _, err = dbconn. - InsertInto(DevOpsProjectMembershipTableName). - Columns(DevOpsProjectMembershipColumns...). - Record(projectMembership).Exec() - if err != nil { - klog.Errorf("%+v", err) - err = projectRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = pipelineRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - return projectMembership, nil -} - -func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, err - } - - jenkinsClient := devops.Jenkins() - if jenkinsClient == nil { - err := fmt.Errorf("could not connect to jenkins") - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - oldMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - )).LoadOne(oldMembership) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - - oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = oldProjectRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = oldPipelineRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = projectRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = pipelineRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - _, err = dbconn.Update(DevOpsProjectMembershipTableName). - Set(DevOpsProjectMembershipRoleColumn, member.Role). - Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - )).Exec() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - - responseMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - )).LoadOne(responseMembership) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - return responseMembership, nil -} - -func DeleteProjectMember(projectId, username string) (string, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkinsClient := devops.Jenkins() - - oldMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - )).LoadOne(oldMembership) - if err != nil { - if err != db.ErrNotFound { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } else if err == db.ErrNotFound { - klog.Warningf("user [%s] not found in project", username) - return username, nil - } - } - - if oldMembership.Role == ProjectOwner { - count, err := dbconn.Select(DevOpsProjectMembershipProjectIdColumn). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipRoleColumn, ProjectOwner))).Count() - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - if count == 1 { - err = fmt.Errorf("project must has at least one admin") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } - } - - oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = oldProjectRole.UnAssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = oldPipelineRole.UnAssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - _, err = dbconn.DeleteFrom(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - )).Exec() - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - return username, nil -} diff --git a/pkg/models/devops/project_pipeline.go b/pkg/models/devops/project_pipeline.go deleted file mode 100644 index 1cd5d2ee4..000000000 --- a/pkg/models/devops/project_pipeline.go +++ /dev/null @@ -1,1132 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "fmt" - "github.com/beevik/etree" - "github.com/kubesphere/sonargo/sonar" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/simple/client" - "strconv" - "strings" - "time" -) - -const ( - NoScmPipelineType = "pipeline" - MultiBranchPipelineType = "multi-branch-pipeline" -) - -type Parameters []*Parameter - -var ParameterTypeMap = map[string]string{ - "hudson.model.StringParameterDefinition": "string", - "hudson.model.ChoiceParameterDefinition": "choice", - "hudson.model.TextParameterDefinition": "text", - "hudson.model.BooleanParameterDefinition": "boolean", - "hudson.model.FileParameterDefinition": "file", - "hudson.model.PasswordParameterDefinition": "password", -} - -const ( - SonarAnalysisActionClass = "hudson.plugins.sonar.action.SonarAnalysisAction" - SonarMetricKeys = "alert_status,quality_gate_details,bugs,new_bugs,reliability_rating,new_reliability_rating,vulnerabilities,new_vulnerabilities,security_rating,new_security_rating,code_smells,new_code_smells,sqale_rating,new_maintainability_rating,sqale_index,new_technical_debt,coverage,new_coverage,new_lines_to_cover,tests,duplicated_lines_density,new_duplicated_lines_density,duplicated_blocks,ncloc,ncloc_language_distribution,projects,new_lines" - SonarAdditionalFields = "metrics,periods" -) - -type SonarStatus struct { - Measures *sonargo.MeasuresComponentObject `json:"measures,omitempty"` - Issues *sonargo.IssuesSearchObject `json:"issues,omitempty"` - JenkinsAction *gojenkins.GeneralObj `json:"jenkinsAction,omitempty"` - Task *sonargo.CeTaskObject `json:"task,omitempty"` -} - -type ProjectPipeline struct { - Type string `json:"type" description:"type of devops pipeline, in scm or no scm"` - Pipeline *NoScmPipeline `json:"pipeline,omitempty" description:"no scm pipeline structs"` - MultiBranchPipeline *MultiBranchPipeline `json:"multi_branch_pipeline,omitempty" description:"in scm pipeline structs"` -} - -type NoScmPipeline struct { - Name string `json:"name" description:"name of pipeline"` - Description string `json:"descriptio,omitempty" description:"description of pipeline"` - Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"` - Parameters *Parameters `json:"parameters,omitempty" description:"Parameters define of pipeline,user could pass param when run pipeline"` - DisableConcurrent bool `json:"disable_concurrent,omitempty" mapstructure:"disable_concurrent" description:"Whether to prohibit the pipeline from running in parallel"` - TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"` - RemoteTrigger *RemoteTrigger `json:"remote_trigger,omitempty" mapstructure:"remote_trigger" description:"Remote api define to trigger pipeline run"` - Jenkinsfile string `json:"jenkinsfile,omitempty" description:"Jenkinsfile's content'"` -} - -type MultiBranchPipeline struct { - Name string `json:"name" description:"name of pipeline"` - Description string `json:"descriptio,omitempty" description:"description of pipeline"` - Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"` - TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"` - SourceType string `json:"source_type" description:"type of scm, such as github/git/svn"` - GitSource *GitSource `json:"git_source,omitempty" description:"git scm define"` - GitHubSource *GithubSource `json:"github_source,omitempty" description:"github scm define"` - SvnSource *SvnSource `json:"svn_source,omitempty" description:"multi branch svn scm define"` - SingleSvnSource *SingleSvnSource `json:"single_svn_source,omitempty" description:"single branch svn scm define"` - BitbucketServerSource *BitbucketServerSource `json:"bitbucket_server_source,omitempty" description:"bitbucket server scm defile"` - ScriptPath string `json:"script_path" mapstructure:"script_path" description:"script path in scm"` - MultiBranchJobTrigger *MultiBranchJobTrigger `json:"multibranch_job_trigger,omitempty" mapstructure:"multibranch_job_trigger" description:"Pipeline tasks that need to be triggered when branch creation/deletion"` -} - -type GitSource struct { - ScmId string `json:"scm_id,omitempty" description:"uid of scm"` - Url string `json:"url,omitempty" mapstructure:"url" description:"url of git source"` - CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access git source"` - DiscoverBranches bool `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Whether to discover a branch"` - CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` - RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` -} - -type GithubSource struct { - ScmId string `json:"scm_id,omitempty" description:"uid of scm"` - Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"` - Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"` - CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"` - ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"` - DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"` - DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"` - DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"` - CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` - RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` -} - -type MultiBranchJobTrigger struct { - CreateActionJobsToTrigger string `json:"create_action_job_to_trigger,omitempty" description:"pipeline name to trigger"` - DeleteActionJobsToTrigger string `json:"delete_action_job_to_trigger,omitempty" description:"pipeline name to trigger"` -} - -type BitbucketServerSource struct { - ScmId string `json:"scm_id,omitempty" description:"uid of scm"` - Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"` - Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"` - CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"` - ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"` - DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"` - DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"` - DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"` - CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"` - RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"` -} - -type GitCloneOption struct { - Shallow bool `json:"shallow,omitempty" mapstructure:"shallow" description:"Whether to use git shallow clone"` - Timeout int `json:"timeout,omitempty" mapstructure:"timeout" description:"git clone timeout mins"` - Depth int `json:"depth,omitempty" mapstructure:"depth" description:"git clone depth"` -} - -type SvnSource struct { - ScmId string `json:"scm_id,omitempty" description:"uid of scm"` - Remote string `json:"remote,omitempty" description:"remote address url"` - CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"` - Includes string `json:"includes,omitempty" description:"branches to run pipeline"` - Excludes string `json:"excludes,omitempty" description:"branches do not run pipeline"` -} -type SingleSvnSource struct { - ScmId string `json:"scm_id,omitempty" description:"uid of scm"` - Remote string `json:"remote,omitempty" description:"remote address url"` - CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"` -} - -type DiscoverPRFromForks struct { - Strategy int `json:"strategy,omitempty" mapstructure:"strategy" description:"github discover strategy"` - Trust int `json:"trust,omitempty" mapstructure:"trust" description:"trust user type"` -} - -type DiscarderProperty struct { - DaysToKeep string `json:"days_to_keep,omitempty" mapstructure:"days_to_keep" description:"days to keep pipeline"` - NumToKeep string `json:"num_to_keep,omitempty" mapstructure:"num_to_keep" description:"nums to keep pipeline"` -} - -type Parameter struct { - Name string `json:"name" description:"name of param"` - DefaultValue string `json:"default_value,omitempty" mapstructure:"default_value" description:"default value of param"` - Type string `json:"type" description:"type of param"` - Description string `json:"description,omitempty" description:"description of pipeline"` -} - -type TimerTrigger struct { - // user in no scm job - Cron string `json:"cron,omitempty" description:"jenkins cron script"` - - // use in multi-branch job - Interval string `json:"interval,omitempty" description:"interval ms"` -} - -type RemoteTrigger struct { - Token string `json:"token,omitempty" description:"remote trigger token"` -} - -func replaceXmlVersion(config, oldVersion, targetVersion string) string { - lines := strings.Split(config, "\n") - lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1) - output := strings.Join(lines, "\n") - return output -} - -func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) { - doc := etree.NewDocument() - xmlString := ` - - - - - - - - - - - -` - doc.ReadFromString(xmlString) - flow := doc.SelectElement("flow-definition") - flow.CreateElement("description").SetText(pipeline.Description) - properties := flow.CreateElement("properties") - - if pipeline.DisableConcurrent { - properties.CreateElement("org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") - } - - if pipeline.Discarder != nil { - discarder := properties.CreateElement("jenkins.model.BuildDiscarderProperty") - strategy := discarder.CreateElement("strategy") - strategy.CreateAttr("class", "hudson.tasks.LogRotator") - strategy.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) - strategy.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) - strategy.CreateElement("artifactDaysToKeep").SetText("-1") - strategy.CreateElement("artifactNumToKeep").SetText("-1") - } - if pipeline.Parameters != nil { - pipeline.Parameters.appendToEtree(properties) - } - - if pipeline.TimerTrigger != nil { - triggers := properties. - CreateElement("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"). - CreateElement("triggers") - triggers.CreateElement("hudson.triggers.TimerTrigger").CreateElement("spec").SetText(pipeline.TimerTrigger.Cron) - } - - pipelineDefine := flow.CreateElement("definition") - pipelineDefine.CreateAttr("class", "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition") - pipelineDefine.CreateAttr("plugin", "workflow-cps") - pipelineDefine.CreateElement("script").SetText(pipeline.Jenkinsfile) - - pipelineDefine.CreateElement("sandbox").SetText("true") - - flow.CreateElement("triggers") - - if pipeline.RemoteTrigger != nil { - flow.CreateElement("authToken").SetText(pipeline.RemoteTrigger.Token) - } - flow.CreateElement("disabled").SetText("false") - - doc.Indent(2) - stringXml, err := doc.WriteToString() - if err != nil { - return "", err - } - return replaceXmlVersion(stringXml, "1.0", "1.1"), err -} - -func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { - pipeline := &NoScmPipeline{} - config = replaceXmlVersion(config, "1.1", "1.0") - doc := etree.NewDocument() - err := doc.ReadFromString(config) - if err != nil { - return nil, err - } - flow := doc.SelectElement("flow-definition") - if flow == nil { - return nil, fmt.Errorf("can not find pipeline definition") - } - pipeline.Description = flow.SelectElement("description").Text() - - properties := flow.SelectElement("properties") - if properties. - SelectElement( - "org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") != nil { - pipeline.DisableConcurrent = true - } - if properties.SelectElement("jenkins.model.BuildDiscarderProperty") != nil { - strategy := properties. - SelectElement("jenkins.model.BuildDiscarderProperty"). - SelectElement("strategy") - pipeline.Discarder = &DiscarderProperty{ - DaysToKeep: strategy.SelectElement("daysToKeep").Text(), - NumToKeep: strategy.SelectElement("numToKeep").Text(), - } - } - pipeline.Parameters = &Parameters{} - pipeline.Parameters = pipeline.Parameters.fromEtree(properties) - if len(*pipeline.Parameters) == 0 { - pipeline.Parameters = nil - } - - if triggerProperty := properties. - SelectElement( - "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil { - triggers := triggerProperty.SelectElement("triggers") - if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil { - pipeline.TimerTrigger = &TimerTrigger{ - Cron: timerTrigger.SelectElement("spec").Text(), - } - } - } - if authToken := flow.SelectElement("authToken"); authToken != nil { - pipeline.RemoteTrigger = &RemoteTrigger{ - Token: authToken.Text(), - } - } - if definition := flow.SelectElement("definition"); definition != nil { - if script := definition.SelectElement("script"); script != nil { - pipeline.Jenkinsfile = script.Text() - } - } - return pipeline, nil -} - -func (s *Parameters) appendToEtree(properties *etree.Element) *Parameters { - parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty"). - CreateElement("parameterDefinitions") - for _, parameter := range *s { - for className, typeName := range ParameterTypeMap { - if typeName == parameter.Type { - paramDefine := parameterDefinitions.CreateElement(className) - paramDefine.CreateElement("name").SetText(parameter.Name) - paramDefine.CreateElement("description").SetText(parameter.Description) - switch parameter.Type { - case "choice": - choices := paramDefine.CreateElement("choices") - choices.CreateAttr("class", "java.util.Arrays$ArrayList") - a := choices.CreateElement("a") - a.CreateAttr("class", "string-array") - choiceValues := strings.Split(parameter.DefaultValue, "\n") - for _, choiceValue := range choiceValues { - a.CreateElement("string").SetText(choiceValue) - } - case "file": - break - default: - paramDefine.CreateElement("defaultValue").SetText(parameter.DefaultValue) - } - } - } - } - return s -} - -func (s *Parameters) fromEtree(properties *etree.Element) *Parameters { - - if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil { - params := parametersProperty.SelectElement("parameterDefinitions").ChildElements() - if *s == nil { - *s = make([]*Parameter, 0) - } - for _, param := range params { - switch param.Tag { - case "hudson.model.StringParameterDefinition": - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.StringParameterDefinition"], - }) - case "hudson.model.BooleanParameterDefinition": - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.BooleanParameterDefinition"], - }) - case "hudson.model.TextParameterDefinition": - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.TextParameterDefinition"], - }) - case "hudson.model.FileParameterDefinition": - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - Type: ParameterTypeMap["hudson.model.FileParameterDefinition"], - }) - case "hudson.model.PasswordParameterDefinition": - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - DefaultValue: param.SelectElement("name").Text(), - Type: ParameterTypeMap["hudson.model.PasswordParameterDefinition"], - }) - case "hudson.model.ChoiceParameterDefinition": - choiceParameter := &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - Type: ParameterTypeMap["hudson.model.ChoiceParameterDefinition"], - } - choices := param.SelectElement("choices").SelectElement("a").SelectElements("string") - for _, choice := range choices { - choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text()) - } - choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue) - *s = append(*s, choiceParameter) - default: - *s = append(*s, &Parameter{ - Name: param.SelectElement("name").Text(), - Description: param.SelectElement("description").Text(), - DefaultValue: "unknown", - Type: param.Tag, - }) - } - } - } - return s -} - -func (s *GitSource) appendToEtree(source *etree.Element) *GitSource { - source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource") - source.CreateAttr("plugin", "git") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("remote").SetText(s.Url) - if s.CredentialId != "" { - source.CreateElement("credentialsId").SetText(s.CredentialId) - } - traits := source.CreateElement("traits") - if s.DiscoverBranches { - traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait") - } - if s.CloneOption != nil { - cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") - cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") - cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) - cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) - cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) - cloneExtension.CreateElement("reference") - if s.CloneOption.Timeout >= 0 { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) - } else { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) - } - - if s.CloneOption.Depth >= 0 { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) - } else { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) - } - } - - if s.RegexFilter != "" { - regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") - regexTraits.CreateAttr("plugin", "scm-api@2.4.0") - regexTraits.CreateElement("regex").SetText(s.RegexFilter) - } - return s -} - -func (s *GitSource) fromEtree(source *etree.Element) *GitSource { - if credential := source.SelectElement("credentialsId"); credential != nil { - s.CredentialId = credential.Text() - } - if remote := source.SelectElement("remote"); remote != nil { - s.Url = remote.Text() - } - - traits := source.SelectElement("traits") - if branchDiscoverTrait := traits.SelectElement( - "jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil { - s.DiscoverBranches = true - } - if cloneTrait := traits.SelectElement( - "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { - if cloneExtension := cloneTrait.SelectElement( - "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} - if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { - s.CloneOption.Shallow = value - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { - s.CloneOption.Timeout = int(value) - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { - s.CloneOption.Depth = int(value) - } - } - } - if regexTrait := traits.SelectElement( - "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { - if regex := regexTrait.SelectElement("regex"); regex != nil { - s.RegexFilter = regex.Text() - } - } - return s -} - -func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource { - if credential := source.SelectElement("credentialsId"); credential != nil { - s.CredentialId = credential.Text() - } - if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { - s.Owner = repoOwner.Text() - } - if repository := source.SelectElement("repository"); repository != nil { - s.Repo = repository.Text() - } - if apiUri := source.SelectElement("apiUri"); apiUri != nil { - s.ApiUri = apiUri.Text() - } - traits := source.SelectElement("traits") - if branchDiscoverTrait := traits.SelectElement( - "org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverBranches = strategyId - } - if originPRDiscoverTrait := traits.SelectElement( - "org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverPRFromOrigin = strategyId - } - if forkPRDiscoverTrait := traits.SelectElement( - "org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) - trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value - trust := strings.Split(trustClass, "$") - switch trust[1] { - case "TrustContributors": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 1, - } - case "TrustEveryone": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 2, - } - case "TrustPermission": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 3, - } - case "TrustNobody": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 4, - } - } - if cloneTrait := traits.SelectElement( - "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { - if cloneExtension := cloneTrait.SelectElement( - "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} - if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { - s.CloneOption.Shallow = value - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { - s.CloneOption.Timeout = int(value) - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { - s.CloneOption.Depth = int(value) - } - } - } - - if regexTrait := traits.SelectElement( - "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { - if regex := regexTrait.SelectElement("regex"); regex != nil { - s.RegexFilter = regex.Text() - } - } - } - return s -} - -func (s *GithubSource) appendToEtree(source *etree.Element) *GithubSource { - source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource") - source.CreateAttr("plugin", "github-branch-source") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("credentialsId").SetText(s.CredentialId) - source.CreateElement("repoOwner").SetText(s.Owner) - source.CreateElement("repository").SetText(s.Repo) - if s.ApiUri != "" { - source.CreateElement("apiUri").SetText(s.ApiUri) - } - traits := source.CreateElement("traits") - if s.DiscoverBranches != 0 { - traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"). - CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches)) - } - if s.DiscoverPRFromOrigin != 0 { - traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"). - CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin)) - } - if s.DiscoverPRFromForks != nil { - forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait") - forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy)) - trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$" - switch s.DiscoverPRFromForks.Trust { - case 1: - trustClass += "TrustContributors" - case 2: - trustClass += "TrustEveryone" - case 3: - trustClass += "TrustPermission" - case 4: - trustClass += "TrustNobody" - default: - } - forkTrait.CreateElement("trust").CreateAttr("class", trustClass) - } - if s.CloneOption != nil { - cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") - cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") - cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) - cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) - cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) - cloneExtension.CreateElement("reference") - if s.CloneOption.Timeout >= 0 { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) - } else { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) - } - - if s.CloneOption.Depth >= 0 { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) - } else { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) - } - } - if s.RegexFilter != "" { - regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") - regexTraits.CreateAttr("plugin", "scm-api@2.4.0") - regexTraits.CreateElement("regex").SetText(s.RegexFilter) - } - return s -} - -func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServerSource { - if credential := source.SelectElement("credentialsId"); credential != nil { - s.CredentialId = credential.Text() - } - if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { - s.Owner = repoOwner.Text() - } - if repository := source.SelectElement("repository"); repository != nil { - s.Repo = repository.Text() - } - if apiUri := source.SelectElement("serverUrl"); apiUri != nil { - s.ApiUri = apiUri.Text() - } - traits := source.SelectElement("traits") - if branchDiscoverTrait := traits.SelectElement( - "com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait"); branchDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverBranches = strategyId - } - if originPRDiscoverTrait := traits.SelectElement( - "com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverPRFromOrigin = strategyId - } - if forkPRDiscoverTrait := traits.SelectElement( - "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { - strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) - trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value - trust := strings.Split(trustClass, "$") - switch trust[1] { - case "TrustEveryone": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 1, - } - case "TrustTeamForks": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 2, - } - case "TrustNobody": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ - Strategy: strategyId, - Trust: 3, - } - } - if cloneTrait := traits.SelectElement( - "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { - if cloneExtension := cloneTrait.SelectElement( - "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} - if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { - s.CloneOption.Shallow = value - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { - s.CloneOption.Timeout = int(value) - } - if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { - s.CloneOption.Depth = int(value) - } - } - } - - if regexTrait := traits.SelectElement( - "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { - if regex := regexTrait.SelectElement("regex"); regex != nil { - s.RegexFilter = regex.Text() - } - } - } - return s -} - -func (s *BitbucketServerSource) appendToEtree(source *etree.Element) *BitbucketServerSource { - source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource") - source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("credentialsId").SetText(s.CredentialId) - source.CreateElement("repoOwner").SetText(s.Owner) - source.CreateElement("repository").SetText(s.Repo) - source.CreateElement("serverUrl").SetText(s.ApiUri) - - traits := source.CreateElement("traits") - if s.DiscoverBranches != 0 { - traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait>"). - CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches)) - } - if s.DiscoverPRFromOrigin != 0 { - traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"). - CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin)) - } - if s.DiscoverPRFromForks != nil { - forkTrait := traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait") - forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy)) - trustClass := "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait$" - switch s.DiscoverPRFromForks.Trust { - case 1: - trustClass += "TrustEveryone" - case 2: - trustClass += "TrustTeamForks" - case 3: - trustClass += "TrustNobody" - default: - trustClass += "TrustEveryone" - } - forkTrait.CreateElement("trust").CreateAttr("class", trustClass) - } - if s.CloneOption != nil { - cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") - cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") - cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) - cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) - cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) - cloneExtension.CreateElement("reference") - if s.CloneOption.Timeout >= 0 { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) - } else { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) - } - - if s.CloneOption.Depth >= 0 { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) - } else { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) - } - } - if s.RegexFilter != "" { - regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") - regexTraits.CreateAttr("plugin", "scm-api@2.4.0") - regexTraits.CreateElement("regex").SetText(s.RegexFilter) - } - return s -} - -func (s *SvnSource) fromEtree(source *etree.Element) *SvnSource { - if remote := source.SelectElement("remoteBase"); remote != nil { - s.Remote = remote.Text() - } - - if credentialsId := source.SelectElement("credentialsId"); credentialsId != nil { - s.CredentialId = credentialsId.Text() - } - - if includes := source.SelectElement("includes"); includes != nil { - s.Includes = includes.Text() - } - - if excludes := source.SelectElement("excludes"); excludes != nil { - s.Excludes = excludes.Text() - } - return s -} - -func (s *SvnSource) appendToEtree(source *etree.Element) *SvnSource { - source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource") - source.CreateAttr("plugin", "subversion") - source.CreateElement("id").SetText(s.ScmId) - if s.CredentialId != "" { - source.CreateElement("credentialsId").SetText(s.CredentialId) - } - if s.Remote != "" { - source.CreateElement("remoteBase").SetText(s.Remote) - } - if s.Includes != "" { - source.CreateElement("includes").SetText(s.Includes) - } - if s.Excludes != "" { - source.CreateElement("excludes").SetText(s.Excludes) - } - return nil -} - -func (s *SingleSvnSource) fromEtree(source *etree.Element) *SingleSvnSource { - if scm := source.SelectElement("scm"); scm != nil { - if locations := scm.SelectElement("locations"); locations != nil { - if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil { - if remote := moduleLocations.SelectElement("remote"); remote != nil { - s.Remote = remote.Text() - } - if credentialId := moduleLocations.SelectElement("credentialsId"); credentialId != nil { - s.CredentialId = credentialId.Text() - } - } - } - } - return s -} - -func (s *SingleSvnSource) appendToEtree(source *etree.Element) *SingleSvnSource { - - source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource") - source.CreateAttr("plugin", "scm-api") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("name").SetText("master") - - scm := source.CreateElement("scm") - scm.CreateAttr("class", "hudson.scm.SubversionSCM") - scm.CreateAttr("plugin", "subversion") - - location := scm.CreateElement("locations").CreateElement("hudson.scm.SubversionSCM_-ModuleLocation") - if s.Remote != "" { - location.CreateElement("remote").SetText(s.Remote) - } - if s.CredentialId != "" { - location.CreateElement("credentialsId").SetText(s.CredentialId) - } - location.CreateElement("local").SetText(".") - location.CreateElement("depthOption").SetText("infinity") - location.CreateElement("ignoreExternalsOption").SetText("true") - location.CreateElement("cancelProcessOnExternalsFail").SetText("true") - - source.CreateElement("excludedRegions") - source.CreateElement("includedRegions") - source.CreateElement("excludedUsers") - source.CreateElement("excludedRevprop") - source.CreateElement("excludedCommitMessages") - source.CreateElement("workspaceUpdater").CreateAttr("class", "hudson.scm.subversion.UpdateUpdater") - source.CreateElement("ignoreDirPropChanges").SetText("false") - source.CreateElement("filterChangelog").SetText("false") - source.CreateElement("quietOperation").SetText("true") - - return s -} - -func (s *MultiBranchJobTrigger) appendToEtree(properties *etree.Element) *MultiBranchJobTrigger { - triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") - triggerProperty.CreateAttr("plugin", "multibranch-action-triggers") - triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger) - triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger) - return s -} - -func (s *MultiBranchJobTrigger) fromEtree(properties *etree.Element) *MultiBranchJobTrigger { - triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") - if triggerProperty != nil { - s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text() - s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text() - } - return s -} -func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranchPipeline) (string, error) { - doc := etree.NewDocument() - xmlString := ` - - - - - - - - - - - - - - - false - - - - - -` - err := doc.ReadFromString(xmlString) - if err != nil { - return "", err - } - - project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") - project.CreateElement("description").SetText(pipeline.Description) - - if pipeline.MultiBranchJobTrigger != nil { - properties := project.SelectElement("properties") - pipeline.MultiBranchJobTrigger.appendToEtree(properties) - } - - if pipeline.Discarder != nil { - discarder := project.CreateElement("orphanedItemStrategy") - discarder.CreateAttr("class", "com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy") - discarder.CreateAttr("plugin", "cloudbees-folder") - discarder.CreateElement("pruneDeadBranches").SetText("true") - discarder.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) - discarder.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) - } - - triggers := project.CreateElement("triggers") - if pipeline.TimerTrigger != nil { - timeTrigger := triggers.CreateElement( - "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger") - timeTrigger.CreateAttr("plugin", "cloudbees-folder") - millis, err := strconv.ParseInt(pipeline.TimerTrigger.Interval, 10, 64) - if err != nil { - return "", err - } - timeTrigger.CreateElement("spec").SetText(toCrontab(millis)) - timeTrigger.CreateElement("interval").SetText(pipeline.TimerTrigger.Interval) - - triggers.CreateElement("disabled").SetText("false") - } - - sources := project.CreateElement("sources") - sources.CreateAttr("class", "jenkins.branch.MultiBranchProject$BranchSourceList") - sources.CreateAttr("plugin", "branch-api") - sourcesOwner := sources.CreateElement("owner") - sourcesOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") - sourcesOwner.CreateAttr("reference", "../..") - - branchSource := sources.CreateElement("data").CreateElement("jenkins.branch.BranchSource") - branchSourceStrategy := branchSource.CreateElement("strategy") - branchSourceStrategy.CreateAttr("class", "jenkins.branch.NamedExceptionsBranchPropertyStrategy") - branchSourceStrategy.CreateElement("defaultProperties").CreateAttr("class", "empty-list") - branchSourceStrategy.CreateElement("namedExceptions").CreateAttr("class", "empty-list") - source := branchSource.CreateElement("source") - - switch pipeline.SourceType { - case "git": - gitDefine := pipeline.GitSource - gitDefine.ScmId = projectName + pipeline.Name - gitDefine.appendToEtree(source) - case "github": - githubDefine := pipeline.GitHubSource - githubDefine.ScmId = projectName + pipeline.Name - githubDefine.appendToEtree(source) - case "svn": - svnDefine := pipeline.SvnSource - svnDefine.ScmId = projectName + pipeline.Name - svnDefine.appendToEtree(source) - - case "single_svn": - singSvnDefine := pipeline.SingleSvnSource - singSvnDefine.ScmId = projectName + pipeline.Name - singSvnDefine.appendToEtree(source) - - case "bitbucket_server": - bitbucketServerDefine := pipeline.BitbucketServerSource - bitbucketServerDefine.ScmId = projectName + pipeline.Name - bitbucketServerDefine.appendToEtree(source) - - default: - return "", fmt.Errorf("unsupport source type") - } - - factory := project.CreateElement("factory") - factory.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory") - factoryOwner := factory.CreateElement("owner") - factoryOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") - factoryOwner.CreateAttr("reference", "../..") - factory.CreateElement("scriptPath").SetText(pipeline.ScriptPath) - - doc.Indent(2) - stringXml, err := doc.WriteToString() - return replaceXmlVersion(stringXml, "1.0", "1.1"), err -} - -func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, error) { - pipeline := &MultiBranchPipeline{} - config = replaceXmlVersion(config, "1.1", "1.0") - doc := etree.NewDocument() - err := doc.ReadFromString(config) - if err != nil { - return nil, err - } - project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") - if project == nil { - return nil, fmt.Errorf("can not parse mutibranch pipeline config") - } - if properties := project.SelectElement("properties"); properties != nil { - if multibranchTrigger := properties.SelectElement( - "org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil { - trigger := &MultiBranchJobTrigger{} - trigger.fromEtree(properties) - pipeline.MultiBranchJobTrigger = trigger - } - } - pipeline.Description = project.SelectElement("description").Text() - - if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil { - pipeline.Discarder = &DiscarderProperty{ - DaysToKeep: discarder.SelectElement("daysToKeep").Text(), - NumToKeep: discarder.SelectElement("numToKeep").Text(), - } - } - if triggers := project.SelectElement("triggers"); triggers != nil { - if timerTrigger := triggers.SelectElement( - "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil { - pipeline.TimerTrigger = &TimerTrigger{ - Interval: timerTrigger.SelectElement("interval").Text(), - } - } - } - - if sources := project.SelectElement("sources"); sources != nil { - if sourcesData := sources.SelectElement("data"); sourcesData != nil { - if branchSource := sourcesData.SelectElement("jenkins.branch.BranchSource"); branchSource != nil { - source := branchSource.SelectElement("source") - switch source.SelectAttr("class").Value { - case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": - githubSource := &GithubSource{} - githubSource.fromEtree(source) - pipeline.GitHubSource = githubSource - pipeline.SourceType = "github" - case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": - bitbucketServerSource := &BitbucketServerSource{} - bitbucketServerSource.fromEtree(source) - pipeline.BitbucketServerSource = bitbucketServerSource - pipeline.SourceType = "bitbucket_server" - - case "jenkins.plugins.git.GitSCMSource": - gitSource := &GitSource{} - gitSource.fromEtree(source) - pipeline.SourceType = "git" - pipeline.GitSource = gitSource - - case "jenkins.scm.impl.SingleSCMSource": - singleSvnSource := &SingleSvnSource{} - singleSvnSource.fromEtree(source) - pipeline.SourceType = "single_svn" - pipeline.SingleSvnSource = singleSvnSource - - case "jenkins.scm.impl.subversion.SubversionSCMSource": - svnSource := &SvnSource{} - svnSource.fromEtree(source) - pipeline.SourceType = "svn" - pipeline.SvnSource = svnSource - } - } - } - } - - pipeline.ScriptPath = project.SelectElement("factory").SelectElement("scriptPath").Text() - return pipeline, nil -} - -func toCrontab(millis int64) string { - if millis*time.Millisecond.Nanoseconds() <= 5*time.Minute.Nanoseconds() { - return "* * * * *" - } - if millis*time.Millisecond.Nanoseconds() <= 30*time.Minute.Nanoseconds() { - return "H/5 * * * *" - } - if millis*time.Millisecond.Nanoseconds() <= 1*time.Hour.Nanoseconds() { - return "H/15 * * * *" - } - if millis*time.Millisecond.Nanoseconds() <= 8*time.Hour.Nanoseconds() { - return "H/30 * * * *" - } - if millis*time.Millisecond.Nanoseconds() <= 24*time.Hour.Nanoseconds() { - return "H H/4 * * *" - } - if millis*time.Millisecond.Nanoseconds() <= 48*time.Hour.Nanoseconds() { - return "H H/12 * * *" - } - return "H H * * *" - -} - -func getBuildSonarResults(build *gojenkins.Build) ([]*SonarStatus, error) { - - sonarClient, err := client.ClientSets().SonarQube() - if err != nil { - return nil, err - } - - actions := build.GetActions() - sonarStatuses := make([]*SonarStatus, 0) - for _, action := range actions { - if action.ClassName == SonarAnalysisActionClass { - sonarStatus := &SonarStatus{} - taskOptions := &sonargo.CeTaskOption{ - Id: action.SonarTaskId, - } - ceTask, _, err := sonarClient.SonarQube().Ce.Task(taskOptions) - if err != nil { - klog.Errorf("get sonar task error [%+v]", err) - continue - } - sonarStatus.Task = ceTask - measuresComponentOption := &sonargo.MeasuresComponentOption{ - Component: ceTask.Task.ComponentKey, - AdditionalFields: SonarAdditionalFields, - MetricKeys: SonarMetricKeys, - } - measures, _, err := sonarClient.SonarQube().Measures.Component(measuresComponentOption) - if err != nil { - klog.Errorf("get sonar task error [%+v]", err) - continue - } - sonarStatus.Measures = measures - - issuesSearchOption := &sonargo.IssuesSearchOption{ - AdditionalFields: "_all", - ComponentKeys: ceTask.Task.ComponentKey, - Resolved: "false", - Ps: "10", - S: "FILE_LINE", - Facets: "severities,types", - } - issuesSearch, _, err := sonarClient.SonarQube().Issues.Search(issuesSearchOption) - sonarStatus.Issues = issuesSearch - jenkinsAction := action - sonarStatus.JenkinsAction = &jenkinsAction - - sonarStatuses = append(sonarStatuses, sonarStatus) - } - } - return sonarStatuses, nil -} diff --git a/pkg/models/devops/project_pipeline_handler.go b/pkg/models/devops/project_pipeline_handler.go deleted file mode 100644 index 96f6986d9..000000000 --- a/pkg/models/devops/project_pipeline_handler.go +++ /dev/null @@ -1,311 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "fmt" - "github.com/emicklei/go-restful" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" -) - -func CreateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - switch pipeline.Type { - case NoScmPipelineType: - - config, err := createPipelineConfigXml(pipeline.Pipeline) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - - job, err := jenkinsClient.GetJob(pipeline.Pipeline.Name, projectId) - if job != nil { - err := fmt.Errorf("job name [%s] has been used", job.GetName()) - klog.Warning(err.Error()) - return "", restful.NewError(http.StatusConflict, err.Error()) - } - - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - _, err = jenkinsClient.CreateJobInFolder(config, pipeline.Pipeline.Name, projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return pipeline.Pipeline.Name, nil - case MultiBranchPipelineType: - config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline) - if err != nil { - klog.Errorf("%+v", err) - - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - - job, err := jenkinsClient.GetJob(pipeline.MultiBranchPipeline.Name, projectId) - if job != nil { - err := fmt.Errorf("job name [%s] has been used", job.GetName()) - klog.Warning(err.Error()) - return "", restful.NewError(http.StatusConflict, err.Error()) - } - - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - _, err = jenkinsClient.CreateJobInFolder(config, pipeline.MultiBranchPipeline.Name, projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return pipeline.MultiBranchPipeline.Name, nil - - default: - err := fmt.Errorf("error unsupport job type") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } -} - -func DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - _, err = jenkinsClient.DeleteJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return pipelineId, nil -} - -func UpdateProjectPipeline(projectId, pipelineId string, pipeline *ProjectPipeline) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - switch pipeline.Type { - case NoScmPipelineType: - - config, err := createPipelineConfigXml(pipeline.Pipeline) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = job.UpdateConfig(config) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return pipeline.Pipeline.Name, nil - case MultiBranchPipelineType: - - config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline) - if err != nil { - klog.Errorf("%+v", err) - - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = job.UpdateConfig(config) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - return pipeline.MultiBranchPipeline.Name, nil - - default: - err := fmt.Errorf("error unsupport job type") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) - } -} - -func GetProjectPipeline(projectId, pipelineId string) (*ProjectPipeline, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - switch job.Raw.Class { - case "org.jenkinsci.plugins.workflow.job.WorkflowJob": - config, err := job.GetConfig() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline, err := parsePipelineConfigXml(config) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline.Name = pipelineId - return &ProjectPipeline{ - Type: NoScmPipelineType, - Pipeline: pipeline, - }, nil - - case "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject": - config, err := job.GetConfig() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline, err := parseMultiBranchPipelineConfigXml(config) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline.Name = pipelineId - return &ProjectPipeline{ - Type: MultiBranchPipelineType, - MultiBranchPipeline: pipeline, - }, nil - default: - err := fmt.Errorf("error unsupport job type") - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - - } -} - -func GetPipelineSonar(projectId, pipelineId string) ([]*SonarStatus, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - build, err := job.GetLastBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - - sonarStatus, err := getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - if len(sonarStatus) == 0 { - build, err := job.GetLastCompletedBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - sonarStatus, err = getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - } - return sonarStatus, nil -} - -func GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*SonarStatus, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(branchId, projectId, pipelineId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - build, err := job.GetLastBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - - sonarStatus, err := getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - if len(sonarStatus) == 0 { - build, err := job.GetLastCompletedBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - sonarStatus, err = getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - } - return sonarStatus, nil -} diff --git a/pkg/models/devops/project_pipeline_sonar_handler.go b/pkg/models/devops/project_pipeline_sonar_handler.go new file mode 100644 index 000000000..474729f93 --- /dev/null +++ b/pkg/models/devops/project_pipeline_sonar_handler.go @@ -0,0 +1,122 @@ +package devops + +import ( + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" + "net/http" +) + +type PipelineSonarGetter interface { + GetPipelineSonar(projectId, pipelineId string) ([]*sonarqube.SonarStatus, error) + GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*sonarqube.SonarStatus, error) +} +type pipelineSonarGetter struct { + devops.BuildGetter + sonarqube.SonarInterface +} + +func NewPipelineSonarGetter(devopClient devops.BuildGetter, sonarClient sonarqube.SonarInterface) PipelineSonarGetter { + return &pipelineSonarGetter{ + BuildGetter: devopClient, + SonarInterface: sonarClient, + } +} + +func (g *pipelineSonarGetter) GetPipelineSonar(projectId, pipelineId string) ([]*sonarqube.SonarStatus, error) { + + build, err := g.GetProjectPipelineBuildByType(projectId, pipelineId, devops.LastBuild) + if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound { + klog.Errorf("%+v", err) + return nil, err + } else if err != nil { + klog.Errorf("%+v", err) + return nil, nil + } + var taskIds []string + for _, action := range build.Actions { + if action.ClassName == sonarqube.SonarAnalysisActionClass { + taskIds = append(taskIds, action.SonarTaskId) + } + } + var sonarStatus []*sonarqube.SonarStatus + + if len(taskIds) != 0 { + sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...) + if err != nil { + klog.Errorf("%+v", err) + return nil, restful.NewError(http.StatusBadRequest, err.Error()) + } + } else if len(taskIds) == 0 { + build, err := g.GetProjectPipelineBuildByType(projectId, pipelineId, devops.LastCompletedBuild) + if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound { + klog.Errorf("%+v", err) + return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error()) + } else if err != nil { + klog.Errorf("%+v", err) + return nil, nil + } + for _, action := range build.Actions { + if action.ClassName == sonarqube.SonarAnalysisActionClass { + taskIds = append(taskIds, action.SonarTaskId) + } + } + sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...) + if err != nil { + klog.Errorf("%+v", err) + return nil, restful.NewError(http.StatusBadRequest, err.Error()) + } + + } + return sonarStatus, nil +} + +func (g *pipelineSonarGetter) GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*sonarqube.SonarStatus, error) { + + build, err := g.GetMultiBranchPipelineBuildByType(projectId, pipelineId, branchId, devops.LastBuild) + if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound { + klog.Errorf("%+v", err) + return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error()) + } else if err != nil { + klog.Errorf("%+v", err) + return nil, nil + } + var taskIds []string + for _, action := range build.Actions { + if action.ClassName == sonarqube.SonarAnalysisActionClass { + taskIds = append(taskIds, action.SonarTaskId) + } + } + var sonarStatus []*sonarqube.SonarStatus + + if len(taskIds) != 0 { + sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...) + if err != nil { + klog.Errorf("%+v", err) + return nil, restful.NewError(http.StatusBadRequest, err.Error()) + } + } else if len(taskIds) == 0 { + build, err := g.GetMultiBranchPipelineBuildByType(projectId, pipelineId, branchId, devops.LastCompletedBuild) + if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound { + klog.Errorf("%+v", err) + return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error()) + } else if err != nil { + klog.Errorf("%+v", err) + return nil, nil + } + for _, action := range build.Actions { + if action.ClassName == sonarqube.SonarAnalysisActionClass { + taskIds = append(taskIds, action.SonarTaskId) + } + } + sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...) + if err != nil { + klog.Errorf("%+v", err) + return nil, restful.NewError(http.StatusBadRequest, err.Error()) + } + + } + return sonarStatus, nil +} diff --git a/pkg/models/devops/project_pipeline_test.go b/pkg/models/devops/project_pipeline_test.go deleted file mode 100644 index 4f572b554..000000000 --- a/pkg/models/devops/project_pipeline_test.go +++ /dev/null @@ -1,633 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 devops - -import ( - "reflect" - "testing" -) - -func Test_NoScmPipelineConfig(t *testing.T) { - inputs := []*NoScmPipeline{ - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - }, - { - Name: "", - Description: "", - Jenkinsfile: "node{echo 'hello'}", - }, - { - Name: "", - Description: "", - Jenkinsfile: "node{echo 'hello'}", - DisableConcurrent: true, - }, - } - for _, input := range inputs { - outputString, err := createPipelineConfigXml(input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parsePipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_NoScmPipelineConfig_Discarder(t *testing.T) { - inputs := []*NoScmPipeline{ - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ - "3", "5", - }, - }, - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ - "3", "", - }, - }, - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ - "", "21321", - }, - }, - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ - "", "", - }, - }, - } - for _, input := range inputs { - outputString, err := createPipelineConfigXml(input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parsePipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_NoScmPipelineConfig_Param(t *testing.T) { - inputs := []*NoScmPipeline{ - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Parameters: &Parameters{ - &Parameter{ - Name: "d", - DefaultValue: "a\nb", - Type: "choice", - Description: "fortest", - }, - }, - }, - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - Parameters: &Parameters{ - &Parameter{ - Name: "a", - DefaultValue: "abc", - Type: "string", - Description: "fortest", - }, - &Parameter{ - Name: "b", - DefaultValue: "false", - Type: "boolean", - Description: "fortest", - }, - &Parameter{ - Name: "c", - DefaultValue: "password \n aaa", - Type: "text", - Description: "fortest", - }, - &Parameter{ - Name: "d", - DefaultValue: "a\nb", - Type: "choice", - Description: "fortest", - }, - }, - }, - } - for _, input := range inputs { - outputString, err := createPipelineConfigXml(input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parsePipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_NoScmPipelineConfig_Trigger(t *testing.T) { - inputs := []*NoScmPipeline{ - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - TimerTrigger: &TimerTrigger{ - Cron: "1 1 1 * * *", - }, - }, - - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - RemoteTrigger: &RemoteTrigger{ - Token: "abc", - }, - }, - { - Name: "", - Description: "for test", - Jenkinsfile: "node{echo 'hello'}", - TimerTrigger: &TimerTrigger{ - Cron: "1 1 1 * * *", - }, - RemoteTrigger: &RemoteTrigger{ - Token: "abc", - }, - }, - } - - for _, input := range inputs { - outputString, err := createPipelineConfigXml(input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parsePipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_MultiBranchPipelineConfig(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - GitSource: &GitSource{}, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{}, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "single_svn", - SingleSvnSource: &SingleSvnSource{}, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "svn", - SvnSource: &SvnSource{}, - }, - } - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - Discarder: &DiscarderProperty{ - DaysToKeep: "1", - NumToKeep: "2", - }, - GitSource: &GitSource{}, - }, - } - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) { - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - GitSource: &GitSource{}, - }, - } - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_MultiBranchPipelineConfig_Source(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - GitSource: &GitSource{ - Url: "https://github.com/kubesphere/devops", - CredentialId: "git", - DiscoverBranches: true, - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "bitbucket_server", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - BitbucketServerSource: &BitbucketServerSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - }, - }, - - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "svn", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - SvnSource: &SvnSource{ - Remote: "https://api.svn.com/bcd", - CredentialId: "svn", - Excludes: "truck", - Includes: "tag/*", - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "single_svn", - TimerTrigger: &TimerTrigger{ - Interval: "12345566", - }, - SingleSvnSource: &SingleSvnSource{ - Remote: "https://api.svn.com/bcd", - CredentialId: "svn", - }, - }, - } - - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } -} - -func Test_MultiBranchPipelineCloneConfig(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - GitSource: &GitSource{ - Url: "https://github.com/kubesphere/devops", - CredentialId: "git", - DiscoverBranches: true, - CloneOption: &GitCloneOption{ - Shallow: false, - Depth: 3, - Timeout: 20, - }, - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - CloneOption: &GitCloneOption{ - Shallow: false, - Depth: 3, - Timeout: 20, - }, - }, - }, - } - - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } - -} - -func Test_MultiBranchPipelineRegexFilter(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "git", - GitSource: &GitSource{ - Url: "https://github.com/kubesphere/devops", - CredentialId: "git", - DiscoverBranches: true, - RegexFilter: ".*", - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - RegexFilter: ".*", - }, - }, - } - - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } - -} - -func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { - - inputs := []*MultiBranchPipeline{ - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - RegexFilter: ".*", - }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ - CreateActionJobsToTrigger: "abc", - DeleteActionJobsToTrigger: "ddd", - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - RegexFilter: ".*", - }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ - CreateActionJobsToTrigger: "abc", - }, - }, - { - Name: "", - Description: "for test", - ScriptPath: "Jenkinsfile", - SourceType: "github", - GitHubSource: &GithubSource{ - Owner: "kubesphere", - Repo: "devops", - CredentialId: "github", - ApiUri: "https://api.github.com", - DiscoverBranches: 1, - DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ - Strategy: 1, - Trust: 1, - }, - RegexFilter: ".*", - }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ - DeleteActionJobsToTrigger: "ddd", - }, - }, - } - - for _, input := range inputs { - outputString, err := createMultiBranchPipelineConfigXml("", input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } - output, err := parseMultiBranchPipelineConfigXml(outputString) - - if err != nil { - t.Fatalf("should not get error %+v", err) - } - if !reflect.DeepEqual(input, output) { - t.Fatalf("input [%+v] output [%+v] should equal ", input, output) - } - } - -} diff --git a/pkg/models/devops/s2ibinary_handler.go b/pkg/models/devops/s2ibinary_handler.go index 73a0d1425..782ec3804 100644 --- a/pkg/models/devops/s2ibinary_handler.go +++ b/pkg/models/devops/s2ibinary_handler.go @@ -3,33 +3,46 @@ package devops import ( "code.cloudfoundry.org/bytefmt" "fmt" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/aws/aws-sdk-go/service/s3/s3manager" + awsS3 "github.com/aws/aws-sdk-go/service/s3" "github.com/emicklei/go-restful" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/util/retry" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/simple/client/s3" "mime/multipart" "net/http" "reflect" - "time" ) const ( GetS2iBinaryURL = "http://ks-apiserver.kubesphere-system.svc/kapis/devops.kubesphere.io/v1alpha2/namespaces/%s/s2ibinaries/%s/file/%s" ) -func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHeader) (*v1alpha1.S2iBinary, error) { - s3Client, err := client.ClientSets().S3() - if err != nil { - return nil, err - } +type S2iBinaryUploader interface { + UploadS2iBinary(namespace, name, md5 string, header *multipart.FileHeader) (*v1alpha1.S2iBinary, error) + DownloadS2iBinary(namespace, name, fileName string) (string, error) +} + +type s2iBinaryUploader struct { + client versioned.Interface + informers externalversions.SharedInformerFactory + s3Client s3.Interface +} + +func NewS2iBinaryUploader(client versioned.Interface, informers externalversions.SharedInformerFactory, s3Client s3.Interface) S2iBinaryUploader { + return &s2iBinaryUploader{ + client: client, + informers: informers, + s3Client: s3Client, + } +} + +func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHeader) (*v1alpha1.S2iBinary, error) { binFile, err := fileHeader.Open() if err != nil { klog.Errorf("%+v", err) @@ -37,7 +50,7 @@ func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHead } defer binFile.Close() - origin, err := informers.KsSharedInformerFactory().Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(namespace).Get(name) + origin, err := s.informers.Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(namespace).Get(name) if err != nil { klog.Errorf("%+v", err) return nil, err @@ -58,7 +71,7 @@ func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHead } //Set status Uploading to lock resource - uploading, err := SetS2iBinaryStatus(copy, v1alpha1.StatusUploading) + uploading, err := s.SetS2iBinaryStatus(copy, v1alpha1.StatusUploading) if err != nil { err := restful.NewError(http.StatusConflict, fmt.Sprintf("could not set status: %+v", err)) klog.Error(err) @@ -71,41 +84,20 @@ func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHead copy.Spec.FileName = fileHeader.Filename copy.Spec.DownloadURL = fmt.Sprintf(GetS2iBinaryURL, namespace, name, copy.Spec.FileName) - s3session := s3Client.Session() - if s3session == nil { - err := fmt.Errorf("could not connect to s2i s3") - klog.Error(err) - _, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) - if serr != nil { - klog.Error(serr) - return nil, err - } - return nil, err - } - uploader := s3manager.NewUploader(s3session, func(uploader *s3manager.Uploader) { - uploader.PartSize = 5 * bytefmt.MEGABYTE - uploader.LeavePartsOnError = true - }) - _, err = uploader.Upload(&s3manager.UploadInput{ - Bucket: s3Client.Bucket(), - Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)), - Body: binFile, - ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", copy.Spec.FileName)), - }) - + err = s.s3Client.Upload(fmt.Sprintf("%s-%s", namespace, name), copy.Spec.FileName, binFile) if err != nil { if aerr, ok := err.(awserr.Error); ok { switch aerr.Code() { - case s3.ErrCodeNoSuchBucket: + case awsS3.ErrCodeNoSuchBucket: klog.Error(err) - _, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) + _, serr := s.SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) if serr != nil { klog.Error(serr) } return nil, err default: klog.Error(err) - _, serr := SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusUploadFailed) + _, serr := s.SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusUploadFailed) if serr != nil { klog.Error(serr) } @@ -120,13 +112,13 @@ func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHead copy.Spec.UploadTimeStamp = new(metav1.Time) } *copy.Spec.UploadTimeStamp = metav1.Now() - copy, err = client.ClientSets().K8s().KubeSphere().DevopsV1alpha1().S2iBinaries(namespace).Update(copy) + copy, err = s.client.DevopsV1alpha1().S2iBinaries(namespace).Update(copy) if err != nil { klog.Error(err) return nil, err } - copy, err = SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusReady) + copy, err = s.SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusReady) if err != nil { klog.Error(err) return nil, err @@ -134,46 +126,32 @@ func UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHead return copy, nil } -func DownloadS2iBinary(namespace, name, fileName string) (string, error) { - s3Client, err := client.ClientSets().S3() - if err != nil { - return "", err - } +func (s *s2iBinaryUploader) DownloadS2iBinary(namespace, name, fileName string) (string, error) { - origin, err := informers.KsSharedInformerFactory().Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(namespace).Get(name) + origin, err := s.informers.Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(namespace).Get(name) if err != nil { klog.Errorf("%+v", err) return "", err } + if origin.Spec.FileName != fileName { err := fmt.Errorf("could not fould file %s", fileName) klog.Error(err) return "", err } + if origin.Status.Phase != v1alpha1.StatusReady { err := restful.NewError(http.StatusBadRequest, "file is not ready, please try later") klog.Error(err) return "", err } - - req, _ := s3Client.Client().GetObjectRequest(&s3.GetObjectInput{ - Bucket: s3Client.Bucket(), - Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)), - ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", origin.Spec.FileName)), - }) - url, err := req.Presign(5 * time.Minute) - if err != nil { - klog.Error(err) - return "", err - } - return url, nil - + return s.s3Client.GetDownloadURL(fmt.Sprintf("%s-%s", namespace, name), fileName) } -func SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { +func (s *s2iBinaryUploader) SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { copy := s2ibin.DeepCopy() copy.Status.Phase = status - copy, err := client.ClientSets().K8s().KubeSphere().DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Update(copy) + copy, err := s.client.DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Update(copy) if err != nil { klog.Error(err) return nil, err @@ -181,18 +159,18 @@ func SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2 return copy, nil } -func SetS2iBinaryStatusWithRetry(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { +func (s *s2iBinaryUploader) SetS2iBinaryStatusWithRetry(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { var bin *v1alpha1.S2iBinary var err error err = retry.RetryOnConflict(retry.DefaultRetry, func() error { - bin, err = informers.KsSharedInformerFactory().Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name) + bin, err = s.client.DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name, metav1.GetOptions{}) if err != nil { klog.Error(err) return err } bin.Status.Phase = status - bin, err = client.ClientSets().K8s().KubeSphere().DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Update(bin) + bin, err = s.client.DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Update(bin) if err != nil { klog.Error(err) return err diff --git a/pkg/models/devops/s2ibinary_handler_test.go b/pkg/models/devops/s2ibinary_handler_test.go new file mode 100644 index 000000000..f056cc935 --- /dev/null +++ b/pkg/models/devops/s2ibinary_handler_test.go @@ -0,0 +1,114 @@ +package devops + +import ( + "code.cloudfoundry.org/bytefmt" + "fmt" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" + clientgotesting "k8s.io/client-go/testing" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + fakeS3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake" + "kubesphere.io/kubesphere/pkg/utils/hashutil" + "mime/multipart" + "reflect" + "strings" + "testing" + "time" +) + +const ( + fileaContents = "This is a test file." + fileaKey = "binary" + fileaName = "filea.txt" + boundary = `MyBoundary` + ns = "testns" + s2ibname = "test" +) + +const message = ` +--MyBoundary +Content-Disposition: form-data; name="binary"; filename="filea.txt" +Content-Type: text/plain + +` + fileaContents + ` +--MyBoundary-- +` + +func TestS2iBinaryUploader(t *testing.T) { + s2ib := s2ibinary(ns, s2ibname) + fakeKubeClient := fake.NewSimpleClientset(s2ib) + fakeWatch := watch.NewFake() + fakeKubeClient.AddWatchReactor("*", clientgotesting.DefaultWatchReactor(fakeWatch, nil)) + informerFactory := ksinformers.NewSharedInformerFactory(fakeKubeClient, 0) + stopCh := make(chan struct{}) + s2iInformer := informerFactory.Devops().V1alpha1().S2iBinaries() + err := s2iInformer.Informer().GetIndexer().Add(s2ib) + defer close(stopCh) + informerFactory.Start(stopCh) + informerFactory.WaitForCacheSync(stopCh) + + s3 := fakeS3.NewFakeS3() + uploader := NewS2iBinaryUploader(fakeKubeClient, informerFactory, s3) + header := prepareFileHeader() + file, err := header.Open() + if err != nil { + t.Fatal(err) + } + md5, err := hashutil.GetMD5(file) + if err != nil { + t.Fatal(err) + } + wantSpec := v1alpha1.S2iBinarySpec{ + FileName: fileaName, + MD5: md5, + Size: bytefmt.ByteSize(uint64(header.Size)), + } + + binary, err := uploader.UploadS2iBinary(ns, s2ibname, md5, header) + if err != nil { + t.Fatal(err) + } + + wantSpec.UploadTimeStamp = binary.Spec.UploadTimeStamp + wantSpec.DownloadURL = binary.Spec.DownloadURL + if !reflect.DeepEqual(binary.Spec, wantSpec) { + t.Fatalf("s2ibinary spec is not same with expected, get: %+v, expected: %+v", binary, wantSpec) + } + + _, ok := s3.Storage[fmt.Sprintf("%s-%s", ns, s2ibname)] + if !ok { + t.Fatalf("should get file in s3") + } + + time.Sleep(3 * time.Second) + url, err := uploader.DownloadS2iBinary(ns, s2ibname, fileaName) + if err != nil { + t.Fatal(err) + } + if url != fmt.Sprintf("http://%s-%s/%s", ns, s2ibname, fileaName) { + t.Fatalf("download url is not equal with expected, get: %+v, expected: %+v", url, fmt.Sprintf("http://%s-%s/%s", ns, s2ibname, fileaName)) + } +} + +func prepareFileHeader() *multipart.FileHeader { + reader := strings.NewReader(message) + multipartReader := multipart.NewReader(reader, boundary) + form, err := multipartReader.ReadForm(25) + if err != nil { + panic(err) + } + return form.File["binary"][0] +} + +func s2ibinary(namespace, name string) *v1alpha1.S2iBinary { + return &v1alpha1.S2iBinary{ + ObjectMeta: v1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: v1alpha1.S2iBinarySpec{}, + Status: v1alpha1.S2iBinaryStatus{}, + } +} diff --git a/pkg/models/devops/urlconfig.go b/pkg/models/devops/urlconfig.go deleted file mode 100644 index 222cc07ab..000000000 --- a/pkg/models/devops/urlconfig.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 devops - -// Some apis for Jenkins. -const ( - GetPipeBranchUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/?" - GetBranchPipeUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/" - GetPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/" - SearchPipelineUrl = "/blue/rest/search/?" - RunBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/" - RunPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/" - GetPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/" - GetPipeBranchRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/" - SearchPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/?" - GetBranchPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/?" - GetPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/?" - GetBranchRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/log/?" - GetRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/log/?" - GetBranchStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/log/?" - GetStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/log/?" - StopBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/stop/?" - StopPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/stop/?" - ReplayBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/replay/" - ReplayPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/replay/" - GetBranchArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/artifacts/?" - GetArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/artifacts/?" - CheckBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/" - CheckPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/" - GetBranchNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/?" - GetNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/?" - GetSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/" - CreateSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/" - ValidateUrl = "/blue/rest/organizations/jenkins/scm/%s/validate" - GetSCMOrgUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/?" - GetOrgRepoUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/%s/repositories/?" - GetConsoleLogUrl = "/job/%s/job/%s/indexing/consoleText" - ScanBranchUrl = "/job/%s/job/%s/build?" - GetCrumbUrl = "/crumbIssuer/api/json/" - ToJenkinsfileUrl = "/pipeline-model-converter/toJenkinsfile" - ToJsonUrl = "/pipeline-model-converter/toJson" - GetNotifyCommitUrl = "/git/notifyCommit/?" - GithubWebhookUrl = "/github-webhook/" - CheckScriptCompileUrl = "/job/%s/job/%s/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile" - CheckPipelienCronUrl = "/job/%s/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s" - CheckCronUrl = "/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s" -) diff --git a/pkg/models/git/git.go b/pkg/models/git/git.go index c4f6a027c..ef147dfd8 100644 --- a/pkg/models/git/git.go +++ b/pkg/models/git/git.go @@ -7,7 +7,7 @@ import ( "gopkg.in/src-d/go-git.v4/plumbing/transport/http" "gopkg.in/src-d/go-git.v4/storage/memory" corev1 "k8s.io/api/core/v1" - "kubesphere.io/kubesphere/pkg/informers" + "k8s.io/client-go/informers" ) type AuthInfo struct { @@ -15,12 +15,23 @@ type AuthInfo struct { SecretRef *corev1.SecretReference `json:"secretRef,omitempty" description:"auth secret reference"` } -func GitReadVerify(namespace string, authInfo AuthInfo) error { - username := "" - password := "" - if authInfo.SecretRef != nil { - secret, err := informers.SharedInformerFactory().Core().V1().Secrets().Lister(). - Secrets(authInfo.SecretRef.Namespace).Get(authInfo.SecretRef.Name) +type GitVerifier interface { + VerifyGitCredential(remoteUrl, namespace, secretName string) error +} + +type gitVerifier struct { + informers informers.SharedInformerFactory +} + +func NewGitVerifier(informers informers.SharedInformerFactory) GitVerifier { + return &gitVerifier{informers: informers} +} + +func (c *gitVerifier) VerifyGitCredential(remoteUrl, namespace, secretName string) error { + var username, password string + + if len(secretName) > 0 { + secret, err := c.informers.Core().V1().Secrets().Lister().Secrets(namespace).Get(secretName) if err != nil { return err } @@ -36,10 +47,10 @@ func GitReadVerify(namespace string, authInfo AuthInfo) error { password = string(passwordBytes) } - return gitReadVerifyWithBasicAuth(string(username), string(password), authInfo.RemoteUrl) + return c.gitReadVerifyWithBasicAuth(username, password, remoteUrl) } -func gitReadVerifyWithBasicAuth(username string, password string, remote string) error { +func (c *gitVerifier) gitReadVerifyWithBasicAuth(username string, password string, remote string) error { r, _ := git.Init(memory.NewStorage(), nil) // Add a new remote, with the default fetch refspec diff --git a/pkg/models/git/git_test.go b/pkg/models/git/git_test.go index fdc0954bd..7989f60ba 100644 --- a/pkg/models/git/git_test.go +++ b/pkg/models/git/git_test.go @@ -34,8 +34,10 @@ func TestGitReadVerifyWithBasicAuth(t *testing.T) { "remote": "git@fdsfs41342`@@@2414!!!!github.com:kubesphere/kubesphere.git", }, } + verifier := gitVerifier{informers: nil} + for _, item := range shouldSuccess { - err := gitReadVerifyWithBasicAuth(item["username"], item["password"], item["remote"]) + err := verifier.gitReadVerifyWithBasicAuth(item["username"], item["password"], item["remote"]) if err != nil { t.Errorf("should could access repo [%s] with %s:%s, %v", item["username"], item["password"], item["remote"], err) @@ -43,7 +45,7 @@ func TestGitReadVerifyWithBasicAuth(t *testing.T) { } for _, item := range shouldFailed { - err := gitReadVerifyWithBasicAuth(item["username"], item["password"], item["remote"]) + err := verifier.gitReadVerifyWithBasicAuth(item["username"], item["password"], item["remote"]) if err == nil { t.Errorf("should could access repo [%s] with %s:%s ", item["username"], item["password"], item["remote"]) } diff --git a/pkg/models/iam/am.go b/pkg/models/iam/am.go deleted file mode 100644 index 8131e14de..000000000 --- a/pkg/models/iam/am.go +++ /dev/null @@ -1,871 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "fmt" - "github.com/go-ldap/ldap" - rbacv1 "k8s.io/api/rbac/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/iam/policy" - "kubesphere.io/kubesphere/pkg/models/kubectl" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" -) - -const ( - ClusterRoleKind = "ClusterRole" - NamespaceAdminRoleBindName = "admin" - NamespaceViewerRoleBindName = "viewer" -) - -func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { - var rules []models.SimpleRule - - switch role { - case "developer": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"view", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - case "owner": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "devops", Actions: []string{"edit", "view", "delete"}}, - } - break - case "maintainer": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - case "reporter": - fallthrough - default: - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"view"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - } - return rules -} - -// Get user roles in namespace -func GetUserRoles(namespace, username string) ([]*rbacv1.Role, error) { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() - roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister() - roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything()) - - if err != nil { - klog.Errorln("get role bindings", namespace, err) - return nil, err - } - - roles := make([]*rbacv1.Role, 0) - - for _, roleBinding := range roleBindings { - if k8sutil.ContainsUser(roleBinding.Subjects, username) { - if roleBinding.RoleRef.Kind == ClusterRoleKind { - clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name) - if err != nil { - if apierrors.IsNotFound(err) { - klog.Warningf("cluster role %s not found but bind user %s in namespace %s", roleBinding.RoleRef.Name, username, namespace) - continue - } else { - klog.Errorln("get cluster role", roleBinding.RoleRef.Name, err) - return nil, err - } - } - role := rbacv1.Role{} - role.TypeMeta = clusterRole.TypeMeta - role.ObjectMeta = clusterRole.ObjectMeta - role.Rules = clusterRole.Rules - role.Namespace = roleBinding.Namespace - roles = append(roles, &role) - } else { - role, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name) - - if err != nil { - if apierrors.IsNotFound(err) { - klog.Warningf("namespace %s role %s not found, but bind user %s", namespace, roleBinding.RoleRef.Name, username) - continue - } else { - klog.Errorln("get role", roleBinding.Namespace, roleBinding.RoleRef.Name, err) - return nil, err - } - } - roles = append(roles, role) - } - } - } - - return roles, nil -} - -func GetUserClusterRoles(username string) (*rbacv1.ClusterRole, []*rbacv1.ClusterRole, error) { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() - clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything()) - - if err != nil { - klog.Errorln("get cluster role bindings", err) - return nil, nil, err - } - - clusterRoles := make([]*rbacv1.ClusterRole, 0) - userFacingClusterRole := &rbacv1.ClusterRole{} - for _, clusterRoleBinding := range clusterRoleBindings { - if k8sutil.ContainsUser(clusterRoleBinding.Subjects, username) { - clusterRole, err := clusterRoleLister.Get(clusterRoleBinding.RoleRef.Name) - if err != nil { - if apierrors.IsNotFound(err) { - klog.Warningf("cluster role %s not found but bind user %s", clusterRoleBinding.RoleRef.Name, username) - continue - } else { - klog.Errorln("get cluster role", clusterRoleBinding.RoleRef.Name, err) - return nil, nil, err - } - } - if clusterRoleBinding.Name == username { - userFacingClusterRole = clusterRole - } - clusterRoles = append(clusterRoles, clusterRole) - } - } - - return userFacingClusterRole, clusterRoles, nil -} - -func GetUserClusterRole(username string) (*rbacv1.ClusterRole, error) { - userFacingClusterRole, _, err := GetUserClusterRoles(username) - if err != nil { - return nil, err - } - return userFacingClusterRole, nil -} - -func GetUserClusterRules(username string) ([]rbacv1.PolicyRule, error) { - _, clusterRoles, err := GetUserClusterRoles(username) - - if err != nil { - return nil, err - } - - rules := make([]rbacv1.PolicyRule, 0) - for _, clusterRole := range clusterRoles { - rules = append(rules, clusterRole.Rules...) - } - - return rules, nil -} - -func GetUserRules(namespace, username string) ([]rbacv1.PolicyRule, error) { - roles, err := GetUserRoles(namespace, username) - - if err != nil { - return nil, err - } - - rules := make([]rbacv1.PolicyRule, 0) - for _, role := range roles { - rules = append(rules, role.Rules...) - } - - return rules, nil -} - -func GetWorkspaceRoleBindings(workspace string) ([]*rbacv1.ClusterRoleBinding, error) { - - clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) - - if err != nil { - klog.Errorln("get cluster role bindings", err) - return nil, err - } - - result := make([]*rbacv1.ClusterRoleBinding, 0) - - for _, roleBinding := range clusterRoleBindings { - if k8sutil.IsControlledBy(roleBinding.OwnerReferences, "Workspace", workspace) { - result = append(result, roleBinding) - } - } - - return result, nil -} - -func GetWorkspaceRole(workspace, role string) (*rbacv1.ClusterRole, error) { - if !sliceutil.HasString(constants.WorkSpaceRoles, role) { - return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role) - } - role = fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) - return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(role) -} - -func GetUserWorkspaceRoleMap(username string) (map[string]string, error) { - - clusterRoleBindings, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) - - if err != nil { - klog.Errorln("get cluster role bindings", err) - return nil, err - } - - result := make(map[string]string, 0) - - for _, roleBinding := range clusterRoleBindings { - if workspace := k8sutil.GetControlledWorkspace(roleBinding.OwnerReferences); workspace != "" && - k8sutil.ContainsUser(roleBinding.Subjects, username) { - result[workspace] = roleBinding.RoleRef.Name - } - } - - return result, nil -} - -func GetUserWorkspaceRole(workspace, username string) (*rbacv1.ClusterRole, error) { - workspaceRoleMap, err := GetUserWorkspaceRoleMap(username) - - if err != nil { - return nil, err - } - - if workspaceRole := workspaceRoleMap[workspace]; workspaceRole != "" { - return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(workspaceRole) - } - - return nil, apierrors.NewNotFound(schema.GroupResource{Resource: "workspace user"}, username) -} - -func GetRoleBindings(namespace string, roleName string) ([]*rbacv1.RoleBinding, error) { - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() - roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything()) - - if err != nil { - klog.Errorln("get role bindings", namespace, err) - return nil, err - } - - items := make([]*rbacv1.RoleBinding, 0) - - for _, roleBinding := range roleBindings { - if roleName == "" { - items = append(items, roleBinding) - } else if roleBinding.RoleRef.Name == roleName { - items = append(items, roleBinding) - } - } - - return items, nil -} - -func GetClusterRoleBindings(clusterRoleName string) ([]*rbacv1.ClusterRoleBinding, error) { - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() - roleBindings, err := clusterRoleBindingLister.List(labels.Everything()) - - if err != nil { - klog.Errorln("get cluster role bindings", err) - return nil, err - } - - items := make([]*rbacv1.ClusterRoleBinding, 0) - - for _, roleBinding := range roleBindings { - if roleBinding.RoleRef.Name == clusterRoleName { - items = append(items, roleBinding) - } - } - - return items, nil -} - -func ListClusterRoleUsers(clusterRoleName string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - - roleBindings, err := GetClusterRoleBindings(clusterRoleName) - - if err != nil { - return nil, err - } - users := make([]*models.User, 0) - for _, roleBinding := range roleBindings { - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - user, err := GetUserInfo(subject.Name) - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - continue - } - if err != nil { - klog.Errorln("get user info", subject.Name, err) - return nil, err - } - users = append(users, user) - } - } - } - - // order & reverse - sort.Slice(users, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - switch orderBy { - default: - fallthrough - case resources.Name: - return strings.Compare(users[i].Username, users[j].Username) <= 0 - } - }) - - result := make([]interface{}, 0) - - for i, d := range users { - if i >= offset && (limit == -1 || len(result) < limit) { - result = append(result, d) - } - } - - return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil - -} - -func RoleUsers(namespace string, roleName string) ([]*models.User, error) { - roleBindings, err := GetRoleBindings(namespace, roleName) - - if err != nil { - return nil, err - } - - users := make([]*models.User, 0) - for _, roleBinding := range roleBindings { - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - user, err := GetUserInfo(subject.Name) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - continue - } - return nil, err - } - - user.Role = roleBinding.RoleRef.Name - - users = append(users, user) - } - } - } - return users, nil -} - -func ListRoles(namespace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - return resources.ListResources(namespace, resources.Roles, conditions, orderBy, reverse, limit, offset) -} - -func ListWorkspaceRoles(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - conditions.Match[resources.OwnerName] = workspace - conditions.Match[resources.OwnerKind] = "Workspace" - result, err := resources.ListResources("", resources.ClusterRoles, conditions, orderBy, reverse, limit, offset) - - if err != nil { - return nil, err - } - - for i, item := range result.Items { - if role, ok := item.(*rbacv1.ClusterRole); ok { - role = role.DeepCopy() - role.Name = role.Annotations[constants.DisplayNameAnnotationKey] - result.Items[i] = role - } - } - return result, nil -} - -func ListClusterRoles(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - return resources.ListResources("", resources.ClusterRoles, conditions, orderBy, reverse, limit, offset) -} - -func NamespaceUsers(namespaceName string) ([]*models.User, error) { - namespace, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespaceName) - if err != nil { - klog.Errorln("get namespace", namespaceName, err) - return nil, err - } - roleBindings, err := GetRoleBindings(namespaceName, "") - - if err != nil { - return nil, err - } - - users := make([]*models.User, 0) - - for _, roleBinding := range roleBindings { - // controlled by ks-controller-manager - if roleBinding.Name == NamespaceViewerRoleBindName { - continue - } - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - - // show creator - if roleBinding.Name == NamespaceAdminRoleBindName && subject.Name != namespace.Annotations[constants.CreatorAnnotationKey] { - continue - } - - user, err := GetUserInfo(subject.Name) - - if err != nil { - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - continue - } - return nil, err - } - - user.Role = roleBinding.RoleRef.Name - user.RoleBindTime = &roleBinding.CreationTimestamp.Time - user.RoleBinding = roleBinding.Name - users = append(users, user) - } - } - } - - return users, nil -} - -func GetUserWorkspaceSimpleRules(workspace, username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) - if err != nil { - return nil, err - } - - // cluster-admin - if RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{ - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }) { - return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspaceAdmin), nil - } - - workspaceRole, err := GetUserWorkspaceRole(workspace, username) - - if err != nil { - if apierrors.IsNotFound(err) { - - // workspaces-manager - if RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{ - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"workspaces", "workspaces/*"}, - }) { - return GetWorkspaceRoleSimpleRules(workspace, constants.WorkspacesManager), nil - } - - return []models.SimpleRule{}, nil - } - - klog.Error(err) - return nil, err - } - - return GetWorkspaceRoleSimpleRules(workspace, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]), nil -} - -func GetWorkspaceRoleSimpleRules(workspace, roleName string) []models.SimpleRule { - - workspaceRules := make([]models.SimpleRule, 0) - - switch roleName { - case constants.WorkspaceAdmin: - workspaceRules = []models.SimpleRule{ - {Name: "workspaces", Actions: []string{"edit", "delete", "view"}}, - {Name: "members", Actions: []string{"edit", "delete", "create", "view"}}, - {Name: "devops", Actions: []string{"edit", "delete", "create", "view"}}, - {Name: "projects", Actions: []string{"edit", "delete", "create", "view"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "apps", Actions: []string{"view", "create", "manage"}}, - {Name: "repos", Actions: []string{"view", "manage"}}, - } - case constants.WorkspaceRegular: - workspaceRules = []models.SimpleRule{ - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view", "create"}}, - {Name: "projects", Actions: []string{"view", "create"}}, - {Name: "apps", Actions: []string{"view", "create"}}, - {Name: "repos", Actions: []string{"view"}}, - } - case constants.WorkspaceViewer: - workspaceRules = []models.SimpleRule{ - {Name: "workspaces", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view"}}, - {Name: "projects", Actions: []string{"view"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "apps", Actions: []string{"view"}}, - {Name: "repos", Actions: []string{"view"}}, - } - case constants.WorkspacesManager: - workspaceRules = []models.SimpleRule{ - {Name: "workspaces", Actions: []string{"edit", "delete", "view"}}, - {Name: "members", Actions: []string{"edit", "delete", "create", "view"}}, - {Name: "roles", Actions: []string{"view"}}, - } - } - - return workspaceRules -} - -// Convert cluster role to rules -func GetClusterRoleSimpleRules(clusterRoleName string) ([]models.SimpleRule, error) { - - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - clusterRole, err := clusterRoleLister.Get(clusterRoleName) - - if err != nil { - klog.Errorln("get cluster role", clusterRoleName, clusterRoleName) - return nil, err - } - - return getClusterSimpleRule(clusterRole.Rules), nil -} - -func GetUserClusterSimpleRules(username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) - if err != nil { - return nil, err - } - return getClusterSimpleRule(clusterRules), nil -} - -func GetUserNamespaceSimpleRules(namespace, username string) ([]models.SimpleRule, error) { - clusterRules, err := GetUserClusterRules(username) - if err != nil { - return nil, err - } - rules, err := GetUserRules(namespace, username) - if err != nil { - return nil, err - } - rules = append(rules, clusterRules...) - - return getSimpleRule(rules), nil -} - -// Convert roles to rules -func GetRoleSimpleRules(namespace string, roleName string) ([]models.SimpleRule, error) { - - roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister() - role, err := roleLister.Roles(namespace).Get(roleName) - - if err != nil { - klog.Errorln("get role", namespace, roleName, err) - return nil, err - } - - return getSimpleRule(role.Rules), nil -} - -func getClusterSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { - rules := make([]models.SimpleRule, 0) - - for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ { - validActions := make([]string, 0) - for j := 0; j < (len(policy.ClusterRoleRuleMapping[i].Actions)); j++ { - if rulesMatchesAction(policyRules, policy.ClusterRoleRuleMapping[i].Actions[j]) { - validActions = append(validActions, policy.ClusterRoleRuleMapping[i].Actions[j].Name) - } - } - if len(validActions) > 0 { - rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions}) - } - } - - return rules -} - -func getSimpleRule(policyRules []rbacv1.PolicyRule) []models.SimpleRule { - simpleRules := make([]models.SimpleRule, 0) - for i := 0; i < len(policy.RoleRuleMapping); i++ { - rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name} - rule.Actions = make([]string, 0) - for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ { - if rulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) { - rule.Actions = append(rule.Actions, policy.RoleRuleMapping[i].Actions[j].Name) - } - } - if len(rule.Actions) > 0 { - simpleRules = append(simpleRules, rule) - } - } - return simpleRules -} - -func CreateClusterRoleBinding(username string, clusterRoleName string) error { - clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister() - - _, err := clusterRoleLister.Get(clusterRoleName) - - if err != nil { - klog.Errorln("get cluster role", clusterRoleName, err) - return err - } - - if clusterRoleName == constants.ClusterAdmin { - // create kubectl pod if cluster role is cluster-admin - if err := kubectl.CreateKubectlDeploy(username); err != nil { - klog.Error("create user terminal pod failed", username, err) - } - - } else { - // delete kubectl pod if cluster role is not cluster-admin, whether it exists or not - if err := kubectl.DelKubectlDeploy(username); err != nil { - klog.Error("delete user terminal pod failed", username, err) - } - } - - clusterRoleBinding := &rbacv1.ClusterRoleBinding{} - clusterRoleBinding.Name = username - clusterRoleBinding.RoleRef = rbacv1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind} - clusterRoleBinding.Subjects = []rbacv1.Subject{{Kind: rbacv1.UserKind, Name: username}} - - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() - found, err := clusterRoleBindingLister.Get(username) - - if apierrors.IsNotFound(err) { - _, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding) - if err != nil { - klog.Errorln("create cluster role binding", err) - return err - } - return nil - } else if err != nil { - return err - } - - // cluster role changed - if found.RoleRef.Name != clusterRoleName { - deletePolicy := metav1.DeletePropagationBackground - gracePeriodSeconds := int64(0) - deleteOption := &metav1.DeleteOptions{PropagationPolicy: &deletePolicy, GracePeriodSeconds: &gracePeriodSeconds} - err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Delete(found.Name, deleteOption) - if err != nil { - klog.Errorln(err) - return err - } - _, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding) - if err != nil { - klog.Errorln(err) - return err - } - return nil - } - - if !k8sutil.ContainsUser(found.Subjects, username) { - found.Subjects = clusterRoleBinding.Subjects - _, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(found) - if err != nil { - klog.Errorln("update cluster role binding", err) - return err - } - } - - return nil -} - -func RulesMatchesRequired(rules []rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { - for _, rule := range rules { - if ruleMatchesRequired(rule, required) { - return true - } - } - return false -} - -func rulesMatchesAction(rules []rbacv1.PolicyRule, action models.Action) bool { - - for _, required := range action.Rules { - if !RulesMatchesRequired(rules, required) { - return false - } - } - - return true -} - -func ruleMatchesRequired(rule rbacv1.PolicyRule, required rbacv1.PolicyRule) bool { - - if len(required.NonResourceURLs) == 0 { - for _, apiGroup := range required.APIGroups { - for _, resource := range required.Resources { - resources := strings.Split(resource, "/") - resource = resources[0] - var subsource string - if len(resources) > 1 { - subsource = resources[1] - } - - if len(required.ResourceNames) == 0 { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) { - return false - } - } - } else { - for _, resourceName := range required.ResourceNames { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) { - return false - } - } - } - } - } - } - } else { - for _, apiGroup := range required.APIGroups { - for _, nonResourceURL := range required.NonResourceURLs { - for _, verb := range required.Verbs { - if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) { - return false - } - } - } - } - } - return true -} - -func ruleMatchesResources(rule rbacv1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool { - - if resource == "" { - return false - } - - if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, rbacv1.ResourceAll) { - return false - } - - if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) { - return false - } - - combinedResource := resource - - if subresource != "" { - combinedResource = combinedResource + "/" + subresource - } - - for _, res := range rule.Resources { - - // match "*" - if res == rbacv1.ResourceAll || res == combinedResource { - return true - } - - // match "*/subresource" - if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") { - return true - } - // match "resource/*" - if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") { - return true - } - } - - return false -} - -func ruleMatchesRequest(rule rbacv1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool { - - if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, rbacv1.VerbAll) { - return false - } - - if nonResourceURL == "" { - return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName) - } else { - return ruleMatchesNonResource(rule, nonResourceURL) - } -} - -func ruleMatchesNonResource(rule rbacv1.PolicyRule, nonResourceURL string) bool { - - if nonResourceURL == "" { - return false - } - - for _, spec := range rule.NonResourceURLs { - if pathMatches(nonResourceURL, spec) { - return true - } - } - - return false -} - -func pathMatches(path, spec string) bool { - // Allow wildcard match - if spec == "*" { - return true - } - // Allow exact match - if spec == path { - return true - } - // Allow a trailing * subpath match - if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) { - return true - } - return false -} - -func hasString(slice []string, value string) bool { - for _, s := range slice { - if s == value { - return true - } - } - return false -} diff --git a/pkg/models/iam/am/am.go b/pkg/models/iam/am/am.go new file mode 100644 index 000000000..c0f8eee53 --- /dev/null +++ b/pkg/models/iam/am/am.go @@ -0,0 +1,374 @@ +/* + * + * Copyright 2020 The KubeSphere 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 am + +import ( + "fmt" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + k8sinformers "k8s.io/client-go/informers" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" + "net/http" +) + +type AccessManagementInterface interface { + GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) + GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) + GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) + GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) + ListRoles(username string, query *query.Query) (*api.ListResult, error) + ListClusterRoles(query *query.Query) (*api.ListResult, error) + ListWorkspaceRoles(query *query.Query) (*api.ListResult, error) + ListGlobalRoles(query *query.Query) (*api.ListResult, error) + + ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) + ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) + ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) + ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) + + GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) +} + +type amOperator struct { + ksinformer ksinformers.SharedInformerFactory + k8sinformer k8sinformers.SharedInformerFactory + resourceGetter *resourcev1alpha3.ResourceGetter +} + +func NewAMOperator(factory informers.InformerFactory) AccessManagementInterface { + return &amOperator{ + ksinformer: factory.KubeSphereSharedInformerFactory(), + k8sinformer: factory.KubernetesSharedInformerFactory(), + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), + } +} + +func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) { + + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*iamv1alpha2.GlobalRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict global role binding, username: %s", username) + } + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: iamv1alpha2.SchemeGroupVersion.Group, + Kind: iamv1alpha2.ResourceKindGlobalRoleBinding, + }, + Message: fmt.Sprintf("global role binding not found for %s", username), + }} + + return nil, err +} + +func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) { + + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.SelectorFromValidatedSet(labels.Set{tenantv1alpha1.WorkspaceLabel: workspace})) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + + if err != nil { + klog.Error(err) + return nil, err + } + + if len(roleBindings) > 1 { + klog.Warningf("conflict workspace role binding, username: %s", username) + } + + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: iamv1alpha2.SchemeGroupVersion.Group, + Kind: iamv1alpha2.ResourceKindWorkspaceRoleBinding, + }, + Message: fmt.Sprintf("workspace role binding not found for %s", username), + }} + + return nil, err +} + +func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(namespace).Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict role binding, username: %s", username) + } + return role, nil + } + + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "RoleBinding", + }, + Message: fmt.Sprintf("role binding not found for %s in %s", username, namespace), + }} + + return nil, err +} + +// Get federated clusterrole of user if cluster is not empty, if cluster is empty means current cluster +func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) { + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + userRoleBindings := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + userRoleBindings = append(userRoleBindings, roleBinding) + } + } + + if len(userRoleBindings) > 0 { + role, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(userRoleBindings[0].RoleRef.Name) + if err != nil { + klog.Error(err) + return nil, err + } + if len(roleBindings) > 1 { + klog.Warningf("conflict cluster role binding, username: %s", username) + } + return role, nil + } + err = &errors.StatusError{ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Details: &metav1.StatusDetails{ + Group: rbacv1.SchemeGroupVersion.Group, + Kind: "ClusterRoleBinding", + }, + Message: fmt.Sprintf("cluster role binding not found for %s in %s", username, cluster), + }} + + return nil, err +} + +func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.WorkspaceRoleBinding, 0) + + for _, roleBinding := range roleBindings { + inSpecifiedWorkspace := workspace == "" || roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] == workspace + if contains(roleBinding.Subjects, username) && inSpecifiedWorkspace { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*rbacv1.ClusterRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) { + roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*iamv1alpha2.GlobalRoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) { + + roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything()) + + if err != nil { + klog.Error(err) + return nil, err + } + + result := make([]*rbacv1.RoleBinding, 0) + + for _, roleBinding := range roleBindings { + if contains(roleBinding.Subjects, username) { + result = append(result, roleBinding) + } + } + + return result, nil +} + +func contains(subjects []rbacv1.Subject, username string) bool { + for _, subject := range subjects { + if subject.Kind == rbacv1.UserKind && (username == "" || subject.Name == username) { + return true + } + } + return false +} + +func (am *amOperator) ListRoles(namespace string, query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("roles", namespace, query) +} + +func (am *amOperator) ListClusterRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List("clusterroles", "", query) +} + +func (am *amOperator) ListWorkspaceRoles(queryParam *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralWorkspaceRole, "", queryParam) +} + +func (am *amOperator) ListGlobalRoles(query *query.Query) (*api.ListResult, error) { + return am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRole, "", query) +} + +// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding. +func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) { + switch roleRef.Kind { + case iamv1alpha2.ResourceKindRole: + role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(bindingNamespace).Get(roleRef.Name) + if err != nil { + return nil, err + } + return role.Rules, nil + case iamv1alpha2.ResourceKindClusterRole: + clusterRole, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return clusterRole.Rules, nil + case iamv1alpha2.ResourceKindGlobalRole: + globalRole, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return globalRole.Rules, nil + case iamv1alpha2.ResourceKindWorkspaceRole: + workspaceRole, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(roleRef.Name) + if err != nil { + return nil, err + } + return workspaceRole.Rules, nil + default: + return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind) + } +} diff --git a/pkg/models/iam/am/rbac/evaluation_helpers.go b/pkg/models/iam/am/rbac/evaluation_helpers.go new file mode 100644 index 000000000..abe010bda --- /dev/null +++ b/pkg/models/iam/am/rbac/evaluation_helpers.go @@ -0,0 +1,179 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authorizerfactory + +import ( + "fmt" + "strings" + + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func RoleRefGroupKind(roleRef rbacv1.RoleRef) schema.GroupKind { + return schema.GroupKind{Group: roleRef.APIGroup, Kind: roleRef.Kind} +} + +func VerbMatches(rule *rbacv1.PolicyRule, requestedVerb string) bool { + for _, ruleVerb := range rule.Verbs { + if ruleVerb == rbacv1.VerbAll { + return true + } + if ruleVerb == requestedVerb { + return true + } + } + + return false +} + +func APIGroupMatches(rule *rbacv1.PolicyRule, requestedGroup string) bool { + for _, ruleGroup := range rule.APIGroups { + if ruleGroup == rbacv1.APIGroupAll { + return true + } + if ruleGroup == requestedGroup { + return true + } + } + + return false +} + +func ResourceMatches(rule *rbacv1.PolicyRule, combinedRequestedResource, requestedSubresource string) bool { + for _, ruleResource := range rule.Resources { + // if everything is allowed, we match + if ruleResource == rbacv1.ResourceAll { + return true + } + // if we have an exact match, we match + if ruleResource == combinedRequestedResource { + return true + } + + // We can also match a */subresource. + // if there isn't a subresource, then continue + if len(requestedSubresource) == 0 { + continue + } + // if the rule isn't in the format */subresource, then we don't match, continue + if len(ruleResource) == len(requestedSubresource)+2 && + strings.HasPrefix(ruleResource, "*/") && + strings.HasSuffix(ruleResource, requestedSubresource) { + return true + + } + } + + return false +} + +func ResourceNameMatches(rule *rbacv1.PolicyRule, requestedName string) bool { + if len(rule.ResourceNames) == 0 { + return true + } + + for _, ruleName := range rule.ResourceNames { + if ruleName == requestedName { + return true + } + } + + return false +} + +func NonResourceURLMatches(rule *rbacv1.PolicyRule, requestedURL string) bool { + for _, ruleURL := range rule.NonResourceURLs { + if ruleURL == rbacv1.NonResourceAll { + return true + } + if ruleURL == requestedURL { + return true + } + if strings.HasSuffix(ruleURL, "*") && strings.HasPrefix(requestedURL, strings.TrimRight(ruleURL, "*")) { + return true + } + } + + return false +} + +// subjectsStrings returns users, groups, serviceaccounts, unknown for display purposes. +func SubjectsStrings(subjects []rbacv1.Subject) ([]string, []string, []string, []string) { + users := []string{} + groups := []string{} + sas := []string{} + others := []string{} + + for _, subject := range subjects { + switch subject.Kind { + case rbacv1.ServiceAccountKind: + sas = append(sas, fmt.Sprintf("%s/%s", subject.Namespace, subject.Name)) + + case rbacv1.UserKind: + users = append(users, subject.Name) + + case rbacv1.GroupKind: + groups = append(groups, subject.Name) + + default: + others = append(others, fmt.Sprintf("%s/%s/%s", subject.Kind, subject.Namespace, subject.Name)) + } + } + + return users, groups, sas, others +} + +func String(r rbacv1.PolicyRule) string { + return "PolicyRule" + CompactString(r) +} + +// CompactString exposes a compact string representation for use in escalation error messages +func CompactString(r rbacv1.PolicyRule) string { + formatStringParts := []string{} + formatArgs := []interface{}{} + if len(r.APIGroups) > 0 { + formatStringParts = append(formatStringParts, "APIGroups:%q") + formatArgs = append(formatArgs, r.APIGroups) + } + if len(r.Resources) > 0 { + formatStringParts = append(formatStringParts, "Resources:%q") + formatArgs = append(formatArgs, r.Resources) + } + if len(r.NonResourceURLs) > 0 { + formatStringParts = append(formatStringParts, "NonResourceURLs:%q") + formatArgs = append(formatArgs, r.NonResourceURLs) + } + if len(r.ResourceNames) > 0 { + formatStringParts = append(formatStringParts, "ResourceNames:%q") + formatArgs = append(formatArgs, r.ResourceNames) + } + if len(r.Verbs) > 0 { + formatStringParts = append(formatStringParts, "Verbs:%q") + formatArgs = append(formatArgs, r.Verbs) + } + formatString := "{" + strings.Join(formatStringParts, ", ") + "}" + return fmt.Sprintf(formatString, formatArgs...) +} + +type SortableRuleSlice []rbacv1.PolicyRule + +func (s SortableRuleSlice) Len() int { return len(s) } +func (s SortableRuleSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s SortableRuleSlice) Less(i, j int) bool { + return strings.Compare(s[i].String(), s[j].String()) < 0 +} diff --git a/pkg/models/iam/im.go b/pkg/models/iam/im.go deleted file mode 100644 index 4a1c02c40..000000000 --- a/pkg/models/iam/im.go +++ /dev/null @@ -1,1539 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "encoding/json" - "errors" - "fmt" - "github.com/emicklei/go-restful" - "io/ioutil" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/models/kubeconfig" - "kubesphere.io/kubesphere/pkg/models/kubectl" - "kubesphere.io/kubesphere/pkg/server/params" - clientset "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "net/http" - "regexp" - "sort" - "strconv" - "strings" - "time" - - "github.com/dgrijalva/jwt-go" - "github.com/go-ldap/ldap" - rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/utils/jwtutil" -) - -var ( - adminEmail string - adminPassword string - tokenIdleTimeout time.Duration - maxAuthFailed int - authTimeInterval time.Duration - initUsers []initUser - enableMultiLogin bool - generateKubeConfig bool -) - -type initUser struct { - models.User - Hidden bool `json:"hidden"` -} - -const ( - userInitFile = "/etc/ks-iam/users.json" - authRateLimitRegex = `(\d+)/(\d+[s|m|h])` - defaultMaxAuthFailed = 5 - defaultAuthTimeInterval = 30 * time.Minute -) - -func Init(email, password, authRateLimit string, idleTimeout time.Duration, multiLogin bool, isGeneratingKubeConfig bool) error { - adminEmail = email - adminPassword = password - tokenIdleTimeout = idleTimeout - maxAuthFailed, authTimeInterval = parseAuthRateLimit(authRateLimit) - enableMultiLogin = multiLogin - generateKubeConfig = isGeneratingKubeConfig - - err := checkAndCreateDefaultUser() - - if err != nil { - klog.Errorln("create default users", err) - return err - } - - err = checkAndCreateDefaultGroup() - - if err != nil { - klog.Errorln("create default groups", err) - return err - } - - return nil -} - -func parseAuthRateLimit(authRateLimit string) (int, time.Duration) { - regex := regexp.MustCompile(authRateLimitRegex) - groups := regex.FindStringSubmatch(authRateLimit) - - maxCount := defaultMaxAuthFailed - timeInterval := defaultAuthTimeInterval - - if len(groups) == 3 { - maxCount, _ = strconv.Atoi(groups[1]) - timeInterval, _ = time.ParseDuration(groups[2]) - } else { - klog.Warning("invalid auth rate limit", authRateLimit) - } - - return maxCount, timeInterval -} - -func checkAndCreateDefaultGroup() error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - - groupSearchRequest := ldap.NewSearchRequest( - client.GroupSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - nil, - nil, - ) - - _, err = conn.Search(groupSearchRequest) - - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - err = createGroupsBaseDN() - if err != nil { - return fmt.Errorf("GroupBaseDN %s create failed: %s\n", client.GroupSearchBase(), err) - } - } - - if err != nil { - return fmt.Errorf("iam database init failed: %s\n", err) - } - - return nil -} - -func checkAndCreateDefaultUser() error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=inetOrgPerson))", - []string{"uid"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { - err = createUserBaseDN() - if err != nil { - return fmt.Errorf("UserBaseDN %s create failed: %s\n", client.UserSearchBase(), err) - } - } - - if err != nil { - return fmt.Errorf("iam database init failed: %s\n", err) - } - - data, err := ioutil.ReadFile(userInitFile) - if err == nil { - json.Unmarshal(data, &initUsers) - } - initUsers = append(initUsers, initUser{User: models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default.", ClusterRole: constants.ClusterAdmin}}) - - for _, user := range initUsers { - if result == nil || !containsUser(result.Entries, user) { - _, err = CreateUser(&user.User) - if err != nil && !ldap.IsErrorWithCode(err, ldap.LDAPResultEntryAlreadyExists) { - klog.Errorln("user init failed", user.Username, err) - return fmt.Errorf("user %s init failed: %s\n", user.Username, err) - } - } - } - - return nil -} - -func containsUser(entries []*ldap.Entry, user initUser) bool { - for _, entry := range entries { - uid := entry.GetAttributeValue("uid") - if uid == user.Username { - return true - } - } - return false -} - -func createUserBaseDN() error { - client, err := clientset.ClientSets().Ldap() - - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - groupsCreateRequest := ldap.NewAddRequest(client.UserSearchBase(), nil) - groupsCreateRequest.Attribute("objectClass", []string{"organizationalUnit", "top"}) - groupsCreateRequest.Attribute("ou", []string{"Users"}) - return conn.Add(groupsCreateRequest) -} - -func createGroupsBaseDN() error { - client, err := clientset.ClientSets().Ldap() - - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - groupsCreateRequest := ldap.NewAddRequest(client.GroupSearchBase(), nil) - groupsCreateRequest.Attribute("objectClass", []string{"organizationalUnit", "top"}) - groupsCreateRequest.Attribute("ou", []string{"Groups"}) - return conn.Add(groupsCreateRequest) -} - -func RefreshToken(refreshToken string) (*models.AuthGrantResponse, error) { - validRefreshToken, err := jwtutil.ValidateToken(refreshToken) - if err != nil { - klog.Error(err) - return nil, err - } - - payload, ok := validRefreshToken.Claims.(jwt.MapClaims) - - if !ok { - err = errors.New("invalid payload") - klog.Error(err) - return nil, err - } - - claims := jwt.MapClaims{} - - // token with expiration time will not auto sliding - claims["username"] = payload["username"] - claims["email"] = payload["email"] - claims["iat"] = time.Now().Unix() - claims["exp"] = time.Now().Add(tokenIdleTimeout * 4).Unix() - - token := jwtutil.MustSigned(claims) - - claims = jwt.MapClaims{} - claims["username"] = payload["username"] - claims["email"] = payload["email"] - claims["iat"] = time.Now().Unix() - claims["type"] = "refresh_token" - claims["exp"] = time.Now().Add(tokenIdleTimeout * 5).Unix() - - refreshToken = jwtutil.MustSigned(claims) - - return &models.AuthGrantResponse{TokenType: "jwt", Token: token, RefreshToken: refreshToken, ExpiresIn: (tokenIdleTimeout * 4).Seconds()}, nil -} - -func PasswordCredentialGrant(username, password, ip string) (*models.AuthGrantResponse, error) { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result() - - if err != nil { - klog.Error(err) - return nil, err - } - - if len(records) >= maxAuthFailed { - return nil, restful.NewError(http.StatusTooManyRequests, "auth rate limit exceeded") - } - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password")) - } - - uid := result.Entries[0].GetAttributeValue("uid") - email := result.Entries[0].GetAttributeValue("mail") - dn := result.Entries[0].DN - - // bind as the user to verify their password - err = conn.Bind(dn, password) - - if err != nil { - klog.Infoln("auth failed", username, err) - - if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { - loginFailedRecord := fmt.Sprintf("kubesphere:authfailed:%s:%d", uid, time.Now().UnixNano()) - redisClient.Set(loginFailedRecord, "", authTimeInterval) - } - - return nil, err - } - - claims := jwt.MapClaims{} - - // token with expiration time will not auto sliding - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() - claims["exp"] = time.Now().Add(tokenIdleTimeout * 4).Unix() - - token := jwtutil.MustSigned(claims) - - if !enableMultiLogin { - // multi login not allowed, remove the previous token - sessions, err := redisClient.Keys(fmt.Sprintf("kubesphere:users:%s:token:*", uid)).Result() - - if err != nil { - klog.Errorln(err) - return nil, err - } - - if len(sessions) > 0 { - klog.V(4).Infoln("revoke token", sessions) - err = redisClient.Del(sessions...).Err() - if err != nil { - klog.Errorln(err) - return nil, err - } - } - } - - claims = jwt.MapClaims{} - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() - claims["type"] = "refresh_token" - claims["exp"] = time.Now().Add(tokenIdleTimeout * 5).Unix() - - refreshToken := jwtutil.MustSigned(claims) - - loginLog(uid, ip) - - return &models.AuthGrantResponse{TokenType: "jwt", Token: token, RefreshToken: refreshToken, ExpiresIn: (tokenIdleTimeout * 4).Seconds()}, nil -} - -// User login -func Login(username, password, ip string) (*models.AuthGrantResponse, error) { - - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result() - - if err != nil { - klog.Error(err) - return nil, err - } - - if len(records) >= maxAuthFailed { - return nil, restful.NewError(http.StatusTooManyRequests, "auth rate limit exceeded") - } - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password")) - } - - uid := result.Entries[0].GetAttributeValue("uid") - email := result.Entries[0].GetAttributeValue("mail") - dn := result.Entries[0].DN - - // bind as the user to verify their password - err = conn.Bind(dn, password) - - if err != nil { - klog.Infoln("auth failed", username, err) - - if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { - loginFailedRecord := fmt.Sprintf("kubesphere:authfailed:%s:%d", uid, time.Now().UnixNano()) - redisClient.Set(loginFailedRecord, "", authTimeInterval) - } - - return nil, err - } - - claims := jwt.MapClaims{} - - // token without expiration time will auto sliding - claims["username"] = uid - claims["email"] = email - claims["iat"] = time.Now().Unix() - - token := jwtutil.MustSigned(claims) - - if !enableMultiLogin { - // multi login not allowed, remove the previous token - sessions, err := redisClient.Keys(fmt.Sprintf("kubesphere:users:%s:token:*", uid)).Result() - - if err != nil { - klog.Errorln(err) - return nil, err - } - - if len(sessions) > 0 { - klog.V(4).Infoln("revoke token", sessions) - err = redisClient.Del(sessions...).Err() - if err != nil { - klog.Errorln(err) - return nil, err - } - } - } - - // cache token with expiration time - if err = redisClient.Set(fmt.Sprintf("kubesphere:users:%s:token:%s", uid, token), token, tokenIdleTimeout).Err(); err != nil { - klog.Errorln(err) - return nil, err - } - - loginLog(uid, ip) - - return &models.AuthGrantResponse{Token: token}, nil -} - -func loginLog(uid, ip string) { - if ip != "" { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return - } - redisClient.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", uid), fmt.Sprintf("%s,%s", time.Now().UTC().Format("2006-01-02T15:04:05Z"), ip)) - redisClient.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -10, -1) - } -} - -func LoginLog(username string) ([]string, error) { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - data, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1).Result() - - if err != nil { - return nil, err - } - - return data, nil -} - -func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - pageControl := ldap.NewControlPaging(1000) - - users := make([]models.User, 0) - - filter := "(&(objectClass=inetOrgPerson))" - - if keyword := conditions.Match["keyword"]; keyword != "" { - filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword) - } - - if username := conditions.Match["username"]; username != "" { - uidFilter := "" - for _, username := range strings.Split(username, "|") { - uidFilter += fmt.Sprintf("(uid=%s)", username) - } - filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter) - } - - if email := conditions.Match["email"]; email != "" { - emailFilter := "" - for _, username := range strings.Split(email, "|") { - emailFilter += fmt.Sprintf("(mail=%s)", username) - } - filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter) - } - - for { - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - filter, - []string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"}, - []ldap.Control{pageControl}, - ) - - response, err := conn.Search(userSearchRequest) - - if err != nil { - klog.Errorln("search user", err) - return nil, err - } - - for _, entry := range response.Entries { - - uid := entry.GetAttributeValue("uid") - email := entry.GetAttributeValue("mail") - description := entry.GetAttributeValue("description") - lang := entry.GetAttributeValue("preferredLanguage") - createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp")) - - user := models.User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} - - if !shouldHidden(user) { - users = append(users, user) - } - } - - updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging) - if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 { - pageControl.SetCookie(ctrl.Cookie) - continue - } - - break - } - - sort.Slice(users, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - switch orderBy { - case "username": - return strings.Compare(users[i].Username, users[j].Username) <= 0 - case "createTime": - fallthrough - default: - return users[i].CreateTime.Before(users[j].CreateTime) - } - }) - - items := make([]interface{}, 0) - - for i, user := range users { - - if i >= offset && len(items) < limit { - - user.LastLoginTime = getLastLoginTime(user.Username) - clusterRole, err := GetUserClusterRole(user.Username) - if err != nil { - return nil, err - } - user.ClusterRole = clusterRole.Name - items = append(items, user) - } - } - - return &models.PageableResponse{Items: items, TotalCount: len(users)}, nil -} - -func shouldHidden(user models.User) bool { - for _, initUser := range initUsers { - if initUser.Username == user.Username { - return initUser.Hidden - } - } - return false -} - -func DescribeUser(username string) (*models.User, error) { - - user, err := GetUserInfo(username) - - if err != nil { - return nil, err - } - - groups, err := GetUserGroups(username) - - if err == nil { - user.Groups = groups - } - - return user, nil -} - -// Get user info only included email description & lang -func GetUserInfo(username string) (*models.User, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username), - []string{"mail", "description", "preferredLanguage", "createTimestamp"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - klog.Errorln("search user", err) - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, fmt.Errorf("user %s does not exist", username)) - } - - email := result.Entries[0].GetAttributeValue("mail") - description := result.Entries[0].GetAttributeValue("description") - lang := result.Entries[0].GetAttributeValue("preferredLanguage") - createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp")) - user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp} - - user.LastLoginTime = getLastLoginTime(username) - - return user, nil -} - -func GetUserGroups(username string) ([]string, error) { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - groupSearchRequest := ldap.NewSearchRequest( - client.GroupSearchBase(), - ldap.ScopeBaseObject, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=posixGroup)(memberUid=%s))", username), - nil, - nil, - ) - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - return nil, err - } - - groups := make([]string, 0) - - for _, group := range result.Entries { - groupName := convertDNToPath(group.DN) - groups = append(groups, groupName) - } - - return groups, nil -} - -func getLastLoginTime(username string) string { - redis, err := clientset.ClientSets().Redis() - if err != nil { - return "" - } - - lastLogin, err := redis.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result() - - if err != nil { - return "" - } - - if len(lastLogin) > 0 { - return strings.Split(lastLogin[0], ",")[0] - } - - return "" -} - -func DeleteUser(username string) error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - - deleteRequest := ldap.NewDelRequest(fmt.Sprintf("uid=%s,%s", username, client.UserSearchBase()), nil) - - if err = conn.Del(deleteRequest); err != nil { - klog.Errorln("delete user", err) - return err - } - - if err = deleteRoleBindings(username); err != nil { - klog.Errorln("delete user role bindings failed", username, err) - } - - if err := kubeconfig.DelKubeConfig(username); err != nil { - klog.Errorln("delete user kubeconfig failed", username, err) - } - - if err := kubectl.DelKubectlDeploy(username); err != nil { - klog.Errorln("delete user terminal pod failed", username, err) - } - - if err := deleteUserInDevOps(username); err != nil { - klog.Errorln("delete user in devops failed", username, err) - } - return nil - -} - -// deleteUserInDevOps is used to clean up user data of devops, such as permission rules -func deleteUserInDevOps(username string) error { - - devopsDb, err := clientset.ClientSets().MySQL() - if err != nil { - if _, ok := err.(clientset.ClientSetNotEnabledError); ok { - klog.Warning("devops client is not enable") - return nil - } - return err - } - - dp, err := clientset.ClientSets().Devops() - if err != nil { - if _, ok := err.(clientset.ClientSetNotEnabledError); ok { - klog.Warning("devops client is not enable") - return nil - } - - return err - } - - jenkinsClient := dp.Jenkins() - - _, err = devopsDb.DeleteFrom(devops.DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username), - )).Exec() - if err != nil { - klog.Errorf("%+v", err) - return err - } - - err = jenkinsClient.DeleteUserInProject(username) - if err != nil { - klog.Errorf("%+v", err) - return err - } - return nil -} - -func deleteRoleBindings(username string) error { - roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister() - roleBindings, err := roleBindingLister.List(labels.Everything()) - - if err != nil { - return err - } - - for _, roleBinding := range roleBindings { - roleBinding = roleBinding.DeepCopy() - length1 := len(roleBinding.Subjects) - - for index, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && subject.Name == username { - roleBinding.Subjects = append(roleBinding.Subjects[:index], roleBinding.Subjects[index+1:]...) - index-- - } - } - - length2 := len(roleBinding.Subjects) - - if length2 == 0 { - deletePolicy := metav1.DeletePropagationBackground - err = clientset.ClientSets().K8s().Kubernetes().RbacV1().RoleBindings(roleBinding.Namespace).Delete(roleBinding.Name, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) - - if err != nil { - klog.Errorf("delete role binding %s %s %s failed: %v", username, roleBinding.Namespace, roleBinding.Name, err) - } - } else if length2 < length1 { - _, err = clientset.ClientSets().K8s().Kubernetes().RbacV1().RoleBindings(roleBinding.Namespace).Update(roleBinding) - - if err != nil { - klog.Errorf("update role binding %s %s %s failed: %v", username, roleBinding.Namespace, roleBinding.Name, err) - } - } - } - - clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister() - clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything()) - - for _, clusterRoleBinding := range clusterRoleBindings { - clusterRoleBinding = clusterRoleBinding.DeepCopy() - length1 := len(clusterRoleBinding.Subjects) - - for index, subject := range clusterRoleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && subject.Name == username { - clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects[:index], clusterRoleBinding.Subjects[index+1:]...) - index-- - } - } - - length2 := len(clusterRoleBinding.Subjects) - if length2 == 0 { - // delete if it's not workspace role binding - if isWorkspaceRoleBinding(clusterRoleBinding) { - _, err = clientset.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding) - } else { - deletePolicy := metav1.DeletePropagationBackground - err = clientset.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Delete(clusterRoleBinding.Name, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) - } - if err != nil { - klog.Errorf("update cluster role binding %s failed:%s", clusterRoleBinding.Name, err) - } - } else if length2 < length1 { - _, err = clientset.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding) - - if err != nil { - klog.Errorf("update cluster role binding %s failed:%s", clusterRoleBinding.Name, err) - } - } - - } - - return nil -} - -func isWorkspaceRoleBinding(clusterRoleBinding *rbacv1.ClusterRoleBinding) bool { - return k8sutil.IsControlledBy(clusterRoleBinding.OwnerReferences, "Workspace", "") -} - -func UserCreateCheck(check string) (exist bool, err error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return false, err - } - conn, err := client.NewConn() - if err != nil { - return false, err - } - defer conn.Close() - - // search for the given username - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", check, check), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - klog.Errorln("search user", err) - return false, err - } - - return len(result.Entries) > 0, nil -} - -func CreateUser(user *models.User) (*models.User, error) { - user.Username = strings.TrimSpace(user.Username) - user.Email = strings.TrimSpace(user.Email) - user.Password = strings.TrimSpace(user.Password) - user.Description = strings.TrimSpace(user.Description) - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", user.Username, user.Email), - []string{"uid", "mail"}, - nil, - ) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - klog.Errorln("search user", err) - return nil, err - } - - if len(result.Entries) > 0 { - return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists")) - } - - maxUid, err := getMaxUid() - - if err != nil { - klog.Errorln("get max uid", err) - return nil, err - } - - maxUid += 1 - - userCreateRequest := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, client.UserSearchBase()), nil) - userCreateRequest.Attribute("objectClass", []string{"inetOrgPerson", "posixAccount", "top"}) - userCreateRequest.Attribute("cn", []string{user.Username}) // RFC4519: common name(s) for which the entity is known by - userCreateRequest.Attribute("sn", []string{" "}) // RFC2256: last (family) name(s) for which the entity is known by - userCreateRequest.Attribute("gidNumber", []string{"500"}) // RFC2307: An integer uniquely identifying a group in an administrative domain - userCreateRequest.Attribute("homeDirectory", []string{"/home/" + user.Username}) // The absolute path to the home directory - userCreateRequest.Attribute("uid", []string{user.Username}) // RFC4519: user identifier - userCreateRequest.Attribute("uidNumber", []string{strconv.Itoa(maxUid)}) // RFC2307: An integer uniquely identifying a user in an administrative domain - userCreateRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox - userCreateRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user - if user.Lang != "" { - userCreateRequest.Attribute("preferredLanguage", []string{user.Lang}) - } - if user.Description != "" { - userCreateRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information - } - - if generateKubeConfig { - if err = kubeconfig.CreateKubeConfig(user.Username); err != nil { - klog.Errorln("create user kubeconfig failed", user.Username, err) - return nil, err - } - } - - err = conn.Add(userCreateRequest) - - if err != nil { - klog.Errorln("create user", err) - return nil, err - } - - if user.ClusterRole != "" { - err := CreateClusterRoleBinding(user.Username, user.ClusterRole) - - if err != nil { - klog.Errorln("create cluster role binding filed", err) - return nil, err - } - } - - return DescribeUser(user.Username) -} - -func getMaxUid() (int, error) { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return 0, err - } - conn, err := client.NewConn() - if err != nil { - return 0, err - } - defer conn.Close() - - userSearchRequest := ldap.NewSearchRequest(client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=inetOrgPerson))", - []string{"uidNumber"}, - nil) - - result, err := conn.Search(userSearchRequest) - - if err != nil { - return 0, err - } - - var maxUid int - - if len(result.Entries) == 0 { - maxUid = 1000 - } else { - for _, usr := range result.Entries { - uid, _ := strconv.Atoi(usr.GetAttributeValue("uidNumber")) - if uid > maxUid { - maxUid = uid - } - } - } - - return maxUid, nil -} - -func getMaxGid() (int, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return 0, err - } - conn, err := client.NewConn() - if err != nil { - return 0, err - } - defer conn.Close() - - groupSearchRequest := ldap.NewSearchRequest(client.GroupSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - []string{"gidNumber"}, - nil) - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - return 0, err - } - - var maxGid int - - if len(result.Entries) == 0 { - maxGid = 500 - } else { - for _, group := range result.Entries { - gid, _ := strconv.Atoi(group.GetAttributeValue("gidNumber")) - if gid > maxGid { - maxGid = gid - } - } - } - - return maxGid, nil -} - -func UpdateUser(user *models.User) (*models.User, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - dn := fmt.Sprintf("uid=%s,%s", user.Username, client.UserSearchBase()) - userModifyRequest := ldap.NewModifyRequest(dn, nil) - if user.Email != "" { - userSearchRequest := ldap.NewSearchRequest( - client.UserSearchBase(), - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=inetOrgPerson)(mail=%s))", user.Email), - []string{"uid", "mail"}, - nil, - ) - result, err := conn.Search(userSearchRequest) - if err != nil { - klog.Error(err) - return nil, err - } - if len(result.Entries) > 1 { - err = ldap.NewError(ldap.ErrorDebugging, fmt.Errorf("email is duplicated: %s", user.Email)) - klog.Error(err) - return nil, err - } - if len(result.Entries) == 1 && result.Entries[0].GetAttributeValue("uid") != user.Username { - err = ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("email is duplicated: %s", user.Email)) - klog.Error(err) - return nil, err - } - userModifyRequest.Replace("mail", []string{user.Email}) - } - if user.Description != "" { - userModifyRequest.Replace("description", []string{user.Description}) - } - - if user.Lang != "" { - userModifyRequest.Replace("preferredLanguage", []string{user.Lang}) - } - - if user.Password != "" { - userModifyRequest.Replace("userPassword", []string{user.Password}) - } - - err = conn.Modify(userModifyRequest) - - if err != nil { - klog.Error(err) - return nil, err - } - - if user.ClusterRole != "" { - err = CreateClusterRoleBinding(user.Username, user.ClusterRole) - - if err != nil { - klog.Errorln(err) - return nil, err - } - } - - // clear auth failed record - if user.Password != "" { - redisClient, err := clientset.ClientSets().Redis() - if err != nil { - return nil, err - } - - records, err := redisClient.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", user.Username)).Result() - - if err == nil { - redisClient.Del(records...) - } - } - - return GetUserInfo(user.Username) -} -func DeleteGroup(path string) error { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return err - } - conn, err := client.NewConn() - if err != nil { - return err - } - defer conn.Close() - - searchBase, cn := splitPath(path) - - groupDeleteRequest := ldap.NewDelRequest(fmt.Sprintf("cn=%s,%s", cn, searchBase), nil) - err = conn.Del(groupDeleteRequest) - - if err != nil { - klog.Errorln("delete user group", err) - return err - } - - return nil -} - -func CreateGroup(group *models.Group) (*models.Group, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - maxGid, err := getMaxGid() - - if err != nil { - klog.Errorln("get max gid", err) - return nil, err - } - - maxGid += 1 - - if group.Path == "" { - group.Path = group.Name - } - - searchBase, cn := splitPath(group.Path) - - groupCreateRequest := ldap.NewAddRequest(fmt.Sprintf("cn=%s,%s", cn, searchBase), nil) - groupCreateRequest.Attribute("objectClass", []string{"posixGroup", "top"}) - groupCreateRequest.Attribute("cn", []string{cn}) - groupCreateRequest.Attribute("gidNumber", []string{strconv.Itoa(maxGid)}) - - if group.Description != "" { - groupCreateRequest.Attribute("description", []string{group.Description}) - } - - if group.Members != nil { - groupCreateRequest.Attribute("memberUid", group.Members) - } - - err = conn.Add(groupCreateRequest) - - if err != nil { - klog.Errorln("create group", err) - return nil, err - } - - group.Gid = strconv.Itoa(maxGid) - - return DescribeGroup(group.Path) -} - -func UpdateGroup(group *models.Group) (*models.Group, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - old, err := DescribeGroup(group.Path) - - if err != nil { - return nil, err - } - - searchBase, cn := splitPath(group.Path) - - groupUpdateRequest := ldap.NewModifyRequest(fmt.Sprintf("cn=%s,%s", cn, searchBase), nil) - - if old.Description == "" { - if group.Description != "" { - groupUpdateRequest.Add("description", []string{group.Description}) - } - } else { - if group.Description != "" { - groupUpdateRequest.Replace("description", []string{group.Description}) - } else { - groupUpdateRequest.Delete("description", []string{}) - } - } - - if group.Members != nil { - groupUpdateRequest.Replace("memberUid", group.Members) - } - - err = conn.Modify(groupUpdateRequest) - - if err != nil { - klog.Errorln("update group", err) - return nil, err - } - - return group, nil -} - -func ChildList(path string) ([]models.Group, error) { - - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - var groupSearchRequest *ldap.SearchRequest - if path == "" { - groupSearchRequest = ldap.NewSearchRequest(client.GroupSearchBase(), - ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - []string{"cn", "gidNumber", "memberUid", "description"}, - nil) - } else { - searchBase, cn := splitPath(path) - groupSearchRequest = ldap.NewSearchRequest(fmt.Sprintf("cn=%s,%s", cn, searchBase), - ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - []string{"cn", "gidNumber", "memberUid", "description"}, - nil) - } - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - return nil, err - } - - groups := make([]models.Group, 0) - - for _, v := range result.Entries { - dn := v.DN - cn := v.GetAttributeValue("cn") - gid := v.GetAttributeValue("gidNumber") - members := v.GetAttributeValues("memberUid") - description := v.GetAttributeValue("description") - - group := models.Group{Path: convertDNToPath(dn), Name: cn, Gid: gid, Members: members, Description: description} - - childSearchRequest := ldap.NewSearchRequest(dn, - ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, - "(&(objectClass=posixGroup))", - []string{""}, - nil) - - result, err = conn.Search(childSearchRequest) - - if err != nil { - return nil, err - } - - childGroups := make([]string, 0) - - for _, v := range result.Entries { - child := convertDNToPath(v.DN) - childGroups = append(childGroups, child) - } - - group.ChildGroups = childGroups - - groups = append(groups, group) - } - - return groups, nil -} - -func DescribeGroup(path string) (*models.Group, error) { - client, err := clientset.ClientSets().Ldap() - if err != nil { - return nil, err - } - conn, err := client.NewConn() - if err != nil { - return nil, err - } - defer conn.Close() - - searchBase, cn := splitPath(path) - - groupSearchRequest := ldap.NewSearchRequest(searchBase, - ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn), - []string{"cn", "gidNumber", "memberUid", "description"}, - nil) - - result, err := conn.Search(groupSearchRequest) - - if err != nil { - klog.Errorln("search group", err) - return nil, err - } - - if len(result.Entries) != 1 { - return nil, ldap.NewError(ldap.LDAPResultNoSuchObject, fmt.Errorf("group %s does not exist", path)) - } - - dn := result.Entries[0].DN - cn = result.Entries[0].GetAttributeValue("cn") - gid := result.Entries[0].GetAttributeValue("gidNumber") - members := result.Entries[0].GetAttributeValues("memberUid") - description := result.Entries[0].GetAttributeValue("description") - - group := models.Group{Path: convertDNToPath(dn), Name: cn, Gid: gid, Members: members, Description: description} - - childGroups := make([]string, 0) - - group.ChildGroups = childGroups - - return &group, nil - -} - -func WorkspaceUsersTotalCount(workspace string) (int, error) { - workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace) - - if err != nil { - return 0, err - } - - users := make([]string, 0) - - for _, roleBinding := range workspaceRoleBindings { - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - users = append(users, subject.Name) - } - } - } - - return len(users), nil -} - -func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - - workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace) - - if err != nil { - return nil, err - } - - users := make([]*models.User, 0) - - for _, roleBinding := range workspaceRoleBindings { - for _, subject := range roleBinding.Subjects { - if subject.Kind == rbacv1.UserKind && !k8sutil.ContainsUser(users, subject.Name) { - user, err := GetUserInfo(subject.Name) - if err != nil { - return nil, err - } - prefix := fmt.Sprintf("workspace:%s:", workspace) - user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix)) - if matchConditions(conditions, user) { - users = append(users, user) - } - } - } - } - - // order & reverse - sort.Slice(users, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - switch orderBy { - default: - fallthrough - case "name": - return strings.Compare(users[i].Username, users[j].Username) <= 0 - } - }) - - result := make([]interface{}, 0) - - for i, d := range users { - if i >= offset && (limit == -1 || len(result) < limit) { - result = append(result, d) - } - } - - return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil -} - -func matchConditions(conditions *params.Conditions, user *models.User) bool { - for k, v := range conditions.Match { - switch k { - case "keyword": - if !strings.Contains(user.Username, v) && - !strings.Contains(user.Email, v) && - !strings.Contains(user.Description, v) { - return false - } - case "name": - names := strings.Split(v, "|") - if !sliceutil.HasString(names, user.Username) { - return false - } - case "email": - email := strings.Split(v, "|") - if !sliceutil.HasString(email, user.Email) { - return false - } - case "role": - if user.WorkspaceRole != v { - return false - } - } - } - return true -} diff --git a/pkg/models/iam/im/fake_operator.go b/pkg/models/iam/im/fake_operator.go new file mode 100644 index 000000000..e6186e044 --- /dev/null +++ b/pkg/models/iam/im/fake_operator.go @@ -0,0 +1,25 @@ +/* + * + * Copyright 2020 The KubeSphere 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 im + +import "kubesphere.io/kubesphere/pkg/simple/client/ldap" + +func NewFakeOperator() IdentityManagementInterface { + return NewLDAPOperator(ldap.NewSimpleLdap()) +} diff --git a/pkg/models/iam/im/im.go b/pkg/models/iam/im/im.go new file mode 100644 index 000000000..c1002c261 --- /dev/null +++ b/pkg/models/iam/im/im.go @@ -0,0 +1,119 @@ +/* + * + * Copyright 2020 The KubeSphere 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 im + +import ( + "github.com/pkg/errors" + "golang.org/x/crypto/bcrypt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" + "kubesphere.io/kubesphere/pkg/informers" + resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" +) + +type IdentityManagementInterface interface { + CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) + ListUsers(query *query.Query) (*api.ListResult, error) + DeleteUser(username string) error + ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) + DescribeUser(username string) (*iamv1alpha2.User, error) + Authenticate(username, password string) (*iamv1alpha2.User, error) +} + +var ( + AuthRateLimitExceeded = errors.New("user auth rate limit exceeded") + AuthFailedIncorrectPassword = errors.New("incorrect password") + UserAlreadyExists = errors.New("user already exists") + UserNotExists = errors.New("user not exists") +) + +func NewOperator(ksClient kubesphereclient.Interface, factory informers.InformerFactory) IdentityManagementInterface { + + return &defaultIMOperator{ + ksClient: ksClient, + resourceGetter: resourcev1alpha3.NewResourceGetter(factory), + } + +} + +type defaultIMOperator struct { + ksClient kubesphereclient.Interface + resourceGetter *resourcev1alpha3.ResourceGetter +} + +func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + return im.ksClient.IamV1alpha2().Users().Update(user) +} + +func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { + + user, err := im.DescribeUser(username) + + if err != nil { + klog.Error(err) + return nil, err + } + if checkPasswordHash(password, user.Spec.EncryptedPassword) { + return user, nil + } + return nil, AuthFailedIncorrectPassword +} + +func (im *defaultIMOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + + result, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil +} + +func checkPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { + user, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username) + + if err != nil { + klog.Error(err) + return nil, err + } + return user.(*iamv1alpha2.User), nil +} + +func (im *defaultIMOperator) DeleteUser(username string) error { + return im.ksClient.IamV1alpha2().Users().Delete(username, metav1.NewDeleteOptions(0)) +} + +func (im *defaultIMOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + user, err := im.ksClient.IamV1alpha2().Users().Create(user) + if err != nil { + klog.Error(err) + return nil, err + } + return user, nil +} diff --git a/pkg/models/iam/im/im_test.go b/pkg/models/iam/im/im_test.go new file mode 100644 index 000000000..a23dd9370 --- /dev/null +++ b/pkg/models/iam/im/im_test.go @@ -0,0 +1,41 @@ +/* + * + * Copyright 2020 The KubeSphere 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 im + +import ( + "golang.org/x/crypto/bcrypt" + "testing" +) + +func TestEncryptPassword(t *testing.T) { + password := "P@88w0rd" + encryptedPassword, err := hashPassword(password) + if err != nil { + t.Fatal(err) + } + if !checkPasswordHash(password, encryptedPassword) { + t.Fatal(err) + } + t.Log(encryptedPassword) +} + +func hashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.MinCost) + return string(bytes), err +} diff --git a/pkg/models/iam/im/ldap_operator.go b/pkg/models/iam/im/ldap_operator.go new file mode 100644 index 000000000..b59556315 --- /dev/null +++ b/pkg/models/iam/im/ldap_operator.go @@ -0,0 +1,93 @@ +/* + * + * Copyright 2020 The KubeSphere 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 im + +import ( + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/simple/client/ldap" +) + +type ldapOperator struct { + ldapClient ldap.Interface +} + +func NewLDAPOperator(ldapClient ldap.Interface) IdentityManagementInterface { + return &ldapOperator{ + ldapClient: ldapClient, + } +} + +func (im *ldapOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + + err := im.ldapClient.Update(user) + + if err != nil { + return nil, err + } + + return im.ldapClient.Get(user.Name) +} + +func (im *ldapOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) { + + user, err := im.ldapClient.Get(username) + + if err != nil { + return nil, err + } + + err = im.ldapClient.Authenticate(user.Name, password) + if err != nil { + return nil, err + } + + return user, nil +} + +func (im *ldapOperator) DescribeUser(username string) (*iamv1alpha2.User, error) { + return im.ldapClient.Get(username) +} + +func (im *ldapOperator) DeleteUser(username string) error { + return im.ldapClient.Delete(username) +} + +func (im *ldapOperator) CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) { + err := im.ldapClient.Create(user) + + if err != nil { + return nil, err + } + + return user, nil +} + +func (im *ldapOperator) ListUsers(query *query.Query) (*api.ListResult, error) { + result, err := im.ldapClient.List(query) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil +} diff --git a/pkg/models/iam/path.go b/pkg/models/iam/path.go deleted file mode 100644 index f7ff2f1ac..000000000 --- a/pkg/models/iam/path.go +++ /dev/null @@ -1,80 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 iam - -import ( - "fmt" - "kubesphere.io/kubesphere/pkg/simple/client" - "regexp" - "strings" -) - -func convertDNToPath(dn string) string { - - paths := regexp.MustCompile("cn=[a-z0-9]([-a-z0-9]*[a-z0-9])?").FindAllString(dn, -1) - - if len(paths) > 1 { - for i := 0; i < len(paths); i++ { - paths[i] = strings.Replace(paths[i], "cn=", "", 1) - } - for i, j := 0, len(paths)-1; i < j; i, j = i+1, j-1 { - paths[i], paths[j] = paths[j], paths[i] - } - return strings.Join(paths, ":") - } else if len(paths) == 1 { - return strings.Replace(paths[0], "cn=", "", -1) - } else { - return "" - } -} - -func splitPath(path string) (searchBase string, cn string) { - ldapClient, err := client.ClientSets().Ldap() - if err != nil { - return "", "" - } - - paths := strings.Split(path, ":") - length := len(paths) - if length > 2 { - - cn = paths[length-1] - basePath := paths[:length-1] - - for i := 0; i < len(basePath); i++ { - basePath[i] = fmt.Sprintf("cn=%s", basePath[i]) - } - - for i, j := 0, length-2; i < j; i, j = i+1, j-1 { - basePath[i], basePath[j] = basePath[j], basePath[i] - } - - searchBase = fmt.Sprintf("%s,%s", strings.Join(basePath, ","), ldapClient.GroupSearchBase()) - } else if length == 2 { - searchBase = fmt.Sprintf("cn=%s,%s", paths[0], ldapClient.GroupSearchBase()) - cn = paths[1] - } else { - searchBase = ldapClient.GroupSearchBase() - if paths[0] == "" { - cn = "*" - } else { - cn = paths[0] - } - } - return -} diff --git a/pkg/models/iam/policy/policy.go b/pkg/models/iam/policy/policy.go deleted file mode 100644 index bce92c8f6..000000000 --- a/pkg/models/iam/policy/policy.go +++ /dev/null @@ -1,1088 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 policy - -import ( - "encoding/json" - "io/ioutil" - - "kubesphere.io/kubesphere/pkg/models" - - "k8s.io/api/rbac/v1" -) - -const ( - rulesConfigPath = "/etc/kubesphere/rules/rules.json" - clusterRulesConfigPath = "/etc/kubesphere/rules/clusterrules.json" -) - -func init() { - rulesConfig, err := ioutil.ReadFile(rulesConfigPath) - - if err == nil { - config := &[]models.Rule{} - json.Unmarshal(rulesConfig, config) - if len(*config) > 0 { - RoleRuleMapping = *config - } - } - - clusterRulesConfig, err := ioutil.ReadFile(clusterRulesConfigPath) - - if err == nil { - config := &[]models.Rule{} - json.Unmarshal(clusterRulesConfig, config) - if len(*config) > 0 { - ClusterRoleRuleMapping = *config - } - } -} - -var ( - ClusterRoleRuleMapping = []models.Rule{ - {Name: "workspaces", - Actions: []models.Action{ - { - Name: "manage", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"workspaces", "workspaces/*"}, - }, - }, - }, - }, - }, - { - Name: "monitoring", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{{ - Verbs: []string{"get", "list"}, - APIGroups: []string{"monitoring.kubesphere.io"}, - Resources: []string{"*"}, - }, { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"health"}, - }}, - }, - }, - }, - { - Name: "alerting", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{{ - Verbs: []string{"get", "list"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - {Name: "create", - Rules: []v1.PolicyRule{{ - Verbs: []string{"create"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - {Name: "delete", - Rules: []v1.PolicyRule{{ - Verbs: []string{"delete"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - }, - }, - { - Name: "logging", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{{ - Verbs: []string{"get", "list"}, - APIGroups: []string{"logging.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - }, - }, - { - Name: "accounts", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users", "users/*"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"rulesmapping"}, - ResourceNames: []string{"clusterroles"}, - }, - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create", "get", "list"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"get"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"clusterrules"}, - ResourceNames: []string{"mapping"}, - }, - { - Verbs: []string{"create", "delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list", "update", "patch"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users"}, - }, - { - Verbs: []string{"create", "delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterrolebindings"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users"}, - }, - }, - }, - }, - }, { - Name: "roles", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"clusterroles", "clusterroles/*"}, - }, - }, - }, - - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"clusterroles"}, - }, - }, - }, - }, - }, { - Name: "storageclasses", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"storage.k8s.io"}, - Resources: []string{"storageclasses"}, - }, { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"storageclasses", "storageclasses/*"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"storage.k8s.io"}, - Resources: []string{"storageclasses"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"storage.k8s.io"}, - Resources: []string{"storageclasses"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"storage.k8s.io"}, - Resources: []string{"storageclasses"}, - }, - }, - }, - }, - }, { - Name: "nodes", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{""}, - Resources: []string{"nodes", "events"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"nodes", "nodes/*"}, - }, { - Verbs: []string{"get", "list"}, - APIGroups: []string{"monitoring.kubesphere.io"}, - Resources: []string{"nodes"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"nodes"}, - }, - }, - }, - }, - }, { - Name: "repos", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"repos"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"repos"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"repos"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete", "deletecollection"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"repos"}, - }, - }, - }, - }, - }, { - Name: "apps", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"apps", "clusters", "repos", "app_versions", "app_version/*"}, - }, - }, - }, - }, - }, { - Name: "components", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"list", "get"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"components", "components/*"}, - }, - }, - }, - }, - }} - - RoleRuleMapping = []models.Rule{{ - Name: "projects", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get"}, - APIGroups: []string{"*"}, - Resources: []string{"namespaces"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{"*"}, - Resources: []string{"events"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, - }, - }, - }, - }, - { - Name: "monitoring", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{{ - Verbs: []string{"get", "list"}, - APIGroups: []string{"monitoring.kubesphere.io"}, - Resources: []string{"*"}, - }, { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"health"}, - }}, - }, - }, - }, - - { - Name: "alerting", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{{ - Verbs: []string{"get", "list"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - {Name: "create", - Rules: []v1.PolicyRule{{ - Verbs: []string{"create"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - {Name: "delete", - Rules: []v1.PolicyRule{{ - Verbs: []string{"delete"}, - APIGroups: []string{"alerting.kubesphere.io"}, - Resources: []string{"*"}, - }}, - }, - }, - }, - { - Name: "members", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"}, - Resources: []string{"rolebindings"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"iam.kubesphere.io"}, - Resources: []string{"users"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"rolebindings"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "watch", "list", "create", "update", "patch"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"rolebindings"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"rolebindings"}, - }, - }, - }, - }, - }, - { - Name: "roles", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"}, - Resources: []string{"roles"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"patch", "update"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"rbac.authorization.k8s.io"}, - Resources: []string{"roles"}, - }, - }, - }, - }, - }, - { - Name: "deployments", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"}, - Resources: []string{"deployments", "deployments/scale"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/*"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments"}, - }, - }, - }, - - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments", "deployments/*"}, - }, - }, - }, - - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments"}, - }, - }, - }, - {Name: "scale", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"deployments/scale"}, - }, - }, - }, - }, - }, { - Name: "statefulsets", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"apps", "resources.kubesphere.io"}, - Resources: []string{"statefulsets"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/*"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - {Name: "scale", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"patch"}, - APIGroups: []string{"apps"}, - Resources: []string{"statefulsets"}, - }, - }, - }, - }, - }, { - Name: "daemonsets", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"}, - Resources: []string{"daemonsets"}, - }, - { - Verbs: []string{"get", "list"}, - APIGroups: []string{""}, - Resources: []string{"pods", "pods/*"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"apps", "extensions"}, - Resources: []string{"daemonsets"}, - }, - }, - }, - }, - }, { - Name: "pods", - Actions: []models.Action{ - {Name: "terminal", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get"}, - APIGroups: []string{"terminal.kubesphere.io"}, - Resources: []string{"pods"}, - }, - }, - }, - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"pods"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"*"}, - Resources: []string{"pods"}, - }, - }, - }, - }, - }, - { - Name: "services", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"list", "get"}, - APIGroups: []string{"", "resources.kubesphere.io"}, - Resources: []string{"services"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"services"}, - }, - }, - }, - }, - }, - { - Name: "internet", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"router"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"router"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"router"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"resources.kubesphere.io"}, - Resources: []string{"router"}, - }, - }, - }, - }, - }, - - { - Name: "routes", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"extensions", "resources.kubesphere.io"}, - Resources: []string{"ingresses"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"extensions"}, - Resources: []string{"ingresses"}, - }, - }, - }, - }, - }, { - Name: "volumes", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"", "resources.kubesphere.io"}, - Resources: []string{"persistentvolumeclaims"}, - }, - }, - }, - {Name: "create", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumeclaims"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumeclaims"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"persistentvolumeclaims"}, - }, - }, - }, - }, - }, { - Name: "applications", - Actions: []models.Action{ - {Name: "view", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"resources.kubesphere.io", "app.k8s.io"}, - Resources: []string{"applications"}, - }, { - Verbs: []string{"get", "list"}, - APIGroups: []string{"servicemesh.kubesphere.io"}, - Resources: []string{"*"}, - }, - { - Verbs: []string{"list"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"repos", "app_versions"}, - }, { - Verbs: []string{"get"}, - APIGroups: []string{"openpitrix.io"}, - Resources: []string{"app_version/*"}, - }, - }, - }, - {Name: "edit", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"create", "update", "patch"}, - APIGroups: []string{"resources.kubesphere.io", "app.k8s.io"}, - Resources: []string{"applications"}, - }, { - Verbs: []string{"create", "update", "patch"}, - APIGroups: []string{"servicemesh.kubesphere.io"}, - Resources: []string{"*"}, - }, - }, - }, - {Name: "delete", - Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"resources.kubesphere.io", "app.k8s.io"}, - Resources: []string{"applications"}, - }, - { - Verbs: []string{"delete"}, - APIGroups: []string{"servicemesh.kubesphere.io"}, - Resources: []string{"*"}, - }, - }, - }, - }, - }, - { - Name: "jobs", - Actions: []models.Action{ - {Name: "view", Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"batch", "resources.kubesphere.io"}, - Resources: []string{"jobs"}, - }, - }}, - {Name: "create", Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"batch"}, - Resources: []string{"jobs"}, - }, - }}, - {Name: "edit", Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"batch"}, - Resources: []string{"jobs"}, - }, - }}, - {Name: "delete", Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"batch"}, - Resources: []string{"jobs"}, - }, - }}, - }, - }, - { - Name: "cronjobs", - Actions: []models.Action{ - {Name: "view", Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"batch", "resources.kubesphere.io"}, - Resources: []string{"cronjobs"}, - }, - }}, - {Name: "create", Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{"batch"}, - Resources: []string{"cronjobs"}, - }, - }}, - {Name: "edit", Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{"batch"}, - Resources: []string{"cronjobs"}, - }, - }}, - {Name: "delete", Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{"batch"}, - Resources: []string{"cronjobs"}, - }, - }}, - }, - }, - { - Name: "secrets", - Actions: []models.Action{ - {Name: "view", Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"", "resources.kubesphere.io"}, - Resources: []string{"secrets"}, - }, - }}, - {Name: "create", Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"secrets"}, - }, - }}, - {Name: "edit", Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"secrets"}, - }, - }}, - {Name: "delete", Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"secrets"}, - }, - }}, - }, - }, - { - Name: "configmaps", - Actions: []models.Action{ - {Name: "view", Rules: []v1.PolicyRule{ - { - Verbs: []string{"get", "list"}, - APIGroups: []string{"", "resources.kubesphere.io"}, - Resources: []string{"configmaps"}, - }, - }}, - {Name: "create", Rules: []v1.PolicyRule{ - { - Verbs: []string{"create"}, - APIGroups: []string{""}, - Resources: []string{"configmaps"}, - }, - }}, - {Name: "edit", Rules: []v1.PolicyRule{ - { - Verbs: []string{"update", "patch"}, - APIGroups: []string{""}, - Resources: []string{"configmaps"}, - }, - }}, - {Name: "delete", Rules: []v1.PolicyRule{ - { - Verbs: []string{"delete"}, - APIGroups: []string{""}, - Resources: []string{"configmaps"}, - }, - }}, - }, - }, - } -) diff --git a/pkg/models/kubeconfig/kubeconfig.go b/pkg/models/kubeconfig/kubeconfig.go index e1a2bd8ed..6eacf850e 100644 --- a/pkg/models/kubeconfig/kubeconfig.go +++ b/pkg/models/kubeconfig/kubeconfig.go @@ -18,298 +18,17 @@ package kubeconfig -import ( - "bytes" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "fmt" - "gopkg.in/yaml.v2" - "io/ioutil" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" - "math/big" - rd "math/rand" - "time" - - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "k8s.io/apimachinery/pkg/api/errors" - - "k8s.io/api/core/v1" - - "kubesphere.io/kubesphere/pkg/constants" -) - -const ( - caPath = "/etc/kubernetes/pki/ca.crt" - keyPath = "/etc/kubernetes/pki/ca.key" - clusterName = "kubernetes" - kubectlConfigKey = "config" - defaultNamespace = "default" -) - -type clusterInfo struct { - CertificateAuthorityData string `yaml:"certificate-authority-data"` - Server string `yaml:"server"` +type Interface interface { + GetKubeConfig(username string) (string, error) } -type cluster struct { - Cluster clusterInfo `yaml:"cluster"` - Name string `yaml:"name"` +type operator struct { } -type contextInfo struct { - Cluster string `yaml:"cluster"` - User string `yaml:"user"` - NameSpace string `yaml:"namespace"` +func (o operator) GetKubeConfig(username string) (string, error) { + panic("implement me") } -type contextObject struct { - Context contextInfo `yaml:"context"` - Name string `yaml:"name"` -} - -type userInfo struct { - CaData string `yaml:"client-certificate-data"` - KeyData string `yaml:"client-key-data"` -} - -type user struct { - Name string `yaml:"name"` - User userInfo `yaml:"user"` -} - -type kubeConfig struct { - ApiVersion string `yaml:"apiVersion"` - Clusters []cluster `yaml:"clusters"` - Contexts []contextObject `yaml:"contexts"` - CurrentContext string `yaml:"current-context"` - Kind string `yaml:"kind"` - Preferences map[string]string `yaml:"preferences"` - Users []user `yaml:"users"` -} - -type CertInformation struct { - Country []string - Organization []string - OrganizationalUnit []string - EmailAddress []string - Province []string - Locality []string - CommonName string - CrtName, KeyName string - IsCA bool - Names []pkix.AttributeTypeAndValue -} - -func createCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) ([]byte, []byte, error) { - var cert, key bytes.Buffer - Crt := newCertificate(info) - Key, err := rsa.GenerateKey(rand.Reader, 2048) - if err != nil { - klog.Error(err) - return nil, nil, err - } - - var buf []byte - - buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey) - - if err != nil { - klog.Error(err) - return nil, nil, err - } - pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: buf}) - - if err != nil { - klog.Error(err) - return nil, nil, err - } - - buf = x509.MarshalPKCS1PrivateKey(Key) - pem.Encode(&key, &pem.Block{Type: "PRIVATE KEY", Bytes: buf}) - - return cert.Bytes(), key.Bytes(), nil -} - -func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) { - rootcertificate, err = parseCrt(crtPath) - if err != nil { - klog.Error(err) - return nil, nil, err - } - rootPrivateKey, err = parseKey(keyPath) - return rootcertificate, rootPrivateKey, nil -} - -func parseCrt(path string) (*x509.Certificate, error) { - buf, err := ioutil.ReadFile(path) - if err != nil { - klog.Error(err) - return nil, err - } - p := &pem.Block{} - p, buf = pem.Decode(buf) - return x509.ParseCertificate(p.Bytes) -} - -func parseKey(path string) (*rsa.PrivateKey, error) { - buf, err := ioutil.ReadFile(path) - if err != nil { - klog.Error(err) - return nil, err - } - p, buf := pem.Decode(buf) - return x509.ParsePKCS1PrivateKey(p.Bytes) -} - -func newCertificate(info CertInformation) *x509.Certificate { - rd.Seed(time.Now().UnixNano()) - return &x509.Certificate{ - SerialNumber: big.NewInt(rd.Int63()), - Subject: pkix.Name{ - Country: info.Country, - Organization: info.Organization, - OrganizationalUnit: info.OrganizationalUnit, - Province: info.Province, - CommonName: info.CommonName, - Locality: info.Locality, - ExtraNames: info.Names, - }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(20, 0, 0), - BasicConstraintsValid: true, - IsCA: info.IsCA, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - EmailAddresses: info.EmailAddress, - } -} - -func generateCaAndKey(user, caPath, keyPath string) (string, string, error) { - crtInfo := CertInformation{CommonName: user, IsCA: false} - - crt, pri, err := Parse(caPath, keyPath) - if err != nil { - klog.Error(err) - return "", "", err - } - cert, key, err := createCRT(crt, pri, crtInfo) - if err != nil { - klog.Error(err) - return "", "", err - } - - base64Cert := base64.StdEncoding.EncodeToString(cert) - base64Key := base64.StdEncoding.EncodeToString(key) - return base64Cert, base64Key, nil -} - -func createKubeConfig(username string) (string, error) { - tmpKubeConfig := kubeConfig{ApiVersion: "v1", Kind: "Config"} - serverCa, err := ioutil.ReadFile(caPath) - if err != nil { - klog.Errorln(err) - return "", err - } - base64ServerCa := base64.StdEncoding.EncodeToString(serverCa) - tmpClusterInfo := clusterInfo{CertificateAuthorityData: base64ServerCa, Server: client.ClientSets().K8s().Master()} - tmpCluster := cluster{Cluster: tmpClusterInfo, Name: clusterName} - tmpKubeConfig.Clusters = append(tmpKubeConfig.Clusters, tmpCluster) - - contextName := username + "@" + clusterName - tmpContext := contextObject{Context: contextInfo{User: username, Cluster: clusterName, NameSpace: defaultNamespace}, Name: contextName} - tmpKubeConfig.Contexts = append(tmpKubeConfig.Contexts, tmpContext) - - cert, key, err := generateCaAndKey(username, caPath, keyPath) - - if err != nil { - return "", err - } - - tmpUser := user{User: userInfo{CaData: cert, KeyData: key}, Name: username} - tmpKubeConfig.Users = append(tmpKubeConfig.Users, tmpUser) - tmpKubeConfig.CurrentContext = contextName - - config, err := yaml.Marshal(tmpKubeConfig) - if err != nil { - return "", err - } - - return string(config), nil -} - -func CreateKubeConfig(username string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() - configName := fmt.Sprintf("kubeconfig-%s", username) - _, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{}) - - if errors.IsNotFound(err) { - config, err := createKubeConfig(username) - if err != nil { - klog.Errorln(err) - return err - } - - data := map[string]string{"config": config} - configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: configName}, Data: data} - _, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap) - if err != nil && !errors.IsAlreadyExists(err) { - klog.Errorf("create username %s's kubeConfig failed, reason: %v", username, err) - return err - } - } - - return nil - -} - -func GetKubeConfig(username string) (string, error) { - k8sClient := client.ClientSets().K8s().Kubernetes() - configName := fmt.Sprintf("kubeconfig-%s", username) - configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{}) - if err != nil { - klog.Errorf("cannot get username %s's kubeConfig, reason: %v", username, err) - return "", err - } - - str := configMap.Data[kubectlConfigKey] - var kubeConfig kubeConfig - err = yaml.Unmarshal([]byte(str), &kubeConfig) - if err != nil { - klog.Error(err) - return "", err - } - masterURL := client.ClientSets().K8s().Master() - for i, cluster := range kubeConfig.Clusters { - cluster.Cluster.Server = masterURL - kubeConfig.Clusters[i] = cluster - } - data, err := yaml.Marshal(kubeConfig) - if err != nil { - klog.Error(err) - return "", err - } - return string(data), nil -} - -func DelKubeConfig(username string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() - configName := fmt.Sprintf("kubeconfig-%s", username) - _, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{}) - if errors.IsNotFound(err) { - return nil - } - - deletePolicy := metaV1.DeletePropagationBackground - err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(configName, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy}) - if err != nil { - klog.Errorf("delete username %s's kubeConfig failed, reason: %v", username, err) - return err - } - return nil +func NewKubeconfigOperator() Interface { + return &operator{} } diff --git a/pkg/models/kubectl/kubectl.go b/pkg/models/kubectl/kubectl.go index f37567d1d..5d91cf80d 100644 --- a/pkg/models/kubectl/kubectl.go +++ b/pkg/models/kubectl/kubectl.go @@ -20,9 +20,10 @@ package kubectl import ( "fmt" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/simple/client" "math/rand" "os" @@ -40,6 +41,21 @@ const ( namespace = constants.KubeSphereControlNamespace ) +type Interface interface { + GetKubectlPod(username string) (models.PodInfo, error) + CreateKubectlDeploy(username string) error + DelKubectlDeploy(username string) error +} + +type operator struct { + k8sClient kubernetes.Interface + informers informers.SharedInformerFactory +} + +func NewKubectlOperator(k8sClient kubernetes.Interface, informers informers.SharedInformerFactory) Interface { + return &operator{k8sClient: k8sClient, informers: informers} +} + var DefaultImage = "kubesphere/kubectl:advanced-1.0.0" func init() { @@ -48,24 +64,23 @@ func init() { } } -func GetKubectlPod(username string) (models.PodInfo, error) { - k8sClient := client.ClientSets().K8s().Kubernetes() +func (o *operator) GetKubectlPod(username string) (models.PodInfo, error) { deployName := fmt.Sprintf("kubectl-%s", username) - deploy, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{}) + deploy, err := o.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(deployName) if err != nil { klog.Errorln(err) return models.PodInfo{}, err } selectors := deploy.Spec.Selector.MatchLabels - labelSelector := labels.Set(selectors).AsSelector().String() - podList, err := k8sClient.CoreV1().Pods(namespace).List(metav1.ListOptions{LabelSelector: labelSelector}) + labelSelector := labels.Set(selectors).AsSelector() + pods, err := o.informers.Core().V1().Pods().Lister().Pods(namespace).List(labelSelector) if err != nil { klog.Errorln(err) return models.PodInfo{}, err } - pod, err := selectCorrectPod(namespace, podList.Items) + pod, err := selectCorrectPod(namespace, pods) if err != nil { klog.Errorln(err) return models.PodInfo{}, err @@ -77,9 +92,9 @@ func GetKubectlPod(username string) (models.PodInfo, error) { } -func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err error) { +func selectCorrectPod(namespace string, pods []*v1.Pod) (kubectlPod *v1.Pod, err error) { - var kubectlPodList []v1.Pod + var kubectlPodList []*v1.Pod for _, pod := range pods { for _, condition := range pod.Status.Conditions { if condition.Type == "Ready" && condition.Status == "True" { @@ -89,15 +104,16 @@ func selectCorrectPod(namespace string, pods []v1.Pod) (kubectlPod v1.Pod, err e } if len(kubectlPodList) < 1 { err = fmt.Errorf("cannot find valid kubectl pod in namespace:%s", namespace) - return v1.Pod{}, err + return &v1.Pod{}, err } random := rand.Intn(len(kubectlPodList)) + return kubectlPodList[random], nil } -func CreateKubectlDeploy(username string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() +func (o *operator) CreateKubectlDeploy(username string) error { + k8sClient := o.k8sClient deployName := fmt.Sprintf("kubectl-%s", username) _, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{}) if err == nil { @@ -136,8 +152,8 @@ func CreateKubectlDeploy(username string) error { return err } -func DelKubectlDeploy(username string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() +func (o *operator) DelKubectlDeploy(username string) error { + k8sClient := o.k8sClient deployName := fmt.Sprintf("kubectl-%s", username) _, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{}) if errors.IsNotFound(err) { diff --git a/pkg/models/log/constants.go b/pkg/models/log/constants.go deleted file mode 100644 index cfb8efcce..000000000 --- a/pkg/models/log/constants.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 log - -type LogQueryLevel int - -const ( - QueryLevelCluster LogQueryLevel = iota - QueryLevelWorkspace - QueryLevelNamespace - QueryLevelWorkload - QueryLevelPod - QueryLevelContainer -) diff --git a/pkg/models/log/logcollector.go b/pkg/models/log/logcollector.go deleted file mode 100644 index 09dfede63..000000000 --- a/pkg/models/log/logcollector.go +++ /dev/null @@ -1,91 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 log - -import ( - "k8s.io/apimachinery/pkg/labels" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/utils/stringutils" - "strconv" - "strings" - "time" -) - -// list namespaces that match search conditions -func MatchNamespace(nsFilter []string, nsQuery []string, wsFilter []string, wsQuery []string) (bool, []string) { - - nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - nsList, err := nsLister.List(labels.Everything()) - if err != nil { - klog.Errorf("failed to list namespace, error: %s", err) - return true, nil - } - - var namespaces []string - - // if no search condition is set on both namespace and workspace, - // then return all namespaces - if nsQuery == nil && nsFilter == nil && wsQuery == nil && wsFilter == nil { - for _, ns := range nsList { - namespaces = append(namespaces, ns.Name) - } - return false, namespaces - } - - for _, ns := range nsList { - if stringutils.StringIn(ns.Name, nsFilter) || - stringutils.StringIn(ns.Annotations[constants.WorkspaceLabelKey], wsFilter) || - containsIn(ns.Name, nsQuery) || - containsIn(ns.Annotations[constants.WorkspaceLabelKey], wsQuery) { - namespaces = append(namespaces, ns.Name) - } - } - - // if namespaces is equal to nil, indicates no namespace matched - // it causes the query to return no result - return namespaces == nil, namespaces -} - -func containsIn(str string, subStrs []string) bool { - for _, sub := range subStrs { - if strings.Contains(str, sub) { - return true - } - } - return false -} - -func MakeNamespaceCreationTimeMap(namespaces []string) map[string]string { - - namespaceWithCreationTime := make(map[string]string) - - nsLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - for _, item := range namespaces { - ns, err := nsLister.Get(item) - if err != nil { - // the ns doesn't exist - continue - } - namespaceWithCreationTime[ns.Name] = strconv.FormatInt(ns.CreationTimestamp.UnixNano()/int64(time.Millisecond), 10) - } - - return namespaceWithCreationTime -} diff --git a/pkg/models/log/logcrd.go b/pkg/models/log/logcrd.go deleted file mode 100644 index 14a21e542..000000000 --- a/pkg/models/log/logcrd.go +++ /dev/null @@ -1,369 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 log - -import ( - _ "github.com/go-sql-driver/mysql" - "github.com/google/uuid" - "github.com/json-iterator/go" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - "kubesphere.io/kubesphere/pkg/informers" - fb "kubesphere.io/kubesphere/pkg/simple/client/fluentbit" - "net/http" - "strings" - "time" -) - -var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary - -const ( - ConfigMapName = "fluent-bit-output-config" - ConfigMapData = "outputs" - LoggingNamespace = "kubesphere-logging-system" -) - -func createCRDClientSet() (*rest.RESTClient, *runtime.Scheme, error) { - config, err := fb.GetClientConfig("") - if err != nil { - //panic(err.Error()) - return nil, nil, err - } - - // Create a new clientset which include our CRD schema - return fb.NewFluentbitCRDClient(config) -} - -func FluentbitOutputsQuery() *FluentbitOutputsResult { - var result FluentbitOutputsResult - - outputs, err := GetFluentbitOutputFromConfigMap() - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - result.Outputs = outputs - result.Status = http.StatusOK - - return &result -} - -func FluentbitOutputInsert(output fb.OutputPlugin) *FluentbitOutputsResult { - var result FluentbitOutputsResult - - // 1. Update ConfigMap - var outputs []fb.OutputPlugin - outputs, err := GetFluentbitOutputFromConfigMap() - if err != nil { - // If the ConfigMap doesn't exist, a new one will be created later - klog.Errorln(err) - } - - // When adding a new output for the first time, one should always set it enabled - output.Enable = true - output.Id = uuid.New().String() - output.Updatetime = time.Now() - - outputs = append(outputs, output) - - err = updateFluentbitOutputConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - // 2. Keep CRD in inline with ConfigMap - err = syncFluentbitCRDOutputWithConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - result.Status = http.StatusOK - return &result -} - -func FluentbitOutputUpdate(output fb.OutputPlugin, id string) *FluentbitOutputsResult { - var result FluentbitOutputsResult - - // 1. Update ConfigMap - var outputs []fb.OutputPlugin - outputs, err := GetFluentbitOutputFromConfigMap() - if err != nil { - // If the ConfigMap doesn't exist, a new one will be created later - klog.Errorln(err) - } - - index := 0 - for _, output := range outputs { - if output.Id == id { - break - } - index++ - } - - if index >= len(outputs) { - result.Status = http.StatusNotFound - result.Error = "The output plugin to update doesn't exist. Please check the output id you provide." - return &result - } - - output.Updatetime = time.Now() - outputs = append(append(outputs[:index], outputs[index+1:]...), output) - - err = updateFluentbitOutputConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - // 2. Keep CRD in inline with ConfigMap - err = syncFluentbitCRDOutputWithConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - result.Status = http.StatusOK - return &result -} - -func FluentbitOutputDelete(id string) *FluentbitOutputsResult { - var result FluentbitOutputsResult - - // 1. Update ConfigMap - // If the ConfigMap doesn't exist, a new one will be created - outputs, _ := GetFluentbitOutputFromConfigMap() - - index := 0 - for _, output := range outputs { - if output.Id == id { - break - } - index++ - } - - if index >= len(outputs) { - result.Status = http.StatusNotFound - result.Error = "The output plugin to delete doesn't exist. Please check the output id you provide." - return &result - } - - outputs = append(outputs[:index], outputs[index+1:]...) - - err := updateFluentbitOutputConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - // 2. Keep CRD in inline with DB - err = syncFluentbitCRDOutputWithConfigMap(outputs) - if err != nil { - result.Status = http.StatusInternalServerError - result.Error = err.Error() - return &result - } - - result.Status = http.StatusOK - return &result -} - -func GetFluentbitOutputFromConfigMap() ([]fb.OutputPlugin, error) { - configMap, err := informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(LoggingNamespace).Get(ConfigMapName) - if err != nil { - return nil, err - } - - data := configMap.Data[ConfigMapData] - - var outputs []fb.OutputPlugin - if err = jsonIter.UnmarshalFromString(data, &outputs); err != nil { - return nil, err - } - - return outputs, nil -} - -func updateFluentbitOutputConfigMap(outputs []fb.OutputPlugin) error { - - var data string - data, err := jsonIter.MarshalToString(outputs) - if err != nil { - klog.Errorln(err) - return err - } - - // Update the ConfigMap - config, err := rest.InClusterConfig() - if err != nil { - klog.Errorln(err) - return err - } - - // Creates the clientset - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - klog.Errorln(err) - return err - } - - configMapClient := clientset.CoreV1().ConfigMaps(LoggingNamespace) - - configMap, err := configMapClient.Get(ConfigMapName, metav1.GetOptions{}) - if err != nil { - - // If the ConfigMap doesn't exist, create a new one - newConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: ConfigMapName, - }, - Data: map[string]string{ConfigMapData: data}, - } - - _, err = configMapClient.Create(newConfigMap) - if err != nil { - klog.Errorln(err) - return err - } - } else { - - // update - configMap.Data = map[string]string{ConfigMapData: data} - _, err = configMapClient.Update(configMap) - if err != nil { - klog.Errorln(err) - return err - } - } - - return nil -} - -func syncFluentbitCRDOutputWithConfigMap(outputs []fb.OutputPlugin) error { - - var enabledOutputs []fb.Plugin - for _, output := range outputs { - if output.Enable { - enabledOutputs = append(enabledOutputs, fb.Plugin{Type: output.Type, Name: output.Name, Parameters: output.Parameters}) - } - } - - // Empty output is not allowed, must specify a null-type output - if len(enabledOutputs) == 0 { - enabledOutputs = []fb.Plugin{ - { - Type: "fluentbit_output", - Name: "fluentbit-output-null", - Parameters: []fb.Parameter{ - { - Name: "Name", - Value: "null", - }, - { - Name: "Match", - Value: "*", - }, - }, - }, - } - } - - crdcs, scheme, err := createCRDClientSet() - if err != nil { - klog.Errorln(err) - return err - } - - // Create a CRD client interface - crdclient := fb.CrdClient(crdcs, scheme, LoggingNamespace) - - fluentbit, err := crdclient.Get("fluent-bit") - if err != nil { - klog.Errorln(err) - return err - } - - fluentbit.Spec.Output = enabledOutputs - _, err = crdclient.Update("fluent-bit", fluentbit) - if err != nil { - klog.Errorln(err) - return err - } - - return nil -} - -// Parse es host, port and index -func ParseEsOutputParams(params []fb.Parameter) *v1alpha2.Config { - - var ( - isEsFound bool - - host = "127.0.0.1" - port = "9200" - index = "logstash" - logstashFormat string - logstashPrefix string - ) - - for _, param := range params { - switch param.Name { - case "Name": - if param.Value == "es" { - isEsFound = true - } - case "Host": - host = param.Value - case "Port": - port = param.Value - case "Index": - index = param.Value - case "Logstash_Format": - logstashFormat = strings.ToLower(param.Value) - case "Logstash_Prefix": - logstashPrefix = param.Value - } - } - - if !isEsFound { - return nil - } - - // If Logstash_Format is On/True, ignore Index - if logstashFormat == "on" || logstashFormat == "true" { - if logstashPrefix != "" { - index = logstashPrefix - } else { - index = "logstash" - } - } - - return &v1alpha2.Config{Host: host, Port: port, Index: index} -} diff --git a/pkg/models/log/types.go b/pkg/models/log/types.go deleted file mode 100644 index b4b402fa6..000000000 --- a/pkg/models/log/types.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 log - -import ( - fb "kubesphere.io/kubesphere/pkg/simple/client/fluentbit" -) - -type FluentbitOutputsResult struct { - Status int `json:"status" description:"response status"` - Error string `json:"error,omitempty" description:"debug information"` - Outputs []fb.OutputPlugin `json:"outputs,omitempty" description:"array of fluent bit output plugins"` -} diff --git a/pkg/models/logging/logging.go b/pkg/models/logging/logging.go new file mode 100644 index 000000000..6878e2ed3 --- /dev/null +++ b/pkg/models/logging/logging.go @@ -0,0 +1,41 @@ +package logging + +import ( + "io" + "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" + "kubesphere.io/kubesphere/pkg/simple/client/logging" +) + +type LoggingOperator interface { + GetCurrentStats(sf logging.SearchFilter) (v1alpha2.APIResponse, error) + CountLogsByInterval(sf logging.SearchFilter, interval string) (v1alpha2.APIResponse, error) + ExportLogs(sf logging.SearchFilter, w io.Writer) error + SearchLogs(sf logging.SearchFilter, from, size int64, order string) (v1alpha2.APIResponse, error) +} + +type loggingOperator struct { + c logging.Interface +} + +func NewLoggingOperator(client logging.Interface) LoggingOperator { + return &loggingOperator{client} +} + +func (l loggingOperator) GetCurrentStats(sf logging.SearchFilter) (v1alpha2.APIResponse, error) { + res, err := l.c.GetCurrentStats(sf) + return v1alpha2.APIResponse{Statistics: &res}, err +} + +func (l loggingOperator) CountLogsByInterval(sf logging.SearchFilter, interval string) (v1alpha2.APIResponse, error) { + res, err := l.c.CountLogsByInterval(sf, interval) + return v1alpha2.APIResponse{Histogram: &res}, err +} + +func (l loggingOperator) ExportLogs(sf logging.SearchFilter, w io.Writer) error { + return l.c.ExportLogs(sf, w) +} + +func (l loggingOperator) SearchLogs(sf logging.SearchFilter, from, size int64, order string) (v1alpha2.APIResponse, error) { + res, err := l.c.SearchLogs(sf, from, size, order) + return v1alpha2.APIResponse{Logs: &res}, err +} diff --git a/pkg/models/metrics/constants.go b/pkg/models/metrics/constants.go deleted file mode 100644 index d20edcb7d..000000000 --- a/pkg/models/metrics/constants.go +++ /dev/null @@ -1,41 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 metrics - -const ( - MonitorLevelCluster = "cluster" - MonitorLevelNode = "node" - MonitorLevelWorkspace = "workspace" - MonitorLevelNamespace = "namespace" - MonitorLevelPod = "pod" - MonitorLevelContainer = "container" - MonitorLevelPVC = "pvc" - MonitorLevelWorkload = "workload" - MonitorLevelComponent = "component" - - ChannelMaxCapacity = 100 - - // prometheus query type - RangeQuery = "query_range" - Query = "query" - DefaultQueryStep = "10m" - - StatefulSet = "StatefulSet" - DaemonSet = "DaemonSet" - Deployment = "Deployment" -) diff --git a/pkg/models/metrics/metrics.go b/pkg/models/metrics/metrics.go deleted file mode 100644 index 2fe5cbb62..000000000 --- a/pkg/models/metrics/metrics.go +++ /dev/null @@ -1,803 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 metrics - -import ( - "fmt" - "github.com/json-iterator/go" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2" - "kubesphere.io/kubesphere/pkg/models/workspaces" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "net/url" - "regexp" - "strings" - "sync" - "time" -) - -var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary - -func GetClusterMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range clusterMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForCluster(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelCluster, - Results: apiResponse, - } -} - -func GetNodeMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range nodeMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForNode(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name, node_ip, node_role to each metric result item - // resouce_name serves as a unique identifier for the monitored resource - // it will be used during metrics sorting - for _, item := range response.Data.Result { - nodeName := item.Metric["node"] - item.Metric["resource_name"] = nodeName - item.Metric["node_ip"], item.Metric["node_role"] = getNodeAddressAndRole(nodeName) - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelNode, - Results: apiResponse, - } -} - -func GetWorkspaceMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range workspaceMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForWorkspace(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["label_kubesphere_io_workspace"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelWorkspace, - Results: apiResponse, - } -} - -func GetNamespaceMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range namespaceMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForNamespace(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["namespace"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelNamespace, - Results: apiResponse, - } -} - -func GetWorkloadMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range workloadMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForWorkload(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["workload"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelWorkload, - Results: apiResponse, - } -} - -func GetPodMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range podMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForPod(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["pod_name"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelPod, - Results: apiResponse, - } -} - -func GetContainerMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range containerMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForContainer(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["container_name"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelContainer, - Results: apiResponse, - } -} - -func GetPVCMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range pvcMetrics { - matched, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matched { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForPVC(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SPrometheus(params.QueryType, v.Encode()) - - // add label resouce_name - for _, item := range response.Data.Result { - item.Metric["resource_name"] = item.Metric["persistentvolumeclaim"] - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelPVC, - Results: apiResponse, - } -} - -func GetComponentMetrics(params RequestParams) *Response { - client, err := cs.ClientSets().Prometheus() - if err != nil { - klog.Error(err) - return nil - } - - ch := make(chan APIResponse, ChannelMaxCapacity) - var wg sync.WaitGroup - - // for each metric, make PromQL expression and send the request to Prometheus servers - for _, metricName := range componentMetrics { - matchComponentName, _ := regexp.MatchString(params.ComponentName, metricName) - matchMetricsFilter, _ := regexp.MatchString(params.MetricsFilter, metricName) - if matchComponentName && matchMetricsFilter { - wg.Add(1) - go func(metricName string, params RequestParams) { - exp := makePromqlForComponent(metricName, params) - v := url.Values{} - for key, value := range params.QueryParams { - v[key] = value - } - v.Set("query", exp) - response := client.QueryToK8SSystemPrometheus(params.QueryType, v.Encode()) - - // add node address when queried metric is etcd_server_list - if metricName == "etcd_server_list" { - for _, item := range response.Data.Result { - item.Metric["node_name"] = getNodeName(item.Metric["node_ip"]) - } - } - - ch <- APIResponse{ - MetricName: metricName, - APIResponse: response, - } - wg.Done() - }(metricName, params) - } - } - wg.Wait() - close(ch) - - var apiResponse []APIResponse - for e := range ch { - apiResponse = append(apiResponse, e) - } - - return &Response{ - MetricsLevel: MonitorLevelComponent, - Results: apiResponse, - } -} - -func makePromqlForCluster(metricName string, _ RequestParams) string { - return metricsPromqlMap[metricName] -} - -func makePromqlForNode(metricName string, params RequestParams) string { - var rule = metricsPromqlMap[metricName] - var nodeSelector string - - if params.NodeName != "" { - nodeSelector = fmt.Sprintf(`node="%s"`, params.NodeName) - } else { - nodeSelector = fmt.Sprintf(`node=~"%s"`, params.ResourcesFilter) - } - - return strings.Replace(rule, "$1", nodeSelector, -1) -} - -func makePromqlForWorkspace(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var workspaceSelector string - - if params.WorkspaceName != "" { - workspaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace="%s"`, params.WorkspaceName) - } else { - workspaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace=~"%s", label_kubesphere_io_workspace!=""`, params.ResourcesFilter) - } - - return strings.Replace(exp, "$1", workspaceSelector, -1) -} - -func makePromqlForNamespace(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var namespaceSelector string - - // For monitoring namespaces in the specific workspace - // GET /workspaces/{workspace}/namespaces - if params.WorkspaceName != "" { - namespaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace="%s", namespace=~"%s"`, params.WorkspaceName, params.ResourcesFilter) - return strings.Replace(exp, "$1", namespaceSelector, -1) - } - - // For monitoring the specific namespaces - // GET /namespaces/{namespace} or - // GET /namespaces - if params.NamespaceName != "" { - namespaceSelector = fmt.Sprintf(`namespace="%s"`, params.NamespaceName) - } else { - namespaceSelector = fmt.Sprintf(`namespace=~"%s"`, params.ResourcesFilter) - } - return strings.Replace(exp, "$1", namespaceSelector, -1) -} - -func makePromqlForWorkload(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var kind, kindSelector, workloadSelector string - - switch params.WorkloadKind { - case "deployment": - kind = Deployment - kindSelector = fmt.Sprintf(`namespace="%s", deployment!="", deployment=~"%s"`, params.NamespaceName, params.ResourcesFilter) - case "statefulset": - kind = StatefulSet - kindSelector = fmt.Sprintf(`namespace="%s", statefulset!="", statefulset=~"%s"`, params.NamespaceName, params.ResourcesFilter) - case "daemonset": - kind = DaemonSet - kindSelector = fmt.Sprintf(`namespace="%s", daemonset!="", daemonset=~"%s"`, params.NamespaceName, params.ResourcesFilter) - default: - kind = ".*" - kindSelector = fmt.Sprintf(`namespace="%s"`, params.NamespaceName) - } - - workloadSelector = fmt.Sprintf(`namespace="%s", workload=~"%s:%s"`, params.NamespaceName, kind, params.ResourcesFilter) - return strings.NewReplacer("$1", workloadSelector, "$2", kindSelector).Replace(exp) -} - -func makePromqlForPod(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var podSelector, workloadSelector string - - // For monitoriong pods of the specific workload - // GET /namespaces/{namespace}/workloads/{kind}/{workload}/pods - if params.WorkloadName != "" { - switch params.WorkloadKind { - case "deployment": - workloadSelector = fmt.Sprintf(`owner_kind="ReplicaSet", owner_name=~"^%s-[^-]{1,10}$"`, params.WorkloadName) - case "statefulset": - workloadSelector = fmt.Sprintf(`owner_kind="StatefulSet", owner_name="%s"`, params.WorkloadName) - case "daemonset": - workloadSelector = fmt.Sprintf(`owner_kind="DaemonSet", owner_name="%s"`, params.WorkloadName) - } - } - - // For monitoring pods in the specific namespace - // GET /namespaces/{namespace}/workloads/{kind}/{workload}/pods or - // GET /namespaces/{namespace}/pods/{pod} or - // GET /namespaces/{namespace}/pods - if params.NamespaceName != "" { - if params.PodName != "" { - podSelector = fmt.Sprintf(`pod="%s", namespace="%s"`, params.PodName, params.NamespaceName) - } else { - podSelector = fmt.Sprintf(`pod=~"%s", namespace="%s"`, params.ResourcesFilter, params.NamespaceName) - } - } - - // For monitoring pods on the specific node - // GET /nodes/{node}/pods/{pod} - if params.NodeName != "" { - if params.PodName != "" { - podSelector = fmt.Sprintf(`pod="%s", node="%s"`, params.PodName, params.NodeName) - } else { - podSelector = fmt.Sprintf(`pod=~"%s", node="%s"`, params.ResourcesFilter, params.NodeName) - } - } - - return strings.NewReplacer("$1", workloadSelector, "$2", podSelector).Replace(exp) -} - -func makePromqlForContainer(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var containerSelector string - - if params.ContainerName != "" { - containerSelector = fmt.Sprintf(`pod_name="%s", namespace="%s", container_name="%s"`, params.PodName, params.NamespaceName, params.ContainerName) - } else { - containerSelector = fmt.Sprintf(`pod_name="%s", namespace="%s", container_name=~"%s"`, params.PodName, params.NamespaceName, params.ResourcesFilter) - } - - return strings.Replace(exp, "$1", containerSelector, -1) -} - -func makePromqlForPVC(metricName string, params RequestParams) string { - var exp = metricsPromqlMap[metricName] - var pvcSelector string - - // For monitoring persistentvolumeclaims in the specific namespace - // GET /namespaces/{namespace}/persistentvolumeclaims/{persistentvolumeclaim} or - // GET /namespaces/{namespace}/persistentvolumeclaims - if params.NamespaceName != "" { - if params.PVCName != "" { - pvcSelector = fmt.Sprintf(`namespace="%s", persistentvolumeclaim="%s"`, params.NamespaceName, params.PVCName) - } else { - pvcSelector = fmt.Sprintf(`namespace="%s", persistentvolumeclaim=~"%s"`, params.NamespaceName, params.ResourcesFilter) - } - return strings.Replace(exp, "$1", pvcSelector, -1) - } - - // For monitoring persistentvolumeclaims of the specific storageclass - // GET /storageclasses/{storageclass}/persistentvolumeclaims - if params.StorageClassName != "" { - pvcSelector = fmt.Sprintf(`storageclass="%s", persistentvolumeclaim=~"%s"`, params.StorageClassName, params.ResourcesFilter) - } - return strings.Replace(exp, "$1", pvcSelector, -1) -} - -func makePromqlForComponent(metricName string, _ RequestParams) string { - return metricsPromqlMap[metricName] -} - -func GetClusterStatistics() *Response { - - now := time.Now().Unix() - - var metricsArray []APIResponse - workspaceStats := APIResponse{MetricName: MetricClusterWorkspaceCount} - devopsStats := APIResponse{MetricName: MetricClusterDevopsCount} - namespaceStats := APIResponse{MetricName: MetricClusterNamespaceCount} - accountStats := APIResponse{MetricName: MetricClusterAccountCount} - - wg := sync.WaitGroup{} - wg.Add(4) - - go func() { - num, err := workspaces.WorkspaceCount() - if err != nil { - klog.Errorln(err) - workspaceStats.Status = "error" - } else { - workspaceStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - num, err := workspaces.GetAllDevOpsProjectsNums() - if err != nil { - if _, notEnabled := err.(cs.ClientSetNotEnabledError); !notEnabled { - klog.Errorln(err) - } - devopsStats.Status = "error" - } else { - devopsStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - num, err := workspaces.GetAllProjectNums() - if err != nil { - klog.Errorln(err) - namespaceStats.Status = "error" - } else { - namespaceStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - ret, err := cs.ClientSets().KubeSphere().ListUsers() - if err != nil { - klog.Errorln(err) - accountStats.Status = "error" - } else { - accountStats.withMetricResult(now, ret.TotalCount) - } - wg.Done() - }() - - wg.Wait() - - metricsArray = append(metricsArray, workspaceStats, devopsStats, namespaceStats, accountStats) - - return &Response{ - MetricsLevel: MonitorLevelCluster, - Results: metricsArray, - } -} - -func GetWorkspaceStatistics(workspaceName string) *Response { - - now := time.Now().Unix() - - var metricsArray []APIResponse - namespaceStats := APIResponse{MetricName: MetricWorkspaceNamespaceCount} - devopsStats := APIResponse{MetricName: MetricWorkspaceDevopsCount} - memberStats := APIResponse{MetricName: MetricWorkspaceMemberCount} - roleStats := APIResponse{MetricName: MetricWorkspaceRoleCount} - - wg := sync.WaitGroup{} - wg.Add(4) - - go func() { - num, err := workspaces.WorkspaceNamespaceCount(workspaceName) - if err != nil { - klog.Errorln(err) - namespaceStats.Status = "error" - } else { - namespaceStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - num, err := workspaces.GetDevOpsProjectsCount(workspaceName) - if err != nil { - if _, notEnabled := err.(cs.ClientSetNotEnabledError); !notEnabled { - klog.Errorln(err) - } - devopsStats.Status = "error" - } else { - devopsStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - num, err := workspaces.WorkspaceUserCount(workspaceName) - if err != nil { - klog.Errorln(err) - memberStats.Status = "error" - } else { - memberStats.withMetricResult(now, num) - } - wg.Done() - }() - - go func() { - num, err := workspaces.GetOrgRolesCount(workspaceName) - if err != nil { - klog.Errorln(err) - roleStats.Status = "error" - } else { - roleStats.withMetricResult(now, num) - } - wg.Done() - }() - - wg.Wait() - - metricsArray = append(metricsArray, namespaceStats, devopsStats, memberStats, roleStats) - - return &Response{ - MetricsLevel: MonitorLevelWorkspace, - Results: metricsArray, - } -} - -func (response *APIResponse) withMetricResult(time int64, value int) { - response.Status = "success" - response.Data = v1alpha2.QueryResult{ - ResultType: "vector", - Result: []v1alpha2.QueryValue{ - { - Value: []interface{}{time, value}, - }, - }, - } -} diff --git a/pkg/models/metrics/metrics_rules.go b/pkg/models/metrics/metrics_rules.go deleted file mode 100644 index 8879a5af6..000000000 --- a/pkg/models/metrics/metrics_rules.go +++ /dev/null @@ -1,508 +0,0 @@ -/* -Copyright 2019 The KubeSphere 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 metrics - -const ( - // TODO: expose the following metrics in prometheus format - MetricClusterWorkspaceCount = "cluster_workspace_count" - MetricClusterAccountCount = "cluster_account_count" - MetricClusterNamespaceCount = "cluster_namespace_count" - MetricClusterDevopsCount = "cluster_devops_project_count" - - MetricWorkspaceNamespaceCount = "workspace_namespace_count" - MetricWorkspaceDevopsCount = "workspace_devops_project_count" - MetricWorkspaceMemberCount = "workspace_member_count" - MetricWorkspaceRoleCount = "workspace_role_count" -) - -var clusterMetrics = []string{ - "cluster_cpu_utilisation", - "cluster_cpu_usage", - "cluster_cpu_total", - "cluster_memory_utilisation", - "cluster_memory_available", - "cluster_memory_total", - "cluster_memory_usage_wo_cache", - "cluster_net_utilisation", - "cluster_net_bytes_transmitted", - "cluster_net_bytes_received", - "cluster_disk_read_iops", - "cluster_disk_write_iops", - "cluster_disk_read_throughput", - "cluster_disk_write_throughput", - "cluster_disk_size_usage", - "cluster_disk_size_utilisation", - "cluster_disk_size_capacity", - "cluster_disk_size_available", - "cluster_disk_inode_total", - "cluster_disk_inode_usage", - "cluster_disk_inode_utilisation", - "cluster_namespace_count", - "cluster_pod_count", - "cluster_pod_quota", - "cluster_pod_utilisation", - "cluster_pod_running_count", - "cluster_pod_succeeded_count", - "cluster_pod_abnormal_count", - "cluster_node_online", - "cluster_node_offline", - "cluster_node_total", - "cluster_cronjob_count", - "cluster_pvc_count", - "cluster_daemonset_count", - "cluster_deployment_count", - "cluster_endpoint_count", - "cluster_hpa_count", - "cluster_job_count", - "cluster_statefulset_count", - "cluster_replicaset_count", - "cluster_service_count", - "cluster_secret_count", - "cluster_pv_count", - "cluster_ingresses_extensions_count", - "cluster_load1", - "cluster_load5", - "cluster_load15", - "cluster_pod_abnormal_ratio", - "cluster_node_offline_ratio", -} - -var nodeMetrics = []string{ - "node_cpu_utilisation", - "node_cpu_total", - "node_cpu_usage", - "node_memory_utilisation", - "node_memory_usage_wo_cache", - "node_memory_available", - "node_memory_total", - "node_net_utilisation", - "node_net_bytes_transmitted", - "node_net_bytes_received", - "node_disk_read_iops", - "node_disk_write_iops", - "node_disk_read_throughput", - "node_disk_write_throughput", - "node_disk_size_capacity", - "node_disk_size_available", - "node_disk_size_usage", - "node_disk_size_utilisation", - "node_disk_inode_total", - "node_disk_inode_usage", - "node_disk_inode_utilisation", - "node_pod_count", - "node_pod_quota", - "node_pod_utilisation", - "node_pod_running_count", - "node_pod_succeeded_count", - "node_pod_abnormal_count", - "node_load1", - "node_load5", - "node_load15", - "node_pod_abnormal_ratio", -} - -var workspaceMetrics = []string{ - "workspace_cpu_usage", - "workspace_memory_usage", - "workspace_memory_usage_wo_cache", - "workspace_net_bytes_transmitted", - "workspace_net_bytes_received", - "workspace_pod_count", - "workspace_pod_running_count", - "workspace_pod_succeeded_count", - "workspace_pod_abnormal_count", - "workspace_ingresses_extensions_count", - "workspace_cronjob_count", - "workspace_pvc_count", - "workspace_daemonset_count", - "workspace_deployment_count", - "workspace_endpoint_count", - "workspace_hpa_count", - "workspace_job_count", - "workspace_statefulset_count", - "workspace_replicaset_count", - "workspace_service_count", - "workspace_secret_count", - "workspace_pod_abnormal_ratio", -} - -var namespaceMetrics = []string{ - "namespace_cpu_usage", - "namespace_memory_usage", - "namespace_memory_usage_wo_cache", - "namespace_net_bytes_transmitted", - "namespace_net_bytes_received", - "namespace_pod_count", - "namespace_pod_running_count", - "namespace_pod_succeeded_count", - "namespace_pod_abnormal_count", - "namespace_pod_abnormal_ratio", - "namespace_memory_limit_hard", - "namespace_cpu_limit_hard", - "namespace_pod_count_hard", - "namespace_cronjob_count", - "namespace_pvc_count", - "namespace_daemonset_count", - "namespace_deployment_count", - "namespace_endpoint_count", - "namespace_hpa_count", - "namespace_job_count", - "namespace_statefulset_count", - "namespace_replicaset_count", - "namespace_service_count", - "namespace_secret_count", - "namespace_configmap_count", - "namespace_ingresses_extensions_count", - "namespace_s2ibuilder_count", -} - -var workloadMetrics = []string{ - // TODO: the following five metrics are deprecated. - "workload_pod_cpu_usage", - "workload_pod_memory_usage", - "workload_pod_memory_usage_wo_cache", - "workload_pod_net_bytes_transmitted", - "workload_pod_net_bytes_received", - - "workload_cpu_usage", - "workload_memory_usage", - "workload_memory_usage_wo_cache", - "workload_net_bytes_transmitted", - "workload_net_bytes_received", - - "workload_deployment_replica", - "workload_deployment_replica_available", - "workload_statefulset_replica", - "workload_statefulset_replica_available", - "workload_daemonset_replica", - "workload_daemonset_replica_available", - "workload_deployment_unavailable_replicas_ratio", - "workload_daemonset_unavailable_replicas_ratio", - "workload_statefulset_unavailable_replicas_ratio", -} - -var podMetrics = []string{ - "pod_cpu_usage", - "pod_memory_usage", - "pod_memory_usage_wo_cache", - "pod_net_bytes_transmitted", - "pod_net_bytes_received", -} - -var containerMetrics = []string{ - "container_cpu_usage", - "container_memory_usage", - "container_memory_usage_wo_cache", -} - -var pvcMetrics = []string{ - "pvc_inodes_available", - "pvc_inodes_used", - "pvc_inodes_total", - "pvc_inodes_utilisation", - "pvc_bytes_available", - "pvc_bytes_used", - "pvc_bytes_total", - "pvc_bytes_utilisation", -} - -var componentMetrics = []string{ - "etcd_server_list", - "etcd_server_total", - "etcd_server_up_total", - "etcd_server_has_leader", - "etcd_server_leader_changes", - "etcd_server_proposals_failed_rate", - "etcd_server_proposals_applied_rate", - "etcd_server_proposals_committed_rate", - "etcd_server_proposals_pending_count", - "etcd_mvcc_db_size", - "etcd_network_client_grpc_received_bytes", - "etcd_network_client_grpc_sent_bytes", - "etcd_grpc_call_rate", - "etcd_grpc_call_failed_rate", - "etcd_grpc_server_msg_received_rate", - "etcd_grpc_server_msg_sent_rate", - "etcd_disk_wal_fsync_duration", - "etcd_disk_wal_fsync_duration_quantile", - "etcd_disk_backend_commit_duration", - "etcd_disk_backend_commit_duration_quantile", - - "apiserver_up_sum", - "apiserver_request_rate", - "apiserver_request_by_verb_rate", - "apiserver_request_latencies", - "apiserver_request_by_verb_latencies", - - "scheduler_up_sum", - "scheduler_schedule_attempts", - "scheduler_schedule_attempt_rate", - "scheduler_e2e_scheduling_latency", - "scheduler_e2e_scheduling_latency_quantile", - - "controller_manager_up_sum", - - "coredns_up_sum", - "coredns_cache_hits", - "coredns_cache_misses", - "coredns_dns_request_rate", - "coredns_dns_request_duration", - "coredns_dns_request_duration_quantile", - "coredns_dns_request_by_type_rate", - "coredns_dns_request_by_rcode_rate", - "coredns_panic_rate", - "coredns_proxy_request_rate", - "coredns_proxy_request_duration", - "coredns_proxy_request_duration_quantile", - - "prometheus_up_sum", - "prometheus_tsdb_head_samples_appended_rate", -} - -var metricsPromqlMap = map[string]string{ - //cluster - "cluster_cpu_utilisation": ":node_cpu_utilisation:avg1m", - "cluster_cpu_usage": `round(:node_cpu_utilisation:avg1m * sum(node:node_num_cpu:sum), 0.001)`, - "cluster_cpu_total": "sum(node:node_num_cpu:sum)", - "cluster_memory_utilisation": ":node_memory_utilisation:", - "cluster_memory_available": "sum(node:node_memory_bytes_available:sum)", - "cluster_memory_total": "sum(node:node_memory_bytes_total:sum)", - "cluster_memory_usage_wo_cache": "sum(node:node_memory_bytes_total:sum) - sum(node:node_memory_bytes_available:sum)", - "cluster_net_utilisation": ":node_net_utilisation:sum_irate", - "cluster_net_bytes_transmitted": "sum(node:node_net_bytes_transmitted:sum_irate)", - "cluster_net_bytes_received": "sum(node:node_net_bytes_received:sum_irate)", - "cluster_disk_read_iops": "sum(node:data_volume_iops_reads:sum)", - "cluster_disk_write_iops": "sum(node:data_volume_iops_writes:sum)", - "cluster_disk_read_throughput": "sum(node:data_volume_throughput_bytes_read:sum)", - "cluster_disk_write_throughput": "sum(node:data_volume_throughput_bytes_written:sum)", - "cluster_disk_size_usage": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} - node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, - "cluster_disk_size_utilisation": `cluster:disk_utilization:ratio`, - "cluster_disk_size_capacity": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, - "cluster_disk_size_available": `sum(max(node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, - "cluster_disk_inode_total": `sum(node:node_inodes_total:)`, - "cluster_disk_inode_usage": `sum(node:node_inodes_total:) - sum(node:node_inodes_free:)`, - "cluster_disk_inode_utilisation": `cluster:disk_inode_utilization:ratio`, - "cluster_namespace_count": `count(kube_namespace_annotations)`, - "cluster_pod_count": `cluster:pod:sum`, - "cluster_pod_quota": `sum(max(kube_node_status_capacity_pods) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`, - "cluster_pod_utilisation": `cluster:pod_utilization:ratio`, - "cluster_pod_running_count": `cluster:pod_running:count`, - "cluster_pod_succeeded_count": `count(kube_pod_info unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Running"} > 0) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`, - "cluster_pod_abnormal_count": `cluster:pod_abnormal:sum`, - "cluster_node_online": `sum(kube_node_status_condition{condition="Ready",status="true"})`, - "cluster_node_offline": `cluster:node_offline:sum`, - "cluster_node_total": `sum(kube_node_status_condition{condition="Ready"})`, - "cluster_cronjob_count": `sum(kube_cronjob_labels)`, - "cluster_pvc_count": `sum(kube_persistentvolumeclaim_info)`, - "cluster_daemonset_count": `sum(kube_daemonset_labels)`, - "cluster_deployment_count": `sum(kube_deployment_labels)`, - "cluster_endpoint_count": `sum(kube_endpoint_labels)`, - "cluster_hpa_count": `sum(kube_hpa_labels)`, - "cluster_job_count": `sum(kube_job_labels)`, - "cluster_statefulset_count": `sum(kube_statefulset_labels)`, - "cluster_replicaset_count": `count(kube_replicaset_created)`, - "cluster_service_count": `sum(kube_service_info)`, - "cluster_secret_count": `sum(kube_secret_info)`, - "cluster_pv_count": `sum(kube_persistentvolume_labels)`, - "cluster_ingresses_extensions_count": `sum(kube_ingress_labels)`, - "cluster_load1": `sum(node_load1{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, - "cluster_load5": `sum(node_load5{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, - "cluster_load15": `sum(node_load15{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, - "cluster_pod_abnormal_ratio": `cluster:pod_abnormal:ratio`, - "cluster_node_offline_ratio": `cluster:node_offline:ratio`, - - //node - "node_cpu_utilisation": "node:node_cpu_utilisation:avg1m{$1}", - "node_cpu_total": "node:node_num_cpu:sum{$1}", - "node_memory_utilisation": "node:node_memory_utilisation:{$1}", - "node_memory_available": "node:node_memory_bytes_available:sum{$1}", - "node_memory_total": "node:node_memory_bytes_total:sum{$1}", - "node_memory_usage_wo_cache": "node:node_memory_bytes_total:sum{$1} - node:node_memory_bytes_available:sum{$1}", - "node_net_utilisation": "node:node_net_utilisation:sum_irate{$1}", - "node_net_bytes_transmitted": "node:node_net_bytes_transmitted:sum_irate{$1}", - "node_net_bytes_received": "node:node_net_bytes_received:sum_irate{$1}", - "node_disk_read_iops": "node:data_volume_iops_reads:sum{$1}", - "node_disk_write_iops": "node:data_volume_iops_writes:sum{$1}", - "node_disk_read_throughput": "node:data_volume_throughput_bytes_read:sum{$1}", - "node_disk_write_throughput": "node:data_volume_throughput_bytes_written:sum{$1}", - "node_disk_size_capacity": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{$1}) by (device, node)) by (node)`, - "node_disk_size_available": `node:disk_space_available:{$1}`, - "node_disk_size_usage": `sum(max((node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} - node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{$1}) by (device, node)) by (node)`, - "node_disk_size_utilisation": `node:disk_space_utilization:ratio{$1}`, - "node_disk_inode_total": `node:node_inodes_total:{$1}`, - "node_disk_inode_usage": `node:node_inodes_total:{$1} - node:node_inodes_free:{$1}`, - "node_disk_inode_utilisation": `node:disk_inode_utilization:ratio{$1}`, - "node_pod_count": `node:pod_count:sum{$1}`, - "node_pod_quota": `max(kube_node_status_capacity_pods{$1}) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`, - "node_pod_utilisation": `node:pod_utilization:ratio{$1}`, - "node_pod_running_count": `node:pod_running:count{$1}`, - "node_pod_succeeded_count": `node:pod_succeeded:count{$1}`, - "node_pod_abnormal_count": `node:pod_abnormal:count{$1}`, - "node_cpu_usage": `round(node:node_cpu_utilisation:avg1m{$1} * node:node_num_cpu:sum{$1}, 0.001)`, - "node_load1": `node:load1:ratio{$1}`, - "node_load5": `node:load5:ratio{$1}`, - "node_load15": `node:load15:ratio{$1}`, - "node_pod_abnormal_ratio": `node:pod_abnormal:ratio{$1}`, - - // workspace - "workspace_cpu_usage": `round(sum by (label_kubesphere_io_workspace) (namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", $1}), 0.001)`, - "workspace_memory_usage": `sum by (label_kubesphere_io_workspace) (namespace:container_memory_usage_bytes:sum{namespace!="", $1})`, - "workspace_memory_usage_wo_cache": `sum by (label_kubesphere_io_workspace) (namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", $1})`, - "workspace_net_bytes_transmitted": `sum by (label_kubesphere_io_workspace) (sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "workspace_net_bytes_received": `sum by (label_kubesphere_io_workspace) (sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "workspace_pod_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_pod_running_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase="Running", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_pod_succeeded_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_pod_abnormal_count": `count by (label_kubesphere_io_workspace) ((kube_pod_info{node!=""} unless on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Succeeded"}>0) unless on (pod, namespace) ((kube_pod_status_ready{job="kube-state-metrics", condition="true"}>0) and on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Running"}>0)) unless on (pod, namespace) (kube_pod_container_status_waiting_reason{job="kube-state-metrics", reason="ContainerCreating"}>0)) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_ingresses_extensions_count": `sum by (label_kubesphere_io_workspace) (kube_ingress_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_cronjob_count": `sum by (label_kubesphere_io_workspace) (kube_cronjob_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_pvc_count": `sum by (label_kubesphere_io_workspace) (kube_persistentvolumeclaim_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_daemonset_count": `sum by (label_kubesphere_io_workspace) (kube_daemonset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_deployment_count": `sum by (label_kubesphere_io_workspace) (kube_deployment_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_endpoint_count": `sum by (label_kubesphere_io_workspace) (kube_endpoint_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_hpa_count": `sum by (label_kubesphere_io_workspace) (kube_hpa_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_job_count": `sum by (label_kubesphere_io_workspace) (kube_job_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_statefulset_count": `sum by (label_kubesphere_io_workspace) (kube_statefulset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_replicaset_count": `count by (label_kubesphere_io_workspace) (kube_replicaset_created{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_service_count": `sum by (label_kubesphere_io_workspace) (kube_service_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_secret_count": `sum by (label_kubesphere_io_workspace) (kube_secret_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - "workspace_pod_abnormal_ratio": `count by (label_kubesphere_io_workspace) ((kube_pod_info{node!=""} unless on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Succeeded"}>0) unless on (pod, namespace) ((kube_pod_status_ready{job="kube-state-metrics", condition="true"}>0) and on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Running"}>0)) unless on (pod, namespace) (kube_pod_container_status_waiting_reason{job="kube-state-metrics", reason="ContainerCreating"}>0)) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1}) / sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase!="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, - - //namespace - "namespace_cpu_usage": `round(namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", $1}, 0.001)`, - "namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace!="", $1}`, - "namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", $1}`, - "namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pod_count": `sum by (namespace) (kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pod_running_count": `sum by (namespace) (kube_pod_status_phase{phase="Running", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pod_succeeded_count": `sum by (namespace) (kube_pod_status_phase{phase="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pod_abnormal_count": `namespace:pod_abnormal:count{namespace!="", $1}`, - "namespace_pod_abnormal_ratio": `namespace:pod_abnormal:ratio{namespace!="", $1}`, - "namespace_memory_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.memory"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_cpu_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.cpu"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pod_count_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="count/pods"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_cronjob_count": `sum by (namespace) (kube_cronjob_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_pvc_count": `sum by (namespace) (kube_persistentvolumeclaim_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_daemonset_count": `sum by (namespace) (kube_daemonset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_deployment_count": `sum by (namespace) (kube_deployment_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_endpoint_count": `sum by (namespace) (kube_endpoint_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_hpa_count": `sum by (namespace) (kube_hpa_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_job_count": `sum by (namespace) (kube_job_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_statefulset_count": `sum by (namespace) (kube_statefulset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_replicaset_count": `count by (namespace) (kube_replicaset_created{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_service_count": `sum by (namespace) (kube_service_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_secret_count": `sum by (namespace) (kube_secret_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_configmap_count": `sum by (namespace) (kube_configmap_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_ingresses_extensions_count": `sum by (namespace) (kube_ingress_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - "namespace_s2ibuilder_count": `sum by (namespace) (s2i_s2ibuilder_created{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, - - // workload - // TODO: the following five metrics are deprecated. - "workload_pod_cpu_usage": `round(namespace:workload_cpu_usage:sum{$1}, 0.001)`, - "workload_pod_memory_usage": `namespace:workload_memory_usage:sum{$1}`, - "workload_pod_memory_usage_wo_cache": `namespace:workload_memory_usage_wo_cache:sum{$1}`, - "workload_pod_net_bytes_transmitted": `namespace:workload_net_bytes_transmitted:sum_irate{$1}`, - "workload_pod_net_bytes_received": `namespace:workload_net_bytes_received:sum_irate{$1}`, - - "workload_cpu_usage": `round(namespace:workload_cpu_usage:sum{$1}, 0.001)`, - "workload_memory_usage": `namespace:workload_memory_usage:sum{$1}`, - "workload_memory_usage_wo_cache": `namespace:workload_memory_usage_wo_cache:sum{$1}`, - "workload_net_bytes_transmitted": `namespace:workload_net_bytes_transmitted:sum_irate{$1}`, - "workload_net_bytes_received": `namespace:workload_net_bytes_received:sum_irate{$1}`, - - "workload_deployment_replica": `label_join(sum (label_join(label_replace(kube_deployment_spec_replicas{$2}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_deployment_replica_available": `label_join(sum (label_join(label_replace(kube_deployment_status_replicas_available{$2}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_statefulset_replica": `label_join(sum (label_join(label_replace(kube_statefulset_replicas{$2}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_statefulset_replica_available": `label_join(sum (label_join(label_replace(kube_statefulset_status_replicas_current{$2}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_daemonset_replica": `label_join(sum (label_join(label_replace(kube_daemonset_status_desired_number_scheduled{$2}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_daemonset_replica_available": `label_join(sum (label_join(label_replace(kube_daemonset_status_number_available{$2}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, - "workload_deployment_unavailable_replicas_ratio": `namespace:deployment_unavailable_replicas:ratio{$1}`, - "workload_daemonset_unavailable_replicas_ratio": `namespace:daemonset_unavailable_replicas:ratio{$1}`, - "workload_statefulset_unavailable_replicas_ratio": `namespace:statefulset_unavailable_replicas:ratio{$1}`, - - // pod - "pod_cpu_usage": `round(label_join(sum by (namespace, pod_name) (irate(container_cpu_usage_seconds_total{job="kubelet", pod_name!="", image!=""}[5m])), "pod", "", "pod_name") * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}, 0.001)`, - "pod_memory_usage": `label_join(sum by (namespace, pod_name) (container_memory_usage_bytes{job="kubelet", pod_name!="", image!=""}), "pod", "", "pod_name") * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, - "pod_memory_usage_wo_cache": `label_join(sum by (namespace, pod_name) (container_memory_usage_bytes{job="kubelet", pod_name!="", image!=""} - container_memory_cache{job="kubelet", pod_name!="", image!=""}), "pod", "", "pod_name") * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, - "pod_net_bytes_transmitted": `label_join(sum by (namespace, pod_name) (irate(container_network_transmit_bytes_total{pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])), "pod", "", "pod_name") * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, - "pod_net_bytes_received": `label_join(sum by (namespace, pod_name) (irate(container_network_receive_bytes_total{pod_name!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])), "pod", "", "pod_name") * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, - - // container - "container_cpu_usage": `round(sum by (namespace, pod_name, container_name) (irate(container_cpu_usage_seconds_total{job="kubelet", container_name!="POD", container_name!="", image!="", $1}[5m])), 0.001)`, - "container_memory_usage": `sum by (namespace, pod_name, container_name) (container_memory_usage_bytes{job="kubelet", container_name!="POD", container_name!="", image!="", $1})`, - "container_memory_usage_wo_cache": `sum by (namespace, pod_name, container_name) (container_memory_usage_bytes{job="kubelet", container_name!="POD", container_name!="", image!="", $1} - container_memory_cache{job="kubelet", container_name!="POD", container_name!="", image!="", $1})`, - - // pvc - "pvc_inodes_available": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_free) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_inodes_used": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_used) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_inodes_total": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_inodes_utilisation": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_used / kubelet_volume_stats_inodes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_bytes_available": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_available_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_bytes_used": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_used_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_bytes_total": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - "pvc_bytes_utilisation": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, - - // component - "etcd_server_list": `label_replace(up{job="etcd"}, "node_ip", "$1", "instance", "(.*):.*")`, - "etcd_server_total": `count(up{job="etcd"})`, - "etcd_server_up_total": `etcd:up:sum`, - "etcd_server_has_leader": `label_replace(etcd_server_has_leader, "node_ip", "$1", "instance", "(.*):.*")`, - "etcd_server_leader_changes": `label_replace(etcd:etcd_server_leader_changes_seen:sum_changes, "node_ip", "$1", "node", "(.*)")`, - "etcd_server_proposals_failed_rate": `avg(etcd:etcd_server_proposals_failed:sum_irate)`, - "etcd_server_proposals_applied_rate": `avg(etcd:etcd_server_proposals_applied:sum_irate)`, - "etcd_server_proposals_committed_rate": `avg(etcd:etcd_server_proposals_committed:sum_irate)`, - "etcd_server_proposals_pending_count": `avg(etcd:etcd_server_proposals_pending:sum)`, - "etcd_mvcc_db_size": `avg(etcd:etcd_debugging_mvcc_db_total_size:sum)`, - "etcd_network_client_grpc_received_bytes": `sum(etcd:etcd_network_client_grpc_received_bytes:sum_irate)`, - "etcd_network_client_grpc_sent_bytes": `sum(etcd:etcd_network_client_grpc_sent_bytes:sum_irate)`, - "etcd_grpc_call_rate": `sum(etcd:grpc_server_started:sum_irate)`, - "etcd_grpc_call_failed_rate": `sum(etcd:grpc_server_handled:sum_irate)`, - "etcd_grpc_server_msg_received_rate": `sum(etcd:grpc_server_msg_received:sum_irate)`, - "etcd_grpc_server_msg_sent_rate": `sum(etcd:grpc_server_msg_sent:sum_irate)`, - "etcd_disk_wal_fsync_duration": `avg(etcd:etcd_disk_wal_fsync_duration:avg)`, - "etcd_disk_wal_fsync_duration_quantile": `avg(etcd:etcd_disk_wal_fsync_duration:histogram_quantile) by (quantile)`, - "etcd_disk_backend_commit_duration": `avg(etcd:etcd_disk_backend_commit_duration:avg)`, - "etcd_disk_backend_commit_duration_quantile": `avg(etcd:etcd_disk_backend_commit_duration:histogram_quantile) by (quantile)`, - - "apiserver_up_sum": `apiserver:up:sum`, - "apiserver_request_rate": `apiserver:apiserver_request_count:sum_irate`, - "apiserver_request_by_verb_rate": `apiserver:apiserver_request_count:sum_verb_irate`, - "apiserver_request_latencies": `apiserver:apiserver_request_latencies:avg`, - "apiserver_request_by_verb_latencies": `apiserver:apiserver_request_latencies:avg_by_verb`, - - "scheduler_up_sum": `scheduler:up:sum`, - "scheduler_schedule_attempts": `scheduler:scheduler_schedule_attempts:sum`, - "scheduler_schedule_attempt_rate": `scheduler:scheduler_schedule_attempts:sum_rate`, - "scheduler_e2e_scheduling_latency": `scheduler:scheduler_e2e_scheduling_latency:avg`, - "scheduler_e2e_scheduling_latency_quantile": `scheduler:scheduler_e2e_scheduling_latency:histogram_quantile`, - - "controller_manager_up_sum": `controller_manager:up:sum`, - - "coredns_up_sum": `coredns:up:sum`, - "coredns_cache_hits": `coredns:coredns_cache_hits_total:sum_irate`, - "coredns_cache_misses": `coredns:coredns_cache_misses:sum_irate`, - "coredns_dns_request_rate": `coredns:coredns_dns_request_count:sum_irate`, - "coredns_dns_request_duration": `coredns:coredns_dns_request_duration:avg`, - "coredns_dns_request_duration_quantile": `coredns:coredns_dns_request_duration:histogram_quantile`, - "coredns_dns_request_by_type_rate": `coredns:coredns_dns_request_type_count:sum_irate`, - "coredns_dns_request_by_rcode_rate": `coredns:coredns_dns_response_rcode_count:sum_irate`, - "coredns_panic_rate": `coredns:coredns_panic_count:sum_irate`, - "coredns_proxy_request_rate": `coredns:coredns_proxy_request_count:sum_irate`, - "coredns_proxy_request_duration": `coredns:coredns_proxy_request_duration:avg`, - "coredns_proxy_request_duration_quantile": `coredns:coredns_proxy_request_duration:histogram_quantile`, - - "prometheus_up_sum": `prometheus:up:sum`, - "prometheus_tsdb_head_samples_appended_rate": `prometheus:prometheus_tsdb_head_samples_appended:sum_rate`, -} diff --git a/pkg/models/metrics/namespaces.go b/pkg/models/metrics/namespaces.go deleted file mode 100644 index 5125cc168..000000000 --- a/pkg/models/metrics/namespaces.go +++ /dev/null @@ -1,68 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 metrics - -import ( - "net/url" - "strings" - - "k8s.io/api/core/v1" -) - -func GetNamespacesWithMetrics(namespaces []*v1.Namespace) []*v1.Namespace { - var nsNameList []string - for i := range namespaces { - nsNameList = append(nsNameList, namespaces[i].Name) - } - nsFilter := "^(" + strings.Join(nsNameList, "|") + ")$" - var timeRelateParams = make(url.Values) - - params := RequestParams{ - ResourcesFilter: nsFilter, - QueryParams: timeRelateParams, - QueryType: Query, - MetricsFilter: "namespace_cpu_usage|namespace_memory_usage_wo_cache|namespace_pod_count", - } - - rawMetrics := GetNamespaceMetrics(params) - if rawMetrics == nil { - return namespaces - } - - for _, result := range rawMetrics.Results { - for _, data := range result.Data.Result { - - ns, exist := data.Metric["namespace"] - - if !exist || len(data.Value) != 2 { - continue - } - - for _, item := range namespaces { - if item.Name == ns { - if item.Annotations == nil { - item.Annotations = make(map[string]string, 0) - } - item.Annotations[result.MetricName] = data.Value[1].(string) - } - } - } - } - - return namespaces -} diff --git a/pkg/models/metrics/types.go b/pkg/models/metrics/types.go deleted file mode 100644 index 1b8023da6..000000000 --- a/pkg/models/metrics/types.go +++ /dev/null @@ -1,59 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 metrics - -import ( - "kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2" - "net/url" -) - -type RequestParams struct { - QueryParams url.Values - QueryType string - SortMetric string - SortType string - PageNum string - LimitNum string - Type string - MetricsFilter string - ResourcesFilter string - NodeName string - WorkspaceName string - NamespaceName string - WorkloadKind string - WorkloadName string - PodName string - ContainerName string - PVCName string - StorageClassName string - ComponentName string -} - -type APIResponse struct { - MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum"` - v1alpha2.APIResponse -} - -type Response struct { - MetricsLevel string `json:"metrics_level" description:"metric level, eg. cluster"` - Results []APIResponse `json:"results" description:"actual array of results"` - CurrentPage int `json:"page,omitempty" description:"current page returned"` - TotalPage int `json:"total_page,omitempty" description:"total number of pages"` - TotalItem int `json:"total_item,omitempty" description:"page size"` -} diff --git a/pkg/models/metrics/util.go b/pkg/models/metrics/util.go deleted file mode 100644 index bead04ff4..000000000 --- a/pkg/models/metrics/util.go +++ /dev/null @@ -1,293 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 metrics - -import ( - "k8s.io/apimachinery/pkg/labels" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2" - "kubesphere.io/kubesphere/pkg/informers" - "math" - "sort" - "strconv" - - "runtime/debug" -) - -const ( - DefaultPageLimit = 5 - DefaultPage = 1 - - ResultTypeVector = "vector" - ResultTypeMatrix = "matrix" - MetricStatusSuccess = "success" - ResultItemMetricResourceName = "resource_name" - ResultSortTypeDesc = "desc" - ResultSortTypeAsc = "asc" -) - -type FormatedMetricDataWrapper struct { - fmtMetricData v1alpha2.QueryResult - by func(p, q *v1alpha2.QueryValue) bool -} - -func (wrapper FormatedMetricDataWrapper) Len() int { - return len(wrapper.fmtMetricData.Result) -} - -func (wrapper FormatedMetricDataWrapper) Less(i, j int) bool { - return wrapper.by(&wrapper.fmtMetricData.Result[i], &wrapper.fmtMetricData.Result[j]) -} - -func (wrapper FormatedMetricDataWrapper) Swap(i, j int) { - wrapper.fmtMetricData.Result[i], wrapper.fmtMetricData.Result[j] = wrapper.fmtMetricData.Result[j], wrapper.fmtMetricData.Result[i] -} - -// sorted metric by ascending or descending order -func (rawMetrics *Response) SortBy(sortMetricName string, sortType string) (*Response, int) { - defer func() { - if err := recover(); err != nil { - klog.Errorln(err) - debug.PrintStack() - } - }() - - if sortMetricName == "" || rawMetrics == nil { - return rawMetrics, -1 - } - - // default sort type is descending order - if sortType == "" { - sortType = ResultSortTypeDesc - } - - var currentResourceMap = make(map[string]int) - - // {: } - var indexMap = make(map[string]int) - i := 0 - - // each metricItem is the result for a specific metric name - // so we find the metricItem with sortMetricName, and sort it - for _, metricItem := range rawMetrics.Results { - // only vector type result can be sorted - if metricItem.Data.ResultType == ResultTypeVector && metricItem.Status == MetricStatusSuccess { - if metricItem.MetricName == sortMetricName { - if sortType == ResultSortTypeAsc { - // asc - sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *v1alpha2.QueryValue) bool { - value1 := p.Value - value2 := q.Value - v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64) - v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64) - if v1 == v2 { - resourceName1 := p.Metric[ResultItemMetricResourceName] - resourceName2 := q.Metric[ResultItemMetricResourceName] - return resourceName1 < resourceName2 - } - - return v1 < v2 - }}) - } else { - // desc - sort.Sort(FormatedMetricDataWrapper{metricItem.Data, func(p, q *v1alpha2.QueryValue) bool { - value1 := p.Value - value2 := q.Value - v1, _ := strconv.ParseFloat(value1[len(value1)-1].(string), 64) - v2, _ := strconv.ParseFloat(value2[len(value2)-1].(string), 64) - - if v1 == v2 { - resourceName1 := p.Metric[ResultItemMetricResourceName] - resourceName2 := q.Metric[ResultItemMetricResourceName] - return resourceName1 > resourceName2 - } - - return v1 > v2 - }}) - } - - for _, r := range metricItem.Data.Result { - // record the ordering of resource_name to indexMap - // example: {"metric":{ResultItemMetricResourceName: "Deployment:xxx"},"value":[1541142931.731,"3"]} - resourceName, exist := r.Metric[ResultItemMetricResourceName] - if exist { - if _, exist := indexMap[resourceName]; !exist { - indexMap[resourceName] = i - i = i + 1 - } - } - } - } - - // iterator all metric to find max metricItems length - for _, r := range metricItem.Data.Result { - k, ok := r.Metric[ResultItemMetricResourceName] - if ok { - currentResourceMap[k] = 1 - } - } - - } - } - - var keys []string - for k := range currentResourceMap { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, resource := range keys { - if _, exist := indexMap[resource]; !exist { - indexMap[resource] = i - i = i + 1 - } - } - - // sort other metric - for i := 0; i < len(rawMetrics.Results); i++ { - re := rawMetrics.Results[i] - if re.Data.ResultType == ResultTypeVector && re.Status == MetricStatusSuccess { - sortedMetric := make([]v1alpha2.QueryValue, len(indexMap)) - for j := 0; j < len(re.Data.Result); j++ { - r := re.Data.Result[j] - k, exist := r.Metric[ResultItemMetricResourceName] - if exist { - index, exist := indexMap[k] - if exist { - sortedMetric[index] = r - } - } - } - - rawMetrics.Results[i].Data.Result = sortedMetric - } - } - - return rawMetrics, len(indexMap) -} - -func (fmtLevelMetric *Response) Page(pageNum string, limitNum string, maxLength int) *Response { - if maxLength <= 0 { - return fmtLevelMetric - } - // matrix type can not be sorted - for _, metricItem := range fmtLevelMetric.Results { - // if metric reterieved field, resultType: "" - if metricItem.Data.ResultType == ResultTypeMatrix { - return fmtLevelMetric - } - } - - var page = DefaultPage - - if pageNum != "" { - p, err := strconv.Atoi(pageNum) - if err != nil { - klog.Errorln(err) - } else { - if p > 0 { - page = p - } - } - } else { - // the default mode is none paging - return fmtLevelMetric - } - - var limit = DefaultPageLimit - - if limitNum != "" { - l, err := strconv.Atoi(limitNum) - if err != nil { - klog.Errorln(err) - } else { - if l > 0 { - limit = l - } - } - } - - // the i page: [(page-1) * limit, (page) * limit - 1] - start := (page - 1) * limit - end := (page)*limit - 1 - - for i := 0; i < len(fmtLevelMetric.Results); i++ { - // only pageing when result type is `vector` and result status is `success` - if fmtLevelMetric.Results[i].Data.ResultType != ResultTypeVector || fmtLevelMetric.Results[i].Status != MetricStatusSuccess { - continue - } - resultLen := len(fmtLevelMetric.Results[i].Data.Result) - if start >= resultLen { - fmtLevelMetric.Results[i].Data.Result = nil - continue - } - if end >= resultLen { - end = resultLen - 1 - } - slice := fmtLevelMetric.Results[i].Data.Result[start : end+1] - fmtLevelMetric.Results[i].Data.Result = slice - } - - allPage := int(math.Ceil(float64(maxLength) / float64(limit))) - - // add page fields - fmtLevelMetric.CurrentPage = page - fmtLevelMetric.TotalItem = maxLength - fmtLevelMetric.TotalPage = allPage - - return fmtLevelMetric -} - -func getNodeAddressAndRole(nodeName string) (string, string) { - nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister() - node, err := nodeLister.Get(nodeName) - if err != nil { - return "", "" - } - - var addr string - for _, address := range node.Status.Addresses { - if address.Type == "InternalIP" { - addr = address.Address - break - } - } - - role := "node" - _, exists := node.Labels["node-role.kubernetes.io/master"] - if exists { - role = "master" - } - return addr, role -} - -func getNodeName(nodeIp string) string { - nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister() - nodes, _ := nodeLister.List(labels.Everything()) - - for _, node := range nodes { - for _, address := range node.Status.Addresses { - if address.Type == "InternalIP" && address.Address == nodeIp { - return node.Name - } - } - } - - return "" -} diff --git a/pkg/models/monitoring/expressions/prometheus.go b/pkg/models/monitoring/expressions/prometheus.go new file mode 100644 index 000000000..9bff99142 --- /dev/null +++ b/pkg/models/monitoring/expressions/prometheus.go @@ -0,0 +1,98 @@ +package expressions + +import ( + "fmt" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/promql" + "github.com/prometheus/prometheus/storage/metric" +) + +func init() { + register("prometheus", labelReplace) +} + +func labelReplace(input, ns string) (string, error) { + root, err := promql.ParseExpr(input) + if err != nil { + return "", err + } + + setRecursive(root, ns) + if err != nil { + return "", err + } + + return root.String(), nil +} + +// Inspired by https://github.com/openshift/prom-label-proxy +func setRecursive(node promql.Node, namespace string) (err error) { + switch n := node.(type) { + case *promql.EvalStmt: + if err := setRecursive(n.Expr, namespace); err != nil { + return err + } + case promql.Expressions: + for _, e := range n { + if err := setRecursive(e, namespace); err != nil { + return err + } + } + case *promql.AggregateExpr: + if err := setRecursive(n.Expr, namespace); err != nil { + return err + } + case *promql.BinaryExpr: + if err := setRecursive(n.LHS, namespace); err != nil { + return err + } + if err := setRecursive(n.RHS, namespace); err != nil { + return err + } + case *promql.Call: + if err := setRecursive(n.Args, namespace); err != nil { + return err + } + case *promql.ParenExpr: + if err := setRecursive(n.Expr, namespace); err != nil { + return err + } + case *promql.UnaryExpr: + if err := setRecursive(n.Expr, namespace); err != nil { + return err + } + case *promql.NumberLiteral, *promql.StringLiteral: + // nothing to do + case *promql.MatrixSelector: + n.LabelMatchers = enforceLabelMatchers(n.LabelMatchers, namespace) + case *promql.VectorSelector: + n.LabelMatchers = enforceLabelMatchers(n.LabelMatchers, namespace) + default: + return fmt.Errorf("promql.Walk: unhandled node type %T", node) + } + return err +} + +func enforceLabelMatchers(matchers metric.LabelMatchers, namespace string) metric.LabelMatchers { + var found bool + for i, m := range matchers { + if m.Name == "namespace" { + matchers[i] = &metric.LabelMatcher{ + Name: "namespace", + Type: metric.Equal, + Value: model.LabelValue(namespace), + } + found = true + break + } + } + + if !found { + matchers = append(matchers, &metric.LabelMatcher{ + Name: "namespace", + Type: metric.Equal, + Value: model.LabelValue(namespace), + }) + } + return matchers +} diff --git a/pkg/models/monitoring/expressions/prometheus_test.go b/pkg/models/monitoring/expressions/prometheus_test.go new file mode 100644 index 000000000..05fafdaa9 --- /dev/null +++ b/pkg/models/monitoring/expressions/prometheus_test.go @@ -0,0 +1,51 @@ +package expressions + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "testing" +) + +func TestLabelReplace(t *testing.T) { + tests := []struct { + expr string + expected string + expectedErr bool + }{ + { + expr: "up", + expected: `up{namespace="default"}`, + expectedErr: false, + }, + { + expr: `up{namespace="random"}`, + expected: `up{namespace="default"}`, + expectedErr: false, + }, + { + expr: `up{namespace="random"} + up{job="test"}`, + expected: `up{namespace="default"} + up{job="test",namespace="default"}`, + expectedErr: false, + }, + { + expr: `@@@@`, + expectedErr: true, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + result, err := labelReplace(tt.expr, "default") + if err != nil { + if !tt.expectedErr { + t.Fatal(err) + } + return + } + + if diff := cmp.Diff(result, tt.expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", tt.expected, diff) + } + }) + } +} diff --git a/pkg/models/monitoring/expressions/registry.go b/pkg/models/monitoring/expressions/registry.go new file mode 100644 index 000000000..8775cd136 --- /dev/null +++ b/pkg/models/monitoring/expressions/registry.go @@ -0,0 +1,9 @@ +package expressions + +type labelReplaceFn func(expr, ns string) (string, error) + +var ReplaceNamespaceFns = make(map[string]labelReplaceFn) + +func register(name string, fn labelReplaceFn) { + ReplaceNamespaceFns[name] = fn +} diff --git a/pkg/models/monitoring/monitoring.go b/pkg/models/monitoring/monitoring.go new file mode 100644 index 000000000..795678932 --- /dev/null +++ b/pkg/models/monitoring/monitoring.go @@ -0,0 +1,80 @@ +/* + + Copyright 2019 The KubeSphere 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 monitoring + +import ( + "kubesphere.io/kubesphere/pkg/models/monitoring/expressions" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "time" +) + +type MonitoringOperator interface { + GetMetric(expr, namespace string, time time.Time) (monitoring.Metric, error) + GetMetricOverTime(expr, namespace string, start, end time.Time, step time.Duration) (monitoring.Metric, error) + GetNamedMetrics(metrics []string, time time.Time, opt monitoring.QueryOption) Metrics + GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt monitoring.QueryOption) Metrics + GetMetadata(namespace string) Metadata +} + +type monitoringOperator struct { + c monitoring.Interface +} + +func NewMonitoringOperator(client monitoring.Interface) MonitoringOperator { + return &monitoringOperator{client} +} + +func (mo monitoringOperator) GetMetric(expr, namespace string, time time.Time) (monitoring.Metric, error) { + // Different monitoring backend implementations have different ways to enforce namespace isolation. + // Each implementation should register itself to `ReplaceNamespaceFns` during init(). + // We hard code "prometheus" here because we only support this datasource so far. + // In the future, maybe the value should be returned from a method like `mo.c.GetMonitoringServiceName()`. + expr, err := expressions.ReplaceNamespaceFns["prometheus"](expr, namespace) + if err != nil { + return monitoring.Metric{}, err + } + return mo.c.GetMetric(expr, time), nil +} + +func (mo monitoringOperator) GetMetricOverTime(expr, namespace string, start, end time.Time, step time.Duration) (monitoring.Metric, error) { + // Different monitoring backend implementations have different ways to enforce namespace isolation. + // Each implementation should register itself to `ReplaceNamespaceFns` during init(). + // We hard code "prometheus" here because we only support this datasource so far. + // In the future, maybe the value should be returned from a method like `mo.c.GetMonitoringServiceName()`. + expr, err := expressions.ReplaceNamespaceFns["prometheus"](expr, namespace) + if err != nil { + return monitoring.Metric{}, err + } + return mo.c.GetMetricOverTime(expr, start, end, step), nil +} + +func (mo monitoringOperator) GetNamedMetrics(metrics []string, time time.Time, opt monitoring.QueryOption) Metrics { + ress := mo.c.GetNamedMetrics(metrics, time, opt) + return Metrics{Results: ress} +} + +func (mo monitoringOperator) GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt monitoring.QueryOption) Metrics { + ress := mo.c.GetNamedMetricsOverTime(metrics, start, end, step, opt) + return Metrics{Results: ress} +} + +func (mo monitoringOperator) GetMetadata(namespace string) Metadata { + data := mo.c.GetMetadata(namespace) + return Metadata{Data: data} +} diff --git a/pkg/models/monitoring/named_metrics.go b/pkg/models/monitoring/named_metrics.go new file mode 100644 index 000000000..322e01f94 --- /dev/null +++ b/pkg/models/monitoring/named_metrics.go @@ -0,0 +1,223 @@ +package monitoring + +var ClusterMetrics = []string{ + "cluster_cpu_utilisation", + "cluster_cpu_usage", + "cluster_cpu_total", + "cluster_memory_utilisation", + "cluster_memory_available", + "cluster_memory_total", + "cluster_memory_usage_wo_cache", + "cluster_net_utilisation", + "cluster_net_bytes_transmitted", + "cluster_net_bytes_received", + "cluster_disk_read_iops", + "cluster_disk_write_iops", + "cluster_disk_read_throughput", + "cluster_disk_write_throughput", + "cluster_disk_size_usage", + "cluster_disk_size_utilisation", + "cluster_disk_size_capacity", + "cluster_disk_size_available", + "cluster_disk_inode_total", + "cluster_disk_inode_usage", + "cluster_disk_inode_utilisation", + "cluster_namespace_count", + "cluster_pod_count", + "cluster_pod_quota", + "cluster_pod_utilisation", + "cluster_pod_running_count", + "cluster_pod_succeeded_count", + "cluster_pod_abnormal_count", + "cluster_node_online", + "cluster_node_offline", + "cluster_node_total", + "cluster_cronjob_count", + "cluster_pvc_count", + "cluster_daemonset_count", + "cluster_deployment_count", + "cluster_endpoint_count", + "cluster_hpa_count", + "cluster_job_count", + "cluster_statefulset_count", + "cluster_replicaset_count", + "cluster_service_count", + "cluster_secret_count", + "cluster_pv_count", + "cluster_ingresses_extensions_count", + "cluster_load1", + "cluster_load5", + "cluster_load15", + "cluster_pod_abnormal_ratio", + "cluster_node_offline_ratio", +} + +var NodeMetrics = []string{ + "node_cpu_utilisation", + "node_cpu_total", + "node_cpu_usage", + "node_memory_utilisation", + "node_memory_usage_wo_cache", + "node_memory_available", + "node_memory_total", + "node_net_utilisation", + "node_net_bytes_transmitted", + "node_net_bytes_received", + "node_disk_read_iops", + "node_disk_write_iops", + "node_disk_read_throughput", + "node_disk_write_throughput", + "node_disk_size_capacity", + "node_disk_size_available", + "node_disk_size_usage", + "node_disk_size_utilisation", + "node_disk_inode_total", + "node_disk_inode_usage", + "node_disk_inode_utilisation", + "node_pod_count", + "node_pod_quota", + "node_pod_utilisation", + "node_pod_running_count", + "node_pod_succeeded_count", + "node_pod_abnormal_count", + "node_load1", + "node_load5", + "node_load15", + "node_pod_abnormal_ratio", +} + +var WorkspaceMetrics = []string{ + "workspace_cpu_usage", + "workspace_memory_usage", + "workspace_memory_usage_wo_cache", + "workspace_net_bytes_transmitted", + "workspace_net_bytes_received", + "workspace_pod_count", + "workspace_pod_running_count", + "workspace_pod_succeeded_count", + "workspace_pod_abnormal_count", + "workspace_ingresses_extensions_count", + "workspace_cronjob_count", + "workspace_pvc_count", + "workspace_daemonset_count", + "workspace_deployment_count", + "workspace_endpoint_count", + "workspace_hpa_count", + "workspace_job_count", + "workspace_statefulset_count", + "workspace_replicaset_count", + "workspace_service_count", + "workspace_secret_count", + "workspace_pod_abnormal_ratio", +} + +var NamespaceMetrics = []string{ + "namespace_cpu_usage", + "namespace_memory_usage", + "namespace_memory_usage_wo_cache", + "namespace_net_bytes_transmitted", + "namespace_net_bytes_received", + "namespace_pod_count", + "namespace_pod_running_count", + "namespace_pod_succeeded_count", + "namespace_pod_abnormal_count", + "namespace_pod_abnormal_ratio", + "namespace_memory_limit_hard", + "namespace_cpu_limit_hard", + "namespace_pod_count_hard", + "namespace_cronjob_count", + "namespace_pvc_count", + "namespace_daemonset_count", + "namespace_deployment_count", + "namespace_endpoint_count", + "namespace_hpa_count", + "namespace_job_count", + "namespace_statefulset_count", + "namespace_replicaset_count", + "namespace_service_count", + "namespace_secret_count", + "namespace_configmap_count", + "namespace_ingresses_extensions_count", + "namespace_s2ibuilder_count", +} + +var WorkloadMetrics = []string{ + "workload_cpu_usage", + "workload_memory_usage", + "workload_memory_usage_wo_cache", + "workload_net_bytes_transmitted", + "workload_net_bytes_received", + "workload_deployment_replica", + "workload_deployment_replica_available", + "workload_statefulset_replica", + "workload_statefulset_replica_available", + "workload_daemonset_replica", + "workload_daemonset_replica_available", + "workload_deployment_unavailable_replicas_ratio", + "workload_daemonset_unavailable_replicas_ratio", + "workload_statefulset_unavailable_replicas_ratio", +} + +var PodMetrics = []string{ + "pod_cpu_usage", + "pod_memory_usage", + "pod_memory_usage_wo_cache", + "pod_net_bytes_transmitted", + "pod_net_bytes_received", +} + +var ContainerMetrics = []string{ + "container_cpu_usage", + "container_memory_usage", + "container_memory_usage_wo_cache", +} + +var PVCMetrics = []string{ + "pvc_inodes_available", + "pvc_inodes_used", + "pvc_inodes_total", + "pvc_inodes_utilisation", + "pvc_bytes_available", + "pvc_bytes_used", + "pvc_bytes_total", + "pvc_bytes_utilisation", +} + +var EtcdMetrics = []string{ + "etcd_server_list", + "etcd_server_total", + "etcd_server_up_total", + "etcd_server_has_leader", + "etcd_server_leader_changes", + "etcd_server_proposals_failed_rate", + "etcd_server_proposals_applied_rate", + "etcd_server_proposals_committed_rate", + "etcd_server_proposals_pending_count", + "etcd_mvcc_db_size", + "etcd_network_client_grpc_received_bytes", + "etcd_network_client_grpc_sent_bytes", + "etcd_grpc_call_rate", + "etcd_grpc_call_failed_rate", + "etcd_grpc_server_msg_received_rate", + "etcd_grpc_server_msg_sent_rate", + "etcd_disk_wal_fsync_duration", + "etcd_disk_wal_fsync_duration_quantile", + "etcd_disk_backend_commit_duration", + "etcd_disk_backend_commit_duration_quantile", +} + +var APIServerMetrics = []string{ + "apiserver_up_sum", + "apiserver_request_rate", + "apiserver_request_by_verb_rate", + "apiserver_request_latencies", + "apiserver_request_by_verb_latencies", +} + +var SchedulerMetrics = []string{ + "scheduler_up_sum", + "scheduler_schedule_attempts", + "scheduler_schedule_attempt_rate", + "scheduler_e2e_scheduling_latency", + "scheduler_e2e_scheduling_latency_quantile", +} diff --git a/pkg/models/monitoring/namespaces.go b/pkg/models/monitoring/namespaces.go new file mode 100644 index 000000000..44a809bc9 --- /dev/null +++ b/pkg/models/monitoring/namespaces.go @@ -0,0 +1,64 @@ +/* + + Copyright 2019 The KubeSphere 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 monitoring + +import "k8s.io/api/core/v1" + +// TODO(wansir): Can we decouple this part from monitoring module, since the project structure has been changed +func GetNamespacesWithMetrics(namespaces []*v1.Namespace) []*v1.Namespace { + // var nsNameList []string + // for i := range namespaces { + // nsNameList = append(nsNameList, namespaces[i].Name) + // } + // nsFilter := "^(" + strings.Join(nsNameList, "|") + ")$" + // + // now := time.Now() + // opt := &monitoring.QueryOptions{ + // Level: monitoring.MetricsLevelNamespace, + // ResourcesFilter: nsFilter, + // Start: now, + // End: now, + // MetricsFilter: "namespace_cpu_usage|namespace_memory_usage_wo_cache|namespace_pod_count", + // } + // + // gm, err := monitoring.Get(opt) + // if err != nil { + // klog.Error(err) + // return namespaces + // } + // + // for _, m := range gm.Results { + // for _, v := range m.Data.MetricsValues { + // ns, exist := v.Metadata["namespace"] + // if !exist { + // continue + // } + // + // for _, item := range namespaces { + // if item.Name == ns { + // if item.Annotations == nil { + // item.Annotations = make(map[string]string, 0) + // } + // item.Annotations[m.MetricsName] = strconv.FormatFloat(v.Sample[1], 'f', -1, 64) + // } + // } + // } + // } + // + return namespaces +} diff --git a/pkg/models/monitoring/sort_page.go b/pkg/models/monitoring/sort_page.go new file mode 100644 index 000000000..678a3ec5e --- /dev/null +++ b/pkg/models/monitoring/sort_page.go @@ -0,0 +1,189 @@ +/* + + Copyright 2019 The KubeSphere 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 monitoring + +import ( + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "math" + "sort" +) + +// TODO(huanggze): the id value is dependent of Prometheus label-value pair (i.e. label_kubesphere_io_workspace). We should regulate the naming convention. +const ( + IdentifierNode = "node" + IdentifierWorkspace = "label_kubesphere_io_workspace" + IdentifierNamespace = "namespace" + IdentifierWorkload = "workload" + IdentifierPod = "pod" + IdentifierContainer = "container" + IdentifierPVC = "persistentvolumeclaim" + + OrderAscending = "asc" + OrderDescending = "desc" +) + +type wrapper struct { + monitoring.MetricData + identifier, order string +} + +func (w wrapper) Len() int { + return len(w.MetricValues) +} + +func (w wrapper) Less(i, j int) bool { + p := w.MetricValues[i] + q := w.MetricValues[j] + + if p.Sample.Value() == q.Sample.Value() { + return p.Metadata[w.identifier] < q.Metadata[w.identifier] + } + + switch w.order { + case OrderAscending: + return p.Sample.Value() < q.Sample.Value() + default: + return p.Sample.Value() > q.Sample.Value() + } +} + +func (id wrapper) Swap(i, j int) { + id.MetricValues[i], id.MetricValues[j] = id.MetricValues[j], id.MetricValues[i] +} + +// SortMetrics sorts a group of resources by a given metric. Range query doesn't support ranking. +// Example: +// +// Before sorting: +// | ID | Metric 1 | Metric 2 | Metric 3 | +// | a | 1 | XL | | +// | b | 1 | S | | +// | c | 3 | M | | +// +// After sorting: target=metric_2, order=asc, identifier=id +// | ID | Metric 1 | Metric 2 (asc) | Metric 3 | +// | a | 1 | XL | | +// | c | 3 | M | | +// | b | 1 | S | | +func (raw *Metrics) Sort(target, order, identifier string) *Metrics { + if target == "" || identifier == "" || len(raw.Results) == 0 { + return raw + } + + resourceSet := make(map[string]bool) // resource set records possible values of the identifier + resourceOrdinal := make(map[string]int) // resource-ordinal map + + ordinal := 0 + for _, item := range raw.Results { + if item.MetricType != monitoring.MetricTypeVector || item.Error != "" { + continue + } + + if item.MetricName == target { + sort.Sort(wrapper{ + MetricData: item.MetricData, + identifier: identifier, + order: order, + }) + + for _, mv := range item.MetricValues { + // Record ordinals in the final result + v, ok := mv.Metadata[identifier] + if ok && v != "" { + resourceOrdinal[v] = ordinal + ordinal++ + } + } + } + + // Add every unique identifier value to the set + for _, mv := range item.MetricValues { + v, ok := mv.Metadata[identifier] + if ok && v != "" { + resourceSet[v] = true + } + } + } + + var resourceList []string + for k := range resourceSet { + resourceList = append(resourceList, k) + } + sort.Strings(resourceList) + + // Fill resource-ordinal map with resources never present in the target, and give them ordinals. + for _, r := range resourceList { + if _, ok := resourceOrdinal[r]; !ok { + resourceOrdinal[r] = ordinal + ordinal++ + } + } + + // Sort metrics + for i, item := range raw.Results { + if item.MetricType != monitoring.MetricTypeVector || item.Error != "" { + continue + } + + sorted := make([]monitoring.MetricValue, len(resourceList)) + for _, mv := range item.MetricValues { + v, ok := mv.Metadata[identifier] + if ok && v != "" { + ordinal, _ := resourceOrdinal[v] + sorted[ordinal] = mv + } + } + raw.Results[i].MetricValues = sorted + } + + raw.CurrentPage = 1 + raw.TotalPages = 1 + raw.TotalItems = len(resourceList) + return raw +} + +func (raw *Metrics) Page(page, limit int) *Metrics { + if page < 1 || limit < 1 || len(raw.Results) == 0 { + return raw + } + + start := (page - 1) * limit + end := page * limit + + for i, item := range raw.Results { + if item.MetricType != monitoring.MetricTypeVector || item.Error != "" { + continue + } + + total := len(item.MetricValues) + if start >= total { + raw.Results[i].MetricValues = nil + continue + } + if end >= total { + end = total + } + + raw.Results[i].MetricValues = item.MetricValues[start:end] + } + + raw.CurrentPage = page + raw.TotalPages = int(math.Ceil(float64(raw.TotalItems) / float64(limit))) + return raw +} diff --git a/pkg/models/monitoring/sort_page_test.go b/pkg/models/monitoring/sort_page_test.go new file mode 100644 index 000000000..cdb750cef --- /dev/null +++ b/pkg/models/monitoring/sort_page_test.go @@ -0,0 +1,147 @@ +package monitoring + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/json-iterator/go" + "io/ioutil" + "testing" +) + +func TestSort(t *testing.T) { + tests := []struct { + target string + order string + identifier string + raw string + expected string + }{ + { + target: "node_cpu_utilisation", + order: "asc", + identifier: "node", + raw: "source-node-metrics.json", + expected: "sorted-node-metrics-asc.json", + }, + { + target: "node_memory_utilisation", + order: "desc", + identifier: "node", + raw: "source-node-metrics.json", + expected: "sorted-node-metrics-desc.json", + }, + { + target: "node_memory_utilisation", + order: "desc", + identifier: "node", + raw: "faulty-node-metrics.json", + expected: "faulty-node-metrics-sorted.json", + }, + { + target: "node_memory_utilisation", + order: "desc", + identifier: "node", + raw: "blank-node-metrics.json", + expected: "blank-node-metrics-sorted.json", + }, + { + target: "node_memory_utilisation", + order: "desc", + identifier: "node", + raw: "null-node-metrics.json", + expected: "null-node-metrics-sorted.json", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + source, expected, err := jsonFromFile(tt.raw, tt.expected) + if err != nil { + t.Fatal(err) + } + + result := source.Sort(tt.target, tt.order, tt.identifier) + if diff := cmp.Diff(*result, *expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} + +func TestPage(t *testing.T) { + tests := []struct { + page int + limit int + raw string + expected string + }{ + { + page: 0, + limit: 5, + raw: "sorted-node-metrics-asc.json", + expected: "sorted-node-metrics-asc.json", + }, + { + page: 1, + limit: 5, + raw: "sorted-node-metrics-asc.json", + expected: "paged-node-metrics-1.json", + }, + { + page: 2, + limit: 5, + raw: "sorted-node-metrics-asc.json", + expected: "paged-node-metrics-2.json", + }, + { + page: 3, + limit: 5, + raw: "sorted-node-metrics-asc.json", + expected: "paged-node-metrics-3.json", + }, + { + page: 1, + limit: 2, + raw: "faulty-node-metrics-sorted.json", + expected: "faulty-node-metrics-paged.json", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + source, expected, err := jsonFromFile(tt.raw, tt.expected) + if err != nil { + t.Fatal(err) + } + + result := source.Page(tt.page, tt.limit) + if diff := cmp.Diff(*result, *expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} + +func jsonFromFile(sourceFile, expectedFile string) (*Metrics, *Metrics, error) { + sourceJson := &Metrics{} + expectedJson := &Metrics{} + + json, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s", sourceFile)) + if err != nil { + return nil, nil, err + } + err = jsoniter.Unmarshal(json, sourceJson) + if err != nil { + return nil, nil, err + } + + json, err = ioutil.ReadFile(fmt.Sprintf("./testdata/%s", expectedFile)) + if err != nil { + return nil, nil, err + } + err = jsoniter.Unmarshal(json, expectedJson) + if err != nil { + return nil, nil, err + } + return sourceJson, expectedJson, nil +} diff --git a/pkg/models/monitoring/testdata/blank-node-metrics-sorted.json b/pkg/models/monitoring/testdata/blank-node-metrics-sorted.json new file mode 100644 index 000000000..968fcf750 --- /dev/null +++ b/pkg/models/monitoring/testdata/blank-node-metrics-sorted.json @@ -0,0 +1,77 @@ +{ + "results":[ + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.29849334024542695 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.195, + 0.23637090535053928 + ] + } + ] + } + } + ], + "page":1, + "total_page":1, + "total_item":3 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/blank-node-metrics.json b/pkg/models/monitoring/testdata/blank-node-metrics.json new file mode 100644 index 000000000..e70a6b291 --- /dev/null +++ b/pkg/models/monitoring/testdata/blank-node-metrics.json @@ -0,0 +1,92 @@ +{ + "results": [ + { + "metric_name": "node_disk_size_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric": { + "node": "" + }, + "value": [ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.193, + 0.2588273152865106 + ] + } + ] + } + }, + { + "metric_name": "node_memory_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric": { + "node": "" + }, + "value": [ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.195, + 0.2497060264216553 + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/faulty-node-metrics-paged.json b/pkg/models/monitoring/testdata/faulty-node-metrics-paged.json new file mode 100644 index 000000000..ff99f2222 --- /dev/null +++ b/pkg/models/monitoring/testdata/faulty-node-metrics-paged.json @@ -0,0 +1,63 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "error":"error" + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + } + ] + } + } + ], + "page":1, + "total_page":2, + "total_item":4 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/faulty-node-metrics-sorted.json b/pkg/models/monitoring/testdata/faulty-node-metrics-sorted.json new file mode 100644 index 000000000..d5242f67c --- /dev/null +++ b/pkg/models/monitoring/testdata/faulty-node-metrics-sorted.json @@ -0,0 +1,99 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "error":"error" + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.2601006025131434 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.195, + 0.1446648505469157 + ] + } + ] + } + } + ], + "page":1, + "total_page":1, + "total_item":4 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/faulty-node-metrics.json b/pkg/models/monitoring/testdata/faulty-node-metrics.json new file mode 100644 index 000000000..e72b9aeca --- /dev/null +++ b/pkg/models/monitoring/testdata/faulty-node-metrics.json @@ -0,0 +1,96 @@ +{ + "results": [ + { + "metric_name": "node_cpu_utilisation", + "error": "error" + }, + { + "metric_name": "node_disk_size_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric": { + "node": "i-9jtsi522" + }, + "value": [ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.193, + 0.2588273152865106 + ] + } + ] + } + }, + { + "metric_name": "node_memory_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric": { + "node": "i-9jtsi522" + }, + "value": [ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.195, + 0.2497060264216553 + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/null-node-metrics-sorted.json b/pkg/models/monitoring/testdata/null-node-metrics-sorted.json new file mode 100644 index 000000000..405270145 --- /dev/null +++ b/pkg/models/monitoring/testdata/null-node-metrics-sorted.json @@ -0,0 +1,53 @@ +{ + "results": [ + { + "metric_name": "node_disk_size_utilisation", + "data": { + "resultType": "vector", + "result": [ + {}, + {}, + {} + ] + } + }, + { + "metric_name": "node_memory_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.195, + 0.23637090535053928 + ] + } + ] + } + } + ], + "page": 1, + "total_page": 1, + "total_item": 3 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/null-node-metrics.json b/pkg/models/monitoring/testdata/null-node-metrics.json new file mode 100644 index 000000000..23f9468f9 --- /dev/null +++ b/pkg/models/monitoring/testdata/null-node-metrics.json @@ -0,0 +1,45 @@ +{ + "results": [ + { + "metric_name": "node_disk_size_utilisation", + "data": { + "resultType": "vector" + } + }, + { + "metric_name": "node_memory_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.195, + 0.2497060264216553 + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/paged-node-metrics-1.json b/pkg/models/monitoring/testdata/paged-node-metrics-1.json new file mode 100644 index 000000000..60cd43507 --- /dev/null +++ b/pkg/models/monitoring/testdata/paged-node-metrics-1.json @@ -0,0 +1,166 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.021645833333483702 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.03250000000007276 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.05066666666655995 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.05210416666595847 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.06745833333334303 + ] + } + ] + } + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.3335848564534758 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.21351118996831508 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.35981263055856705 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.195, + 0.12824588180084573 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.195, + 0.21291125105270192 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.195, + 0.40309723127991315 + ] + } + ] + } + } + ], + "page":1, + "total_page":2, + "total_item":8 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/paged-node-metrics-2.json b/pkg/models/monitoring/testdata/paged-node-metrics-2.json new file mode 100644 index 000000000..097872631 --- /dev/null +++ b/pkg/models/monitoring/testdata/paged-node-metrics-2.json @@ -0,0 +1,112 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.07443750000044626 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.07756249999996119 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.18095833333306172 + ] + } + ] + } + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.4329682466178235 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.195, + 0.823247832787681 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + } + ] + } + } + ], + "page":2, + "total_page":2, + "total_item":8 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/paged-node-metrics-3.json b/pkg/models/monitoring/testdata/paged-node-metrics-3.json new file mode 100644 index 000000000..16ea7c859 --- /dev/null +++ b/pkg/models/monitoring/testdata/paged-node-metrics-3.json @@ -0,0 +1,25 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "data":{ + "resultType":"vector" + } + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector" + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector" + } + } + ], + "page":3, + "total_page":2, + "total_item":8 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/sorted-node-metrics-asc.json b/pkg/models/monitoring/testdata/sorted-node-metrics-asc.json new file mode 100644 index 000000000..909e4a58e --- /dev/null +++ b/pkg/models/monitoring/testdata/sorted-node-metrics-asc.json @@ -0,0 +1,247 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.021645833333483702 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.03250000000007276 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.05066666666655995 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.05210416666595847 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.06745833333334303 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.07443750000044626 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.07756249999996119 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.18095833333306172 + ] + } + ] + } + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.3335848564534758 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.21351118996831508 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.35981263055856705 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.4329682466178235 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.195, + 0.12824588180084573 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.195, + 0.21291125105270192 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.195, + 0.40309723127991315 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.195, + 0.823247832787681 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + } + ] + } + } + ], + "page":1, + "total_page":1, + "total_item":8 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/sorted-node-metrics-desc.json b/pkg/models/monitoring/testdata/sorted-node-metrics-desc.json new file mode 100644 index 000000000..4542ee8c5 --- /dev/null +++ b/pkg/models/monitoring/testdata/sorted-node-metrics-desc.json @@ -0,0 +1,247 @@ +{ + "results":[ + { + "metric_name":"node_cpu_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.07756249999996119 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.18095833333306172 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.06745833333334303 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.05066666666655995 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.07443750000044626 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.05210416666595847 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.03250000000007276 + ] + }, + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.021645833333483702 + ] + } + ] + } + }, + { + "metric_name":"node_disk_size_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.193, + 0.4329682466178235 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.193, + 0.35981263055856705 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.193, + 0.21351118996831508 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.193, + 0.3335848564534758 + ] + } + ] + } + }, + { + "metric_name":"node_memory_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "node":"i-o13skypq" + }, + "value":[ + 1585658599.195, + 0.823247832787681 + ] + }, + { + "metric":{ + "node":"i-2dazc1d6" + }, + "value":[ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric":{ + "node":"i-xfcxdn7z" + }, + "value":[ + 1585658599.195, + 0.40309723127991315 + ] + }, + { + "metric":{ + "node":"i-hgcoippu" + }, + "value":[ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric":{ + "node":"i-ezjb7gsk" + }, + "value":[ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric":{ + "node":"i-ircdnrao" + }, + "value":[ + 1585658599.195, + 0.21291125105270192 + ] + }, + { + "metric":{ + "node":"i-9jtsi522" + }, + "value":[ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric":{ + "node":"i-tl1i71hr" + }, + "value":[ + 1585658599.195, + 0.12824588180084573 + ] + } + ] + } + } + ], + "page":1, + "total_page":1, + "total_item":8 +} \ No newline at end of file diff --git a/pkg/models/monitoring/testdata/source-node-metrics.json b/pkg/models/monitoring/testdata/source-node-metrics.json new file mode 100644 index 000000000..ad1d3298c --- /dev/null +++ b/pkg/models/monitoring/testdata/source-node-metrics.json @@ -0,0 +1,244 @@ +{ + "results": [ + { + "metric_name": "node_cpu_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.193, + 0.18095833333306172 + ] + }, + { + "metric": { + "node": "i-9jtsi522" + }, + "value": [ + 1585658599.193, + 0.03250000000007276 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.193, + 0.07443750000044626 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.193, + 0.05066666666655995 + ] + }, + { + "metric": { + "node": "i-ircdnrao" + }, + "value": [ + 1585658599.193, + 0.05210416666595847 + ] + }, + { + "metric": { + "node": "i-o13skypq" + }, + "value": [ + 1585658599.193, + 0.07756249999996119 + ] + }, + { + "metric": { + "node": "i-tl1i71hr" + }, + "value": [ + 1585658599.193, + 0.021645833333483702 + ] + }, + { + "metric": { + "node": "i-xfcxdn7z" + }, + "value": [ + 1585658599.193, + 0.06745833333334303 + ] + } + ] + } + }, + { + "metric_name": "node_disk_size_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.193, + 0.42012898861983516 + ] + }, + { + "metric": { + "node": "i-9jtsi522" + }, + "value": [ + 1585658599.193, + 0.2601006025131434 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.193, + 0.29849334024542695 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.193, + 0.2588273152865106 + ] + }, + { + "metric": { + "node": "i-ircdnrao" + }, + "value": [ + 1585658599.193, + 0.21351118996831508 + ] + }, + { + "metric": { + "node": "i-o13skypq" + }, + "value": [ + 1585658599.193, + 0.4329682466178235 + ] + }, + { + "metric": { + "node": "i-tl1i71hr" + }, + "value": [ + 1585658599.193, + 0.3335848564534758 + ] + }, + { + "metric": { + "node": "i-xfcxdn7z" + }, + "value": [ + 1585658599.193, + 0.35981263055856705 + ] + } + ] + } + }, + { + "metric_name": "node_memory_utilisation", + "data": { + "resultType": "vector", + "result": [ + { + "metric": { + "node": "i-2dazc1d6" + }, + "value": [ + 1585658599.195, + 0.5286875837861773 + ] + }, + { + "metric": { + "node": "i-9jtsi522" + }, + "value": [ + 1585658599.195, + 0.1446648505469157 + ] + }, + { + "metric": { + "node": "i-ezjb7gsk" + }, + "value": [ + 1585658599.195, + 0.23637090535053928 + ] + }, + { + "metric": { + "node": "i-hgcoippu" + }, + "value": [ + 1585658599.195, + 0.2497060264216553 + ] + }, + { + "metric": { + "node": "i-ircdnrao" + }, + "value": [ + 1585658599.195, + 0.21291125105270192 + ] + }, + { + "metric": { + "node": "i-o13skypq" + }, + "value": [ + 1585658599.195, + 0.823247832787681 + ] + }, + { + "metric": { + "node": "i-tl1i71hr" + }, + "value": [ + 1585658599.195, + 0.12824588180084573 + ] + }, + { + "metric": { + "node": "i-xfcxdn7z" + }, + "value": [ + 1585658599.195, + 0.40309723127991315 + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/pkg/models/monitoring/types.go b/pkg/models/monitoring/types.go new file mode 100644 index 000000000..7e5c2c6c5 --- /dev/null +++ b/pkg/models/monitoring/types.go @@ -0,0 +1,14 @@ +package monitoring + +import "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + +type Metrics struct { + Results []monitoring.Metric `json:"results" description:"actual array of results"` + CurrentPage int `json:"page,omitempty" description:"current page returned"` + TotalPages int `json:"total_page,omitempty" description:"total number of pages"` + TotalItems int `json:"total_item,omitempty" description:"page size"` +} + +type Metadata struct { + Data []monitoring.Metadata `json:"data" description:"actual array of results"` +} diff --git a/pkg/models/nodes/nodes.go b/pkg/models/nodes/nodes.go deleted file mode 100644 index b05a237c2..000000000 --- a/pkg/models/nodes/nodes.go +++ /dev/null @@ -1,279 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 nodes - -import ( - "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" - "math" - "strings" - "time" - - appsv1 "k8s.io/api/apps/v1" - "k8s.io/api/core/v1" - policy "k8s.io/api/policy/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" -) - -func DrainNode(nodename string) (err error) { - - k8sclient := client.ClientSets().K8s().Kubernetes() - node, err := k8sclient.CoreV1().Nodes().Get(nodename, metav1.GetOptions{}) - if err != nil { - return err - } - - if node.Spec.Unschedulable { - return fmt.Errorf("node %s have been drained", nodename) - } - - data := []byte(" {\"spec\":{\"unschedulable\":true}}") - _, err = k8sclient.CoreV1().Nodes().Patch(nodename, types.StrategicMergePatchType, data) - if err != nil { - return err - } - donech := make(chan bool) - errch := make(chan error) - go drainEviction(nodename, donech, errch) - for { - select { - case err := <-errch: - return err - case <-donech: - return nil - } - } - -} - -func drainEviction(nodename string, donech chan bool, errch chan error) { - - k8sclient := client.ClientSets().K8s().Kubernetes() - var options metav1.ListOptions - pods := make([]v1.Pod, 0) - options.FieldSelector = "spec.nodeName=" + nodename - podList, err := k8sclient.CoreV1().Pods("").List(options) - if err != nil { - klog.Fatal(err) - errch <- err - } - options.FieldSelector = "" - daemonsetList, err := k8sclient.AppsV1().DaemonSets("").List(options) - - if err != nil { - - klog.Fatal(err) - errch <- err - - } - // remove mirror pod static pod - if len(podList.Items) > 0 { - - for _, pod := range podList.Items { - - if !containDaemonset(pod, *daemonsetList) { - //static or mirror pod - if isStaticPod(&pod) || isMirrorPod(&pod) { - continue - } else { - pods = append(pods, pod) - } - } - } - } - if len(pods) == 0 { - donech <- true - } else { - - //create eviction - getPodFn := func(namespace, name string) (*v1.Pod, error) { - return k8sclient.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{}) - } - evicerr := evictPods(pods, 0, getPodFn) - - if evicerr == nil { - donech <- true - } else { - klog.Fatal(evicerr) - errch <- err - } - } -} - -func getPodSource(pod *v1.Pod) (string, error) { - if pod.Annotations != nil { - if source, ok := pod.Annotations["kubernetes.io/config.source"]; ok { - return source, nil - } - } - return "", fmt.Errorf("cannot get source of pod %q", pod.UID) -} - -func isStaticPod(pod *v1.Pod) bool { - source, err := getPodSource(pod) - return err == nil && source != "api" -} - -func isMirrorPod(pod *v1.Pod) bool { - _, ok := pod.Annotations[v1.MirrorPodAnnotationKey] - return ok -} - -func containDaemonset(pod v1.Pod, daemonsetList appsv1.DaemonSetList) bool { - - flag := false - for _, daemonset := range daemonsetList.Items { - - if strings.Contains(pod.Name, daemonset.Name) { - - flag = true - } - - } - return flag - -} - -func evictPod(pod v1.Pod, GracePeriodSeconds int) error { - - k8sclient := client.ClientSets().K8s().Kubernetes() - deleteOptions := &metav1.DeleteOptions{} - if GracePeriodSeconds >= 0 { - gracePeriodSeconds := int64(GracePeriodSeconds) - deleteOptions.GracePeriodSeconds = &gracePeriodSeconds - } - - var eviction policy.Eviction - eviction.Kind = "Eviction" - eviction.APIVersion = "policy/v1beta1" - eviction.Namespace = pod.Namespace - eviction.Name = pod.Name - eviction.DeleteOptions = deleteOptions - err := k8sclient.CoreV1().Pods(pod.Namespace).Evict(&eviction) - if err != nil { - return err - } - - return nil -} - -func evictPods(pods []v1.Pod, GracePeriodSeconds int, getPodFn func(namespace, name string) (*v1.Pod, error)) error { - doneCh := make(chan bool, len(pods)) - errCh := make(chan error, 1) - - for _, pod := range pods { - go func(pod v1.Pod, doneCh chan bool, errCh chan error) { - var err error - var count int - for { - err = evictPod(pod, GracePeriodSeconds) - if err == nil { - count++ - if count > 2 { - break - } else { - continue - } - } else if apierrors.IsNotFound(err) { - count = 0 - doneCh <- true - klog.Info(fmt.Sprintf("pod %s evict", pod.Name)) - return - } else if apierrors.IsTooManyRequests(err) { - count = 0 - time.Sleep(5 * time.Second) - } else { - count = 0 - errCh <- fmt.Errorf("error when evicting pod %q: %v", pod.Name, err) - return - } - } - - podArray := []v1.Pod{pod} - _, err = waitForDelete(podArray, time.Second, time.Duration(math.MaxInt64), getPodFn) - if err == nil { - doneCh <- true - klog.Info(fmt.Sprintf("pod %s delete", pod.Name)) - } else { - errCh <- fmt.Errorf("error when waiting for pod %q terminating: %v", pod.Name, err) - } - }(pod, doneCh, errCh) - } - - Timeout := 300 * power(10, 9) - doneCount := 0 - // 0 timeout means infinite, we use MaxInt64 to represent it. - var globalTimeout time.Duration - if Timeout == 0 { - globalTimeout = time.Duration(math.MaxInt64) - } else { - globalTimeout = time.Duration(Timeout) - } - for { - select { - case err := <-errCh: - return err - case <-doneCh: - doneCount++ - if doneCount == len(pods) { - return nil - } - case <-time.After(globalTimeout): - return fmt.Errorf("drain did not complete within %v", globalTimeout) - } - } -} - -func waitForDelete(pods []v1.Pod, interval, timeout time.Duration, getPodFn func(string, string) (*v1.Pod, error)) ([]v1.Pod, error) { - - err := wait.PollImmediate(interval, timeout, func() (bool, error) { - var pendingPods []v1.Pod - for i, pod := range pods { - p, err := getPodFn(pod.Namespace, pod.Name) - if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) { - continue - } else if err != nil { - return false, err - } else { - pendingPods = append(pendingPods, pods[i]) - } - } - pods = pendingPods - if len(pendingPods) > 0 { - return false, nil - } - return true, nil - }) - return pods, err -} - -func power(x int64, n int) int64 { - - var res int64 = 1 - for n != 0 { - res *= x - n-- - } - - return res - -} diff --git a/pkg/models/openpitrix/applications.go b/pkg/models/openpitrix/applications.go index 2254f1619..7eb726b63 100644 --- a/pkg/models/openpitrix/applications.go +++ b/pkg/models/openpitrix/applications.go @@ -1,20 +1,20 @@ /* - - Copyright 2019 The KubeSphere 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. - -*/ + * + * Copyright 2020 The KubeSphere 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 openpitrix import ( @@ -26,20 +26,34 @@ import ( "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/resources" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) +type ApplicationInterface interface { + ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) + DescribeApplication(namespace, clusterId string) (*Application, error) + CreateApplication(namespace string, request CreateClusterRequest) error + ModifyApplication(request ModifyClusterAttributesRequest) error + DeleteApplication(id string) error +} + +type applicationOperator struct { + informers informers.SharedInformerFactory + opClient openpitrix.Client +} + +func newApplicationOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) ApplicationInterface { + return &applicationOperator{informers: informers, opClient: opClient} +} + type Application struct { Name string `json:"name" description:"application name"` Cluster *Cluster `json:"cluster,omitempty" description:"application cluster info"` @@ -56,35 +70,30 @@ type workLoads struct { Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty" description:"daemonset list"` } -func ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *applicationOperator) ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) { describeClustersRequest := &pb.DescribeClustersRequest{ Limit: uint32(limit), Offset: uint32(offset)} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword} } - if runtimeId := conditions.Match["runtime_id"]; runtimeId != "" { + if runtimeId := conditions.Match[RuntimeId]; runtimeId != "" { describeClustersRequest.RuntimeId = []string{runtimeId} } - if appId := conditions.Match["app_id"]; appId != "" { + if appId := conditions.Match[AppId]; appId != "" { describeClustersRequest.AppId = []string{appId} } - if versionId := conditions.Match["version_id"]; versionId != "" { + if versionId := conditions.Match[VersionId]; versionId != "" { describeClustersRequest.VersionId = []string{versionId} } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { describeClustersRequest.Status = strings.Split(status, "|") } if orderBy != "" { describeClustersRequest.SortKey = &wrappers.StringValue{Value: orderBy} } - describeClustersRequest.Reverse = &wrappers.BoolValue{Value: !reverse} - resp, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), describeClustersRequest) + describeClustersRequest.Reverse = &wrappers.BoolValue{Value: reverse} + resp, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), describeClustersRequest) if err != nil { klog.Errorln(err) return nil, err @@ -92,7 +101,7 @@ func ListApplications(conditions *params.Conditions, limit, offset int, orderBy result := models.PageableResponse{TotalCount: int(resp.TotalCount)} result.Items = make([]interface{}, 0) for _, cluster := range resp.ClusterSet { - app, err := describeApplication(cluster) + app, err := c.describeApplication(cluster) if err != nil { klog.Errorln(err) return nil, err @@ -103,16 +112,11 @@ func ListApplications(conditions *params.Conditions, limit, offset int, orderBy return &result, nil } -func describeApplication(cluster *pb.Cluster) (*Application, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *applicationOperator) describeApplication(cluster *pb.Cluster) (*Application, error) { var app Application app.Name = cluster.Name.Value app.Cluster = convertCluster(cluster) - versionInfo, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}}) + versionInfo, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}}) if err != nil { klog.Errorln(err) return nil, err @@ -120,7 +124,7 @@ func describeApplication(cluster *pb.Cluster) (*Application, error) { if len(versionInfo.AppVersionSet) > 0 { app.Version = convertAppVersion(versionInfo.AppVersionSet[0]) } - appInfo, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1}) + appInfo, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1}) if err != nil { klog.Errorln(err) return nil, err @@ -131,15 +135,9 @@ func describeApplication(cluster *pb.Cluster) (*Application, error) { return &app, nil } -func DescribeApplication(namespace string, clusterId string) (*Application, error) { +func (c *applicationOperator) DescribeApplication(namespace string, clusterId string) (*Application, error) { - client, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - - clusters, err := client.Cluster().DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1}) + clusters, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1}) if err != nil { klog.Errorln(err) @@ -154,26 +152,26 @@ func DescribeApplication(namespace string, clusterId string) (*Application, erro klog.Errorln(err) return nil, err } - app, err := describeApplication(cluster) + app, err := c.describeApplication(cluster) if err != nil { klog.Errorln(err) return nil, err } - workloads, err := getWorkLoads(namespace, cluster.ClusterRoleSet) + workloads, err := c.getWorkLoads(namespace, cluster.ClusterRoleSet) if err != nil { klog.Errorln(err) return nil, err } app.WorkLoads = workloads - workloadLabels := getLabels(namespace, app.WorkLoads) - app.Services = getSvcs(namespace, workloadLabels) - app.Ingresses = getIng(namespace, app.Services) + workloadLabels := c.getLabels(namespace, app.WorkLoads) + app.Services = c.getSvcs(namespace, workloadLabels) + app.Ingresses = c.getIng(namespace, app.Services) return app, nil } -func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, error) { +func (c *applicationOperator) getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, error) { var works workLoads for _, clusterRole := range clusterRoles { @@ -182,7 +180,7 @@ func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) { name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0] - item, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) + item, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) if err != nil { // app not ready @@ -199,7 +197,7 @@ func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, if strings.HasSuffix(workLoadName, openpitrix.DaemonSuffix) { name := strings.Split(workLoadName, openpitrix.DaemonSuffix)[0] - item, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name) + item, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name) if err != nil { // app not ready if errors.IsNotFound(err) { @@ -214,7 +212,7 @@ func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, if strings.HasSuffix(workLoadName, openpitrix.StateSuffix) { name := strings.Split(workLoadName, openpitrix.StateSuffix)[0] - item, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name) + item, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name) if err != nil { // app not ready if errors.IsNotFound(err) { @@ -231,8 +229,7 @@ func getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, return &works, nil } -func getLabels(namespace string, workloads *workLoads) *[]map[string]string { - k8sClient := cs.ClientSets().K8s().Kubernetes() +func (c *applicationOperator) getLabels(namespace string, workloads *workLoads) *[]map[string]string { var workloadLabels []map[string]string if workloads == nil { @@ -240,7 +237,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { } for _, workload := range workloads.Deployments { - deploy, err := k8sClient.AppsV1().Deployments(namespace).Get(workload.Name, metav1.GetOptions{}) + deploy, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(workload.Name) if errors.IsNotFound(err) { continue } @@ -248,7 +245,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { } for _, workload := range workloads.Daemonsets { - daemonset, err := k8sClient.AppsV1().DaemonSets(namespace).Get(workload.Name, metav1.GetOptions{}) + daemonset, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(workload.Name) if errors.IsNotFound(err) { continue } @@ -256,7 +253,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { } for _, workload := range workloads.Statefulsets { - statefulset, err := k8sClient.AppsV1().StatefulSets(namespace).Get(workload.Name, metav1.GetOptions{}) + statefulset, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(workload.Name) if errors.IsNotFound(err) { continue } @@ -266,7 +263,7 @@ func getLabels(namespace string, workloads *workLoads) *[]map[string]string { return &workloadLabels } -func isExist(svcs []v1.Service, svc v1.Service) bool { +func (c *applicationOperator) isExist(svcs []v1.Service, svc *v1.Service) bool { for _, item := range svcs { if item.Name == svc.Name && item.Namespace == svc.Namespace { return true @@ -275,21 +272,20 @@ func isExist(svcs []v1.Service, svc v1.Service) bool { return false } -func getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service { +func (c *applicationOperator) getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service { if len(*workLoadLabels) == 0 { return nil } - k8sClient := cs.ClientSets().K8s().Kubernetes() var services []v1.Service for _, label := range *workLoadLabels { - labelSelector := labels.Set(label).AsSelector().String() - svcs, err := k8sClient.CoreV1().Services(namespace).List(metav1.ListOptions{LabelSelector: labelSelector}) + labelSelector := labels.Set(label).AsSelector() + svcs, err := c.informers.Core().V1().Services().Lister().Services(namespace).List(labelSelector) if err != nil { klog.Errorf("get app's svc failed, reason: %v", err) } - for _, item := range svcs.Items { - if !isExist(services, item) { - services = append(services, item) + for _, item := range svcs { + if !c.isExist(services, item) { + services = append(services, *item) } } } @@ -297,21 +293,23 @@ func getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service return services } -func getIng(namespace string, services []v1.Service) []v1beta1.Ingress { +func (c *applicationOperator) getIng(namespace string, services []v1.Service) []v1beta1.Ingress { if services == nil { return nil } var ings []v1beta1.Ingress for _, svc := range services { - result, err := resources.ListResources(namespace, "ingress", ¶ms.Conditions{Fuzzy: map[string]string{"serviceName": svc.Name}}, "", false, -1, 0) + ingresses, err := c.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything()) if err != nil { - klog.Errorln(err) - return nil + klog.Error(err) + return ings } - for _, i := range result.Items { - ingress := i.(*v1beta1.Ingress) + for _, ingress := range ingresses { + if ingress.Spec.Backend.ServiceName != svc.Name { + continue + } exist := false var tmpRules []v1beta1.IngressRule @@ -322,7 +320,6 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress { tmpRules = append(tmpRules, rule) } } - } if exist { @@ -337,8 +334,8 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress { return ings } -func CreateApplication(namespace string, request CreateClusterRequest) error { - ns, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespace) +func (c *applicationOperator) CreateApplication(namespace string, request CreateClusterRequest) error { + ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace) if err != nil { klog.Error(err) return err @@ -350,14 +347,7 @@ func CreateApplication(namespace string, request CreateClusterRequest) error { return fmt.Errorf("runtime not init: namespace %s", namespace) } - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - - _, err = client.Cluster().CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{ + _, err = c.opClient.CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{ AppId: &wrappers.StringValue{Value: request.AppId}, VersionId: &wrappers.StringValue{Value: request.VersionId}, RuntimeId: &wrappers.StringValue{Value: request.RuntimeId}, @@ -372,13 +362,7 @@ func CreateApplication(namespace string, request CreateClusterRequest) error { return nil } -func PatchApplication(request *ModifyClusterAttributesRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } +func (c *applicationOperator) ModifyApplication(request ModifyClusterAttributesRequest) error { modifyClusterAttributesRequest := &pb.ModifyClusterAttributesRequest{ClusterId: &wrappers.StringValue{Value: request.ClusterID}} if request.Name != nil { @@ -388,24 +372,18 @@ func PatchApplication(request *ModifyClusterAttributesRequest) error { modifyClusterAttributesRequest.Description = &wrappers.StringValue{Value: *request.Description} } - _, err = op.Cluster().ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest) - - if err != nil { - klog.Errorln(err) - return err - } - return nil -} - -func DeleteApplication(clusterId string) error { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - - _, err = client.Cluster().DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}}) + _, err := c.opClient.ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest) + + if err != nil { + klog.Errorln(err) + return err + } + + return nil +} + +func (c *applicationOperator) DeleteApplication(clusterId string) error { + _, err := c.opClient.DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}}) if err != nil { klog.Errorln(err) diff --git a/pkg/models/openpitrix/applications_test.go b/pkg/models/openpitrix/applications_test.go new file mode 100644 index 000000000..0dff68d0c --- /dev/null +++ b/pkg/models/openpitrix/applications_test.go @@ -0,0 +1,113 @@ +/* + * + * Copyright 2020 The KubeSphere 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 openpitrix + +import ( + "github.com/golang/mock/gomock" + "github.com/golang/protobuf/ptypes/wrappers" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" + "openpitrix.io/openpitrix/pkg/pb" + "testing" +) + +func namespacesToRuntimeObjects(namespaces ...*v1.Namespace) []runtime.Object { + var objs []runtime.Object + for _, deploy := range namespaces { + objs = append(objs, deploy) + } + + return objs +} + +func TestApplicationOperator_CreateApplication(t *testing.T) { + tests := []struct { + description string + existNamespaces []*v1.Namespace + targetNamespace string + createClusterRequest CreateClusterRequest + expected error + }{ + { + description: "create application test", + existNamespaces: []*v1.Namespace{{ + ObjectMeta: metav1.ObjectMeta{Name: "test", Annotations: map[string]string{openpitrix.RuntimeAnnotationKey: "runtime-ncafface"}}, + }}, + targetNamespace: "test", + createClusterRequest: CreateClusterRequest{ + Conf: "app-agwerl", + RuntimeId: "runtime-ncafface", + VersionId: "version-acklmalkds", + Username: "system", + }, + expected: nil, + }, + { + description: "create application test2", + existNamespaces: []*v1.Namespace{}, + targetNamespace: "test2", + createClusterRequest: CreateClusterRequest{ + Conf: "app-agwerl", + RuntimeId: "runtime-ncafface", + VersionId: "version-acklmalkds", + Username: "system", + }, + expected: errors.NewNotFound(schema.GroupResource{Group: "", Resource: "namespace"}, "test2"), + }, + } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + for _, test := range tests { + op := openpitrix.NewMockClient(ctrl) + objs := namespacesToRuntimeObjects(test.existNamespaces...) + k8s := fake.NewSimpleClientset(objs...) + informer := informers.NewSharedInformerFactory(k8s, 0) + stopChan := make(chan struct{}, 0) + informer.Core().V1().Namespaces().Lister() + informer.Start(stopChan) + informer.WaitForCacheSync(stopChan) + + applicationOperator := newApplicationOperator(informer, op) + + // setup expect response + // op.EXPECT().CreateCluster(gomock.Any(), gomock.Any()).Return(&pb.CreateClusterResponse{}, nil).AnyTimes() + op.EXPECT().CreateCluster(openpitrix.ContextWithUsername(test.createClusterRequest.Username), &pb.CreateClusterRequest{ + AppId: &wrappers.StringValue{Value: test.createClusterRequest.AppId}, + VersionId: &wrappers.StringValue{Value: test.createClusterRequest.VersionId}, + RuntimeId: &wrappers.StringValue{Value: test.createClusterRequest.RuntimeId}, + Conf: &wrappers.StringValue{Value: test.createClusterRequest.Conf}, + }).Return(&pb.CreateClusterResponse{}, nil).AnyTimes() + + t.Run(test.description, func(t *testing.T) { + + err := applicationOperator.CreateApplication(test.targetNamespace, test.createClusterRequest) + + if err != nil && err.Error() != test.expected.Error() { + t.Error(err) + } + }) + } +} diff --git a/pkg/models/openpitrix/apps.go b/pkg/models/openpitrix/apps.go index 1ba62acfb..d96bc5370 100644 --- a/pkg/models/openpitrix/apps.go +++ b/pkg/models/openpitrix/apps.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 The KubeSphere Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,38 +26,57 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) -const ( - BuiltinRepoId = "repo-helm" - StatusActive = "active" -) +type AppTemplateInterface interface { + ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + DescribeApp(id string) (*App, error) + DeleteApp(id string) error + CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) + ModifyApp(appId string, request *ModifyAppRequest) error + DeleteAppVersion(id string) error + ModifyAppVersion(id string, request *ModifyAppVersionRequest) error + DescribeAppVersion(id string) (*AppVersion, error) + CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) + ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) + GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) + DoAppAction(appId string, request *ActionRequest) error + DoAppVersionAction(versionId string, request *ActionRequest) error + GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) + ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) +} -func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type appTemplateOperator struct { + opClient openpitrix.Client +} + +func newAppTemplateOperator(opClient openpitrix.Client) AppTemplateInterface { + return &appTemplateOperator{ + opClient: opClient, } +} + +func (c *appTemplateOperator) ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppsRequest := &pb.DescribeAppsRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { describeAppsRequest.SearchWord = &wrappers.StringValue{Value: keyword} } - if appId := conditions.Match["app_id"]; appId != "" { + if appId := conditions.Match[AppId]; appId != "" { describeAppsRequest.AppId = strings.Split(appId, "|") } - if isv := conditions.Match["isv"]; isv != "" { + if isv := conditions.Match[ISV]; isv != "" { describeAppsRequest.Isv = strings.Split(isv, "|") } - if categoryId := conditions.Match["category_id"]; categoryId != "" { + if categoryId := conditions.Match[CategoryId]; categoryId != "" { describeAppsRequest.CategoryId = strings.Split(categoryId, "|") } - if repoId := conditions.Match["repo"]; repoId != "" { + if repoId := conditions.Match[RepoId]; repoId != "" { // hard code, app template in built-in repo has no repo_id attribute if repoId == BuiltinRepoId { describeAppsRequest.RepoId = []string{"\u0000"} @@ -65,7 +84,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit describeAppsRequest.RepoId = strings.Split(repoId, "|") } } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { describeAppsRequest.Status = strings.Split(status, "|") } if orderBy != "" { @@ -74,7 +93,7 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit describeAppsRequest.Reverse = &wrappers.BoolValue{Value: reverse} describeAppsRequest.Limit = uint32(limit) describeAppsRequest.Offset = uint32(offset) - resp, err := client.App().DescribeApps(openpitrix.SystemContext(), describeAppsRequest) + resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), describeAppsRequest) if err != nil { klog.Error(err) return nil, err @@ -89,13 +108,8 @@ func ListApps(conditions *params.Conditions, orderBy string, reverse bool, limit return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func DescribeApp(id string) (*App, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{ +func (c *appTemplateOperator) DescribeApp(id string) (*App, error) { + resp, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{ AppId: []string{id}, Limit: 1, }) @@ -116,13 +130,8 @@ func DescribeApp(id string) (*App, error) { } } -func DeleteApp(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.App().DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{ +func (c *appTemplateOperator) DeleteApp(id string) error { + _, err := c.opClient.DeleteApps(openpitrix.SystemContext(), &pb.DeleteAppsRequest{ AppId: []string{id}, }) if err != nil { @@ -132,12 +141,7 @@ func DeleteApp(id string) error { return nil } -func CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { createAppRequest := &pb.CreateAppRequest{ Name: &wrappers.StringValue{Value: request.Name}, VersionType: &wrappers.StringValue{Value: request.VersionType}, @@ -152,7 +156,7 @@ func CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { if request.Isv != "" { createAppRequest.Isv = &wrappers.StringValue{Value: request.Isv} } - resp, err := op.App().CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest) + resp, err := c.opClient.CreateApp(openpitrix.ContextWithUsername(request.Username), createAppRequest) if err != nil { klog.Error(err) return nil, err @@ -163,14 +167,7 @@ func CreateApp(request *CreateAppRequest) (*CreateAppResponse, error) { }, nil } -func PatchApp(appId string, request *ModifyAppRequest) error { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) ModifyApp(appId string, request *ModifyAppRequest) error { // upload app attachment if request.AttachmentContent != nil { uploadAttachmentRequest := &pb.UploadAppAttachmentRequest{ @@ -184,7 +181,7 @@ func PatchApp(appId string, request *ModifyAppRequest) error { uploadAttachmentRequest.Sequence = &wrappers.UInt32Value{Value: uint32(*request.Sequence)} } - _, err := client.App().UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest) + _, err := c.opClient.UploadAppAttachment(openpitrix.SystemContext(), uploadAttachmentRequest) if err != nil { klog.Error(err) @@ -227,7 +224,7 @@ func PatchApp(appId string, request *ModifyAppRequest) error { patchAppRequest.Tos = &wrappers.StringValue{Value: *request.Tos} } - _, err = client.App().ModifyApp(openpitrix.SystemContext(), patchAppRequest) + _, err := c.opClient.ModifyApp(openpitrix.SystemContext(), patchAppRequest) if err != nil { klog.Error(err) @@ -237,12 +234,7 @@ func PatchApp(appId string, request *ModifyAppRequest) error { return nil } -func CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionResponse, error) { createAppVersionRequest := &pb.CreateAppVersionRequest{ AppId: &wrappers.StringValue{Value: request.AppId}, Name: &wrappers.StringValue{Value: request.Name}, @@ -254,7 +246,7 @@ func CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionRespon createAppVersionRequest.Package = &wrappers.BytesValue{Value: request.Package} } - resp, err := op.App().CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest) + resp, err := c.opClient.CreateAppVersion(openpitrix.ContextWithUsername(request.Username), createAppVersionRequest) if err != nil { klog.Error(err) return nil, err @@ -264,14 +256,7 @@ func CreateAppVersion(request *CreateAppVersionRequest) (*CreateAppVersionRespon }, nil } -func ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, error) { r := &pb.ValidatePackageRequest{} if request.VersionPackage != nil { @@ -281,7 +266,7 @@ func ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, r.VersionType = request.VersionType } - resp, err := client.App().ValidatePackage(openpitrix.SystemContext(), r) + resp, err := c.opClient.ValidatePackage(openpitrix.SystemContext(), r) if err != nil { klog.Error(err) @@ -314,13 +299,8 @@ func ValidatePackage(request *ValidatePackageRequest) (*ValidatePackageResponse, return result, nil } -func DeleteAppVersion(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.App().DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{ +func (c *appTemplateOperator) DeleteAppVersion(id string) error { + _, err := c.opClient.DeleteAppVersion(openpitrix.SystemContext(), &pb.DeleteAppVersionRequest{ VersionId: &wrappers.StringValue{Value: id}, }) if err != nil { @@ -330,12 +310,7 @@ func DeleteAppVersion(id string) error { return nil } -func PatchAppVersion(id string, request *ModifyAppVersionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *appTemplateOperator) ModifyAppVersion(id string, request *ModifyAppVersionRequest) error { modifyAppVersionRequest := &pb.ModifyAppVersionRequest{ VersionId: &wrappers.StringValue{Value: id}, } @@ -353,7 +328,7 @@ func PatchAppVersion(id string, request *ModifyAppVersionRequest) error { modifyAppVersionRequest.PackageFiles = request.PackageFiles } - _, err = op.App().ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest) + _, err := c.opClient.ModifyAppVersion(openpitrix.SystemContext(), modifyAppVersionRequest) if err != nil { klog.Error(err) return err @@ -361,13 +336,8 @@ func PatchAppVersion(id string, request *ModifyAppVersionRequest) error { return nil } -func DescribeAppVersion(id string) (*AppVersion, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ +func (c *appTemplateOperator) DescribeAppVersion(id string) (*AppVersion, error) { + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ VersionId: []string{id}, Limit: 1, }) @@ -388,13 +358,8 @@ func DescribeAppVersion(id string) (*AppVersion, error) { } } -func GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.App().GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{ +func (c *appTemplateOperator) GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageResponse, error) { + resp, err := c.opClient.GetAppVersionPackage(openpitrix.SystemContext(), &pb.GetAppVersionPackageRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) if err != nil { @@ -414,21 +379,14 @@ func GetAppVersionPackage(appId, versionId string) (*GetAppVersionPackageRespons return app, nil } -func DoAppAction(appId string, request *ActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) DoAppAction(appId string, request *ActionRequest) error { switch request.Action { - case "recover": + case ActionRecover: // TODO openpitrix need to implement app recover interface - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ AppId: []string{appId}, - Status: []string{"suspended"}, + Status: []string{StatusSuspended}, Limit: 200, Offset: 0, }) @@ -438,7 +396,7 @@ func DoAppAction(appId string, request *ActionRequest) error { } for _, version := range resp.AppVersionSet { - _, err = op.App().RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{ + _, err = c.opClient.RecoverAppVersion(openpitrix.SystemContext(), &pb.RecoverAppVersionRequest{ VersionId: version.VersionId, }) if err != nil { @@ -447,11 +405,11 @@ func DoAppAction(appId string, request *ActionRequest) error { } } - case "suspend": + case ActionSuspend: // TODO openpitrix need to implement app suspend interface - resp, err := op.App().DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{ AppId: []string{appId}, - Status: []string{"active"}, + Status: []string{StatusActive}, Limit: 200, Offset: 0, }) @@ -460,7 +418,7 @@ func DoAppAction(appId string, request *ActionRequest) error { return err } for _, version := range resp.AppVersionSet { - _, err = op.App().SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{ + _, err = c.opClient.SuspendAppVersion(openpitrix.SystemContext(), &pb.SuspendAppVersionRequest{ VersionId: version.VersionId, }) @@ -471,7 +429,7 @@ func DoAppAction(appId string, request *ActionRequest) error { } default: - err = status.New(codes.InvalidArgument, "action not support").Err() + err := status.New(codes.InvalidArgument, "action not support").Err() klog.Error(err) return err } @@ -479,42 +437,36 @@ func DoAppAction(appId string, request *ActionRequest) error { return nil } -func DoAppVersionAction(versionId string, request *ActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return err - } - +func (c *appTemplateOperator) DoAppVersionAction(versionId string, request *ActionRequest) error { + var err error switch request.Action { - case "cancel": - _, err = op.App().CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{ + case ActionCancel: + _, err = c.opClient.CancelAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.CancelAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "pass": - _, err = op.App().AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{ + case ActionPass: + _, err = c.opClient.AdminPassAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.PassAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "recover": - _, err = op.App().RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{ + case ActionRecover: + _, err = c.opClient.RecoverAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RecoverAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "reject": - _, err = op.App().AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{ + case ActionReject: + _, err = c.opClient.AdminRejectAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.RejectAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, Message: &wrappers.StringValue{Value: request.Message}, }) - case "submit": - _, err = op.App().SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{ + case ActionSubmit: + _, err = c.opClient.SubmitAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SubmitAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "suspend": - _, err = op.App().SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{ + case ActionSuspend: + _, err = c.opClient.SuspendAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.SuspendAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) - case "release": - _, err = op.App().ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{ + case ActionRelease: + _, err = c.opClient.ReleaseAppVersion(openpitrix.ContextWithUsername(request.Username), &pb.ReleaseAppVersionRequest{ VersionId: &wrappers.StringValue{Value: versionId}, }) default: @@ -529,12 +481,7 @@ func DoAppVersionAction(versionId string, request *ActionRequest) error { return nil } -func GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } +func (c *appTemplateOperator) GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (*GetAppVersionPackageFilesResponse, error) { getAppVersionPackageFilesRequest := &pb.GetAppVersionPackageFilesRequest{ VersionId: &wrappers.StringValue{Value: versionId}, } @@ -542,7 +489,7 @@ func GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (* getAppVersionPackageFilesRequest.Files = request.Files } - resp, err := op.App().GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest) + resp, err := c.opClient.GetAppVersionPackageFiles(openpitrix.SystemContext(), getAppVersionPackageFilesRequest) if err != nil { klog.Error(err) return nil, err @@ -562,14 +509,7 @@ func GetAppVersionFiles(versionId string, request *GetAppVersionFilesRequest) (* return version, nil } -func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionAudits := &pb.DescribeAppVersionAuditsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -587,10 +527,10 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse if orderBy != "" { describeAppVersionAudits.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionAudits.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionAudits.Limit = uint32(limit) describeAppVersionAudits.Offset = uint32(offset) - resp, err := client.App().DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits) + resp, err := c.opClient.DescribeAppVersionAudits(openpitrix.SystemContext(), describeAppVersionAudits) if err != nil { klog.Error(err) @@ -607,14 +547,7 @@ func ListAppVersionAudits(conditions *params.Conditions, orderBy string, reverse return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersionReviews(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionReviews := &pb.DescribeAppVersionReviewsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -626,11 +559,11 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers if orderBy != "" { describeAppVersionReviews.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionReviews.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionReviews.Limit = uint32(limit) describeAppVersionReviews.Offset = uint32(offset) // TODO icon is needed - resp, err := client.App().DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews) + resp, err := c.opClient.DescribeAppVersionReviews(openpitrix.SystemContext(), describeAppVersionReviews) if err != nil { klog.Error(err) @@ -647,14 +580,7 @@ func ListAppVersionReviews(conditions *params.Conditions, orderBy string, revers return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *appTemplateOperator) ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { describeAppVersionsRequest := &pb.DescribeAppVersionsRequest{} if keyword := conditions.Match["keyword"]; keyword != "" { @@ -669,10 +595,10 @@ func ListAppVersions(conditions *params.Conditions, orderBy string, reverse bool if orderBy != "" { describeAppVersionsRequest.SortKey = &wrappers.StringValue{Value: orderBy} } - describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: !reverse} + describeAppVersionsRequest.Reverse = &wrappers.BoolValue{Value: reverse} describeAppVersionsRequest.Limit = uint32(limit) describeAppVersionsRequest.Offset = uint32(offset) - resp, err := client.App().DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest) + resp, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), describeAppVersionsRequest) if err != nil { klog.Error(err) diff --git a/pkg/models/openpitrix/attachments.go b/pkg/models/openpitrix/attachments.go index 07722b04a..aec779ca0 100644 --- a/pkg/models/openpitrix/attachments.go +++ b/pkg/models/openpitrix/attachments.go @@ -22,18 +22,26 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" ) -func DescribeAttachment(id string) (*Attachment, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type AttachmentInterface interface { + DescribeAttachment(id string) (*Attachment, error) +} + +type attachmentOperator struct { + opClient openpitrix.Client +} + +func newAttachmentOperator(opClient openpitrix.Client) AttachmentInterface { + return &attachmentOperator{ + opClient: opClient, } - resp, err := op.Attachment().GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{ +} + +func (c *attachmentOperator) DescribeAttachment(id string) (*Attachment, error) { + resp, err := c.opClient.GetAttachments(openpitrix.SystemContext(), &pb.GetAttachmentsRequest{ AttachmentId: []string{id}, }) if err != nil { diff --git a/pkg/models/openpitrix/categories.go b/pkg/models/openpitrix/categories.go index 7215151ce..0a238e1c0 100644 --- a/pkg/models/openpitrix/categories.go +++ b/pkg/models/openpitrix/categories.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 The KubeSphere Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,17 +25,29 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" ) -func CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type CategoryInterface interface { + CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) + DeleteCategory(id string) error + ModifyCategory(id string, request *ModifyCategoryRequest) error + ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + DescribeCategory(id string) (*Category, error) +} + +type categoryOperator struct { + opClient openpitrix.Client +} + +func newCategoryOperator(opClient openpitrix.Client) CategoryInterface { + return &categoryOperator{ + opClient: opClient, } +} + +func (c *categoryOperator) CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, error) { r := &pb.CreateCategoryRequest{ Name: &wrappers.StringValue{Value: request.Name}, Locale: &wrappers.StringValue{Value: request.Locale}, @@ -45,7 +57,7 @@ func CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, er r.Icon = &wrappers.BytesValue{Value: request.Icon} } - resp, err := op.Category().CreateCategory(openpitrix.SystemContext(), r) + resp, err := c.opClient.CreateCategory(openpitrix.SystemContext(), r) if err != nil { klog.Error(err) return nil, err @@ -55,13 +67,8 @@ func CreateCategory(request *CreateCategoryRequest) (*CreateCategoryResponse, er }, nil } -func DeleteCategory(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.Category().DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{ +func (c *categoryOperator) DeleteCategory(id string) error { + _, err := c.opClient.DeleteCategories(openpitrix.SystemContext(), &pb.DeleteCategoriesRequest{ CategoryId: []string{id}, }) if err != nil { @@ -71,12 +78,7 @@ func DeleteCategory(id string) error { return nil } -func PatchCategory(id string, request *ModifyCategoryRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *categoryOperator) ModifyCategory(id string, request *ModifyCategoryRequest) error { modifyCategoryRequest := &pb.ModifyCategoryRequest{ CategoryId: &wrappers.StringValue{Value: id}, } @@ -93,7 +95,7 @@ func PatchCategory(id string, request *ModifyCategoryRequest) error { modifyCategoryRequest.Icon = &wrappers.BytesValue{Value: request.Icon} } - _, err = op.Category().ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest) + _, err := c.opClient.ModifyCategory(openpitrix.SystemContext(), modifyCategoryRequest) if err != nil { klog.Error(err) return err @@ -101,13 +103,8 @@ func PatchCategory(id string, request *ModifyCategoryRequest) error { return nil } -func DescribeCategory(id string) (*Category, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.Category().DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{ +func (c *categoryOperator) DescribeCategory(id string) (*Category, error) { + resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), &pb.DescribeCategoriesRequest{ CategoryId: []string{id}, Limit: 1, }) @@ -128,26 +125,19 @@ func DescribeCategory(id string) (*Category, error) { } } -func ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *categoryOperator) ListCategories(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { req := &pb.DescribeCategoriesRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { req.SearchWord = &wrappers.StringValue{Value: keyword} } if orderBy != "" { req.SortKey = &wrappers.StringValue{Value: orderBy} } - req.Reverse = &wrappers.BoolValue{Value: !reverse} + req.Reverse = &wrappers.BoolValue{Value: reverse} req.Limit = uint32(limit) req.Offset = uint32(offset) - resp, err := client.Category().DescribeCategories(openpitrix.SystemContext(), req) + resp, err := c.opClient.DescribeCategories(openpitrix.SystemContext(), req) if err != nil { klog.Error(err) return nil, err diff --git a/pkg/models/openpitrix/interface.go b/pkg/models/openpitrix/interface.go new file mode 100644 index 000000000..15b133a47 --- /dev/null +++ b/pkg/models/openpitrix/interface.go @@ -0,0 +1,49 @@ +/* + * + * Copyright 2020 The KubeSphere 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 openpitrix + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" +) + +type Interface interface { + ApplicationInterface + AppTemplateInterface + AttachmentInterface + CategoryInterface + RepoInterface +} +type openpitrixOperator struct { + ApplicationInterface + AppTemplateInterface + AttachmentInterface + CategoryInterface + RepoInterface +} + +func NewOpenpitrixOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) Interface { + return &openpitrixOperator{ + ApplicationInterface: newApplicationOperator(informers, opClient), + AppTemplateInterface: newAppTemplateOperator(opClient), + AttachmentInterface: newAttachmentOperator(opClient), + CategoryInterface: newCategoryOperator(opClient), + RepoInterface: newRepoOperator(opClient), + } +} diff --git a/pkg/models/openpitrix/repos.go b/pkg/models/openpitrix/repos.go index 754cc63d0..43900df91 100644 --- a/pkg/models/openpitrix/repos.go +++ b/pkg/models/openpitrix/repos.go @@ -1,6 +1,6 @@ /* * - * Copyright 2019 The KubeSphere Authors. + * Copyright 2020 The KubeSphere Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,18 +26,33 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "openpitrix.io/openpitrix/pkg/pb" "strings" ) -func CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err +type RepoInterface interface { + CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) + DeleteRepo(id string) error + ModifyRepo(id string, request *ModifyRepoRequest) error + DescribeRepo(id string) (*Repo, error) + ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) + ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) + DoRepoAction(repoId string, request *RepoActionRequest) error + ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) +} + +type repoOperator struct { + opClient openpitrix.Client +} + +func newRepoOperator(opClient openpitrix.Client) RepoInterface { + return &repoOperator{ + opClient: opClient, } +} + +func (c *repoOperator) CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { createRepoRequest := &pb.CreateRepoRequest{ Name: &wrappers.StringValue{Value: request.Name}, Description: &wrappers.StringValue{Value: request.Description}, @@ -56,7 +71,7 @@ func CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { createRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} } - resp, err := op.Repo().CreateRepo(openpitrix.SystemContext(), createRepoRequest) + resp, err := c.opClient.CreateRepo(openpitrix.SystemContext(), createRepoRequest) if err != nil { klog.Error(err) return nil, err @@ -66,13 +81,8 @@ func CreateRepo(request *CreateRepoRequest) (*CreateRepoResponse, error) { }, nil } -func DeleteRepo(id string) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - _, err = op.Repo().DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{ +func (c *repoOperator) DeleteRepo(id string) error { + _, err := c.opClient.DeleteRepos(openpitrix.SystemContext(), &pb.DeleteReposRequest{ RepoId: []string{id}, }) if err != nil { @@ -82,12 +92,7 @@ func DeleteRepo(id string) error { return nil } -func PatchRepo(id string, request *ModifyRepoRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } +func (c *repoOperator) ModifyRepo(id string, request *ModifyRepoRequest) error { modifyRepoRequest := &pb.ModifyRepoRequest{ RepoId: &wrappers.StringValue{Value: id}, } @@ -124,7 +129,7 @@ func PatchRepo(id string, request *ModifyRepoRequest) error { modifyRepoRequest.Labels = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", *request.Workspace)} } - _, err = op.Repo().ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest) + _, err := c.opClient.ModifyRepo(openpitrix.SystemContext(), modifyRepoRequest) if err != nil { klog.Error(err) return err @@ -132,13 +137,8 @@ func PatchRepo(id string, request *ModifyRepoRequest) error { return nil } -func DescribeRepo(id string) (*Repo, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := op.Repo().DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{ +func (c *repoOperator) DescribeRepo(id string) (*Repo, error) { + resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), &pb.DescribeReposRequest{ RepoId: []string{id}, Limit: 1, }) @@ -159,41 +159,34 @@ func DescribeRepo(id string) (*Repo, error) { } } -func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *repoOperator) ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { req := &pb.DescribeReposRequest{} - if keyword := conditions.Match["keyword"]; keyword != "" { + if keyword := conditions.Match[Keyword]; keyword != "" { req.SearchWord = &wrappers.StringValue{Value: keyword} } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { req.Status = strings.Split(status, "|") } - if typeStr := conditions.Match["type"]; typeStr != "" { + if typeStr := conditions.Match[Type]; typeStr != "" { req.Type = strings.Split(typeStr, "|") } - if visibility := conditions.Match["visibility"]; visibility != "" { + if visibility := conditions.Match[Visibility]; visibility != "" { req.Visibility = strings.Split(visibility, "|") } - if status := conditions.Match["status"]; status != "" { + if status := conditions.Match[Status]; status != "" { req.Status = strings.Split(status, "|") } - if workspace := conditions.Match["workspace"]; workspace != "" { + if workspace := conditions.Match[WorkspaceLabel]; workspace != "" { req.Label = &wrappers.StringValue{Value: fmt.Sprintf("workspace=%s", workspace)} } if orderBy != "" { req.SortKey = &wrappers.StringValue{Value: orderBy} } - req.Reverse = &wrappers.BoolValue{Value: !reverse} + req.Reverse = &wrappers.BoolValue{Value: reverse} req.Limit = uint32(limit) req.Offset = uint32(offset) - resp, err := client.Repo().DescribeRepos(openpitrix.SystemContext(), req) + resp, err := c.opClient.DescribeRepos(openpitrix.SystemContext(), req) if err != nil { klog.Error(err) return nil, err @@ -208,15 +201,8 @@ func ListRepos(conditions *params.Conditions, orderBy string, reverse bool, limi return &models.PageableResponse{Items: items, TotalCount: int(resp.TotalCount)}, nil } -func ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) { - client, err := cs.ClientSets().OpenPitrix() - - if err != nil { - klog.Error(err) - return nil, err - } - - resp, err := client.Repo().ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{ +func (c *repoOperator) ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) { + resp, err := c.opClient.ValidateRepo(openpitrix.SystemContext(), &pb.ValidateRepoRequest{ Type: &wrappers.StringValue{Value: request.Type}, Credential: &wrappers.StringValue{Value: request.Credential}, Url: &wrappers.StringValue{Value: request.Url}, @@ -233,19 +219,14 @@ func ValidateRepo(request *ValidateRepoRequest) (*ValidateRepoResponse, error) { }, nil } -func DoRepoAction(repoId string, request *RepoActionRequest) error { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return err - } - +func (c *repoOperator) DoRepoAction(repoId string, request *RepoActionRequest) error { + var err error switch request.Action { - case "index": + case ActionIndex: indexRepoRequest := &pb.IndexRepoRequest{ RepoId: &wrappers.StringValue{Value: repoId}, } - _, err := op.RepoIndexer().IndexRepo(openpitrix.SystemContext(), indexRepoRequest) + _, err := c.opClient.IndexRepo(openpitrix.SystemContext(), indexRepoRequest) if err != nil { klog.Error(err) @@ -260,13 +241,7 @@ func DoRepoAction(repoId string, request *RepoActionRequest) error { } } -func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { - op, err := cs.ClientSets().OpenPitrix() - if err != nil { - klog.Error(err) - return nil, err - } - +func (c *repoOperator) ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset int) (*models.PageableResponse, error) { describeRepoEventsRequest := &pb.DescribeRepoEventsRequest{ RepoId: []string{repoId}, } @@ -279,7 +254,7 @@ func ListRepoEvents(repoId string, conditions *params.Conditions, limit, offset describeRepoEventsRequest.Limit = uint32(limit) describeRepoEventsRequest.Offset = uint32(offset) - resp, err := op.RepoIndexer().DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest) + resp, err := c.opClient.DescribeRepoEvents(openpitrix.SystemContext(), describeRepoEventsRequest) if err != nil { klog.Error(err) diff --git a/pkg/models/openpitrix/types.go b/pkg/models/openpitrix/types.go index 9cec02864..92f1e021e 100644 --- a/pkg/models/openpitrix/types.go +++ b/pkg/models/openpitrix/types.go @@ -835,3 +835,30 @@ type ModifyClusterAttributesRequest struct { // cluster name Name *string `json:"name,omitempty"` } + +const ( + CreateTime = "create_time" + StatusTime = "status_time" + RuntimeId = "runtime_id" + VersionId = "version_id" + RepoId = "repo_id" + CategoryId = "category_id" + Status = "status" + Type = "type" + Visibility = "visibility" + AppId = "app_id" + Keyword = "keyword" + ISV = "isv" + WorkspaceLabel = "workspace" + BuiltinRepoId = "repo-helm" + StatusActive = "active" + StatusSuspended = "suspended" + ActionRecover = "recover" + ActionSuspend = "suspend" + ActionCancel = "cancel" + ActionPass = "pass" + ActionReject = "reject" + ActionSubmit = "submit" + ActionRelease = "release" + ActionIndex = "index" +) diff --git a/pkg/models/openpitrix/convert.go b/pkg/models/openpitrix/utils.go similarity index 100% rename from pkg/models/openpitrix/convert.go rename to pkg/models/openpitrix/utils.go diff --git a/pkg/models/quotas/quotas.go b/pkg/models/quotas/quotas.go index 53259bef2..6ab606adb 100644 --- a/pkg/models/quotas/quotas.go +++ b/pkg/models/quotas/quotas.go @@ -22,11 +22,10 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/informers" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/api" ) const ( @@ -42,71 +41,69 @@ const ( s2iBuilders = "count/s2ibuilders.devops.kubesphere.io" ) -type NamespacedResourceQuota struct { - Namespace string `json:"namespace,omitempty"` - - Data struct { - v1.ResourceQuotaStatus - - // quota left status, do the math on the side, cause it's - // a lot easier with go-client library - Left v1.ResourceList `json:"left,omitempty"` - } `json:"data,omitempty"` +var supportedResources = map[string]schema.GroupVersionResource{ + deploymentsKey: {Group: "apps", Version: "v1", Resource: "deployments"}, + daemonsetsKey: {Group: "apps", Version: "v1", Resource: "daemonsets"}, + statefulsetsKey: {Group: "apps", Version: "v1", Resource: "statefulsets"}, + podsKey: {Group: "", Version: "v1", Resource: "pods"}, + servicesKey: {Group: "", Version: "v1", Resource: "services"}, + persistentvolumeclaimsKey: {Group: "", Version: "v1", Resource: "persistentvolumeclaims"}, + ingressKey: {Group: "extensions", Version: "v1beta1", Resource: "ingresses"}, + jobsKey: {Group: "batch", Version: "v1", Resource: "jobs"}, + cronJobsKey: {Group: "batch", Version: "v1beta1", Resource: "cronjobs"}, + s2iBuilders: {Group: "devops.kubesphere.io", Version: "v1alpha1", Resource: "s2ibuilders"}, } -var ( - resourceMap = map[string]string{ - daemonsetsKey: resources.DaemonSets, - deploymentsKey: resources.Deployments, - ingressKey: resources.Ingresses, - servicesKey: resources.Services, - statefulsetsKey: resources.StatefulSets, - persistentvolumeclaimsKey: resources.PersistentVolumeClaims, - podsKey: resources.Pods, - jobsKey: resources.Jobs, - cronJobsKey: resources.CronJobs, - s2iBuilders: resources.S2iBuilders, - } -) +type ResourceQuotaGetter interface { + GetClusterQuota() (*api.ResourceQuota, error) + GetNamespaceQuota(namespace string) (*api.NamespacedResourceQuota, error) +} -func getUsage(namespace, resource string) (int, error) { - var result *models.PageableResponse - var err error - if resource == resources.Namespaces || resource == resources.StorageClasses { - result, err = resources.ListResources("", resource, ¶ms.Conditions{}, "", false, 1, 0) - } else { - result, err = resources.ListResources(namespace, resource, ¶ms.Conditions{}, "", false, 1, 0) - } +type resourceQuotaGetter struct { + informers informers.SharedInformerFactory +} +func NewResourceQuotaGetter(informers informers.SharedInformerFactory) ResourceQuotaGetter { + return &resourceQuotaGetter{informers: informers} +} + +func (c *resourceQuotaGetter) getUsage(namespace, resource string) (int, error) { + + genericInformer, err := c.informers.ForResource(supportedResources[resource]) + if err != nil { + // we deliberately ignore error if trying to get non existed resource + return 0, nil + } + + result, err := genericInformer.Lister().ByNamespace(namespace).List(labels.Everything()) if err != nil { - klog.Error(err) return 0, err } - return result.TotalCount, nil + return len(result), nil } // no one use this api anymore, marked as deprecated -func GetClusterQuotas() (*models.ResourceQuota, error) { +func (c *resourceQuotaGetter) GetClusterQuota() (*api.ResourceQuota, error) { quota := v1.ResourceQuotaStatus{Hard: make(v1.ResourceList), Used: make(v1.ResourceList)} - for k, v := range resourceMap { - used, err := getUsage("", v) + for r := range supportedResources { + used, err := c.getUsage("", r) if err != nil { return nil, err } var quantity resource.Quantity quantity.Set(int64(used)) - quota.Used[v1.ResourceName(k)] = quantity + quota.Used[v1.ResourceName(r)] = quantity } - return &models.ResourceQuota{Namespace: "\"\"", Data: quota}, nil + return &api.ResourceQuota{Namespace: "\"\"", Data: quota}, nil } -func GetNamespaceQuotas(namespace string) (*NamespacedResourceQuota, error) { - quota, err := getNamespaceResourceQuota(namespace) +func (c *resourceQuotaGetter) GetNamespaceQuota(namespace string) (*api.NamespacedResourceQuota, error) { + quota, err := c.getNamespaceResourceQuota(namespace) if err != nil { klog.Error(err) return nil, err @@ -130,10 +127,10 @@ func GetNamespaceQuotas(namespace string) (*NamespacedResourceQuota, error) { } // add extra quota usage, cause user may not specify them - for key, val := range resourceMap { + for key := range supportedResources { // only add them when they don't exist in quotastatus if _, ok := quota.Used[v1.ResourceName(key)]; !ok { - used, err := getUsage(namespace, val) + used, err := c.getUsage(namespace, key) if err != nil { klog.Error(err) return nil, err @@ -143,7 +140,7 @@ func GetNamespaceQuotas(namespace string) (*NamespacedResourceQuota, error) { } } - var result = NamespacedResourceQuota{ + var result = api.NamespacedResourceQuota{ Namespace: namespace, } result.Data.Hard = quota.Hard @@ -169,8 +166,8 @@ func updateNamespaceQuota(tmpResourceList, resourceList v1.ResourceList) { } } -func getNamespaceResourceQuota(namespace string) (*v1.ResourceQuotaStatus, error) { - resourceQuotaLister := informers.SharedInformerFactory().Core().V1().ResourceQuotas().Lister() +func (c *resourceQuotaGetter) getNamespaceResourceQuota(namespace string) (*v1.ResourceQuotaStatus, error) { + resourceQuotaLister := c.informers.Core().V1().ResourceQuotas().Lister() quotaList, err := resourceQuotaLister.ResourceQuotas(namespace).List(labels.Everything()) if err != nil { klog.Error(err) diff --git a/pkg/models/registries/image_type.go b/pkg/models/registries/image_type.go index f7646d95d..e52690623 100644 --- a/pkg/models/registries/image_type.go +++ b/pkg/models/registries/image_type.go @@ -5,26 +5,15 @@ import ( "time" ) -type AuthInfo struct { - Username string `json:"username" description:"username"` - Password string `json:"password" description:"password"` - ServerHost string `json:"serverhost" description:"registry server host"` -} - // ImageBlobInfo describes the info of an image. type ImageDetails struct { // Status is the status of the image search, such as "failed","succeeded". - Status string `json:"status,omitempty" description:"Status is the status of the image search, such as \"succeeded\"."` - - Message string `json:"message,omitempty" description:"Status message."` - + Status string `json:"status,omitempty" description:"Status is the status of the image search, such as \"succeeded\"."` + Message string `json:"message,omitempty" description:"Status message."` ImageManifest *ImageManifest `json:"imageManifest,omitempty" description:"Retrieve the manifest from the registry identified. Reference: https://docs.docker.com/registry/spec/api/#manifest"` - - ImageBlob *ImageBlob `json:"imageBlob,omitempty" description:"Retrieve the blob from the registry identified. Reference: https://docs.docker.com/registry/spec/api/#blob"` - - ImageTag string `json:"imageTag,omitempty" description:"image tag."` - - Registry string `json:"registry,omitempty" description:"registry domain."` + ImageBlob *ImageBlob `json:"imageBlob,omitempty" description:"Retrieve the blob from the registry identified. Reference: https://docs.docker.com/registry/spec/api/#blob"` + ImageTag string `json:"imageTag,omitempty" description:"image tag."` + Registry string `json:"registry,omitempty" description:"registry domain."` } type ImageBlob struct { diff --git a/pkg/models/registries/registries.go b/pkg/models/registries/registries.go index 6fbf5a041..e176a1090 100644 --- a/pkg/models/registries/registries.go +++ b/pkg/models/registries/registries.go @@ -24,10 +24,12 @@ import ( "fmt" "github.com/docker/docker/api/types" "github.com/docker/docker/client" + "github.com/emicklei/go-restful" corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/informers" "k8s.io/klog" - log "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/api" + "strings" ) const ( @@ -52,8 +54,21 @@ type DockerConfigEntry struct { ServerAddress string `json:"serverAddress,omitempty"` } -func RegistryVerify(authInfo AuthInfo) error { - auth := base64.StdEncoding.EncodeToString([]byte(authInfo.Username + ":" + authInfo.Password)) +type RegistryGetter interface { + VerifyRegistryCredential(credential api.RegistryCredential) error + GetEntry(namespace, secretName, imageName string) (ImageDetails, error) +} + +type registryGetter struct { + informers informers.SharedInformerFactory +} + +func NewRegistryGetter(informers informers.SharedInformerFactory) RegistryGetter { + return ®istryGetter{informers: informers} +} + +func (c *registryGetter) VerifyRegistryCredential(credential api.RegistryCredential) error { + auth := base64.StdEncoding.EncodeToString([]byte(credential.Username + ":" + credential.Password)) ctx := context.Background() cli, err := client.NewClientWithOpts(client.WithAPIVersionNegotiation()) @@ -62,10 +77,10 @@ func RegistryVerify(authInfo AuthInfo) error { } config := types.AuthConfig{ - Username: authInfo.Username, - Password: authInfo.Password, + Username: credential.Username, + Password: credential.Password, Auth: auth, - ServerAddress: authInfo.ServerHost, + ServerAddress: credential.ServerHost, } resp, err := cli.RegistryLogin(ctx, config) @@ -82,21 +97,96 @@ func RegistryVerify(authInfo AuthInfo) error { } } -func GetEntryBySecret(namespace, secretName string) (dockerConfigEntry *DockerConfigEntry, err error) { - if namespace == "" || secretName == "" { - return &DockerConfigEntry{}, nil +func (c *registryGetter) GetEntry(namespace, secretName, imageName string) (ImageDetails, error) { + imageDetails, err := c.getEntryBySecret(namespace, secretName, imageName) + if imageDetails.Status == StatusFailed { + imageDetails.Message = err.Error() } - secret, err := informers.SharedInformerFactory().Core().V1().Secrets().Lister().Secrets(namespace).Get(secretName) + + return imageDetails, err +} + +func (c *registryGetter) getEntryBySecret(namespace, secretName, imageName string) (ImageDetails, error) { + failedImageDetails := ImageDetails{ + Status: StatusFailed, + Message: "", + } + + if namespace == "" || secretName == "" { + return failedImageDetails, fmt.Errorf("namespace or secret name not provided") + } + secret, err := c.informers.Core().V1().Secrets().Lister().Secrets(namespace).Get(secretName) if err != nil { - log.Errorf("%+v", err) - return nil, err + return failedImageDetails, err } entry, err := getDockerEntryFromDockerSecret(secret) if err != nil { - log.Errorf("%+v", err) - return nil, err + return failedImageDetails, err } - return entry, nil + + // default use ssl + checkSSl := func(serverAddress string) bool { + if strings.HasPrefix(serverAddress, "http://") { + return false + } else { + return true + } + } + + if strings.HasPrefix(imageName, "http") { + dockerurl, err := ParseDockerURL(imageName) + if err != nil { + return failedImageDetails, err + } + imageName = dockerurl.StringWithoutScheme() + } + + // parse image + image, err := ParseImage(imageName) + if err != nil { + return failedImageDetails, err + } + + useSSL := checkSSl(entry.ServerAddress) + + // Create the registry client. + r, err := CreateRegistryClient(entry.Username, entry.Password, image.Domain, useSSL) + if err != nil { + return failedImageDetails, err + } + + digestUrl := r.GetDigestUrl(image) + + // Get token. + token, err := r.Token(digestUrl) + if err != nil { + return failedImageDetails, err + } + + // Get digest. + imageManifest, err := r.ImageManifest(image, token) + if err != nil { + if serviceError, ok := err.(restful.ServiceError); ok { + return failedImageDetails, serviceError + } + return failedImageDetails, err + } + + image.Digest = imageManifest.ManifestConfig.Digest + + // Get blob. + imageBlob, err := r.ImageBlob(image, token) + if err != nil { + return failedImageDetails, err + } + + return ImageDetails{ + Status: StatusSuccess, + ImageManifest: imageManifest, + ImageBlob: imageBlob, + ImageTag: image.Tag, + Registry: image.Domain, + }, nil } func getDockerEntryFromDockerSecret(instance *corev1.Secret) (dockerConfigEntry *DockerConfigEntry, err error) { @@ -117,9 +207,9 @@ func getDockerEntryFromDockerSecret(instance *corev1.Secret) (dockerConfigEntry if len(dockerConfig.Auths) == 0 { return nil, fmt.Errorf("docker config auth len should not be 0") } - for registryAddress, dockerConfigEntry := range dockerConfig.Auths { - dockerConfigEntry.ServerAddress = registryAddress - return &dockerConfigEntry, nil + for registryAddress, dce := range dockerConfig.Auths { + dce.ServerAddress = registryAddress + return &dce, nil } return nil, nil } diff --git a/pkg/models/resources/appplications.go b/pkg/models/resources/appplications.go deleted file mode 100644 index 53bad8c38..000000000 --- a/pkg/models/resources/appplications.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" -) - -type appSearcher struct { -} - -func (*appSearcher) get(namespace, name string) (interface{}, error) { - return informers.AppSharedInformerFactory().App().V1beta1().Applications().Lister().Applications(namespace).Get(name) -} - -// exactly Match -func (*appSearcher) match(match map[string]string, item *v1beta1.Application) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*appSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Application) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*appSearcher) compare(a, b *v1beta1.Application, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *appSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - apps, err := informers.AppSharedInformerFactory().App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1beta1.Application, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = apps - } else { - for _, item := range apps { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/clusterroles.go b/pkg/models/resources/clusterroles.go deleted file mode 100644 index e0dfac238..000000000 --- a/pkg/models/resources/clusterroles.go +++ /dev/null @@ -1,154 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - rbac "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type clusterRoleSearcher struct { -} - -func (*clusterRoleSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().Get(name) -} - -// exactly Match -func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool { - for k, v := range match { - switch k { - case OwnerKind: - fallthrough - case OwnerName: - kind := match[OwnerKind] - name := match[OwnerName] - if !k8sutil.IsControlledBy(item.OwnerReferences, kind, name) { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - case UserFacing: - if v == "true" { - if !isUserFacingClusterRole(item) { - return false - } - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*clusterRoleSearcher) compare(a, b *rbac.ClusterRole, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *clusterRoleSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - clusterRoles, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*rbac.ClusterRole, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = clusterRoles - } else { - for _, item := range clusterRoles { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} - -// cluster role created by user from kubesphere dashboard -func isUserFacingClusterRole(role *rbac.ClusterRole) bool { - if role.Annotations[constants.CreatorAnnotationKey] != "" && role.Labels[constants.WorkspaceLabelKey] == "" { - return true - } - return false -} diff --git a/pkg/models/resources/configmaps.go b/pkg/models/resources/configmaps.go deleted file mode 100644 index 312d989ab..000000000 --- a/pkg/models/resources/configmaps.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type configMapSearcher struct { -} - -func (*configMapSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) -} - -// exactly Match -func (*configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*configMapSearcher) compare(a, b *v1.ConfigMap, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *configMapSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - configMaps, err := informers.SharedInformerFactory().Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.ConfigMap, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = configMaps - } else { - for _, item := range configMaps { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/cronjobs.go b/pkg/models/resources/cronjobs.go deleted file mode 100644 index 8a61a776b..000000000 --- a/pkg/models/resources/cronjobs.go +++ /dev/null @@ -1,156 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/batch/v1beta1" - - "k8s.io/apimachinery/pkg/labels" -) - -type cronJobSearcher struct { -} - -func (*cronJobSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).Get(name) -} - -func cronJobStatus(item *v1beta1.CronJob) string { - if item.Spec.Suspend != nil && *item.Spec.Suspend { - return StatusPaused - } - return StatusRunning -} - -// Exactly Match -func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Status: - if cronJobStatus(item) != v { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bool { - - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - - return true -} - -func (*cronJobSearcher) compare(a, b *v1beta1.CronJob, orderBy string) bool { - switch orderBy { - case LastScheduleTime: - if a.Status.LastScheduleTime == nil { - return true - } - if b.Status.LastScheduleTime == nil { - return false - } - return a.Status.LastScheduleTime.Before(b.Status.LastScheduleTime) - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - default: - fallthrough - case Name: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *cronJobSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - cronJobs, err := informers.SharedInformerFactory().Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1beta1.CronJob, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = cronJobs - } else { - for _, item := range cronJobs { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/daemonsets.go b/pkg/models/resources/daemonsets.go deleted file mode 100644 index 9ae1eeef4..000000000 --- a/pkg/models/resources/daemonsets.go +++ /dev/null @@ -1,150 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type daemonSetSearcher struct { -} - -func (*daemonSetSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name) -} - -func daemonSetStatus(item *v1.DaemonSet) string { - if item.Status.NumberAvailable == 0 { - return StatusStopped - } else if item.Status.DesiredNumberScheduled == item.Status.NumberAvailable { - return StatusRunning - } else { - return StatusUpdating - } -} - -// Exactly Match -func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool { - for k, v := range match { - switch k { - case Status: - if daemonSetStatus(item) != v { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*daemonSetSearcher) fuzzy(fuzzy map[string]string, item *v1.DaemonSet) bool { - - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - - return true -} - -func (*daemonSetSearcher) compare(a, b *v1.DaemonSet, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *daemonSetSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - daemonSets, err := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister().DaemonSets(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.DaemonSet, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = daemonSets - } else { - for _, item := range daemonSets { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/deployments.go b/pkg/models/resources/deployments.go deleted file mode 100644 index 5980c6d70..000000000 --- a/pkg/models/resources/deployments.go +++ /dev/null @@ -1,167 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "time" - - "k8s.io/apimachinery/pkg/labels" - - "k8s.io/api/apps/v1" -) - -type deploymentSearcher struct { -} - -func (*deploymentSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) -} - -func deploymentStatus(item *v1.Deployment) string { - if item.Spec.Replicas != nil { - if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { - return StatusStopped - } else if item.Status.ReadyReplicas == *item.Spec.Replicas { - return StatusRunning - } else { - return StatusUpdating - } - } - return StatusStopped -} - -// Exactly Match -func (*deploymentSearcher) match(match map[string]string, item *v1.Deployment) bool { - for k, v := range match { - switch k { - case Status: - if deploymentStatus(item) != v { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*deploymentSearcher) fuzzy(fuzzy map[string]string, item *v1.Deployment) bool { - - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - - return true -} - -func (s *deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case UpdateTime: - return s.lastUpdateTime(a).Before(s.lastUpdateTime(b)) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *deploymentSearcher) lastUpdateTime(deployment *v1.Deployment) time.Time { - lastUpdateTime := deployment.CreationTimestamp.Time - for _, condition := range deployment.Status.Conditions { - if condition.LastUpdateTime.After(lastUpdateTime) { - lastUpdateTime = condition.LastUpdateTime.Time - } - } - return lastUpdateTime -} - -func (s *deploymentSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - deployments, err := informers.SharedInformerFactory().Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Deployment, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = deployments - } else { - for _, item := range deployments { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/extraannotations.go b/pkg/models/resources/extraannotations.go deleted file mode 100644 index 711c6602d..000000000 --- a/pkg/models/resources/extraannotations.go +++ /dev/null @@ -1,120 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/informers" - "strconv" -) - -type extraAnnotationInjector struct { -} - -func (i extraAnnotationInjector) addExtraAnnotations(item interface{}) interface{} { - - switch item.(type) { - case *corev1.PersistentVolumeClaim: - return i.injectPersistentVolumeClaim(item.(*corev1.PersistentVolumeClaim)) - case *storagev1.StorageClass: - return i.injectStorageClass(item.(*storagev1.StorageClass)) - } - - return item -} - -func (i extraAnnotationInjector) injectStorageClass(item *storagev1.StorageClass) *storagev1.StorageClass { - - count, err := countPvcByStorageClass(item.Name) - - if err != nil { - klog.Errorf("inject annotation failed %+v", err) - return item - } - - item = item.DeepCopy() - - if item.Annotations == nil { - item.Annotations = make(map[string]string, 0) - } - - item.Annotations["kubesphere.io/pvc-count"] = strconv.Itoa(count) - - return item -} - -func (i extraAnnotationInjector) injectPersistentVolumeClaim(item *corev1.PersistentVolumeClaim) *corev1.PersistentVolumeClaim { - podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister() - pods, err := podLister.Pods(item.Namespace).List(labels.Everything()) - if err != nil { - klog.Errorf("inject annotation failed %+v", err) - return item - } - - item = item.DeepCopy() - - if item.Annotations == nil { - item.Annotations = make(map[string]string, 0) - } - - if isPvcInUse(pods, item.Name) { - item.Annotations["kubesphere.io/in-use"] = "true" - } else { - item.Annotations["kubesphere.io/in-use"] = "false" - } - - return item -} - -func isPvcInUse(pods []*corev1.Pod, pvcName string) bool { - for _, pod := range pods { - volumes := pod.Spec.Volumes - for _, volume := range volumes { - pvc := volume.PersistentVolumeClaim - if pvc != nil && pvc.ClaimName == pvcName { - return true - } - } - } - return false -} - -func countPvcByStorageClass(scName string) (int, error) { - persistentVolumeClaimLister := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister() - all, err := persistentVolumeClaimLister.List(labels.Everything()) - - if err != nil { - return 0, err - } - - count := 0 - - for _, item := range all { - if item.Spec.StorageClassName != nil { - if *item.Spec.StorageClassName == scName { - count++ - } - } else if item.GetAnnotations()[corev1.BetaStorageClassAnnotation] == scName { - count++ - } - } - return count, nil -} diff --git a/pkg/models/resources/horizontalpodautoscalers.go b/pkg/models/resources/horizontalpodautoscalers.go deleted file mode 100644 index 1b64656d2..000000000 --- a/pkg/models/resources/horizontalpodautoscalers.go +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * Copyright 2019 The KubeSphere 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 resources - -import ( - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" -) - -type hpaSearcher struct { -} - -func (*hpaSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Autoscaling().V2beta2().HorizontalPodAutoscalers().Lister().HorizontalPodAutoscalers(namespace).Get(name) -} - -func hpaTargetMatch(item *autoscalingv2beta2.HorizontalPodAutoscaler, kind, name string) bool { - return item.Spec.ScaleTargetRef.Kind == kind && item.Spec.ScaleTargetRef.Name == name -} - -// exactly Match -func (*hpaSearcher) match(match map[string]string, item *autoscalingv2beta2.HorizontalPodAutoscaler) bool { - for k, v := range match { - switch k { - case TargetKind: - fallthrough - case TargetName: - kind := match[TargetKind] - name := match[TargetName] - if !hpaTargetMatch(item, kind, name) { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - if item.Labels[k] != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*hpaSearcher) fuzzy(fuzzy map[string]string, item *autoscalingv2beta2.HorizontalPodAutoscaler) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) && !searchFuzzy(item.Annotations, k, v) { - return false - } - } - } - return true -} - -func (*hpaSearcher) compare(a, b *autoscalingv2beta2.HorizontalPodAutoscaler, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *hpaSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - - horizontalPodAutoscalers, err := informers.SharedInformerFactory().Autoscaling().V2beta2().HorizontalPodAutoscalers().Lister().HorizontalPodAutoscalers(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*autoscalingv2beta2.HorizontalPodAutoscaler, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = horizontalPodAutoscalers - } else { - for _, item := range horizontalPodAutoscalers { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/ingresses.go b/pkg/models/resources/ingresses.go deleted file mode 100644 index 28ba94279..000000000 --- a/pkg/models/resources/ingresses.go +++ /dev/null @@ -1,136 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - extensions "k8s.io/api/extensions/v1beta1" - - "k8s.io/apimachinery/pkg/labels" -) - -type ingressSearcher struct { -} - -func (*ingressSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name) -} - -// exactly Match -func (*ingressSearcher) match(match map[string]string, item *extensions.Ingress) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *extensions.Ingress) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*ingressSearcher) compare(a, b *extensions.Ingress, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *ingressSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - ingresses, err := informers.SharedInformerFactory().Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*extensions.Ingress, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = ingresses - } else { - for _, item := range ingresses { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/jobs.go b/pkg/models/resources/jobs.go deleted file mode 100644 index d9585f20c..000000000 --- a/pkg/models/resources/jobs.go +++ /dev/null @@ -1,177 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - "time" - - batchv1 "k8s.io/api/batch/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type jobSearcher struct { -} - -func (*jobSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Batch().V1().Jobs().Lister().Jobs(namespace).Get(name) -} - -func jobStatus(item *batchv1.Job) string { - status := StatusFailed - if item.Status.Active > 0 { - status = StatusRunning - } else if item.Status.Failed > 0 { - status = StatusFailed - } else if item.Status.Succeeded > 0 { - status = StatusComplete - } - return status -} - -// Exactly Match -func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { - for k, v := range match { - switch k { - case Status: - if jobStatus(item) != v { - return false - } - case includeCronJob: - if v == "false" && k8sutil.IsControlledBy(item.OwnerReferences, cronJobKind, "") { - return false - } - case includeS2iRun: - if v == "false" && k8sutil.IsControlledBy(item.OwnerReferences, s2iRunKind, "") { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool { - - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - - return true -} - -func jobUpdateTime(item *batchv1.Job) time.Time { - updateTime := item.CreationTimestamp.Time - for _, condition := range item.Status.Conditions { - if updateTime.Before(condition.LastProbeTime.Time) { - updateTime = condition.LastProbeTime.Time - } - if updateTime.Before(condition.LastTransitionTime.Time) { - updateTime = condition.LastTransitionTime.Time - } - } - return updateTime -} - -func (*jobSearcher) compare(a, b *batchv1.Job, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case UpdateTime: - return jobUpdateTime(a).Before(jobUpdateTime(b)) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *jobSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - jobs, err := informers.SharedInformerFactory().Batch().V1().Jobs().Lister().Jobs(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*batchv1.Job, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = jobs - } else { - for _, item := range jobs { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/namespaces.go b/pkg/models/resources/namespaces.go deleted file mode 100644 index 1ba4e8670..000000000 --- a/pkg/models/resources/namespaces.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type namespaceSearcher struct { -} - -func (*namespaceSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(name) -} - -// exactly Match -func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *namespaceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - namespaces, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Namespace, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = namespaces - } else { - for _, item := range namespaces { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/nodes.go b/pkg/models/resources/nodes.go deleted file mode 100644 index 0a2e02fbe..000000000 --- a/pkg/models/resources/nodes.go +++ /dev/null @@ -1,179 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "fmt" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type nodeSearcher struct { -} - -func (*nodeSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().Nodes().Lister().Get(name) -} - -func getNodeStatus(node *v1.Node) string { - if node.Spec.Unschedulable { - return StatusUnschedulable - } - for _, condition := range node.Status.Conditions { - if isUnhealthStatus(condition) { - return StatusWarning - } - } - - return StatusRunning -} - -const NodeConfigOK v1.NodeConditionType = "ConfigOK" -const NodeKubeletReady v1.NodeConditionType = "KubeletReady" - -var expectedConditions = map[v1.NodeConditionType]v1.ConditionStatus{ - v1.NodeMemoryPressure: v1.ConditionFalse, - v1.NodeDiskPressure: v1.ConditionFalse, - v1.NodePIDPressure: v1.ConditionFalse, - v1.NodeNetworkUnavailable: v1.ConditionFalse, - NodeConfigOK: v1.ConditionTrue, - NodeKubeletReady: v1.ConditionTrue, - v1.NodeReady: v1.ConditionTrue, -} - -func isUnhealthStatus(condition v1.NodeCondition) bool { - expectedStatus := expectedConditions[condition.Type] - if expectedStatus != "" && condition.Status != expectedStatus { - return true - } - return false -} - -// exactly Match -func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Role: - labelKey := fmt.Sprintf("node-role.kubernetes.io/%s", v) - if _, ok := item.Labels[labelKey]; !ok { - return false - } - case Status: - if getNodeStatus(item) != v { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*nodeSearcher) compare(a, b *v1.Node, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *nodeSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - nodes, err := informers.SharedInformerFactory().Core().V1().Nodes().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Node, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = nodes - } else { - for _, item := range nodes { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/persistentvolumeclaims.go b/pkg/models/resources/persistentvolumeclaims.go deleted file mode 100644 index bdbdeaec2..000000000 --- a/pkg/models/resources/persistentvolumeclaims.go +++ /dev/null @@ -1,156 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type persistentVolumeClaimSearcher struct { -} - -func (*persistentVolumeClaimSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).Get(name) -} - -func pvcStatus(item *v1.PersistentVolumeClaim) string { - status := StatusPending - if item.Status.Phase == v1.ClaimPending { - status = StatusPending - } else if item.Status.Phase == v1.ClaimBound { - status = StatusBound - } else if item.Status.Phase == v1.ClaimLost { - status = StatusLost - } - return status -} - -// exactly Match -func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Status: - statuses := strings.Split(v, "|") - if !sliceutil.HasString(statuses, pvcStatus(item)) { - return false - } - case storageClassName: - if item.Spec.StorageClassName == nil || *item.Spec.StorageClassName != v { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.PersistentVolumeClaim) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*persistentVolumeClaimSearcher) compare(a, b *v1.PersistentVolumeClaim, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *persistentVolumeClaimSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - persistentVolumeClaims, err := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.PersistentVolumeClaim, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = persistentVolumeClaims - } else { - for _, item := range persistentVolumeClaims { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/pods.go b/pkg/models/resources/pods.go deleted file mode 100644 index 8652ca00a..000000000 --- a/pkg/models/resources/pods.go +++ /dev/null @@ -1,274 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - appsv1 "k8s.io/api/apps/v1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type podSearcher struct { -} - -func (*podSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().Pods().Lister().Pods(namespace).Get(name) -} - -func podBelongTo(item *v1.Pod, kind string, name string) bool { - switch kind { - case "Deployment": - if podBelongToDeployment(item, name) { - return true - } - case "ReplicaSet": - if podBelongToReplicaSet(item, name) { - return true - } - case "DaemonSet": - if podBelongToDaemonSet(item, name) { - return true - } - case "StatefulSet": - if podBelongToStatefulSet(item, name) { - return true - } - case "Job": - if podBelongToJob(item, name) { - return true - } - } - return false -} - -func replicaSetBelongToDeployment(replicaSet *appsv1.ReplicaSet, deploymentName string) bool { - for _, owner := range replicaSet.OwnerReferences { - if owner.Kind == "Deployment" && owner.Name == deploymentName { - return true - } - } - return false -} - -func podBelongToDaemonSet(item *v1.Pod, name string) bool { - for _, owner := range item.OwnerReferences { - if owner.Kind == "DaemonSet" && owner.Name == name { - return true - } - } - return false -} - -func podBelongToJob(item *v1.Pod, name string) bool { - for _, owner := range item.OwnerReferences { - if owner.Kind == "Job" && owner.Name == name { - return true - } - } - return false -} - -func podBelongToReplicaSet(item *v1.Pod, replicaSetName string) bool { - for _, owner := range item.OwnerReferences { - if owner.Kind == "ReplicaSet" && owner.Name == replicaSetName { - return true - } - } - return false -} - -func podBelongToStatefulSet(item *v1.Pod, statefulSetName string) bool { - for _, owner := range item.OwnerReferences { - if owner.Kind == "StatefulSet" && owner.Name == statefulSetName { - return true - } - } - return false -} - -func podBelongToDeployment(item *v1.Pod, deploymentName string) bool { - replicas, err := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything()) - if err != nil { - return false - } - - for _, r := range replicas { - if replicaSetBelongToDeployment(r, deploymentName) && podBelongToReplicaSet(item, r.Name) { - return true - } - } - - return false -} - -func podBindPVC(item *v1.Pod, pvcName string) bool { - for _, v := range item.Spec.Volumes { - if v.VolumeSource.PersistentVolumeClaim != nil && - v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { - return true - } - } - return false -} - -func podBelongToService(item *v1.Pod, serviceName string) bool { - service, err := informers.SharedInformerFactory().Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName) - if err != nil { - return false - } - - selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated() - if selector.Empty() || !selector.Matches(labels.Set(item.Labels)) { - return false - } - return true -} - -// exactly Match -func (*podSearcher) match(match map[string]string, item *v1.Pod) bool { - for k, v := range match { - switch k { - case OwnerKind: - fallthrough - case OwnerName: - kind := match[OwnerKind] - name := match[OwnerName] - if !podBelongTo(item, kind, name) { - return false - } - case "nodeName": - if item.Spec.NodeName != v { - return false - } - case "pvcName": - if !podBindPVC(item, v) { - return false - } - case "serviceName": - if !podBelongToService(item, v) { - return false - } - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*podSearcher) compare(a, b *v1.Pod, orderBy string) bool { - switch orderBy { - case StartTime: - if a.Status.StartTime == nil { - return false - } - if b.Status.StartTime == nil { - return true - } - return a.Status.StartTime.Before(b.Status.StartTime) - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *podSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - - pods, err := informers.SharedInformerFactory().Core().V1().Pods().Lister().Pods(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Pod, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = pods - } else { - for _, item := range pods { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/resources.go b/pkg/models/resources/resources.go deleted file mode 100644 index d23fffd5b..000000000 --- a/pkg/models/resources/resources.go +++ /dev/null @@ -1,186 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "strings" -) - -func init() { - resources[ConfigMaps] = &configMapSearcher{} - resources[CronJobs] = &cronJobSearcher{} - resources[DaemonSets] = &daemonSetSearcher{} - resources[Deployments] = &deploymentSearcher{} - resources[Ingresses] = &ingressSearcher{} - resources[Jobs] = &jobSearcher{} - resources[PersistentVolumeClaims] = &persistentVolumeClaimSearcher{} - resources[Secrets] = &secretSearcher{} - resources[Services] = &serviceSearcher{} - resources[StatefulSets] = &statefulSetSearcher{} - resources[Pods] = &podSearcher{} - resources[Roles] = &roleSearcher{} - resources[S2iBuilders] = &s2iBuilderSearcher{} - resources[S2iRuns] = &s2iRunSearcher{} - resources[HorizontalPodAutoscalers] = &hpaSearcher{} - resources[Applications] = &appSearcher{} - - resources[Nodes] = &nodeSearcher{} - resources[Namespaces] = &namespaceSearcher{} - resources[ClusterRoles] = &clusterRoleSearcher{} - resources[StorageClasses] = &storageClassesSearcher{} - resources[S2iBuilderTemplates] = &s2iBuilderTemplateSearcher{} - resources[Workspaces] = &workspaceSearcher{} -} - -var ( - injector = extraAnnotationInjector{} - resources = make(map[string]resourceSearchInterface) - clusterResources = []string{Nodes, Workspaces, Namespaces, ClusterRoles, StorageClasses, S2iBuilderTemplates} -) - -const ( - Name = "name" - Label = "label" - OwnerKind = "ownerKind" - OwnerName = "ownerName" - TargetKind = "targetKind" - TargetName = "targetName" - Role = "role" - CreateTime = "createTime" - UpdateTime = "updateTime" - StartTime = "startTime" - LastScheduleTime = "lastScheduleTime" - chart = "chart" - release = "release" - annotation = "annotation" - Keyword = "keyword" - UserFacing = "userfacing" - Status = "status" - includeCronJob = "includeCronJob" - storageClassName = "storageClassName" - cronJobKind = "CronJob" - s2iRunKind = "S2iRun" - includeS2iRun = "includeS2iRun" - StatusRunning = "running" - StatusPaused = "paused" - StatusPending = "pending" - StatusUpdating = "updating" - StatusStopped = "stopped" - StatusFailed = "failed" - StatusBound = "bound" - StatusLost = "lost" - StatusComplete = "complete" - StatusWarning = "warning" - StatusUnschedulable = "unschedulable" - app = "app" - Deployments = "deployments" - DaemonSets = "daemonsets" - Roles = "roles" - Workspaces = "workspaces" - WorkspaceRoles = "workspaceroles" - CronJobs = "cronjobs" - ConfigMaps = "configmaps" - Ingresses = "ingresses" - Jobs = "jobs" - PersistentVolumeClaims = "persistentvolumeclaims" - Pods = "pods" - Secrets = "secrets" - Services = "services" - StatefulSets = "statefulsets" - HorizontalPodAutoscalers = "horizontalpodautoscalers" - Applications = "applications" - Nodes = "nodes" - Namespaces = "namespaces" - StorageClasses = "storageclasses" - ClusterRoles = "clusterroles" - S2iBuilderTemplates = "s2ibuildertemplates" - S2iBuilders = "s2ibuilders" - S2iRuns = "s2iruns" -) - -type resourceSearchInterface interface { - get(namespace, name string) (interface{}, error) - search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) -} - -func GetResource(namespace, resource, name string) (interface{}, error) { - if searcher, ok := resources[resource]; ok { - resource, err := searcher.get(namespace, name) - if err != nil { - klog.Errorf("resource %s.%s.%s not found: %s", namespace, resource, name, err) - return nil, err - } - return resource, nil - } - return nil, fmt.Errorf("resource %s.%s.%s not found", namespace, resource, name) -} - -func ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { - items := make([]interface{}, 0) - var err error - var result []interface{} - - // none namespace resource - if namespace != "" && sliceutil.HasString(clusterResources, resource) { - err = fmt.Errorf("namespaced resource %s not found", resource) - klog.Errorln(err) - return nil, err - } - - if searcher, ok := resources[resource]; ok { - result, err = searcher.search(namespace, conditions, orderBy, reverse) - } else { - err = fmt.Errorf("namespaced resource %s not found", resource) - klog.Errorln(err) - return nil, err - } - - if err != nil { - klog.Errorln(err) - return nil, err - } - - if limit == -1 || limit+offset > len(result) { - limit = len(result) - offset - } - - result = result[offset : offset+limit] - for _, item := range result { - items = append(items, injector.addExtraAnnotations(item)) - } - - return &models.PageableResponse{TotalCount: len(result), Items: items}, nil -} - -func searchFuzzy(m map[string]string, key, value string) bool { - - val, exist := m[key] - - if value == "" && (!exist || val == "") { - return true - } else if value != "" && strings.Contains(val, value) { - return true - } - - return false -} diff --git a/pkg/models/resources/roles.go b/pkg/models/resources/roles.go deleted file mode 100644 index b08f30ef6..000000000 --- a/pkg/models/resources/roles.go +++ /dev/null @@ -1,145 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - rbac "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type roleSearcher struct { -} - -func (*roleSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).Get(name) -} - -// exactly Match -func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - case UserFacing: - if v == "true" { - if !isUserFacingRole(item) { - return false - } - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*roleSearcher) compare(a, b *rbac.Role, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *roleSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - roles, err := informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*rbac.Role, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = roles - } else { - for _, item := range roles { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} - -// role created by user from kubesphere dashboard -func isUserFacingRole(role *rbac.Role) bool { - if role.Annotations[constants.CreatorAnnotationKey] != "" { - return true - } - return false -} diff --git a/pkg/models/resources/s2ibuilder.go b/pkg/models/resources/s2ibuilder.go deleted file mode 100644 index 4710a6cdf..000000000 --- a/pkg/models/resources/s2ibuilder.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" -) - -type s2iBuilderSearcher struct { -} - -func (*s2iBuilderSearcher) get(namespace, name string) (interface{}, error) { - return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).Get(name) -} - -// exactly Match -func (*s2iBuilderSearcher) match(match map[string]string, item *v1alpha1.S2iBuilder) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*s2iBuilderSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilder) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*s2iBuilderSearcher) compare(a, b *v1alpha1.S2iBuilder, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *s2iBuilderSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - s2iBuilders, err := informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1alpha1.S2iBuilder, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = s2iBuilders - } else { - for _, item := range s2iBuilders { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/s2ibuildertemplate.go b/pkg/models/resources/s2ibuildertemplate.go deleted file mode 100644 index 03c6b469c..000000000 --- a/pkg/models/resources/s2ibuildertemplate.go +++ /dev/null @@ -1,131 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" -) - -type s2iBuilderTemplateSearcher struct { -} - -func (*s2iBuilderTemplateSearcher) get(namespace, name string) (interface{}, error) { - return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilderTemplates().Lister().Get(name) -} - -// exactly Match -func (*s2iBuilderTemplateSearcher) match(match map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*s2iBuilderTemplateSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*s2iBuilderTemplateSearcher) compare(a, b *v1alpha1.S2iBuilderTemplate, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *s2iBuilderTemplateSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - builderTemplates, err := informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iBuilderTemplates().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1alpha1.S2iBuilderTemplate, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = builderTemplates - } else { - for _, item := range builderTemplates { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/s2irun.go b/pkg/models/resources/s2irun.go deleted file mode 100644 index 6d437e225..000000000 --- a/pkg/models/resources/s2irun.go +++ /dev/null @@ -1,142 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - - "k8s.io/apimachinery/pkg/labels" - - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" -) - -type s2iRunSearcher struct { -} - -func (*s2iRunSearcher) get(namespace, name string) (interface{}, error) { - return informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).Get(name) -} - -// exactly Match -func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Status: - if string(item.Status.RunState) != v { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*s2iRunSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iRun) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*s2iRunSearcher) compare(a, b *v1alpha1.S2iRun, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *s2iRunSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - s2iRuns, err := informers.S2iSharedInformerFactory().Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1alpha1.S2iRun, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = s2iRuns - } else { - for _, item := range s2iRuns { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/secrets.go b/pkg/models/resources/secrets.go deleted file mode 100644 index 2fe113af3..000000000 --- a/pkg/models/resources/secrets.go +++ /dev/null @@ -1,139 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type secretSearcher struct { -} - -func (*secretSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().Secrets().Lister().Secrets(namespace).Get(name) -} - -// exactly Match -func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case "type": - if string(item.Type) != v { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*secretSearcher) compare(a, b *v1.Secret, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *secretSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - secrets, err := informers.SharedInformerFactory().Core().V1().Secrets().Lister().Secrets(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Secret, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = secrets - } else { - for _, item := range secrets { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/services.go b/pkg/models/resources/services.go deleted file mode 100644 index f957d5d2b..000000000 --- a/pkg/models/resources/services.go +++ /dev/null @@ -1,135 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type serviceSearcher struct { -} - -func (*serviceSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Core().V1().Services().Lister().Services(namespace).Get(name) -} - -// exactly Match -func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*serviceSearcher) compare(a, b *v1.Service, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *serviceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - services, err := informers.SharedInformerFactory().Core().V1().Services().Lister().Services(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.Service, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = services - } else { - for _, item := range services { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/statefulsets.go b/pkg/models/resources/statefulsets.go deleted file mode 100644 index fd915dcd6..000000000 --- a/pkg/models/resources/statefulsets.go +++ /dev/null @@ -1,153 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type statefulSetSearcher struct { -} - -func (*statefulSetSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name) -} - -func statefulSetStatus(item *v1.StatefulSet) string { - if item.Spec.Replicas != nil { - if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { - return StatusStopped - } else if item.Status.ReadyReplicas == *item.Spec.Replicas { - return StatusRunning - } else { - return StatusUpdating - } - } - return StatusStopped -} - -// Exactly Match -func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - case Status: - if statefulSetStatus(item) != v { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*statefulSetSearcher) fuzzy(fuzzy map[string]string, item *v1.StatefulSet) bool { - - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - - return true -} - -func (*statefulSetSearcher) compare(a, b *v1.StatefulSet, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *statefulSetSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - statefulSets, err := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister().StatefulSets(namespace).List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.StatefulSet, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = statefulSets - } else { - for _, item := range statefulSets { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/storageclasses.go b/pkg/models/resources/storageclasses.go deleted file mode 100644 index 248047978..000000000 --- a/pkg/models/resources/storageclasses.go +++ /dev/null @@ -1,131 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/api/storage/v1" - "k8s.io/apimachinery/pkg/labels" -) - -type storageClassesSearcher struct { -} - -func (*storageClassesSearcher) get(namespace, name string) (interface{}, error) { - return informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().Get(name) -} - -// exactly Match -func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageClass) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*storageClassesSearcher) compare(a, b *v1.StorageClass, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *storageClassesSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - storageClasses, err := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*v1.StorageClass, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = storageClasses - } else { - for _, item := range storageClasses { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/resources/v1alpha2/application/appplications.go b/pkg/models/resources/v1alpha2/application/appplications.go new file mode 100644 index 000000000..c0994134e --- /dev/null +++ b/pkg/models/resources/v1alpha2/application/appplications.go @@ -0,0 +1,90 @@ +/* + * + * Copyright 2019 The KubeSphere 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 application + +import ( + "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type appSearcher struct { + informer externalversions.SharedInformerFactory +} + +func NewApplicationSearcher(informers externalversions.SharedInformerFactory) v1alpha2.Interface { + return &appSearcher{informer: informers} +} + +func (s *appSearcher) Get(namespace, name string) (interface{}, error) { + return s.informer.App().V1beta1().Applications().Lister().Applications(namespace).Get(name) +} + +func (s *appSearcher) match(match map[string]string, item *v1beta1.Application) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *appSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Application) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *appSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + apps, err := s.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1beta1.Application, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = apps + } else { + for _, item := range apps { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go b/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go new file mode 100644 index 000000000..a1722813b --- /dev/null +++ b/pkg/models/resources/v1alpha2/clusterrole/clusterroles.go @@ -0,0 +1,116 @@ +/* + + Copyright 2019 The KubeSphere 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 clusterrole + +import ( + rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" + "sort" +) + +type clusterRoleSearcher struct { + informer informers.SharedInformerFactory +} + +func NewClusterRoleSearcher(informer informers.SharedInformerFactory) v1alpha2.Interface { + return &clusterRoleSearcher{informer: informer} +} + +func (s *clusterRoleSearcher) Get(namespace, name string) (interface{}, error) { + return s.informer.Rbac().V1().ClusterRoles().Lister().Get(name) +} + +func (*clusterRoleSearcher) match(match map[string]string, item *rbac.ClusterRole) bool { + for k, v := range match { + switch k { + case v1alpha2.OwnerKind: + fallthrough + case v1alpha2.OwnerName: + kind := match[v1alpha2.OwnerKind] + name := match[v1alpha2.OwnerName] + if !k8sutil.IsControlledBy(item.OwnerReferences, kind, name) { + return false + } + case v1alpha2.UserFacing: + if v == "true" { + if !isUserFacingClusterRole(item) { + return false + } + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (s *clusterRoleSearcher) fuzzy(fuzzy map[string]string, item *rbac.ClusterRole) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *clusterRoleSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + clusterRoles, err := s.informer.Rbac().V1().ClusterRoles().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*rbac.ClusterRole, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = clusterRoles + } else { + for _, item := range clusterRoles { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} + +// cluster role created by user from kubesphere dashboard +func isUserFacingClusterRole(role *rbac.ClusterRole) bool { + if role.Annotations[constants.CreatorAnnotationKey] != "" && role.Labels[constants.WorkspaceLabelKey] == "" { + return true + } + return false +} diff --git a/pkg/models/resources/v1alpha2/configmap/configmaps.go b/pkg/models/resources/v1alpha2/configmap/configmaps.go new file mode 100644 index 000000000..bcb50fce5 --- /dev/null +++ b/pkg/models/resources/v1alpha2/configmap/configmaps.go @@ -0,0 +1,89 @@ +/* + + Copyright 2019 The KubeSphere 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 configmap + +import ( + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type configMapSearcher struct { + informer informers.SharedInformerFactory +} + +func NewConfigmapSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &configMapSearcher{informer: informers} +} + +func (s *configMapSearcher) Get(namespace, name string) (interface{}, error) { + return s.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) +} + +func (s *configMapSearcher) match(match map[string]string, item *v1.ConfigMap) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *configMapSearcher) fuzzy(fuzzy map[string]string, item *v1.ConfigMap) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *configMapSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + configMaps, err := s.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.ConfigMap, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = configMaps + } else { + for _, item := range configMaps { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/cronjob/cronjobs.go b/pkg/models/resources/v1alpha2/cronjob/cronjobs.go new file mode 100644 index 000000000..1fdbd029c --- /dev/null +++ b/pkg/models/resources/v1alpha2/cronjob/cronjobs.go @@ -0,0 +1,118 @@ +/* + + Copyright 2019 The KubeSphere 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 cronjob + +import ( + "k8s.io/api/batch/v1beta1" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" + + "k8s.io/apimachinery/pkg/labels" +) + +type cronJobSearcher struct { + informer informers.SharedInformerFactory +} + +func NewCronJobSearcher(informer informers.SharedInformerFactory) v1alpha2.Interface { + return &cronJobSearcher{informer: informer} +} + +func (c *cronJobSearcher) Get(namespace, name string) (interface{}, error) { + return c.informer.Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).Get(name) +} + +func cronJobStatus(item *v1beta1.CronJob) string { + if item.Spec.Suspend != nil && *item.Spec.Suspend { + return v1alpha2.StatusPaused + } + return v1alpha2.StatusRunning +} + +func (*cronJobSearcher) match(match map[string]string, item *v1beta1.CronJob) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + if cronJobStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*cronJobSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.CronJob) bool { + + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + + return true +} + +func (*cronJobSearcher) compare(left, right *v1beta1.CronJob, orderBy string) bool { + switch orderBy { + case v1alpha2.LastScheduleTime: + if left.Status.LastScheduleTime == nil { + return true + } + if right.Status.LastScheduleTime == nil { + return false + } + return left.Status.LastScheduleTime.Before(right.Status.LastScheduleTime) + default: + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) + } +} + +func (c *cronJobSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + cronJobs, err := c.informer.Batch().V1beta1().CronJobs().Lister().CronJobs(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1beta1.CronJob, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = cronJobs + } else { + for _, item := range cronJobs { + if c.match(conditions.Match, item) && c.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + return c.compare(result[i], result[j], orderBy) && !reverse + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/daemonset/daemonsets.go b/pkg/models/resources/v1alpha2/daemonset/daemonsets.go new file mode 100644 index 000000000..99705fa6f --- /dev/null +++ b/pkg/models/resources/v1alpha2/daemonset/daemonsets.go @@ -0,0 +1,106 @@ +/* + + Copyright 2019 The KubeSphere 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 daemonset + +import ( + "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type daemonSetSearcher struct { + informer informers.SharedInformerFactory +} + +func NewDaemonSetSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &daemonSetSearcher{informer: informers} +} + +func (c *daemonSetSearcher) Get(namespace, name string) (interface{}, error) { + return c.informer.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name) +} + +func daemonSetStatus(item *v1.DaemonSet) string { + if item.Status.NumberAvailable == 0 { + return v1alpha2.StatusStopped + } else if item.Status.DesiredNumberScheduled == item.Status.NumberAvailable { + return v1alpha2.StatusRunning + } else { + return v1alpha2.StatusUpdating + } +} + +func (*daemonSetSearcher) match(match map[string]string, item *v1.DaemonSet) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + if daemonSetStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*daemonSetSearcher) fuzzy(kv map[string]string, item *v1.DaemonSet) bool { + for k, v := range kv { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (c *daemonSetSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + daemonSets, err := c.informer.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.DaemonSet, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = daemonSets + } else { + for _, item := range daemonSets { + if c.match(conditions.Match, item) && c.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/deployment/deployments.go b/pkg/models/resources/v1alpha2/deployment/deployments.go new file mode 100644 index 000000000..f7bdf783a --- /dev/null +++ b/pkg/models/resources/v1alpha2/deployment/deployments.go @@ -0,0 +1,155 @@ +/* + * + * Copyright 2020 The KubeSphere 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. + * / + */ + +/* + + Copyright 2019 The KubeSphere 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 deployment + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" + "strings" + "time" + + "k8s.io/apimachinery/pkg/labels" + + "k8s.io/api/apps/v1" +) + +type deploymentSearcher struct { + informer informers.SharedInformerFactory +} + +func NewDeploymentSetSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &deploymentSearcher{informer: informers} +} + +func (s *deploymentSearcher) Get(namespace, name string) (interface{}, error) { + return s.informer.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) +} + +func deploymentStatus(item *v1.Deployment) string { + if item.Spec.Replicas != nil { + if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { + return v1alpha2.StatusStopped + } else if item.Status.ReadyReplicas == *item.Spec.Replicas { + return v1alpha2.StatusRunning + } else { + return v1alpha2.StatusUpdating + } + } + return v1alpha2.StatusStopped +} + +func (*deploymentSearcher) match(kv map[string]string, item *v1.Deployment) bool { + for k, v := range kv { + switch k { + case v1alpha2.Status: + if deploymentStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*deploymentSearcher) fuzzy(kv map[string]string, item *v1.Deployment) bool { + for k, v := range kv { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *deploymentSearcher) compare(a, b *v1.Deployment, orderBy string) bool { + switch orderBy { + case v1alpha2.UpdateTime: + aLastUpdateTime := s.lastUpdateTime(a) + bLastUpdateTime := s.lastUpdateTime(b) + if aLastUpdateTime.Equal(bLastUpdateTime) { + return strings.Compare(a.Name, b.Name) <= 0 + } + return aLastUpdateTime.Before(bLastUpdateTime) + default: + return v1alpha2.ObjectMetaCompare(a.ObjectMeta, b.ObjectMeta, orderBy) + } +} + +func (s *deploymentSearcher) lastUpdateTime(deployment *v1.Deployment) time.Time { + lastUpdateTime := deployment.CreationTimestamp.Time + for _, condition := range deployment.Status.Conditions { + if condition.LastUpdateTime.After(lastUpdateTime) { + lastUpdateTime = condition.LastUpdateTime.Time + } + } + return lastUpdateTime +} + +func (s *deploymentSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + deployments, err := s.informer.Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Deployment, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = deployments + } else { + for _, item := range deployments { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return s.compare(result[i], result[j], orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go b/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go new file mode 100644 index 000000000..7aa119776 --- /dev/null +++ b/pkg/models/resources/v1alpha2/hpa/horizontalpodautoscalers.go @@ -0,0 +1,106 @@ +/* + * + * Copyright 2019 The KubeSphere 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 hpa + +import ( + autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type hpaSearcher struct { + informers informers.SharedInformerFactory +} + +func NewHpaSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &hpaSearcher{informers: informers} +} + +func (s *hpaSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Autoscaling().V2beta2().HorizontalPodAutoscalers().Lister().HorizontalPodAutoscalers(namespace).Get(name) +} + +func hpaTargetMatch(item *autoscalingv2beta2.HorizontalPodAutoscaler, kind, name string) bool { + return item.Spec.ScaleTargetRef.Kind == kind && item.Spec.ScaleTargetRef.Name == name +} + +// exactly Match +func (*hpaSearcher) match(match map[string]string, item *autoscalingv2beta2.HorizontalPodAutoscaler) bool { + for k, v := range match { + switch k { + case v1alpha2.TargetKind: + fallthrough + case v1alpha2.TargetName: + kind := match[v1alpha2.TargetKind] + name := match[v1alpha2.TargetName] + if !hpaTargetMatch(item, kind, name) { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*hpaSearcher) fuzzy(fuzzy map[string]string, item *autoscalingv2beta2.HorizontalPodAutoscaler) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *hpaSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + + horizontalPodAutoscalers, err := s.informers.Autoscaling().V2beta2().HorizontalPodAutoscalers().Lister().HorizontalPodAutoscalers(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*autoscalingv2beta2.HorizontalPodAutoscaler, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = horizontalPodAutoscalers + } else { + for _, item := range horizontalPodAutoscalers { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/ingress/ingresses.go b/pkg/models/resources/v1alpha2/ingress/ingresses.go new file mode 100644 index 000000000..9bfc3e9eb --- /dev/null +++ b/pkg/models/resources/v1alpha2/ingress/ingresses.go @@ -0,0 +1,90 @@ +/* + + Copyright 2019 The KubeSphere 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 ingress + +import ( + "k8s.io/api/extensions/v1beta1" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" + + "k8s.io/apimachinery/pkg/labels" +) + +type ingressSearcher struct { + informers informers.SharedInformerFactory +} + +func NewIngressSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &ingressSearcher{informers: informers} +} + +func (s *ingressSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).Get(name) +} + +func (*ingressSearcher) match(match map[string]string, item *v1beta1.Ingress) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*ingressSearcher) fuzzy(fuzzy map[string]string, item *v1beta1.Ingress) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *ingressSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + ingresses, err := s.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1beta1.Ingress, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = ingresses + } else { + for _, item := range ingresses { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/interface.go b/pkg/models/resources/v1alpha2/interface.go new file mode 100644 index 000000000..803cefa0e --- /dev/null +++ b/pkg/models/resources/v1alpha2/interface.go @@ -0,0 +1,152 @@ +package v1alpha2 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "strings" +) + +const ( + App = "app" + Chart = "chart" + Release = "release" + Name = "name" + Label = "label" + OwnerKind = "ownerKind" + OwnerName = "ownerName" + TargetKind = "targetKind" + TargetName = "targetName" + Role = "role" + CreateTime = "createTime" + UpdateTime = "updateTime" + StartTime = "startTime" + LastScheduleTime = "lastScheduleTime" + Annotation = "Annotation" + Keyword = "keyword" + UserFacing = "userfacing" + Status = "status" + Owner = "owner" + + StatusRunning = "running" + StatusPaused = "paused" + StatusPending = "pending" + StatusUpdating = "updating" + StatusStopped = "stopped" + StatusFailed = "failed" + StatusBound = "bound" + StatusLost = "lost" + StatusComplete = "complete" + StatusWarning = "warning" + StatusUnschedulable = "unschedulable" + Deployments = "deployments" + DaemonSets = "daemonsets" + Roles = "roles" + Workspaces = "workspaces" + WorkspaceRoles = "workspaceroles" + CronJobs = "cronjobs" + ConfigMaps = "configmaps" + Ingresses = "ingresses" + Jobs = "jobs" + PersistentVolumeClaims = "persistentvolumeclaims" + Pods = "pods" + Secrets = "secrets" + Services = "services" + StatefulSets = "statefulsets" + HorizontalPodAutoscalers = "horizontalpodautoscalers" + Applications = "applications" + Nodes = "nodes" + Namespaces = "namespaces" + StorageClasses = "storageclasses" + ClusterRoles = "clusterroles" + S2iBuilderTemplates = "s2ibuildertemplates" + S2iBuilders = "s2ibuilders" + S2iRuns = "s2iruns" +) + +type Interface interface { + Get(namespace, name string) (interface{}, error) + Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) +} + +func ObjectMetaExactlyMath(key, value string, item metav1.ObjectMeta) bool { + switch key { + case Name: + names := strings.Split(value, "|") + if !sliceutil.HasString(names, item.Name) { + return false + } + case Keyword: + if !strings.Contains(item.Name, value) && !FuzzyMatch(item.Labels, "", value) && !FuzzyMatch(item.Annotations, "", value) { + return false + } + case Owner: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(string(ownerReference.UID), value) == 0 { + return true + } + } + return false + default: + // label not exist or value not equal + if val, ok := item.Labels[key]; !ok || val != value { + return false + } + } + return true +} + +func ObjectMetaFuzzyMath(key, value string, item metav1.ObjectMeta) bool { + switch key { + case Name: + if !strings.Contains(item.Name, value) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], value) { + return false + } + case Label: + if !FuzzyMatch(item.Labels, "", value) { + return false + } + case Annotation: + if !FuzzyMatch(item.Annotations, "", value) { + return false + } + return false + case App: + if !strings.Contains(item.Labels[Chart], value) && !strings.Contains(item.Labels[Release], value) { + return false + } + default: + if !FuzzyMatch(item.Labels, key, value) { + return false + } + } + return true +} + +func FuzzyMatch(m map[string]string, key, value string) bool { + + val, exist := m[key] + + if value == "" && (!exist || val == "") { + return true + } else if value != "" && strings.Contains(val, value) { + return true + } + + return false +} + +func ObjectMetaCompare(left, right metav1.ObjectMeta, compareField string) bool { + switch compareField { + case CreateTime: + if left.CreationTimestamp.Equal(&right.CreationTimestamp) { + return strings.Compare(left.Name, right.Name) <= 0 + } + return left.CreationTimestamp.Time.Before(right.CreationTimestamp.Time) + case Name: + fallthrough + default: + return strings.Compare(left.Name, right.Name) <= 0 + } +} diff --git a/pkg/models/resources/v1alpha2/job/jobs.go b/pkg/models/resources/v1alpha2/job/jobs.go new file mode 100644 index 000000000..9530d6c7b --- /dev/null +++ b/pkg/models/resources/v1alpha2/job/jobs.go @@ -0,0 +1,149 @@ +/* + + Copyright 2019 The KubeSphere 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 job + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/k8sutil" + "sort" + "time" + + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/labels" +) + +const ( + includeCronJob = "includeCronJob" + cronJobKind = "CronJob" + s2iRunKind = "S2iRun" + includeS2iRun = "includeS2iRun" +) + +type jobSearcher struct { + informers informers.SharedInformerFactory +} + +func NewJobSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &jobSearcher{informers: informers} +} + +func (s *jobSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Batch().V1().Jobs().Lister().Jobs(namespace).Get(name) +} + +func jobStatus(item *batchv1.Job) string { + status := v1alpha2.StatusFailed + if item.Status.Active > 0 { + status = v1alpha2.StatusRunning + } else if item.Status.Failed > 0 { + status = v1alpha2.StatusFailed + } else if item.Status.Succeeded > 0 { + status = v1alpha2.StatusComplete + } + return status +} + +func (*jobSearcher) match(match map[string]string, item *batchv1.Job) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + if jobStatus(item) != v { + return false + } + case includeCronJob: + if v == "false" && k8sutil.IsControlledBy(item.OwnerReferences, cronJobKind, "") { + return false + } + case includeS2iRun: + if v == "false" && k8sutil.IsControlledBy(item.OwnerReferences, s2iRunKind, "") { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*jobSearcher) fuzzy(fuzzy map[string]string, item *batchv1.Job) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func jobUpdateTime(item *batchv1.Job) time.Time { + updateTime := item.CreationTimestamp.Time + for _, condition := range item.Status.Conditions { + if updateTime.Before(condition.LastProbeTime.Time) { + updateTime = condition.LastProbeTime.Time + } + if updateTime.Before(condition.LastTransitionTime.Time) { + updateTime = condition.LastTransitionTime.Time + } + } + return updateTime +} + +func (*jobSearcher) compare(left, right *batchv1.Job, orderBy string) bool { + switch orderBy { + case v1alpha2.UpdateTime: + return jobUpdateTime(left).Before(jobUpdateTime(right)) + default: + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) + } +} + +func (s *jobSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + jobs, err := s.informers.Batch().V1().Jobs().Lister().Jobs(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*batchv1.Job, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = jobs + } else { + for _, item := range jobs { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return s.compare(result[i], result[j], orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/namespace/namespaces.go b/pkg/models/resources/v1alpha2/namespace/namespaces.go new file mode 100644 index 000000000..1faf5987d --- /dev/null +++ b/pkg/models/resources/v1alpha2/namespace/namespaces.go @@ -0,0 +1,90 @@ +/* + + Copyright 2019 The KubeSphere 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 namespace + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type namespaceSearcher struct { + informers informers.SharedInformerFactory +} + +func NewNamespaceSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &namespaceSearcher{informers: informers} +} + +func (s *namespaceSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Core().V1().Namespaces().Lister().Get(name) +} + +func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *namespaceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + namespaces, err := s.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Namespace, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = namespaces + } else { + for _, item := range namespaces { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/node/nodes.go b/pkg/models/resources/v1alpha2/node/nodes.go new file mode 100644 index 000000000..62865a311 --- /dev/null +++ b/pkg/models/resources/v1alpha2/node/nodes.go @@ -0,0 +1,139 @@ +/* + + Copyright 2019 The KubeSphere 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 node + +import ( + "fmt" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +const ( + nodeConfigOK v1.NodeConditionType = "ConfigOK" + nodeKubeletReady v1.NodeConditionType = "KubeletReady" +) + +type nodeSearcher struct { + informers informers.SharedInformerFactory +} + +func NewNodeSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &nodeSearcher{informers: informers} +} + +func (s *nodeSearcher) Get(_, name string) (interface{}, error) { + return s.informers.Core().V1().Nodes().Lister().Get(name) +} + +func getNodeStatus(node *v1.Node) string { + if node.Spec.Unschedulable { + return v1alpha2.StatusUnschedulable + } + for _, condition := range node.Status.Conditions { + if isUnhealthyStatus(condition) { + return v1alpha2.StatusWarning + } + } + + return v1alpha2.StatusRunning +} + +var expectedConditions = map[v1.NodeConditionType]v1.ConditionStatus{ + v1.NodeMemoryPressure: v1.ConditionFalse, + v1.NodeDiskPressure: v1.ConditionFalse, + v1.NodePIDPressure: v1.ConditionFalse, + v1.NodeNetworkUnavailable: v1.ConditionFalse, + nodeConfigOK: v1.ConditionTrue, + nodeKubeletReady: v1.ConditionTrue, + v1.NodeReady: v1.ConditionTrue, +} + +func isUnhealthyStatus(condition v1.NodeCondition) bool { + expectedStatus := expectedConditions[condition.Type] + if expectedStatus != "" && condition.Status != expectedStatus { + return true + } + return false +} + +func (*nodeSearcher) match(match map[string]string, item *v1.Node) bool { + for k, v := range match { + switch k { + case v1alpha2.Role: + labelKey := fmt.Sprintf("node-role.kubernetes.io/%s", v) + if _, ok := item.Labels[labelKey]; !ok { + return false + } + case v1alpha2.Status: + if getNodeStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*nodeSearcher) fuzzy(fuzzy map[string]string, item *v1.Node) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *nodeSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + nodes, err := s.informers.Core().V1().Nodes().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Node, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = nodes + } else { + for _, item := range nodes { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go b/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go new file mode 100644 index 000000000..516217861 --- /dev/null +++ b/pkg/models/resources/v1alpha2/persistentvolumeclaim/persistentvolumeclaims.go @@ -0,0 +1,144 @@ +/* + + Copyright 2019 The KubeSphere 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 persistentvolumeclaim + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "strconv" + + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" + "sort" + "strings" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" +) + +const ( + storageClassName = "storageClassName" +) + +type persistentVolumeClaimSearcher struct { + informers informers.SharedInformerFactory +} + +func NewPersistentVolumeClaimSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &persistentVolumeClaimSearcher{informers: informers} +} + +func (s *persistentVolumeClaimSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).Get(name) +} + +func pvcStatus(item *v1.PersistentVolumeClaim) string { + status := v1alpha2.StatusPending + if item.Status.Phase == v1.ClaimPending { + status = v1alpha2.StatusPending + } else if item.Status.Phase == v1.ClaimBound { + status = v1alpha2.StatusBound + } else if item.Status.Phase == v1.ClaimLost { + status = v1alpha2.StatusLost + } + return status +} + +func (*persistentVolumeClaimSearcher) match(match map[string]string, item *v1.PersistentVolumeClaim) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + statuses := strings.Split(v, "|") + if !sliceutil.HasString(statuses, pvcStatus(item)) { + return false + } + case storageClassName: + if item.Spec.StorageClassName == nil || *item.Spec.StorageClassName != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*persistentVolumeClaimSearcher) fuzzy(fuzzy map[string]string, item *v1.PersistentVolumeClaim) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *persistentVolumeClaimSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + persistentVolumeClaims, err := s.informers.Core().V1().PersistentVolumeClaims().Lister().PersistentVolumeClaims(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.PersistentVolumeClaim, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = persistentVolumeClaims + } else { + for _, item := range persistentVolumeClaims { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + inUse := s.countPods(i.Name, i.Namespace) + if i.Annotations == nil { + i.Annotations = make(map[string]string) + } + i.Annotations["kubesphere.io/in-use"] = strconv.FormatBool(inUse) + + r = append(r, i) + } + return r, nil +} + +func (s *persistentVolumeClaimSearcher) countPods(name, namespace string) bool { + pods, err := s.informers.Core().V1().Pods().Lister().Pods(namespace).List(labels.Everything()) + if err != nil { + return false + } + for _, pod := range pods { + for _, pvc := range pod.Spec.Volumes { + if pvc.PersistentVolumeClaim != nil && pvc.PersistentVolumeClaim.ClaimName == name { + return true + } + } + } + + return false +} diff --git a/pkg/models/resources/v1alpha2/pod/pods.go b/pkg/models/resources/v1alpha2/pod/pods.go new file mode 100644 index 000000000..fa980b46c --- /dev/null +++ b/pkg/models/resources/v1alpha2/pod/pods.go @@ -0,0 +1,239 @@ +/* + + Copyright 2019 The KubeSphere 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 pod + +import ( + appsv1 "k8s.io/api/apps/v1" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type podSearcher struct { + informers informers.SharedInformerFactory +} + +func NewPodSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &podSearcher{informers: informers} +} + +func (s *podSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Core().V1().Pods().Lister().Pods(namespace).Get(name) +} + +func (s *podSearcher) podBelongTo(item *v1.Pod, kind string, name string) bool { + switch kind { + case "Deployment": + if s.podBelongToDeployment(item, name) { + return true + } + case "ReplicaSet": + if podBelongToReplicaSet(item, name) { + return true + } + case "DaemonSet": + if podBelongToDaemonSet(item, name) { + return true + } + case "StatefulSet": + if podBelongToStatefulSet(item, name) { + return true + } + case "Job": + if podBelongToJob(item, name) { + return true + } + } + return false +} + +func replicaSetBelongToDeployment(replicaSet *appsv1.ReplicaSet, deploymentName string) bool { + for _, owner := range replicaSet.OwnerReferences { + if owner.Kind == "Deployment" && owner.Name == deploymentName { + return true + } + } + return false +} + +func podBelongToDaemonSet(item *v1.Pod, name string) bool { + for _, owner := range item.OwnerReferences { + if owner.Kind == "DaemonSet" && owner.Name == name { + return true + } + } + return false +} + +func podBelongToJob(item *v1.Pod, name string) bool { + for _, owner := range item.OwnerReferences { + if owner.Kind == "Job" && owner.Name == name { + return true + } + } + return false +} + +func podBelongToReplicaSet(item *v1.Pod, replicaSetName string) bool { + for _, owner := range item.OwnerReferences { + if owner.Kind == "ReplicaSet" && owner.Name == replicaSetName { + return true + } + } + return false +} + +func podBelongToStatefulSet(item *v1.Pod, statefulSetName string) bool { + for _, owner := range item.OwnerReferences { + if owner.Kind == "StatefulSet" && owner.Name == statefulSetName { + return true + } + } + return false +} + +func (s *podSearcher) podBelongToDeployment(item *v1.Pod, deploymentName string) bool { + replicas, err := s.informers.Apps().V1().ReplicaSets().Lister().ReplicaSets(item.Namespace).List(labels.Everything()) + if err != nil { + return false + } + + for _, r := range replicas { + if replicaSetBelongToDeployment(r, deploymentName) && podBelongToReplicaSet(item, r.Name) { + return true + } + } + + return false +} + +func podBindPVC(item *v1.Pod, pvcName string) bool { + for _, v := range item.Spec.Volumes { + if v.VolumeSource.PersistentVolumeClaim != nil && + v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { + return true + } + } + return false +} + +func (s *podSearcher) podBelongToService(item *v1.Pod, serviceName string) bool { + service, err := s.informers.Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName) + if err != nil { + return false + } + + selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated() + if selector.Empty() || !selector.Matches(labels.Set(item.Labels)) { + return false + } + return true +} + +func (s *podSearcher) match(match map[string]string, item *v1.Pod) bool { + for k, v := range match { + switch k { + case v1alpha2.OwnerKind: + fallthrough + case v1alpha2.OwnerName: + kind := match[v1alpha2.OwnerKind] + name := match[v1alpha2.OwnerName] + if !s.podBelongTo(item, kind, name) { + return false + } + case "nodeName": + if item.Spec.NodeName != v { + return false + } + case "pvcName": + if !podBindPVC(item, v) { + return false + } + case "serviceName": + if !s.podBelongToService(item, v) { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*podSearcher) fuzzy(fuzzy map[string]string, item *v1.Pod) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*podSearcher) compare(left, right *v1.Pod, orderBy string) bool { + switch orderBy { + case v1alpha2.StartTime: + if left.Status.StartTime == nil { + return false + } + if right.Status.StartTime == nil { + return true + } + return left.Status.StartTime.Before(right.Status.StartTime) + default: + return v1alpha2.ObjectMetaCompare(left.ObjectMeta, right.ObjectMeta, orderBy) + } +} + +func (s *podSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + + pods, err := s.informers.Core().V1().Pods().Lister().Pods(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Pod, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = pods + } else { + for _, item := range pods { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return s.compare(result[i], result[j], orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/resource/resource_test.go b/pkg/models/resources/v1alpha2/resource/resource_test.go new file mode 100644 index 000000000..c55ed5555 --- /dev/null +++ b/pkg/models/resources/v1alpha2/resource/resource_test.go @@ -0,0 +1,236 @@ +/* + * + * Copyright 2020 The KubeSphere 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 resource + +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "testing" +) + +func TestConditions(t *testing.T) { + factory, err := prepare() + if err != nil { + t.Fatal(err) + } + resource := NewResourceGetter(factory) + + tests := []struct { + Name string + Namespace string + Resource string + Conditions *params.Conditions + OrderBy string + Reverse bool + Limit int + Offset int + ExpectResponse *models.PageableResponse + ExpectError error + }{{ + Name: "list namespace order by name asc", + Namespace: "", + Resource: "namespaces", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: false, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{defaultNamespace, kubesphereNamespace}, + TotalCount: 2, + }, + ExpectError: nil, + }, { + Name: "list namespace order by name desc", + Namespace: "", + Resource: "namespaces", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{kubesphereNamespace, defaultNamespace}, + TotalCount: 2, + }, + ExpectError: nil, + }, + { + Name: "list deployment", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{}, + OrderBy: "name", + Reverse: false, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment, redisDeployment}, + TotalCount: 2, + }, + ExpectError: nil, + }, + { + Name: "filter deployment by keyword", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{v1alpha2.Keyword: "ngin"}, + Fuzzy: nil, + }, + OrderBy: "name", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, + { + Name: "filter deployment by label", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{"kubesphere.io/creator": "admin"}, + Fuzzy: nil, + }, + OrderBy: "", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{redisDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, { + Name: "filter deployment by status", + Namespace: "default", + Resource: "deployments", + Conditions: ¶ms.Conditions{ + Match: map[string]string{v1alpha2.Status: v1alpha2.StatusRunning}, + Fuzzy: nil, + }, + OrderBy: "", + Reverse: true, + Limit: 10, + Offset: 0, + ExpectResponse: &models.PageableResponse{ + Items: []interface{}{nginxDeployment}, + TotalCount: 1, + }, + ExpectError: nil, + }, + } + + for _, test := range tests { + response, err := resource.ListResources(test.Namespace, test.Resource, test.Conditions, test.OrderBy, test.Reverse, test.Limit, test.Offset) + if err != test.ExpectError { + t.Fatalf("expected error: %s, got: %s", test.ExpectError, err) + } + if diff := cmp.Diff(test.ExpectResponse, response); diff != "" { + t.Errorf(diff) + } + } + +} + +var ( + defaultNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + kubesphereNamespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "kubesphere-system", + Labels: map[string]string{"kubesphere.io/workspace": "system-workspace"}, + }, + } + + replicas = int32(1) + + nginxDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx", + Namespace: "default", + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 1, + }, + } + redisDeployment = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "redis", + Namespace: "default", + Labels: map[string]string{"kubesphere.io/creator": "admin"}, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + }, + Status: appsv1.DeploymentStatus{ + ReadyReplicas: 0, + }, + } +) + +func prepare() (informers.InformerFactory, error) { + + namespaces := []interface{}{defaultNamespace, kubesphereNamespace} + deployments := []interface{}{nginxDeployment, redisDeployment} + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + k8sInformerFactory := fakeInformerFactory.KubernetesSharedInformerFactory() + + for _, namespace := range namespaces { + err := k8sInformerFactory.Core().V1().Namespaces().Informer().GetIndexer().Add(namespace) + if err != nil { + return nil, err + } + } + for _, deployment := range deployments { + err := k8sInformerFactory.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + if err != nil { + return nil, err + } + } + + return fakeInformerFactory, nil +} diff --git a/pkg/models/resources/v1alpha2/resource/resources.go b/pkg/models/resources/v1alpha2/resource/resources.go new file mode 100644 index 000000000..ae0a709fd --- /dev/null +++ b/pkg/models/resources/v1alpha2/resource/resources.go @@ -0,0 +1,143 @@ +/* + + Copyright 2019 The KubeSphere 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 resource + +import ( + "fmt" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/application" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/clusterrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/configmap" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/cronjob" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/daemonset" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/deployment" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/hpa" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/ingress" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/job" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/namespace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/node" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/persistentvolumeclaim" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/pod" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/role" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/s2buildertemplate" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/s2ibuilder" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/s2irun" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/secret" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/service" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/statefulset" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/storageclass" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/workspace" + "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/utils/sliceutil" +) + +type ResourceGetter struct { + resourcesGetters map[string]v1alpha2.Interface +} + +func (r ResourceGetter) Add(resource string, getter v1alpha2.Interface) { + if r.resourcesGetters == nil { + r.resourcesGetters = make(map[string]v1alpha2.Interface) + } + r.resourcesGetters[resource] = getter +} + +func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { + resourceGetters := make(map[string]v1alpha2.Interface) + + resourceGetters[v1alpha2.ConfigMaps] = configmap.NewConfigmapSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.CronJobs] = cronjob.NewCronJobSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.DaemonSets] = daemonset.NewDaemonSetSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Deployments] = deployment.NewDeploymentSetSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Ingresses] = ingress.NewIngressSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Jobs] = job.NewJobSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.PersistentVolumeClaims] = persistentvolumeclaim.NewPersistentVolumeClaimSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Secrets] = secret.NewSecretSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Services] = service.NewServiceSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.StatefulSets] = statefulset.NewStatefulSetSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Pods] = pod.NewPodSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Roles] = role.NewRoleSearcher(factory.KubernetesSharedInformerFactory()) + + resourceGetters[v1alpha2.Nodes] = node.NewNodeSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.Namespaces] = namespace.NewNamespaceSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.ClusterRoles] = clusterrole.NewClusterRoleSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.StorageClasses] = storageclass.NewStorageClassesSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.HorizontalPodAutoscalers] = hpa.NewHpaSearcher(factory.KubernetesSharedInformerFactory()) + resourceGetters[v1alpha2.S2iBuilders] = s2ibuilder.NewS2iBuilderSearcher(factory.KubeSphereSharedInformerFactory()) + resourceGetters[v1alpha2.S2iRuns] = s2irun.NewS2iRunSearcher(factory.KubeSphereSharedInformerFactory()) + resourceGetters[v1alpha2.S2iBuilderTemplates] = s2buildertemplate.NewS2iBuidlerTemplateSearcher(factory.KubeSphereSharedInformerFactory()) + resourceGetters[v1alpha2.Workspaces] = workspace.NewWorkspaceSearcher(factory.KubeSphereSharedInformerFactory()) + resourceGetters[v1alpha2.Applications] = application.NewApplicationSearcher(factory.ApplicationSharedInformerFactory()) + + return &ResourceGetter{resourcesGetters: resourceGetters} + +} + +var ( + //injector = v1alpha2.extraAnnotationInjector{} + clusterResources = []string{v1alpha2.Nodes, v1alpha2.Workspaces, v1alpha2.Namespaces, v1alpha2.ClusterRoles, v1alpha2.StorageClasses, v1alpha2.S2iBuilderTemplates} +) + +func (r *ResourceGetter) GetResource(namespace, resource, name string) (interface{}, error) { + if searcher, ok := r.resourcesGetters[resource]; ok { + resource, err := searcher.Get(namespace, name) + if err != nil { + klog.Errorf("resource %s.%s.%s not found: %s", namespace, resource, name, err) + return nil, err + } + return resource, nil + } + return nil, fmt.Errorf("resource %s.%s.%s not found", namespace, resource, name) +} + +func (r *ResourceGetter) ListResources(namespace, resource string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + items := make([]interface{}, 0) + var err error + var result []interface{} + + // none namespace resource + if namespace != "" && sliceutil.HasString(clusterResources, resource) { + err = fmt.Errorf("resource %s is not supported", resource) + klog.Errorln(err) + return nil, err + } + + if searcher, ok := r.resourcesGetters[resource]; ok { + result, err = searcher.Search(namespace, conditions, orderBy, reverse) + } else { + err = fmt.Errorf("resource %s is not supported", resource) + klog.Errorln(err) + return nil, err + } + + if err != nil { + klog.Errorln(err) + return nil, err + } + + if limit == -1 || limit+offset > len(result) { + limit = len(result) - offset + } + + items = result[offset : offset+limit] + + return &models.PageableResponse{TotalCount: len(result), Items: items}, nil +} diff --git a/pkg/models/resources/v1alpha2/role/roles.go b/pkg/models/resources/v1alpha2/role/roles.go new file mode 100644 index 000000000..b045a28ff --- /dev/null +++ b/pkg/models/resources/v1alpha2/role/roles.go @@ -0,0 +1,108 @@ +/* + + Copyright 2019 The KubeSphere 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 role + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type roleSearcher struct { + informers informers.SharedInformerFactory +} + +func NewRoleSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &roleSearcher{informers: informers} +} + +func (s *roleSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Rbac().V1().Roles().Lister().Roles(namespace).Get(name) +} + +func (*roleSearcher) match(match map[string]string, item *rbac.Role) bool { + for k, v := range match { + switch k { + case v1alpha2.UserFacing: + if v == "true" { + if !isUserFacingRole(item) { + return false + } + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*roleSearcher) fuzzy(fuzzy map[string]string, item *rbac.Role) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *roleSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + roles, err := s.informers.Rbac().V1().Roles().Lister().Roles(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*rbac.Role, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = roles + } else { + for _, item := range roles { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} + +// role created by user from kubesphere dashboard +func isUserFacingRole(role *rbac.Role) bool { + if role.Annotations[constants.CreatorAnnotationKey] != "" { + return true + } + return false +} diff --git a/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go b/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go new file mode 100644 index 000000000..b2d81c93d --- /dev/null +++ b/pkg/models/resources/v1alpha2/s2buildertemplate/s2ibuildertemplate.go @@ -0,0 +1,90 @@ +/* + + Copyright 2019 The KubeSphere 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 s2buildertemplate + +import ( + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type s2iBuilderTemplateSearcher struct { + informers externalversions.SharedInformerFactory +} + +func NewS2iBuidlerTemplateSearcher(informers externalversions.SharedInformerFactory) v1alpha2.Interface { + return &s2iBuilderTemplateSearcher{informers: informers} +} + +func (s *s2iBuilderTemplateSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Devops().V1alpha1().S2iBuilderTemplates().Lister().Get(name) +} + +func (*s2iBuilderTemplateSearcher) match(match map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*s2iBuilderTemplateSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilderTemplate) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *s2iBuilderTemplateSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + builderTemplates, err := s.informers.Devops().V1alpha1().S2iBuilderTemplates().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1alpha1.S2iBuilderTemplate, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = builderTemplates + } else { + for _, item := range builderTemplates { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go b/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go new file mode 100644 index 000000000..2f99f721e --- /dev/null +++ b/pkg/models/resources/v1alpha2/s2ibuilder/s2ibuilder.go @@ -0,0 +1,91 @@ +/* + + Copyright 2019 The KubeSphere 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 s2ibuilder + +import ( + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type s2iBuilderSearcher struct { + informers externalversions.SharedInformerFactory +} + +func NewS2iBuilderSearcher(informers externalversions.SharedInformerFactory) v1alpha2.Interface { + return &s2iBuilderSearcher{informers: informers} +} + +func (s *s2iBuilderSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).Get(name) +} + +func (*s2iBuilderSearcher) match(match map[string]string, item *v1alpha1.S2iBuilder) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*s2iBuilderSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iBuilder) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *s2iBuilderSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + s2iBuilders, err := s.informers.Devops().V1alpha1().S2iBuilders().Lister().S2iBuilders(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1alpha1.S2iBuilder, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = s2iBuilders + } else { + for _, item := range s2iBuilders { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/s2irun/s2irun.go b/pkg/models/resources/v1alpha2/s2irun/s2irun.go new file mode 100644 index 000000000..47997fa72 --- /dev/null +++ b/pkg/models/resources/v1alpha2/s2irun/s2irun.go @@ -0,0 +1,97 @@ +/* + + Copyright 2019 The KubeSphere 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 s2irun + +import ( + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type s2iRunSearcher struct { + informers externalversions.SharedInformerFactory +} + +func NewS2iRunSearcher(informers externalversions.SharedInformerFactory) v1alpha2.Interface { + return &s2iRunSearcher{informers: informers} +} + +func (s *s2iRunSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).Get(name) +} + +func (*s2iRunSearcher) match(match map[string]string, item *v1alpha1.S2iRun) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + if string(item.Status.RunState) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*s2iRunSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.S2iRun) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *s2iRunSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + s2iRuns, err := s.informers.Devops().V1alpha1().S2iRuns().Lister().S2iRuns(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1alpha1.S2iRun, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = s2iRuns + } else { + for _, item := range s2iRuns { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/secret/secrets.go b/pkg/models/resources/v1alpha2/secret/secrets.go new file mode 100644 index 000000000..f7b115549 --- /dev/null +++ b/pkg/models/resources/v1alpha2/secret/secrets.go @@ -0,0 +1,97 @@ +/* + + Copyright 2019 The KubeSphere 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 secret + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type secretSearcher struct { + informers informers.SharedInformerFactory +} + +func NewSecretSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &secretSearcher{informers: informers} +} + +func (s *secretSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Core().V1().Secrets().Lister().Secrets(namespace).Get(name) +} + +func (*secretSearcher) match(match map[string]string, item *v1.Secret) bool { + for k, v := range match { + switch k { + case "type": + if string(item.Type) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*secretSearcher) fuzzy(fuzzy map[string]string, item *v1.Secret) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *secretSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + secrets, err := s.informers.Core().V1().Secrets().Lister().Secrets(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Secret, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = secrets + } else { + for _, item := range secrets { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/service/services.go b/pkg/models/resources/v1alpha2/service/services.go new file mode 100644 index 000000000..cf0f0930b --- /dev/null +++ b/pkg/models/resources/v1alpha2/service/services.go @@ -0,0 +1,90 @@ +/* + + Copyright 2019 The KubeSphere 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 service + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type serviceSearcher struct { + informers informers.SharedInformerFactory +} + +func NewServiceSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &serviceSearcher{informers: informers} +} + +func (s *serviceSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Core().V1().Services().Lister().Services(namespace).Get(name) +} + +func (*serviceSearcher) match(match map[string]string, item *v1.Service) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*serviceSearcher) fuzzy(fuzzy map[string]string, item *v1.Service) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *serviceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + services, err := s.informers.Core().V1().Services().Lister().Services(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.Service, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = services + } else { + for _, item := range services { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/statefulset/statefulsets.go b/pkg/models/resources/v1alpha2/statefulset/statefulsets.go new file mode 100644 index 000000000..c2e8ffee2 --- /dev/null +++ b/pkg/models/resources/v1alpha2/statefulset/statefulsets.go @@ -0,0 +1,110 @@ +/* + + Copyright 2019 The KubeSphere 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 statefulset + +import ( + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type statefulSetSearcher struct { + informers informers.SharedInformerFactory +} + +func NewStatefulSetSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &statefulSetSearcher{informers: informers} +} + +func (s *statefulSetSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name) +} + +func statefulSetStatus(item *v1.StatefulSet) string { + if item.Spec.Replicas != nil { + if item.Status.ReadyReplicas == 0 && *item.Spec.Replicas == 0 { + return v1alpha2.StatusStopped + } else if item.Status.ReadyReplicas == *item.Spec.Replicas { + return v1alpha2.StatusRunning + } else { + return v1alpha2.StatusUpdating + } + } + return v1alpha2.StatusStopped +} + +func (*statefulSetSearcher) match(match map[string]string, item *v1.StatefulSet) bool { + for k, v := range match { + switch k { + case v1alpha2.Status: + if statefulSetStatus(item) != v { + return false + } + default: + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + } + return true +} + +func (*statefulSetSearcher) fuzzy(fuzzy map[string]string, item *v1.StatefulSet) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *statefulSetSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + statefulSets, err := s.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.StatefulSet, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = statefulSets + } else { + for _, item := range statefulSets { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha2/storageclass/storageclasses.go b/pkg/models/resources/v1alpha2/storageclass/storageclasses.go new file mode 100644 index 000000000..a92d7606c --- /dev/null +++ b/pkg/models/resources/v1alpha2/storageclass/storageclasses.go @@ -0,0 +1,112 @@ +/* + + Copyright 2019 The KubeSphere 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 storageclass + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/api/storage/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type storageClassesSearcher struct { + informers informers.SharedInformerFactory +} + +func NewStorageClassesSearcher(informers informers.SharedInformerFactory) v1alpha2.Interface { + return &storageClassesSearcher{informers: informers} +} + +func (s *storageClassesSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Storage().V1().StorageClasses().Lister().Get(name) +} + +func (*storageClassesSearcher) match(match map[string]string, item *v1.StorageClass) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*storageClassesSearcher) fuzzy(fuzzy map[string]string, item *v1.StorageClass) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *storageClassesSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + storageClasses, err := s.informers.Storage().V1().StorageClasses().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*v1.StorageClass, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = storageClasses + } else { + for _, item := range storageClasses { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + count := s.countPersistentVolumeClaims(i.Name) + if i.Annotations == nil { + i.Annotations = make(map[string]string) + i.Annotations["kubesphere.io/pvc-count"] = string(count) + } + + r = append(r, i) + } + return r, nil +} + +func (s *storageClassesSearcher) countPersistentVolumeClaims(name string) int { + pvcs, err := s.informers.Core().V1().PersistentVolumeClaims().Lister().List(labels.Everything()) + if err != nil { + return 0 + } + var count int + + for _, pvc := range pvcs { + if *pvc.Spec.StorageClassName == name || (pvc.Annotations != nil && pvc.Annotations[corev1.BetaStorageClassAnnotation] == name) { + count++ + } + } + + return count +} diff --git a/pkg/models/resources/v1alpha2/workspace/workspaces.go b/pkg/models/resources/v1alpha2/workspace/workspaces.go new file mode 100644 index 000000000..77a15af4f --- /dev/null +++ b/pkg/models/resources/v1alpha2/workspace/workspaces.go @@ -0,0 +1,91 @@ +/* + + Copyright 2019 The KubeSphere 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 workspace + +import ( + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha2" + + "k8s.io/apimachinery/pkg/labels" + "kubesphere.io/kubesphere/pkg/server/params" + "sort" +) + +type workspaceSearcher struct { + informers externalversions.SharedInformerFactory +} + +func NewWorkspaceSearcher(informers externalversions.SharedInformerFactory) v1alpha2.Interface { + return &workspaceSearcher{informers: informers} +} + +func (s *workspaceSearcher) Get(namespace, name string) (interface{}, error) { + return s.informers.Tenant().V1alpha1().Workspaces().Lister().Get(name) +} + +func (*workspaceSearcher) match(match map[string]string, item *tenantv1alpha1.Workspace) bool { + for k, v := range match { + if !v1alpha2.ObjectMetaExactlyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *tenantv1alpha1.Workspace) bool { + for k, v := range fuzzy { + if !v1alpha2.ObjectMetaFuzzyMath(k, v, item.ObjectMeta) { + return false + } + } + return true +} + +func (s *workspaceSearcher) Search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { + + workspaces, err := s.informers.Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) + + if err != nil { + return nil, err + } + + result := make([]*tenantv1alpha1.Workspace, 0) + + if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { + result = workspaces + } else { + for _, item := range workspaces { + if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { + result = append(result, item) + } + } + } + sort.Slice(result, func(i, j int) bool { + if reverse { + i, j = j, i + } + return v1alpha2.ObjectMetaCompare(result[i].ObjectMeta, result[j].ObjectMeta, orderBy) + }) + + r := make([]interface{}, 0) + for _, i := range result { + r = append(r, i) + } + return r, nil +} diff --git a/pkg/models/resources/v1alpha3/application/applications.go b/pkg/models/resources/v1alpha3/application/applications.go new file mode 100644 index 000000000..aabc517b8 --- /dev/null +++ b/pkg/models/resources/v1alpha3/application/applications.go @@ -0,0 +1,94 @@ +/* + + Copyright 2019 The KubeSphere 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 application + +import ( + appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "time" +) + +type applicationsGetter struct { + informer externalversions.SharedInformerFactory +} + +func New(sharedInformers externalversions.SharedInformerFactory) v1alpha3.Interface { + return &applicationsGetter{informer: sharedInformers} +} + +func (d *applicationsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.App().V1beta1().Applications().Lister().Applications(namespace).Get(name) +} + +func (d *applicationsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *applicationsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftApplication, ok := left.(*appv1beta1.Application) + if !ok { + return false + } + + rightApplication, ok := right.(*appv1beta1.Application) + if !ok { + return false + } + switch field { + case query.FieldUpdateTime: + fallthrough + case query.FieldLastUpdateTimestamp: + return lastUpdateTime(leftApplication).After(lastUpdateTime(rightApplication)) + default: + return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field) + } +} + +func (d *applicationsGetter) filter(object runtime.Object, filter query.Filter) bool { + application, ok := object.(*appv1beta1.Application) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(application.ObjectMeta, filter) +} + +func lastUpdateTime(application *appv1beta1.Application) time.Time { + lut := application.CreationTimestamp.Time + for _, condition := range application.Status.Conditions { + if condition.LastUpdateTime.After(lut) { + lut = condition.LastUpdateTime.Time + } + } + return lut +} diff --git a/pkg/models/resources/v1alpha3/application/applications_test.go b/pkg/models/resources/v1alpha3/application/applications_test.go new file mode 100644 index 000000000..41c988e1e --- /dev/null +++ b/pkg/models/resources/v1alpha3/application/applications_test.go @@ -0,0 +1,104 @@ +package application + +import ( + "github.com/google/go-cmp/cmp" + appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + "github.com/kubernetes-sigs/application/pkg/client/informers/externalversions" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "testing" +) + +func applicationsToRuntimeObjects(applications ...*appv1beta1.Application) []runtime.Object { + var objs []runtime.Object + for _, app := range applications { + objs = append(objs, app) + } + return objs +} + +func TestListApplications(t *testing.T) { + tests := []struct { + description string + namespace string + deployments []*appv1beta1.Application + query *query.Query + expected api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar2", + []*appv1beta1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-1", + Namespace: "bar", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-2", + Namespace: "bar", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-2", + Namespace: "bar2", + }, + }, + }, + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("bar2")}, + }, + api.ListResult{ + Items: []interface{}{ + &appv1beta1.Application{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar-2", + Namespace: "bar2", + }, + }, + }, + TotalItems: 2, + }, + nil, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + objs := applicationsToRuntimeObjects(test.deployments...) + client := fake.NewSimpleClientset(objs...) + + informer := externalversions.NewSharedInformerFactory(client, 0) + + for _, deployment := range test.deployments { + informer.App().V1beta1().Applications().Informer().GetIndexer().Add(deployment) + } + + getter := New(informer) + + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got.Items, test.expected.Items); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go new file mode 100644 index 000000000..239243120 --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 clusterrole + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().ClusterRoles().Lister().Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Rbac().V1().ClusterRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.ClusterRole) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.ClusterRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.ClusterRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go new file mode 100644 index 000000000..7e8f60c24 --- /dev/null +++ b/pkg/models/resources/v1alpha3/clusterrole/clusterroles_test.go @@ -0,0 +1,94 @@ +package clusterrole + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListClusterRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().ClusterRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps.go b/pkg/models/resources/v1alpha3/configmap/configmaps.go new file mode 100644 index 000000000..76715aa2b --- /dev/null +++ b/pkg/models/resources/v1alpha3/configmap/configmaps.go @@ -0,0 +1,77 @@ +/* + + Copyright 2019 The KubeSphere 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 configmap + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type configmapsGetter struct { + informer informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &configmapsGetter{informer: sharedInformers} +} + +func (d *configmapsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) +} + +func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *configmapsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftConfigMap, ok := left.(*corev1.ConfigMap) + if !ok { + return false + } + + rightConfigMap, ok := right.(*corev1.ConfigMap) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftConfigMap.ObjectMeta, rightConfigMap.ObjectMeta, field) +} + +func (d *configmapsGetter) filter(object runtime.Object, filter query.Filter) bool { + configMap, ok := object.(*corev1.ConfigMap) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(configMap.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/configmap/configmaps_test.go b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go new file mode 100644 index 000000000..bc99a632c --- /dev/null +++ b/pkg/models/resources/v1alpha3/configmap/configmaps_test.go @@ -0,0 +1,91 @@ +package configmap + +import ( + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListConfigMaps(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "default", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, + }, + &api.ListResult{ + Items: []interface{}{foo3, foo2, foo1}, + TotalItems: len(configmaps), + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +var ( + foo1 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + foo2 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + foo3 = &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: "default", + }, + } + configmaps = []interface{}{foo1, foo2, foo3} +) + +func prepare() v1alpha3.Interface { + + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, configmap := range configmaps { + informer.Core().V1().ConfigMaps().Informer().GetIndexer().Add(configmap) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/deployment/deployments.go b/pkg/models/resources/v1alpha3/deployment/deployments.go new file mode 100644 index 000000000..b3083beb8 --- /dev/null +++ b/pkg/models/resources/v1alpha3/deployment/deployments.go @@ -0,0 +1,120 @@ +/* + + Copyright 2019 The KubeSphere 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 deployment + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "strings" + "time" + + "k8s.io/api/apps/v1" +) + +const ( + statusStopped = "stopped" + statusRunning = "running" + statusUpdating = "updating" +) + +type deploymentsGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &deploymentsGetter{sharedInformers: sharedInformers} +} + +func (d *deploymentsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name) +} + +func (d *deploymentsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + // first retrieves all deployments within given namespace + all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *deploymentsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftDeployment, ok := left.(*v1.Deployment) + if !ok { + return false + } + + rightDeployment, ok := right.(*v1.Deployment) + if !ok { + return false + } + + switch field { + case query.FieldUpdateTime: + fallthrough + case query.FieldLastUpdateTimestamp: + return lastUpdateTime(leftDeployment).After(lastUpdateTime(rightDeployment)) + default: + return v1alpha3.DefaultObjectMetaCompare(leftDeployment.ObjectMeta, rightDeployment.ObjectMeta, field) + } +} + +func (d *deploymentsGetter) filter(object runtime.Object, filter query.Filter) bool { + deployment, ok := object.(*v1.Deployment) + if !ok { + return false + } + + switch filter.Field { + + case query.FieldStatus: + return strings.Compare(deploymentStatus(deployment.Status), string(filter.Value)) == 0 + default: + return v1alpha3.DefaultObjectMetaFilter(deployment.ObjectMeta, filter) + } +} + +func deploymentStatus(status v1.DeploymentStatus) string { + if status.ReadyReplicas == 0 && status.Replicas == 0 { + return statusStopped + } else if status.ReadyReplicas == status.Replicas { + return statusRunning + } else { + return statusUpdating + } +} + +func lastUpdateTime(deployment *v1.Deployment) time.Time { + lut := deployment.CreationTimestamp.Time + for _, condition := range deployment.Status.Conditions { + if condition.LastUpdateTime.After(lut) { + lut = condition.LastUpdateTime.Time + } + } + return lut +} diff --git a/pkg/models/resources/v1alpha3/deployment/deployments_test.go b/pkg/models/resources/v1alpha3/deployment/deployments_test.go new file mode 100644 index 000000000..caad84fad --- /dev/null +++ b/pkg/models/resources/v1alpha3/deployment/deployments_test.go @@ -0,0 +1,98 @@ +package deployment + +import ( + "github.com/google/go-cmp/cmp" + appsv1 "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListDeployments(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + deployments = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, deployment := range deployments { + informer.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles.go b/pkg/models/resources/v1alpha3/globalrole/globalroles.go new file mode 100644 index 000000000..750a04767 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 globalrole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type globalrolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &globalrolesGetter{sharedInformers: sharedInformers} +} + +func (d *globalrolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().Get(name) +} + +func (d *globalrolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *globalrolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.GlobalRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *globalrolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.GlobalRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go new file mode 100644 index 000000000..85e32ce29 --- /dev/null +++ b/pkg/models/resources/v1alpha3/globalrole/globalroles_test.go @@ -0,0 +1,94 @@ +package globalrole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListGlobalRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().GlobalRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go new file mode 100644 index 000000000..6e3040b14 --- /dev/null +++ b/pkg/models/resources/v1alpha3/interface.go @@ -0,0 +1,156 @@ +package v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "sort" + "strings" +) + +type Interface interface { + // Get retrieves a single object by its namespace and name + Get(namespace, name string) (runtime.Object, error) + + // List retrieves a collection of objects matches given query + List(namespace string, query *query.Query) (*api.ListResult, error) +} + +// CompareFunc return true is left great than right +type CompareFunc func(runtime.Object, runtime.Object, query.Field) bool + +type FilterFunc func(runtime.Object, query.Filter) bool + +func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult { + // selected matched ones + var filtered []runtime.Object + for _, object := range objects { + selected := true + for field, value := range q.Filters { + if !filterFunc(object, query.Filter{Field: field, Value: value}) { + selected = false + break + } + } + + if selected { + filtered = append(filtered, object) + } + } + + // sort by sortBy field + sort.Slice(filtered, func(i, j int) bool { + if !q.Ascending { + return compareFunc(filtered[i], filtered[j], q.SortBy) + } + return !compareFunc(filtered[i], filtered[j], q.SortBy) + }) + + total := len(filtered) + start, end := q.Pagination.GetValidPagination(total) + + return &api.ListResult{ + TotalItems: len(filtered), + Items: objectsToInterfaces(filtered[start:end]), + } +} + +// DefaultObjectMetaCompare return true is left great than right +func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) bool { + switch sortBy { + // ?sortBy=name + case query.FieldName: + return strings.Compare(left.Name, right.Name) > 0 + // ?sortBy=creationTimestamp + case query.FieldCreateTime: + fallthrough + case query.FieldCreationTimeStamp: + // compare by name if creation timestamp is equal + if left.CreationTimestamp.Equal(&right.CreationTimestamp) { + return strings.Compare(left.Name, right.Name) > 0 + } + return left.CreationTimestamp.After(right.CreationTimestamp.Time) + default: + return false + } +} + +// Default metadata filter +func DefaultObjectMetaFilter(item metav1.ObjectMeta, filter query.Filter) bool { + switch filter.Field { + // /namespaces?page=1&limit=10&name=default + case query.FieldName: + return strings.Contains(item.Name, string(filter.Value)) + // /namespaces?page=1&limit=10&uid=a8a8d6cf-f6a5-4fea-9c1b-e57610115706 + case query.FieldUID: + return strings.Compare(string(item.UID), string(filter.Value)) == 0 + // /deployments?page=1&limit=10&namespace=kubesphere-system + case query.FieldNamespace: + return strings.Compare(item.Namespace, string(filter.Value)) == 0 + // /namespaces?page=1&limit=10&ownerReference=a8a8d6cf-f6a5-4fea-9c1b-e57610115706 + case query.FieldOwnerReference: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(string(ownerReference.UID), string(filter.Value)) == 0 { + return true + } + } + return false + // /namespaces?page=1&limit=10&ownerKind=Workspace + case query.FieldOwnerKind: + for _, ownerReference := range item.OwnerReferences { + if strings.Compare(ownerReference.Kind, string(filter.Value)) == 0 { + return true + } + } + return false + // /namespaces?page=1&limit=10&annotation=openpitrix_runtime + case query.FieldAnnotation: + return labelMatch(item.Annotations, string(filter.Value)) + // /namespaces?page=1&limit=10&label=kubesphere.io/workspace:system-workspace + case query.FieldLabel: + return labelMatch(item.Labels, string(filter.Value)) + default: + return false + } +} + +// Filter format (key!?=)?value,if the key is defined, the key must match exactly, value match according to strings.Contains. +func labelMatch(labels map[string]string, filter string) bool { + fields := strings.SplitN(filter, "=", 2) + var key, value string + var opposite bool + if len(fields) == 2 { + key = fields[0] + if strings.HasSuffix(key, "!") { + key = strings.TrimSuffix(key, "!") + opposite = true + } + value = fields[1] + } else { + value = fields[0] + } + for k, v := range labels { + if opposite { + if (key == "" || k == key) && !strings.Contains(v, value) { + return true + } + } else { + if (key == "" || k == key) && strings.Contains(v, value) { + return true + } + } + } + if opposite && labels[key] == "" { + return true + } + return false +} + +func objectsToInterfaces(objs []runtime.Object) []interface{} { + res := make([]interface{}, 0) + for _, obj := range objs { + res = append(res, obj) + } + return res +} diff --git a/pkg/models/resources/v1alpha3/namespace/namespaces.go b/pkg/models/resources/v1alpha3/namespace/namespaces.go new file mode 100644 index 000000000..8febda6ff --- /dev/null +++ b/pkg/models/resources/v1alpha3/namespace/namespaces.go @@ -0,0 +1,63 @@ +package namespace + +import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "strings" +) + +type namespaceGetter struct { + informers informers.SharedInformerFactory +} + +func New(informers informers.SharedInformerFactory) v1alpha3.Interface { + return &namespaceGetter{informers: informers} +} + +func (n namespaceGetter) Get(_, name string) (runtime.Object, error) { + return n.informers.Core().V1().Namespaces().Lister().Get(name) +} + +func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + ns, err := n.informers.Core().V1().Namespaces().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, item := range ns { + result = append(result, item) + } + + return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil +} + +func (n namespaceGetter) filter(item runtime.Object, filter query.Filter) bool { + namespace, ok := item.(*v1.Namespace) + if !ok { + return false + } + switch filter.Field { + case query.FieldStatus: + return strings.Compare(string(namespace.Status.Phase), string(filter.Value)) == 0 + default: + return v1alpha3.DefaultObjectMetaFilter(namespace.ObjectMeta, filter) + } +} + +func (n namespaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + leftNs, ok := left.(*v1.Namespace) + if !ok { + return false + } + + rightNs, ok := right.(*v1.Namespace) + if !ok { + return true + } + return v1alpha3.DefaultObjectMetaCompare(leftNs.ObjectMeta, rightNs.ObjectMeta, field) +} diff --git a/pkg/models/resources/v1alpha3/namespace/namespaces_test.go b/pkg/models/resources/v1alpha3/namespace/namespaces_test.go new file mode 100644 index 000000000..df9055971 --- /dev/null +++ b/pkg/models/resources/v1alpha3/namespace/namespaces_test.go @@ -0,0 +1 @@ +package namespace diff --git a/pkg/models/resources/v1alpha3/pod/pods.go b/pkg/models/resources/v1alpha3/pod/pods.go new file mode 100644 index 000000000..bdfa845a3 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods.go @@ -0,0 +1,115 @@ +/* + + Copyright 2019 The KubeSphere 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 pod + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type podsGetter struct { + informer informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &podsGetter{informer: sharedInformers} +} + +func (d *podsGetter) Get(namespace, name string) (runtime.Object, error) { + return d.informer.Core().V1().Pods().Lister().Pods(namespace).Get(name) +} + +func (d *podsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + + all, err := d.informer.Core().V1().Pods().Lister().Pods(namespace).List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, app := range all { + result = append(result, app) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *podsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftPod, ok := left.(*corev1.Pod) + if !ok { + return false + } + + rightPod, ok := right.(*corev1.Pod) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftPod.ObjectMeta, rightPod.ObjectMeta, field) +} + +func (d *podsGetter) filter(object runtime.Object, filter query.Filter) bool { + pod, ok := object.(*corev1.Pod) + + if !ok { + return false + } + switch filter.Field { + case "nodeName": + if pod.Spec.NodeName != string(filter.Value) { + return false + } + case "pvcName": + if !d.podBindPVC(pod, string(filter.Value)) { + return false + } + case "serviceName": + if !d.podBelongToService(pod, string(filter.Value)) { + return false + } + } + return v1alpha3.DefaultObjectMetaFilter(pod.ObjectMeta, filter) +} + +func (s *podsGetter) podBindPVC(item *corev1.Pod, pvcName string) bool { + for _, v := range item.Spec.Volumes { + if v.VolumeSource.PersistentVolumeClaim != nil && + v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { + return true + } + } + return false +} + +func (s *podsGetter) podBelongToService(item *corev1.Pod, serviceName string) bool { + service, err := s.informer.Core().V1().Services().Lister().Services(item.Namespace).Get(serviceName) + if err != nil { + return false + } + selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated() + if selector.Empty() || !selector.Matches(labels.Set(item.Labels)) { + return false + } + return true +} diff --git a/pkg/models/resources/v1alpha3/pod/pods_test.go b/pkg/models/resources/v1alpha3/pod/pods_test.go new file mode 100644 index 000000000..d5fb59652 --- /dev/null +++ b/pkg/models/resources/v1alpha3/pod/pods_test.go @@ -0,0 +1,91 @@ +package pod + +import ( + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListPods(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "default", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldNamespace: query.Value("default")}, + }, + &api.ListResult{ + Items: []interface{}{foo3, foo2, foo1}, + TotalItems: len(pods), + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + got, err := getter.List(test.namespace, test.query) + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + } +} + +var ( + foo1 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "default", + }, + } + foo2 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "default", + }, + } + foo3 = &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo3", + Namespace: "default", + }, + } + pods = []interface{}{foo1, foo2, foo3} +) + +func prepare() v1alpha3.Interface { + + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, pod := range pods { + informer.Core().V1().Pods().Informer().GetIndexer().Add(pod) + } + + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource.go b/pkg/models/resources/v1alpha3/resource/resource.go new file mode 100644 index 000000000..6a9b81af6 --- /dev/null +++ b/pkg/models/resources/v1alpha3/resource/resource.go @@ -0,0 +1,96 @@ +/* + * + * Copyright 2020 The KubeSphere 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 resource + +import ( + "errors" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/informers" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/application" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/configmap" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrole" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspace" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacerole" +) + +var ErrResourceNotSupported = errors.New("resource is not supported") + +type ResourceGetter struct { + getters map[schema.GroupVersionResource]v1alpha3.Interface +} + +func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter { + getters := make(map[schema.GroupVersionResource]v1alpha3.Interface) + + getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] = deployment.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}] = namespace.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}] = configmap.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}] = pod.New(factory.KubernetesSharedInformerFactory()) + getters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(factory.ApplicationSharedInformerFactory()) + getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace)] = workspace.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRole)] = globalrole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRole)] = workspacerole.New(factory.KubeSphereSharedInformerFactory()) + getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralUser)] = user.New(factory.KubeSphereSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("roles")] = role.New(factory.KubernetesSharedInformerFactory()) + getters[rbacv1.SchemeGroupVersion.WithResource("clusterroles")] = clusterrole.New(factory.KubernetesSharedInformerFactory()) + + return &ResourceGetter{ + getters: getters, + } +} + +// tryResource will retrieve a getter with resource name, it doesn't guarantee find resource with correct group version +// need to refactor this use schema.GroupVersionResource +func (r *ResourceGetter) tryResource(resource string) v1alpha3.Interface { + for k, v := range r.getters { + if k.Resource == resource { + return v + } + } + return nil +} + +func (r *ResourceGetter) Get(resource, namespace, name string) (runtime.Object, error) { + getter := r.tryResource(resource) + if getter == nil { + return nil, ErrResourceNotSupported + } + return getter.Get(namespace, name) +} + +func (r *ResourceGetter) List(resource, namespace string, query *query.Query) (*api.ListResult, error) { + getter := r.tryResource(resource) + if getter == nil { + return nil, ErrResourceNotSupported + } + return getter.List(namespace, query) +} diff --git a/pkg/models/resources/v1alpha3/resource/resource_test.go b/pkg/models/resources/v1alpha3/resource/resource_test.go new file mode 100644 index 000000000..199690958 --- /dev/null +++ b/pkg/models/resources/v1alpha3/resource/resource_test.go @@ -0,0 +1,119 @@ +/* + * + * Copyright 2020 The KubeSphere 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 resource + +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + fakek8s "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "testing" +) + +func TestResourceGetter(t *testing.T) { + + resource := prepare() + + tests := []struct { + Name string + Resource string + Namespace string + Query *query.Query + ExpectError error + ExpectResponse *api.ListResult + }{ + { + Name: "normal case", + Resource: "namespaces", + Namespace: "", + Query: &query.Query{ + Pagination: &query.Pagination{ + Limit: 10, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{}, + }, + ExpectError: nil, + ExpectResponse: &api.ListResult{ + Items: []interface{}{foo2, foo1, bar1}, + TotalItems: 3, + }, + }, + } + + for _, test := range tests { + + result, err := resource.List(test.Resource, test.Namespace, test.Query) + + if err != test.ExpectError { + t.Errorf("expected error: %s, got: %s", test.ExpectError, err) + } + if diff := cmp.Diff(test.ExpectResponse, result); diff != "" { + t.Errorf(diff) + } + } +} + +var ( + foo1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + namespaces = []interface{}{foo1, foo2, bar1} +) + +func prepare() *ResourceGetter { + + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + for _, namespace := range namespaces { + fakeInformerFactory.KubernetesSharedInformerFactory().Core().V1(). + Namespaces().Informer().GetIndexer().Add(namespace) + } + + return NewResourceGetter(fakeInformerFactory) +} diff --git a/pkg/models/resources/v1alpha3/role/roles.go b/pkg/models/resources/v1alpha3/role/roles.go new file mode 100644 index 000000000..4ebba9faa --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 role + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type rolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &rolesGetter{sharedInformers: sharedInformers} +} + +func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) { + return d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).Get(name) +} + +func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) { + all, err := d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).List(query.Selector()) + + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*rbacv1.Role) + if !ok { + return false + } + + rightRole, ok := right.(*rbacv1.Role) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*rbacv1.Role) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/role/roles_test.go b/pkg/models/resources/v1alpha3/role/roles_test.go new file mode 100644 index 000000000..7097831f9 --- /dev/null +++ b/pkg/models/resources/v1alpha3/role/roles_test.go @@ -0,0 +1,97 @@ +package role + +import ( + "github.com/google/go-cmp/cmp" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Namespace: "bar", + }, + } + + foo2 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Namespace: "bar", + }, + } + bar1 = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Namespace: "bar", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Rbac().V1().Roles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/user/users.go b/pkg/models/resources/v1alpha3/user/users.go new file mode 100644 index 000000000..3c74e9d45 --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 user + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type usersGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &usersGetter{sharedInformers: sharedInformers} +} + +func (d *usersGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().Users().Lister().Get(name) +} + +func (d *usersGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().Users().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *usersGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.User) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.User) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *usersGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.User) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/user/users_test.go b/pkg/models/resources/v1alpha3/user/users_test.go new file mode 100644 index 000000000..6ce6e0805 --- /dev/null +++ b/pkg/models/resources/v1alpha3/user/users_test.go @@ -0,0 +1,94 @@ +package user + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListUsers(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + users = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, user := range users { + informer.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces.go b/pkg/models/resources/v1alpha3/workspace/workspaces.go new file mode 100644 index 000000000..539fcd817 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 workspace + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspaceGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspaceGetter{sharedInformers: sharedInformers} +} + +func (d *workspaceGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().Get(name) +} + +func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + rightRole, ok := right.(*tenantv1alpha1.Workspace) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspaceGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*tenantv1alpha1.Workspace) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspace/workspaces_test.go b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go new file mode 100644 index 000000000..3c748399d --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspace/workspaces_test.go @@ -0,0 +1,94 @@ +package workspace + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaces(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + workspaces = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, workspace := range workspaces { + informer.Tenant().V1alpha1().Workspaces().Informer().GetIndexer().Add(workspace) + } + return New(informer) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go new file mode 100644 index 000000000..71df90721 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles.go @@ -0,0 +1,79 @@ +/* + + Copyright 2019 The KubeSphere 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 workspacerole + +import ( + "k8s.io/apimachinery/pkg/runtime" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" +) + +type workspacerolesGetter struct { + sharedInformers informers.SharedInformerFactory +} + +func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface { + return &workspacerolesGetter{sharedInformers: sharedInformers} +} + +func (d *workspacerolesGetter) Get(_, name string) (runtime.Object, error) { + return d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().Get(name) +} + +func (d *workspacerolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) { + + all, err := d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().List(query.Selector()) + if err != nil { + return nil, err + } + + var result []runtime.Object + for _, deploy := range all { + result = append(result, deploy) + } + + return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil +} + +func (d *workspacerolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { + + leftRole, ok := left.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + rightRole, ok := right.(*iamv1alpha2.WorkspaceRole) + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field) +} + +func (d *workspacerolesGetter) filter(object runtime.Object, filter query.Filter) bool { + role, ok := object.(*iamv1alpha2.WorkspaceRole) + + if !ok { + return false + } + + return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter) +} diff --git a/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go new file mode 100644 index 000000000..56b7b8187 --- /dev/null +++ b/pkg/models/resources/v1alpha3/workspacerole/workspaceroles_test.go @@ -0,0 +1,94 @@ +package workspacerole + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "testing" +) + +func TestListWorkspaceRoles(t *testing.T) { + tests := []struct { + description string + namespace string + query *query.Query + expected *api.ListResult + expectedErr error + }{ + { + "test name filter", + "bar", + &query.Query{ + Pagination: &query.Pagination{ + Limit: 1, + Offset: 0, + }, + SortBy: query.FieldName, + Ascending: false, + Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")}, + }, + &api.ListResult{ + Items: []interface{}{ + foo2, + }, + TotalItems: 1, + }, + nil, + }, + } + + getter := prepare() + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + + got, err := getter.List(test.namespace, test.query) + + if test.expectedErr != nil && err != test.expectedErr { + t.Errorf("expected error, got nothing") + } else if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(got, test.expected); diff != "" { + t.Errorf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} + +var ( + foo1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + }, + } + + foo2 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + }, + } + bar1 = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + }, + } + + roles = []interface{}{foo1, foo2, bar1} +) + +func prepare() v1alpha3.Interface { + client := fake.NewSimpleClientset() + informer := informers.NewSharedInformerFactory(client, 0) + + for _, role := range roles { + informer.Iam().V1alpha2().WorkspaceRoles().Informer().GetIndexer().Add(role) + } + return New(informer) +} diff --git a/pkg/models/resources/workspaces.go b/pkg/models/resources/workspaces.go deleted file mode 100644 index 43c92e0d3..000000000 --- a/pkg/models/resources/workspaces.go +++ /dev/null @@ -1,136 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 resources - -import ( - tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" - - "k8s.io/apimachinery/pkg/labels" -) - -type workspaceSearcher struct { -} - -func (*workspaceSearcher) get(namespace, name string) (interface{}, error) { - return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(name) -} - -// exactly Match -func (*workspaceSearcher) match(match map[string]string, item *tenantv1alpha1.Workspace) bool { - for k, v := range match { - switch k { - case Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case Keyword: - if !strings.Contains(item.Name, v) && !searchFuzzy(item.Labels, "", v) && !searchFuzzy(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -// Fuzzy searchInNamespace -func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *tenantv1alpha1.Workspace) bool { - for k, v := range fuzzy { - switch k { - case Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - case Label: - if !searchFuzzy(item.Labels, "", v) { - return false - } - case annotation: - if !searchFuzzy(item.Annotations, "", v) { - return false - } - return false - case app: - if !strings.Contains(item.Labels[chart], v) && !strings.Contains(item.Labels[release], v) { - return false - } - default: - if !searchFuzzy(item.Labels, k, v) { - return false - } - } - } - return true -} - -func (*workspaceSearcher) compare(a, b *tenantv1alpha1.Workspace, orderBy string) bool { - switch orderBy { - case CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *workspaceSearcher) search(namespace string, conditions *params.Conditions, orderBy string, reverse bool) ([]interface{}, error) { - - workspaces, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - - if err != nil { - return nil, err - } - - result := make([]*tenantv1alpha1.Workspace, 0) - - if len(conditions.Match) == 0 && len(conditions.Fuzzy) == 0 { - result = workspaces - } else { - for _, item := range workspaces { - if s.match(conditions.Match, item) && s.fuzzy(conditions.Fuzzy, item) { - result = append(result, item) - } - } - } - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - r := make([]interface{}, 0) - for _, i := range result { - r = append(r, i) - } - return r, nil -} diff --git a/pkg/models/revisions/revisions.go b/pkg/models/revisions/revisions.go index d3d402ce4..c5e3f0687 100644 --- a/pkg/models/revisions/revisions.go +++ b/pkg/models/revisions/revisions.go @@ -20,15 +20,29 @@ package revisions import ( "fmt" + "k8s.io/client-go/informers" "k8s.io/klog" "k8s.io/api/apps/v1" "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/informers" ) -func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error) { - deploymentLister := informers.SharedInformerFactory().Apps().V1().Deployments().Lister() +type RevisionGetter interface { + GetDeploymentRevision(namespace, name, revision string) (*v1.ReplicaSet, error) + GetStatefulSetRevision(namespace, name string, revision int) (*v1.ControllerRevision, error) + GetDaemonSetRevision(namespace, name string, revision int) (*v1.ControllerRevision, error) +} + +type revisionGetter struct { + informers informers.SharedInformerFactory +} + +func NewRevisionGetter(informers informers.SharedInformerFactory) RevisionGetter { + return &revisionGetter{informers: informers} +} + +func (c *revisionGetter) GetDeploymentRevision(namespace, name, revision string) (*v1.ReplicaSet, error) { + deploymentLister := c.informers.Apps().V1().Deployments().Lister() deploy, err := deploymentLister.Deployments(namespace).Get(name) if err != nil { klog.Errorf("get deployment %s failed, reason: %s", name, err) @@ -38,7 +52,7 @@ func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error) labelMap := deploy.Spec.Template.Labels labelSelector := labels.Set(labelMap).AsSelector() - replicaSetLister := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister() + replicaSetLister := c.informers.Apps().V1().ReplicaSets().Lister() rsList, err := replicaSetLister.ReplicaSets(namespace).List(labelSelector) if err != nil { return nil, err @@ -53,34 +67,34 @@ func GetDeployRevision(namespace, name, revision string) (*v1.ReplicaSet, error) return nil, fmt.Errorf("revision not found %v#%v", name, revision) } -func GetDaemonSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) { - daemonSetLister := informers.SharedInformerFactory().Apps().V1().DaemonSets().Lister() +func (c *revisionGetter) GetDaemonSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) { + daemonSetLister := c.informers.Apps().V1().DaemonSets().Lister() ds, err := daemonSetLister.DaemonSets(namespace).Get(name) if err != nil { return nil, err } - labels := ds.Spec.Template.Labels + lbs := ds.Spec.Template.Labels - return getControllerRevision(namespace, name, labels, revisionInt) + return c.getControllerRevision(namespace, name, lbs, revisionInt) } -func GetStatefulSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) { - statefulSetLister := informers.SharedInformerFactory().Apps().V1().StatefulSets().Lister() +func (c *revisionGetter) GetStatefulSetRevision(namespace, name string, revisionInt int) (*v1.ControllerRevision, error) { + statefulSetLister := c.informers.Apps().V1().StatefulSets().Lister() st, err := statefulSetLister.StatefulSets(namespace).Get(name) if err != nil { return nil, err } - return getControllerRevision(namespace, name, st.Spec.Template.Labels, revisionInt) + return c.getControllerRevision(namespace, name, st.Spec.Template.Labels, revisionInt) } -func getControllerRevision(namespace, name string, labelMap map[string]string, revision int) (*v1.ControllerRevision, error) { +func (c *revisionGetter) getControllerRevision(namespace, name string, labelMap map[string]string, revision int) (*v1.ControllerRevision, error) { labelSelector := labels.Set(labelMap).AsSelector() - controllerRevisionLister := informers.SharedInformerFactory().Apps().V1().ControllerRevisions().Lister() + controllerRevisionLister := c.informers.Apps().V1().ControllerRevisions().Lister() revisions, err := controllerRevisionLister.ControllerRevisions(namespace).List(labelSelector) if err != nil { diff --git a/pkg/models/routers/routers.go b/pkg/models/routers/routers.go index 602067231..20ba1520e 100644 --- a/pkg/models/routers/routers.go +++ b/pkg/models/routers/routers.go @@ -22,22 +22,17 @@ import ( "fmt" "io/ioutil" v1 "k8s.io/api/apps/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" - "sort" - - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/informers" - corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" - + "k8s.io/klog" + "sort" "strings" - - "kubesphere.io/kubesphere/pkg/constants" ) // choose router node ip by labels, currently select master node @@ -46,20 +41,32 @@ var routerNodeIPLabelSelector = map[string]string{ } const ( - servicemeshEnabled = "servicemesh.kubesphere.io/enabled" - sidecarInject = "sidecar.istio.io/inject" + servicemeshEnabled = "servicemesh.kubesphere.io/enabled" + sidecarInject = "sidecar.istio.io/inject" + ingressControllerFolder = "/etc/kubesphere/ingress-controller" + ingressControllerPrefix = "kubesphere-router-" + ingressControllerNamespace = "kubesphere-controls-system" ) -var routerTemplates map[string]runtime.Object +type RouterOperator interface { + GetRouter(namespace string) (*corev1.Service, error) + CreateRouter(namespace string, serviceType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) + DeleteRouter(namespace string) (*corev1.Service, error) + UpdateRouter(namespace string, serviceType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) +} -// Load yamls -func init() { +type routerOperator struct { + routerTemplates map[string]runtime.Object + client kubernetes.Interface + informers informers.SharedInformerFactory +} + +func NewRouterOperator(client kubernetes.Interface, informers informers.SharedInformerFactory) RouterOperator { yamls, err := loadYamls() - routerTemplates = make(map[string]runtime.Object, 2) + routerTemplates := make(map[string]runtime.Object, 2) if err != nil { - klog.Warning("error happened during loading external yamls", err) - return + klog.Errorf("error happened during loading external yamls, %v", err) } for _, f := range yamls { @@ -79,13 +86,18 @@ func init() { } } + return &routerOperator{ + client: client, + informers: informers, + routerTemplates: routerTemplates, + } } // get master node ip, if there are multiple master nodes, // choose first one according by their names alphabetically -func getMasterNodeIp() string { +func (c *routerOperator) getMasterNodeIp() string { - nodeLister := informers.SharedInformerFactory().Core().V1().Nodes().Lister() + nodeLister := c.informers.Core().V1().Nodes().Lister() selector := labels.SelectorFromSet(routerNodeIPLabelSelector) masters, err := nodeLister.List(selector) @@ -110,7 +122,7 @@ func getMasterNodeIp() string { return "" } -func addLoadBalancerIp(service *corev1.Service) { +func (c *routerOperator) addLoadBalancerIp(service *corev1.Service) { if service == nil { return @@ -118,7 +130,7 @@ func addLoadBalancerIp(service *corev1.Service) { // append selected node ip as loadbalancer ingress ip if service.Spec.Type != corev1.ServiceTypeLoadBalancer && len(service.Status.LoadBalancer.Ingress) == 0 { - rip := getMasterNodeIp() + rip := c.getMasterNodeIp() if len(rip) == 0 { klog.Info("can not get node ip") return @@ -132,36 +144,17 @@ func addLoadBalancerIp(service *corev1.Service) { } } -func GetAllRouters() ([]*corev1.Service, error) { - - selector := labels.SelectorFromSet(labels.Set{"app": "kubesphere", "component": "ks-router", "tier": "backend"}) - serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister() - services, err := serviceLister.Services(constants.IngressControllerNamespace).List(selector) - - for i := range services { - addLoadBalancerIp(services[i]) - } - - if err != nil { - klog.Error(err) - return nil, err - } - - return services, nil -} - // Get router from a namespace -func GetRouter(namespace string) (*corev1.Service, error) { - service, err := getRouterService(namespace) - addLoadBalancerIp(service) +func (c *routerOperator) GetRouter(namespace string) (*corev1.Service, error) { + service, err := c.getRouterService(namespace) + c.addLoadBalancerIp(service) return service, err } -func getRouterService(namespace string) (*corev1.Service, error) { - serviceName := constants.IngressControllerPrefix + namespace - - serviceLister := informers.SharedInformerFactory().Core().V1().Services().Lister() - service, err := serviceLister.Services(constants.IngressControllerNamespace).Get(serviceName) +func (c *routerOperator) getRouterService(namespace string) (*corev1.Service, error) { + serviceName := ingressControllerPrefix + namespace + serviceLister := c.informers.Core().V1().Services().Lister() + service, err := serviceLister.Services(ingressControllerNamespace).Get(serviceName) if err != nil { if errors.IsNotFound(err) { @@ -175,10 +168,8 @@ func getRouterService(namespace string) (*corev1.Service, error) { // Load all resource yamls func loadYamls() ([]string, error) { - var yamls []string - - files, err := ioutil.ReadDir(constants.IngressControllerFolder) + files, err := ioutil.ReadDir(ingressControllerFolder) if err != nil { klog.Warning(err) return nil, err @@ -188,7 +179,7 @@ func loadYamls() ([]string, error) { if file.IsDir() || !strings.HasSuffix(file.Name(), ".yaml") { continue } - content, err := ioutil.ReadFile(constants.IngressControllerFolder + "/" + file.Name()) + content, err := ioutil.ReadFile(ingressControllerFolder + "/" + file.Name()) if err != nil { klog.Error(err) @@ -202,7 +193,7 @@ func loadYamls() ([]string, error) { } // Create a ingress controller in a namespace -func CreateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { +func (c *routerOperator) CreateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { injectSidecar := false if enabled, ok := annotations[servicemeshEnabled]; ok { @@ -211,32 +202,32 @@ func CreateRouter(namespace string, routerType corev1.ServiceType, annotations m } } - err := createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, injectSidecar) + err := c.createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, injectSidecar) if err != nil { klog.Error(err) return nil, err } - router, err := createRouterService(namespace, routerType, annotations) + router, err := c.createRouterService(namespace, routerType, annotations) if err != nil { klog.Error(err) - _ = deleteRouterWorkload(namespace) + _ = c.deleteRouterWorkload(namespace) return nil, err } - addLoadBalancerIp(router) + c.addLoadBalancerIp(router) return router, nil } // DeleteRouter is used to delete ingress controller related resources in namespace // It will not delete ClusterRole resource cause it maybe used by other controllers -func DeleteRouter(namespace string) (*corev1.Service, error) { - err := deleteRouterWorkload(namespace) +func (c *routerOperator) DeleteRouter(namespace string) (*corev1.Service, error) { + err := c.deleteRouterWorkload(namespace) if err != nil { klog.Error(err) } - router, err := deleteRouterService(namespace) + router, err := c.deleteRouterService(namespace) if err != nil { klog.Error(err) @@ -245,28 +236,23 @@ func DeleteRouter(namespace string) (*corev1.Service, error) { return router, nil } -func createRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { +func (c *routerOperator) createRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { - obj, ok := routerTemplates["SERVICE"] + obj, ok := c.routerTemplates["SERVICE"] if !ok { klog.Error("service template not loaded") return nil, fmt.Errorf("service template not loaded") } - k8sClient := client.ClientSets().K8s().Kubernetes() - service := obj.(*corev1.Service) - service.SetAnnotations(annotations) service.Spec.Type = routerType - service.Name = constants.IngressControllerPrefix + namespace + service.Name = ingressControllerPrefix + namespace // Add project selector service.Labels["project"] = namespace - service.Spec.Selector["project"] = namespace - - service, err := k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Create(service) + service, err := c.client.CoreV1().Services(ingressControllerNamespace).Create(service) if err != nil { klog.Error(err) return nil, err @@ -275,41 +261,32 @@ func createRouterService(namespace string, routerType corev1.ServiceType, annota return service, nil } -func updateRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { - - k8sClient := client.ClientSets().K8s().Kubernetes() - - service, err := getRouterService(namespace) +func (c *routerOperator) updateRouterService(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { + service, err := c.getRouterService(namespace) if err != nil { klog.Error(err, "get router failed") return service, err } service.Spec.Type = routerType - service.SetAnnotations(annotations) - - service, err = k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Update(service) - + service, err = c.client.CoreV1().Services(ingressControllerNamespace).Update(service) return service, err } -func deleteRouterService(namespace string) (*corev1.Service, error) { - - service, err := getRouterService(namespace) +func (c *routerOperator) deleteRouterService(namespace string) (*corev1.Service, error) { + service, err := c.getRouterService(namespace) if err != nil { klog.Error(err) return service, err } - k8sClient := client.ClientSets().K8s().Kubernetes() - // delete controller service - serviceName := constants.IngressControllerPrefix + namespace + serviceName := ingressControllerPrefix + namespace deleteOptions := metav1.DeleteOptions{} - err = k8sClient.CoreV1().Services(constants.IngressControllerNamespace).Delete(serviceName, &deleteOptions) + err = c.client.CoreV1().Services(ingressControllerNamespace).Delete(serviceName, &deleteOptions) if err != nil { klog.Error(err) return service, err @@ -318,17 +295,16 @@ func deleteRouterService(namespace string) (*corev1.Service, error) { return service, nil } -func createOrUpdateRouterWorkload(namespace string, publishService bool, servicemeshEnabled bool) error { - obj, ok := routerTemplates["DEPLOYMENT"] +func (c *routerOperator) createOrUpdateRouterWorkload(namespace string, publishService bool, servicemeshEnabled bool) error { + obj, ok := c.routerTemplates["DEPLOYMENT"] if !ok { klog.Error("Deployment template file not loaded") return fmt.Errorf("deployment template file not loaded") } - deployName := constants.IngressControllerPrefix + namespace + deployName := ingressControllerPrefix + namespace - k8sClient := client.ClientSets().K8s().Kubernetes() - deployment, err := k8sClient.AppsV1().Deployments(constants.IngressControllerNamespace).Get(deployName, metav1.GetOptions{}) + deployment, err := c.client.AppsV1().Deployments(ingressControllerNamespace).Get(deployName, metav1.GetOptions{}) createDeployment := true @@ -336,7 +312,7 @@ func createOrUpdateRouterWorkload(namespace string, publishService bool, service if errors.IsNotFound(err) { deployment = obj.(*v1.Deployment) - deployment.Name = constants.IngressControllerPrefix + namespace + deployment.Name = ingressControllerPrefix + namespace // Add project label deployment.Spec.Selector.MatchLabels["project"] = namespace @@ -382,15 +358,15 @@ func createOrUpdateRouterWorkload(namespace string, publishService bool, service } if publishService { - deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--publish-service="+constants.IngressControllerNamespace+"/"+constants.IngressControllerPrefix+namespace) + deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--publish-service="+ingressControllerNamespace+"/"+ingressControllerPrefix+namespace) } else { deployment.Spec.Template.Spec.Containers[0].Args = append(deployment.Spec.Template.Spec.Containers[0].Args, "--report-node-internal-ip-address") } if createDeployment { - deployment, err = k8sClient.AppsV1().Deployments(constants.IngressControllerNamespace).Create(deployment) + deployment, err = c.client.AppsV1().Deployments(ingressControllerNamespace).Create(deployment) } else { - deployment, err = k8sClient.AppsV1().Deployments(constants.IngressControllerNamespace).Update(deployment) + deployment, err = c.client.AppsV1().Deployments(ingressControllerNamespace).Update(deployment) } if err != nil { @@ -401,13 +377,11 @@ func createOrUpdateRouterWorkload(namespace string, publishService bool, service return nil } -func deleteRouterWorkload(namespace string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() - +func (c *routerOperator) deleteRouterWorkload(namespace string) error { deleteOptions := metav1.DeleteOptions{} // delete controller deployment - deploymentName := constants.IngressControllerPrefix + namespace - err := k8sClient.AppsV1().Deployments(constants.IngressControllerNamespace).Delete(deploymentName, &deleteOptions) + deploymentName := ingressControllerPrefix + namespace + err := c.client.AppsV1().Deployments(ingressControllerNamespace).Delete(deploymentName, &deleteOptions) if err != nil { klog.Error(err) } @@ -420,15 +394,14 @@ func deleteRouterWorkload(namespace string) error { "tier": "backend", "project": namespace, }) - replicaSetLister := informers.SharedInformerFactory().Apps().V1().ReplicaSets().Lister() - replicaSets, err := replicaSetLister.ReplicaSets(constants.IngressControllerNamespace).List(selector) - + replicaSetLister := c.informers.Apps().V1().ReplicaSets().Lister() + replicaSets, err := replicaSetLister.ReplicaSets(ingressControllerNamespace).List(selector) if err != nil { klog.Error(err) } for i := range replicaSets { - err = k8sClient.AppsV1().ReplicaSets(constants.IngressControllerNamespace).Delete(replicaSets[i].Name, &deleteOptions) + err = c.client.AppsV1().ReplicaSets(ingressControllerNamespace).Delete(replicaSets[i].Name, &deleteOptions) if err != nil { klog.Error(err) } @@ -438,10 +411,10 @@ func deleteRouterWorkload(namespace string) error { } // Update Ingress Controller Service, change type from NodePort to loadbalancer or vice versa. -func UpdateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { +func (c *routerOperator) UpdateRouter(namespace string, routerType corev1.ServiceType, annotations map[string]string) (*corev1.Service, error) { var router *corev1.Service - router, err := getRouterService(namespace) + router, err := c.getRouterService(namespace) if err != nil { klog.Error(err) @@ -450,14 +423,13 @@ func UpdateRouter(namespace string, routerType corev1.ServiceType, annotations m enableServicemesh := annotations[servicemeshEnabled] == "true" - err = createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, enableServicemesh) + err = c.createOrUpdateRouterWorkload(namespace, routerType == corev1.ServiceTypeLoadBalancer, enableServicemesh) if err != nil { klog.Error(err) return router, err } - newRouter, err := updateRouterService(namespace, routerType, annotations) - + newRouter, err := c.updateRouterService(namespace, routerType, annotations) if err != nil { klog.Error(err) return newRouter, err diff --git a/pkg/models/status/status.go b/pkg/models/status/status.go deleted file mode 100644 index e92a11911..000000000 --- a/pkg/models/status/status.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 status - -import ( - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/server/params" - "strings" - - "kubesphere.io/kubesphere/pkg/models/resources" -) - -type WorkLoadStatus struct { - Namespace string `json:"namespace" description:"the name of the namespace"` - Count map[string]int `json:"data" description:"the number of unhealthy workloads"` - Items map[string]interface{} `json:"items,omitempty" description:"unhealthy workloads"` -} - -func GetNamespacesResourceStatus(namespace string) (*WorkLoadStatus, error) { - res := WorkLoadStatus{Count: make(map[string]int), Namespace: namespace, Items: make(map[string]interface{})} - var notReadyList *models.PageableResponse - var err error - for _, resource := range []string{resources.Deployments, resources.StatefulSets, resources.DaemonSets, resources.PersistentVolumeClaims, resources.Jobs} { - var notReadyStatus string - - switch resource { - case resources.PersistentVolumeClaims: - notReadyStatus = strings.Join([]string{resources.StatusPending, resources.StatusLost}, "|") - case resources.Jobs: - notReadyStatus = resources.StatusFailed - default: - notReadyStatus = resources.StatusUpdating - } - - notReadyList, err = resources.ListResources(namespace, resource, ¶ms.Conditions{Match: map[string]string{resources.Status: notReadyStatus}}, "", false, -1, 0) - - if err != nil { - klog.Errorf("list resources failed: %+v", err) - return nil, err - } - - res.Count[resource] = notReadyList.TotalCount - } - - return &res, nil -} - -func GetClusterResourceStatus() (*WorkLoadStatus, error) { - - return GetNamespacesResourceStatus("") -} diff --git a/pkg/models/storage/storage.go b/pkg/models/storage/storage.go index 4958d3c4a..88dca126c 100644 --- a/pkg/models/storage/storage.go +++ b/pkg/models/storage/storage.go @@ -18,19 +18,13 @@ package storage import ( - "kubesphere.io/kubesphere/pkg/simple/client" + "k8s.io/client-go/informers" "strconv" "k8s.io/api/core/v1" storageV1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/informers" -) - -const ( - IsDefaultStorageClassAnnotation = "storageclass.kubernetes.io/is-default-class" - betaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class" ) type ScMetrics struct { @@ -39,8 +33,20 @@ type ScMetrics struct { PvcNumber string `json:"pvcNumber"` } -func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) { - persistentVolumeClaimLister := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister() +type PersistentVolumeClaimGetter interface { + GetPersistentVolumeClaimByStorageClass(storageClassName string) ([]*v1.PersistentVolumeClaim, error) +} + +type persistentVolumeClaimGetter struct { + informers informers.SharedInformerFactory +} + +func NewPersistentVolumeClaimGetter(informers informers.SharedInformerFactory) PersistentVolumeClaimGetter { + return &persistentVolumeClaimGetter{informers: informers} +} + +func (c *persistentVolumeClaimGetter) GetPersistentVolumeClaimByStorageClass(scName string) ([]*v1.PersistentVolumeClaim, error) { + persistentVolumeClaimLister := c.informers.Core().V1().PersistentVolumeClaims().Lister() all, err := persistentVolumeClaimLister.List(labels.Everything()) if err != nil { @@ -62,14 +68,14 @@ func GetPvcListBySc(scName string) ([]*v1.PersistentVolumeClaim, error) { } // Get info of metrics -func GetScMetrics(scName string) (*ScMetrics, error) { - persistentVolumeLister := informers.SharedInformerFactory().Core().V1().PersistentVolumes().Lister() +func (c *persistentVolumeClaimGetter) GetScMetrics(scName string) (*ScMetrics, error) { + persistentVolumeLister := c.informers.Core().V1().PersistentVolumes().Lister() pvList, err := persistentVolumeLister.List(labels.Everything()) if err != nil { return nil, err } // Get PVC - pvcList, err := GetPvcListBySc(scName) + pvcList, err := c.GetPersistentVolumeClaimByStorageClass(scName) if err != nil { return nil, err @@ -93,10 +99,10 @@ func GetScMetrics(scName string) (*ScMetrics, error) { } // Get SC item list -func GetScList() ([]*storageV1.StorageClass, error) { +func (c *persistentVolumeClaimGetter) GetScList() ([]*storageV1.StorageClass, error) { // Get StorageClass list - scList, err := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister().List(labels.Everything()) + scList, err := c.informers.Storage().V1().StorageClasses().Lister().List(labels.Everything()) if err != nil { return nil, err @@ -104,36 +110,3 @@ func GetScList() ([]*storageV1.StorageClass, error) { return scList, nil } - -func SetDefaultStorageClass(defaultScName string) (*storageV1.StorageClass, error) { - scLister := informers.SharedInformerFactory().Storage().V1().StorageClasses().Lister() - // 1. verify storage class name - sc, err := scLister.Get(defaultScName) - if sc == nil || err != nil { - return sc, err - } - // 2. unset all default sc and then set default sc - scList, err := scLister.List(labels.Everything()) - if err != nil { - return nil, err - } - k8sClient := client.ClientSets().K8s().Kubernetes() - var defaultSc *storageV1.StorageClass - for _, sc := range scList { - _, hasDefault := sc.Annotations[IsDefaultStorageClassAnnotation] - _, hasBeta := sc.Annotations[betaIsDefaultStorageClassAnnotation] - if sc.Name == defaultScName || hasDefault || hasBeta { - delete(sc.Annotations, IsDefaultStorageClassAnnotation) - delete(sc.Annotations, betaIsDefaultStorageClassAnnotation) - if sc.Name == defaultScName { - sc.Annotations[IsDefaultStorageClassAnnotation] = "true" - defaultSc = sc - } - _, err := k8sClient.StorageV1().StorageClasses().Update(sc) - if err != nil { - return nil, err - } - } - } - return defaultSc, nil -} diff --git a/pkg/models/storage/volumes.go b/pkg/models/storage/volumes.go deleted file mode 100644 index a656ad895..000000000 --- a/pkg/models/storage/volumes.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 storage - -import ( - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/informers" -) - -// List pods of a specific persistent volume claims -func GetPodListByPvc(pvc string, ns string) (res []*v1.Pod, err error) { - podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister() - podList, err := podLister.Pods(ns).List(labels.Everything()) - if err != nil { - return nil, err - } - for _, pod := range podList { - if IsPvcInPod(pod, pvc) == true { - res = append(res, pod.DeepCopy()) - } - } - return res, nil -} - -// Check if the persistent volume claim is related to the pod -func IsPvcInPod(pod *v1.Pod, pvcName string) bool { - for _, v := range pod.Spec.Volumes { - if v.VolumeSource.PersistentVolumeClaim != nil && - v.VolumeSource.PersistentVolumeClaim.ClaimName == pvcName { - return true - } - } - return false -} diff --git a/pkg/models/tenant/devops.go b/pkg/models/tenant/devops.go index 5860e6a31..bb0bfcbc4 100644 --- a/pkg/models/tenant/devops.go +++ b/pkg/models/tenant/devops.go @@ -18,354 +18,26 @@ package tenant import ( - "fmt" - "github.com/emicklei/go-restful" - "github.com/gocraft/dbr" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" - "net/http" - "sync" + dsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" ) -type DevOpsProjectRoleResponse struct { - ProjectRole *gojenkins.ProjectRole - Err error +type DevOpsProjectLister interface { + ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) } -func ListDevopsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { - - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - if _, ok := err.(cs.ClientSetNotEnabledError); ok { - klog.V(4).Info("devops client is not enable") - return nil, err - } - klog.Error(err) - return nil, err - } - - query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). - From(devops.DevOpsProjectTableName) - var sqconditions []dbr.Builder - - sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspace)) - - switch username { - case devops.KS_ADMIN: - default: - onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) - query.Join(devops.DevOpsProjectMembershipTableName, onCondition) - sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) - sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) - } - - sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectTableName+"."+devops.StatusColumn, devops.StatusActive)) - if keyword := conditions.Match["keyword"]; keyword != "" { - sqconditions = append(sqconditions, db.Like(devops.DevOpsProjectNameColumn, keyword)) - } - projects := make([]*v1alpha2.DevOpsProject, 0) - - if len(sqconditions) > 0 { - query.Where(db.And(sqconditions...)) - } - switch orderBy { - case "name": - if reverse { - query.OrderDesc(devops.DevOpsProjectNameColumn) - } else { - query.OrderAsc(devops.DevOpsProjectNameColumn) - } - default: - if reverse { - query.OrderAsc(devops.DevOpsProjectCreateTimeColumn) - } else { - query.OrderDesc(devops.DevOpsProjectCreateTimeColumn) - } - - } - query.Limit(uint64(limit)) - query.Offset(uint64(offset)) - _, err = query.Load(&projects) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - count, err := query.Count() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - - result := make([]interface{}, 0) - for _, v := range projects { - result = append(result, v) - } - - return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil +type devopsProjectLister struct { + dsProject dsClient.ProjectOperator } -func GetDevOpsProjectsCount(username string) (uint32, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - klog.Error(err) - return 0, err +func newProjectLister(client dsClient.ProjectOperator) DevOpsProjectLister { + return &devopsProjectLister{ + dsProject: client, } - - query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). - From(devops.DevOpsProjectTableName) - var sqconditions []dbr.Builder - - if username != devops.KS_ADMIN { - onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) - query.Join(devops.DevOpsProjectMembershipTableName, onCondition) - sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) - sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) - } - - sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectTableName+"."+devops.StatusColumn, devops.StatusActive)) - if len(sqconditions) > 0 { - query.Where(db.And(sqconditions...)) - } - count, err := query.Count() - if err != nil { - klog.Errorf("%+v", err) - return 0, restful.NewError(http.StatusInternalServerError, err.Error()) - } - return count, nil } -func DeleteDevOpsProject(projectId, username string) error { - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(http.StatusForbidden, err.Error()) - } - - dp, err := cs.ClientSets().Devops() - if err != nil { - klog.Error(err) - return restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkins := dp.Jenkins() - - devopsdb, err := cs.ClientSets().MySQL() - if err != nil { - klog.Error(err) - return restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - _, err = jenkins.DeleteJob(projectId) - - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - roleNames := make([]string, 0) - for role := range devops.JenkinsProjectPermissionMap { - roleNames = append(roleNames, devops.GetProjectRoleName(projectId, role)) - roleNames = append(roleNames, devops.GetPipelineRoleName(projectId, role)) - } - err = jenkins.DeleteProjectRoles(roleNames...) - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - _, err = devopsdb.DeleteFrom(devops.DevOpsProjectMembershipTableName). - Where(db.Eq(devops.DevOpsProjectMembershipProjectIdColumn, projectId)).Exec() - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - _, err = devopsdb.Update(devops.DevOpsProjectTableName). - Set(devops.StatusColumn, devops.StatusDeleted). - Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).Exec() - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - project := &v1alpha2.DevOpsProject{} - err = devopsdb.Select(devops.DevOpsProjectColumns...). - From(devops.DevOpsProjectTableName). - Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)). - LoadOne(project) - if err != nil { - klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return nil -} - -func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { - - dp, err := cs.ClientSets().Devops() - if err != nil { - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - - } - jenkinsClient := dp.Jenkins() - - devopsdb, err := cs.ClientSets().MySQL() - if err != nil { - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - project := devops.NewDevOpsProject(req.Name, req.Description, username, req.Extra, workspace) - _, err = jenkinsClient.CreateFolder(project.ProjectId, project.Description) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - var addRoleCh = make(chan *DevOpsProjectRoleResponse, 8) - var addRoleWg sync.WaitGroup - for role, permission := range devops.JenkinsProjectPermissionMap { - addRoleWg.Add(1) - go func(role string, permission gojenkins.ProjectPermissionIds) { - _, err := jenkinsClient.AddProjectRole(devops.GetProjectRoleName(project.ProjectId, role), - devops.GetProjectRolePattern(project.ProjectId), permission, true) - addRoleCh <- &DevOpsProjectRoleResponse{nil, err} - addRoleWg.Done() - }(role, permission) - } - for role, permission := range devops.JenkinsPipelinePermissionMap { - addRoleWg.Add(1) - go func(role string, permission gojenkins.ProjectPermissionIds) { - _, err := jenkinsClient.AddProjectRole(devops.GetPipelineRoleName(project.ProjectId, role), - devops.GetPipelineRolePattern(project.ProjectId), permission, true) - addRoleCh <- &DevOpsProjectRoleResponse{nil, err} - addRoleWg.Done() - }(role, permission) - } - addRoleWg.Wait() - close(addRoleCh) - for addRoleResponse := range addRoleCh { - if addRoleResponse.Err != nil { - klog.Errorf("%+v", addRoleResponse.Err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(addRoleResponse.Err), addRoleResponse.Err.Error()) - } - } - - globalRole, err := jenkinsClient.GetGlobalRole(devops.JenkinsAllUserRoleName) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - if globalRole == nil { - _, err := jenkinsClient.AddGlobalRole(devops.JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{ - GlobalRead: true, - }, true) - if err != nil { - klog.Error("failed to create jenkins global role") - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - } - err = globalRole.AssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - projectRole, err := jenkinsClient.GetProjectRole(devops.GetProjectRoleName(project.ProjectId, devops.ProjectOwner)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = projectRole.AssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - pipelineRole, err := jenkinsClient.GetProjectRole(devops.GetPipelineRoleName(project.ProjectId, devops.ProjectOwner)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = pipelineRole.AssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - _, err = devopsdb.InsertInto(devops.DevOpsProjectTableName). - Columns(devops.DevOpsProjectColumns...).Record(project).Exec() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - - projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, devops.ProjectOwner, username) - _, err = devopsdb.InsertInto(devops.DevOpsProjectMembershipTableName). - Columns(devops.DevOpsProjectMembershipColumns...).Record(projectMembership).Exec() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - return project, nil -} - -func GetUserDevopsSimpleRules(username, projectId string) ([]models.SimpleRule, error) { - role, err := devops.GetProjectUserRole(username, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusForbidden, err.Error()) - } - return GetDevopsRoleSimpleRules(role), nil -} - -func GetDevopsRoleSimpleRules(role string) []models.SimpleRule { - var rules []models.SimpleRule - - switch role { - case "developer": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"view", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - case "owner": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "devops", Actions: []string{"edit", "view", "delete"}}, - } - break - case "maintainer": - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"create", "edit", "view", "delete", "trigger"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "credentials", Actions: []string{"create", "edit", "view", "delete"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - case "reporter": - fallthrough - default: - rules = []models.SimpleRule{ - {Name: "pipelines", Actions: []string{"view"}}, - {Name: "roles", Actions: []string{"view"}}, - {Name: "members", Actions: []string{"view"}}, - {Name: "devops", Actions: []string{"view"}}, - } - break - } - return rules +func (o *devopsProjectLister) ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { + //TODO: @runzexia use informer to impl it + return nil, nil } diff --git a/pkg/models/tenant/namespaces.go b/pkg/models/tenant/namespaces.go deleted file mode 100644 index 5fb4a791e..000000000 --- a/pkg/models/tenant/namespaces.go +++ /dev/null @@ -1,159 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 tenant - -import ( - "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" -) - -type namespaceSearcher struct { -} - -// Exactly Match -func (*namespaceSearcher) match(match map[string]string, item *v1.Namespace) bool { - for k, v := range match { - switch k { - case resources.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case resources.Keyword: - if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*namespaceSearcher) fuzzy(fuzzy map[string]string, item *v1.Namespace) bool { - - for k, v := range fuzzy { - switch k { - case resources.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - default: - return false - } - } - - return true -} - -func (*namespaceSearcher) compare(a, b *v1.Namespace, orderBy string) bool { - switch orderBy { - case "createTime": - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case "name": - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (*namespaceSearcher) GetNamespaces(username string) ([]*v1.Namespace, error) { - - roles, err := iam.GetUserRoles("", username) - - if err != nil { - return nil, err - } - namespaces := make([]*v1.Namespace, 0) - namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - for _, role := range roles { - namespace, err := namespaceLister.Get(role.Namespace) - if err != nil { - klog.Errorf("get namespace failed: %+v", err) - return nil, err - } - if !containsNamespace(namespaces, namespace) { - namespaces = append(namespaces, namespace) - } - } - - return namespaces, nil -} - -func containsNamespace(namespaces []*v1.Namespace, namespace *v1.Namespace) bool { - for _, item := range namespaces { - if item.Name == namespace.Name { - return true - } - } - return false -} - -func (s *namespaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1.Namespace, error) { - - rules, err := iam.GetUserClusterRules(username) - - if err != nil { - return nil, err - } - - namespaces := make([]*v1.Namespace, 0) - - if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"namespaces"}}) { - namespaces, err = informers.SharedInformerFactory().Core().V1().Namespaces().Lister().List(labels.Everything()) - } else { - namespaces, err = s.GetNamespaces(username) - } - - if err != nil { - return nil, err - } - - result := make([]*v1.Namespace, 0) - - for _, namespace := range namespaces { - if s.match(conditions.Match, namespace) && s.fuzzy(conditions.Fuzzy, namespace) { - result = append(result, namespace) - } - } - - // order & reverse - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - return result, nil -} diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 45ffd8e03..3bd88d6f5 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -18,102 +18,189 @@ package tenant import ( - "k8s.io/api/core/v1" - "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/constants" + "fmt" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" + "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models" - ws "kubesphere.io/kubesphere/pkg/models/workspaces" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/simple/client" - "strconv" + "kubesphere.io/kubesphere/pkg/models/iam/am" + resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource" ) -var ( - workspaces = workspaceSearcher{} - namespaces = namespaceSearcher{} -) - -func CreateNamespace(workspaceName string, namespace *v1.Namespace, username string) (*v1.Namespace, error) { - if namespace.Labels == nil { - namespace.Labels = make(map[string]string, 0) - } - if username != "" { - namespace.Annotations[constants.CreatorAnnotationKey] = username - } - - namespace.Labels[constants.WorkspaceLabelKey] = workspaceName - - return client.ClientSets().K8s().Kubernetes().CoreV1().Namespaces().Create(namespace) +type Interface interface { + ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error) + ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error) } -func DescribeWorkspace(username, workspaceName string) (*v1alpha1.Workspace, error) { - workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName) +type tenantOperator struct { + am am.AccessManagementInterface + authorizer authorizer.Authorizer + resourceGetter *resourcesv1alpha3.ResourceGetter +} + +func New(informers informers.InformerFactory) Interface { + amOperator := am.NewAMOperator(informers) + opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator) + return &tenantOperator{ + am: amOperator, + authorizer: opaAuthorizer, + resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), + } +} + +func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) (*api.ListResult, error) { + + listWS := authorizer.AttributesRecord{ + User: user, + Verb: "list", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + Resource: "workspaces", + } + + decision, _, err := t.authorizer.Authorize(listWS) if err != nil { + klog.Error(err) return nil, err } - workspace = appendAnnotations(username, workspace) + if decision == authorizer.DecisionAllow { - return workspace, nil -} + result, err := t.resourceGetter.List(tenantv1alpha1.ResourcePluralWorkspace, "", queryParam) -func ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + if err != nil { + klog.Error(err) + return nil, err + } - workspaces, err := workspaces.search(username, conditions, orderBy, reverse) + return result, nil + } + + workspaceRoleBindings, err := t.am.ListWorkspaceRoleBindings(user.GetName(), "") if err != nil { + klog.Error(err) return nil, err } - // limit offset - result := make([]interface{}, 0) - for i, workspace := range workspaces { - if len(result) < limit && i >= offset { - workspace := appendAnnotations(username, workspace) - result = append(result, workspace) + workspaces := make([]runtime.Object, 0) + + for _, roleBinding := range workspaceRoleBindings { + + workspaceName := roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] + workspace, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspaceName) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !contains(workspaces, workspace) { + workspaces = append(workspaces, workspace) } } - return &models.PageableResponse{Items: result, TotalCount: len(workspaces)}, nil + result := resources.DefaultList(workspaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*tenantv1alpha1.Workspace).ObjectMeta, right.(*tenantv1alpha1.Workspace).ObjectMeta, field) + }, func(workspace runtime.Object, filter query.Filter) bool { + return resources.DefaultObjectMetaFilter(workspace.(*tenantv1alpha1.Workspace).ObjectMeta, filter) + }) + + return result, nil } -func appendAnnotations(username string, workspace *v1alpha1.Workspace) *v1alpha1.Workspace { - workspace = workspace.DeepCopy() - if workspace.Annotations == nil { - workspace.Annotations = make(map[string]string) - } - ns, err := ListNamespaces(username, ¶ms.Conditions{Match: map[string]string{constants.WorkspaceLabelKey: workspace.Name}}, "", false, 1, 0) - if err == nil { - workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount) - } - devops, err := ListDevopsProjects(workspace.Name, username, ¶ms.Conditions{}, "", false, 1, 0) - if err == nil { - workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount) - } - userCount, err := ws.WorkspaceUserCount(workspace.Name) - if err == nil { - workspace.Annotations["kubesphere.io/member-count"] = strconv.Itoa(userCount) - } - return workspace -} +func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) { -func ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) { + listNSInWS := authorizer.AttributesRecord{ + User: user, + Verb: "list", + APIGroup: "", + APIVersion: "v1", + Workspace: workspace, + Resource: "namespaces", + } - namespaces, err := namespaces.search(username, conditions, orderBy, reverse) + decision, _, err := t.authorizer.Authorize(listNSInWS) if err != nil { + klog.Error(err) return nil, err } - // limit offset - result := make([]interface{}, 0) - for i, v := range namespaces { - if len(result) < limit && i >= offset { - result = append(result, v) + if decision == authorizer.DecisionAllow { + + queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1alpha1.WorkspaceLabel, workspace)) + + result, err := t.resourceGetter.List("namespaces", "", queryParam) + + if err != nil { + klog.Error(err) + return nil, err + } + + return result, nil + } + + roleBindings, err := t.am.ListRoleBindings(user.GetName(), "") + + if err != nil { + klog.Error(err) + return nil, err + } + + namespaces := make([]runtime.Object, 0) + + for _, roleBinding := range roleBindings { + namespaceName := roleBinding.Namespace + namespace, err := t.resourceGetter.Get("namespaces", "", namespaceName) + + if errors.IsNotFound(err) { + klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + continue + } + + if err != nil { + klog.Error(err) + return nil, err + } + + if !contains(namespaces, namespace) { + namespaces = append(namespaces, namespace) } } - return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil + result := resources.DefaultList(namespaces, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool { + return resources.DefaultObjectMetaCompare(left.(*corev1.Namespace).ObjectMeta, right.(*corev1.Namespace).ObjectMeta, field) + }, func(object runtime.Object, filter query.Filter) bool { + namespace := object.(*corev1.Namespace).ObjectMeta + if workspaceLabel, ok := namespace.Labels[tenantv1alpha1.WorkspaceLabel]; !ok || workspaceLabel != workspace { + return false + } + return resources.DefaultObjectMetaFilter(namespace, filter) + }) + + return result, nil +} + +func contains(objects []runtime.Object, object runtime.Object) bool { + for _, item := range objects { + if item == object { + return true + } + } + return false } diff --git a/pkg/models/tenant/workspaces.go b/pkg/models/tenant/workspaces.go deleted file mode 100644 index fefc8d9e4..000000000 --- a/pkg/models/tenant/workspaces.go +++ /dev/null @@ -1,151 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 tenant - -import ( - rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/labels" - "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/params" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "sort" - "strings" -) - -type workspaceSearcher struct { -} - -// Exactly Match -func (*workspaceSearcher) match(match map[string]string, item *v1alpha1.Workspace) bool { - for k, v := range match { - switch k { - case resources.Name: - names := strings.Split(v, "|") - if !sliceutil.HasString(names, item.Name) { - return false - } - case resources.Keyword: - if !strings.Contains(item.Name, v) && !contains(item.Labels, "", v) && !contains(item.Annotations, "", v) { - return false - } - default: - // label not exist or value not equal - if val, ok := item.Labels[k]; !ok || val != v { - return false - } - } - } - return true -} - -func (*workspaceSearcher) fuzzy(fuzzy map[string]string, item *v1alpha1.Workspace) bool { - - for k, v := range fuzzy { - switch k { - case resources.Name: - if !strings.Contains(item.Name, v) && !strings.Contains(item.Annotations[constants.DisplayNameAnnotationKey], v) { - return false - } - default: - return false - } - } - - return true -} - -func (*workspaceSearcher) compare(a, b *v1alpha1.Workspace, orderBy string) bool { - switch orderBy { - case resources.CreateTime: - return a.CreationTimestamp.Time.Before(b.CreationTimestamp.Time) - case resources.Name: - fallthrough - default: - return strings.Compare(a.Name, b.Name) <= 0 - } -} - -func (s *workspaceSearcher) search(username string, conditions *params.Conditions, orderBy string, reverse bool) ([]*v1alpha1.Workspace, error) { - rules, err := iam.GetUserClusterRules(username) - - if err != nil { - return nil, err - } - - workspaces := make([]*v1alpha1.Workspace, 0) - - if iam.RulesMatchesRequired(rules, rbacv1.PolicyRule{Verbs: []string{"list"}, APIGroups: []string{"tenant.kubesphere.io"}, Resources: []string{"workspaces"}}) { - workspaces, err = informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().List(labels.Everything()) - if err != nil { - return nil, err - } - } else { - workspaceRoles, err := iam.GetUserWorkspaceRoleMap(username) - if err != nil { - return nil, err - } - for k := range workspaceRoles { - workspace, err := informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(k) - if err != nil { - return nil, err - } - workspaces = append(workspaces, workspace) - } - } - - result := make([]*v1alpha1.Workspace, 0) - - for _, workspace := range workspaces { - if s.match(conditions.Match, workspace) && s.fuzzy(conditions.Fuzzy, workspace) { - result = append(result, workspace) - } - } - - // order & reverse - sort.Slice(result, func(i, j int) bool { - if reverse { - tmp := i - i = j - j = tmp - } - return s.compare(result[i], result[j], orderBy) - }) - - return result, nil -} - -func GetWorkspace(workspaceName string) (*v1alpha1.Workspace, error) { - return informers.KsSharedInformerFactory().Tenant().V1alpha1().Workspaces().Lister().Get(workspaceName) -} - -func contains(m map[string]string, key, value string) bool { - for k, v := range m { - if key == "" { - if strings.Contains(k, value) || strings.Contains(v, value) { - return true - } - } else if k == key && strings.Contains(v, value) { - return true - } - } - return false -} diff --git a/pkg/models/terminal/terminal.go b/pkg/models/terminal/terminal.go index a7467bb79..45943252c 100644 --- a/pkg/models/terminal/terminal.go +++ b/pkg/models/terminal/terminal.go @@ -24,10 +24,11 @@ import ( "github.com/gorilla/websocket" "io" "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" "time" ) @@ -134,15 +135,23 @@ func (t TerminalSession) Close(status uint32, reason string) { t.conn.Close() } +type Interface interface { + HandleSession(shell, namespace, podName, containerName string, conn *websocket.Conn) +} + +type terminaler struct { + client kubernetes.Interface + config *rest.Config +} + +func NewTerminaler(client kubernetes.Interface, config *rest.Config) Interface { + return &terminaler{client: client, config: config} +} + // startProcess is called by handleAttach // Executed cmd in the container specified in request and connects it up with the ptyHandler (a session) -func startProcess(namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error { - - k8sClient := client.ClientSets().K8s().Kubernetes() - - cfg := client.ClientSets().K8s().Config() - - req := k8sClient.CoreV1().RESTClient().Post(). +func (t *terminaler) startProcess(namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error { + req := t.client.CoreV1().RESTClient().Post(). Resource("pods"). Name(podName). Namespace(namespace). @@ -156,7 +165,7 @@ func startProcess(namespace, podName, containerName string, cmd []string, ptyHan TTY: true, }, scheme.ParameterCodec) - exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL()) + exec, err := remotecommand.NewSPDYExecutor(t.config, "POST", req.URL()) if err != nil { return err } @@ -185,8 +194,7 @@ func isValidShell(validShells []string, shell string) bool { return false } -func HandleSession(shell, namespace, podName, containerName string, conn *websocket.Conn) { - +func (t *terminaler) HandleSession(shell, namespace, podName, containerName string, conn *websocket.Conn) { var err error validShells := []string{"sh", "bash"} @@ -194,13 +202,13 @@ func HandleSession(shell, namespace, podName, containerName string, conn *websoc if isValidShell(validShells, shell) { cmd := []string{shell} - err = startProcess(namespace, podName, containerName, cmd, session) + err = t.startProcess(namespace, podName, containerName, cmd, session) } else { // No shell given or it was not valid: try some shells until one succeeds or all fail // FIXME: if the first shell fails then the first keyboard event is lost for _, testShell := range validShells { cmd := []string{testShell} - if err = startProcess(namespace, podName, containerName, cmd, session); err == nil { + if err = t.startProcess(namespace, podName, containerName, cmd, session); err == nil { break } } diff --git a/pkg/models/types.go b/pkg/models/types.go index f22fbd4ad..fccff9c87 100644 --- a/pkg/models/types.go +++ b/pkg/models/types.go @@ -17,13 +17,6 @@ */ package models -import ( - corev1 "k8s.io/api/core/v1" - "time" - - "k8s.io/api/rbac/v1" -) - type PageableResponse struct { Items []interface{} `json:"items" description:"paging data"` TotalCount int `json:"total_count" description:"total count"` @@ -36,41 +29,6 @@ type Workspace struct { DevopsProjects []string `json:"devops_projects"` } -type Action struct { - Name string `json:"name"` - Rules []v1.PolicyRule `json:"rules"` -} - -type Rule struct { - Name string `json:"name"` - Actions []Action `json:"actions"` -} - -type SimpleRule struct { - Name string `json:"name" description:"rule name"` - Actions []string `json:"actions" description:"actions"` -} - -type User struct { - Username string `json:"username"` - Email string `json:"email"` - Lang string `json:"lang,omitempty"` - Description string `json:"description"` - CreateTime time.Time `json:"create_time"` - Groups []string `json:"groups,omitempty"` - Password string `json:"password,omitempty"` - CurrentPassword string `json:"current_password,omitempty"` - AvatarUrl string `json:"avatar_url"` - LastLoginTime string `json:"last_login_time"` - Status int `json:"status"` - ClusterRole string `json:"cluster_role"` - Roles map[string]string `json:"roles,omitempty"` - Role string `json:"role,omitempty"` - RoleBinding string `json:"role_binding,omitempty"` - RoleBindTime *time.Time `json:"role_bind_time,omitempty"` - WorkspaceRole string `json:"workspace_role,omitempty"` -} - type Group struct { Path string `json:"path"` Name string `json:"name"` @@ -81,25 +39,6 @@ type Group struct { Description string `json:"description"` } -type ComponentStatus struct { - Name string `json:"name" description:"component name"` - Namespace string `json:"namespace" description:"the name of the namespace"` - SelfLink string `json:"selfLink" description:"self link"` - Label interface{} `json:"label" description:"labels"` - StartedAt time.Time `json:"startedAt" description:"started time"` - TotalBackends int `json:"totalBackends" description:"the total replicas of each backend system component"` - HealthyBackends int `json:"healthyBackends" description:"the number of healthy backend components"` -} -type NodeStatus struct { - TotalNodes int `json:"totalNodes" description:"total number of nodes"` - HealthyNodes int `json:"healthyNodes" description:"the number of healthy nodes"` -} - -type HealthStatus struct { - KubeSphereComponents []ComponentStatus `json:"kubesphereStatus" description:"kubesphere components status"` - NodeStatus NodeStatus `json:"nodeStatus" description:"nodes status"` -} - type PodInfo struct { Namespace string `json:"namespace" description:"namespace"` Pod string `json:"pod" description:"pod name"` @@ -112,8 +51,3 @@ type AuthGrantResponse struct { ExpiresIn float64 `json:"expires_in,omitempty"` RefreshToken string `json:"refresh_token,omitempty"` } - -type ResourceQuota struct { - Namespace string `json:"namespace" description:"namespace"` - Data corev1.ResourceQuotaStatus `json:"data" description:"resource quota status"` -} diff --git a/pkg/models/workloads/jobs.go b/pkg/models/workloads/jobs.go index 9c5838a4f..f639f866a 100644 --- a/pkg/models/workloads/jobs.go +++ b/pkg/models/workloads/jobs.go @@ -23,17 +23,28 @@ import ( k8serr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/simple/client" "strings" "time" ) const retryTimes = 3 -func JobReRun(namespace, jobName, resourceVersion string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() - job, err := k8sClient.BatchV1().Jobs(namespace).Get(jobName, metav1.GetOptions{}) +type JobRunner interface { + JobReRun(namespace, name, resourceVersion string) error +} + +type jobRunner struct { + client kubernetes.Interface +} + +func NewJobRunner(client kubernetes.Interface) JobRunner { + return &jobRunner{client: client} +} + +func (r *jobRunner) JobReRun(namespace, jobName, resourceVersion string) error { + job, err := r.client.BatchV1().Jobs(namespace).Get(jobName, metav1.GetOptions{}) if err != nil { return err } @@ -55,7 +66,7 @@ func JobReRun(namespace, jobName, resourceVersion string) error { delete(newJob.Spec.Selector.MatchLabels, "controller-uid") delete(newJob.Spec.Template.ObjectMeta.Labels, "controller-uid") - err = deleteJob(namespace, jobName) + err = r.deleteJob(namespace, jobName) if err != nil { klog.Errorf("failed to rerun job %s, reason: %s", jobName, err) @@ -63,7 +74,7 @@ func JobReRun(namespace, jobName, resourceVersion string) error { } for i := 0; i < retryTimes; i++ { - _, err = k8sClient.BatchV1().Jobs(namespace).Create(&newJob) + _, err = r.client.BatchV1().Jobs(namespace).Create(&newJob) if err != nil { time.Sleep(time.Second) continue @@ -79,9 +90,8 @@ func JobReRun(namespace, jobName, resourceVersion string) error { return nil } -func deleteJob(namespace, job string) error { - k8sClient := client.ClientSets().K8s().Kubernetes() +func (r *jobRunner) deleteJob(namespace, job string) error { deletePolicy := metav1.DeletePropagationBackground - err := k8sClient.BatchV1().Jobs(namespace).Delete(job, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) + err := r.client.BatchV1().Jobs(namespace).Delete(job, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) return err } diff --git a/pkg/models/workspaces/workspaces.go b/pkg/models/workspaces/workspaces.go deleted file mode 100644 index 47df460f0..000000000 --- a/pkg/models/workspaces/workspaces.go +++ /dev/null @@ -1,264 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 workspaces - -import ( - "fmt" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/informers" - "kubesphere.io/kubesphere/pkg/models" - "kubesphere.io/kubesphere/pkg/models/devops" - "kubesphere.io/kubesphere/pkg/models/iam" - "kubesphere.io/kubesphere/pkg/models/resources" - "kubesphere.io/kubesphere/pkg/server/params" - clientset "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/k8sutil" - "kubesphere.io/kubesphere/pkg/utils/sliceutil" - "strings" - - core "k8s.io/api/core/v1" - - "errors" - "k8s.io/api/rbac/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" -) - -func Namespaces(workspaceName string) ([]*core.Namespace, error) { - namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - namespaces, err := namespaceLister.List(labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName})) - - if err != nil { - return nil, err - } - - if namespaces == nil { - return make([]*core.Namespace, 0), nil - } - - out := make([]*core.Namespace, len(namespaces)) - - for i, v := range namespaces { - out[i] = v.DeepCopy() - } - - return out, nil -} - -func DeleteNamespace(workspace string, namespaceName string) error { - namespace, err := clientset.ClientSets().K8s().Kubernetes().CoreV1().Namespaces().Get(namespaceName, metav1.GetOptions{}) - if err != nil { - return err - } - if namespace.Labels[constants.WorkspaceLabelKey] == workspace { - deletePolicy := metav1.DeletePropagationBackground - return clientset.ClientSets().K8s().Kubernetes().CoreV1().Namespaces().Delete(namespaceName, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy}) - } else { - return errors.New("resource not found") - } -} - -func RemoveUser(workspaceName string, username string) error { - workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, username) - if err != nil { - return err - } - err = DeleteWorkspaceRoleBinding(workspaceName, username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) - if err != nil { - return err - } - return nil -} - -func InviteUser(workspaceName string, user *models.User) error { - - workspaceRole, err := iam.GetUserWorkspaceRole(workspaceName, user.Username) - - if err != nil && !apierrors.IsNotFound(err) { - klog.Errorf("get workspace role failed: %+v", err) - return err - } - - workspaceRoleName := fmt.Sprintf("workspace:%s:%s", workspaceName, strings.TrimPrefix(user.WorkspaceRole, "workspace-")) - var currentWorkspaceRoleName string - if workspaceRole != nil { - currentWorkspaceRoleName = workspaceRole.Name - } - - if currentWorkspaceRoleName != workspaceRoleName && currentWorkspaceRoleName != "" { - err := DeleteWorkspaceRoleBinding(workspaceName, user.Username, workspaceRole.Annotations[constants.DisplayNameAnnotationKey]) - if err != nil { - klog.Errorf("delete workspace role binding failed: %+v", err) - return err - } - } else if currentWorkspaceRoleName != "" { - return nil - } - - return CreateWorkspaceRoleBinding(workspaceName, user.Username, user.WorkspaceRole) -} - -func CreateWorkspaceRoleBinding(workspace, username string, role string) error { - - if !sliceutil.HasString(constants.WorkSpaceRoles, role) { - return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role) - } - - roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) - workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) - if err != nil { - return err - } - - if !k8sutil.ContainsUser(workspaceRoleBinding.Subjects, username) { - workspaceRoleBinding = workspaceRoleBinding.DeepCopy() - workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects, v1.Subject{APIGroup: "rbac.authorization.k8s.io", Kind: "User", Name: username}) - _, err = clientset.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) - if err != nil { - klog.Errorf("update workspace role binding failed: %+v", err) - return err - } - } - - return nil -} - -func DeleteWorkspaceRoleBinding(workspace, username string, role string) error { - - if !sliceutil.HasString(constants.WorkSpaceRoles, role) { - return apierrors.NewNotFound(schema.GroupResource{Resource: "workspace role"}, role) - } - - roleBindingName := fmt.Sprintf("workspace:%s:%s", workspace, strings.TrimPrefix(role, "workspace-")) - - workspaceRoleBinding, err := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister().Get(roleBindingName) - if err != nil { - return err - } - workspaceRoleBinding = workspaceRoleBinding.DeepCopy() - - for i, v := range workspaceRoleBinding.Subjects { - if v.Kind == v1.UserKind && v.Name == username { - workspaceRoleBinding.Subjects = append(workspaceRoleBinding.Subjects[:i], workspaceRoleBinding.Subjects[i+1:]...) - i-- - } - } - - workspaceRoleBinding, err = clientset.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(workspaceRoleBinding) - - return err -} - -func GetDevOpsProjectsCount(workspaceName string) (int, error) { - _, err := clientset.ClientSets().Devops() - if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled { - return 0, err - } - - dbconn, err := clientset.ClientSets().MySQL() - if err != nil { - return 0, err - } - - query := dbconn.Select(devops.DevOpsProjectIdColumn). - From(devops.DevOpsProjectTableName). - Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName), - db.Eq(devops.StatusColumn, devops.StatusActive))) - - devOpsProjects := make([]string, 0) - - if _, err := query.Load(&devOpsProjects); err != nil { - return 0, err - } - return len(devOpsProjects), nil -} - -func WorkspaceUserCount(workspace string) (int, error) { - count, err := iam.WorkspaceUsersTotalCount(workspace) - if err != nil { - return 0, err - } - return count, nil -} - -func GetOrgRolesCount(name string) (int, error) { - return len(constants.WorkSpaceRoles), nil -} - -func WorkspaceNamespaceCount(workspaceName string) (int, error) { - ns, err := Namespaces(workspaceName) - - namespaces := make([]string, 0) - - if err != nil { - return 0, err - } - - for i := 0; i < len(ns); i++ { - namespaces = append(namespaces, ns[i].Name) - } - - return len(namespaces), nil -} - -func WorkspaceCount() (int, error) { - - ws, err := resources.ListResources("", resources.Workspaces, ¶ms.Conditions{}, "", false, 1, 0) - - if err != nil { - return 0, err - } - - return ws.TotalCount, nil -} - -func GetAllProjectNums() (int, error) { - namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister() - list, err := namespaceLister.List(labels.Everything()) - if err != nil { - return 0, err - } - return len(list), nil -} - -func GetAllDevOpsProjectsNums() (int, error) { - _, err := clientset.ClientSets().Devops() - if _, notEnabled := err.(clientset.ClientSetNotEnabledError); notEnabled { - return 0, err - } - - dbconn, err := clientset.ClientSets().MySQL() - if err != nil { - return 0, err - } - - query := dbconn.Select(devops.DevOpsProjectIdColumn). - From(devops.DevOpsProjectTableName). - Where(db.Eq(devops.StatusColumn, devops.StatusActive)) - - devOpsProjects := make([]string, 0) - - if _, err := query.Load(&devOpsProjects); err != nil { - return 0, err - } - return len(devOpsProjects), nil -} diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go deleted file mode 100644 index fcba02cc4..000000000 --- a/pkg/server/config/config.go +++ /dev/null @@ -1,308 +0,0 @@ -package config - -import ( - "fmt" - "github.com/emicklei/go-restful" - "github.com/spf13/viper" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/apiserver/runtime" - "kubesphere.io/kubesphere/pkg/simple/client/alerting" - "kubesphere.io/kubesphere/pkg/simple/client/devops" - "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" - "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" - "kubesphere.io/kubesphere/pkg/simple/client/ldap" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" - "kubesphere.io/kubesphere/pkg/simple/client/notification" - "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/simple/client/prometheus" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "kubesphere.io/kubesphere/pkg/simple/client/s2is3" - "kubesphere.io/kubesphere/pkg/simple/client/servicemesh" - "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" - "net/http" - "reflect" - "strings" -) - -// Package config saves configuration for running KubeSphere components -// -// Config can be configured from command line flags and configuration file. -// Command line flags hold higher priority than configuration file. But if -// component Endpoint/Host/APIServer was left empty, all of that component -// command line flags will be ignored, use configuration file instead. -// For example, we have configuration file -// -// mysql: -// host: mysql.kubesphere-system.svc -// username: root -// password: password -// -// At the same time, have command line flags like following: -// -// --mysql-host mysql.openpitrix-system.svc --mysql-username king --mysql-password 1234 -// -// We will use `king:1234@mysql.openpitrix-system.svc` from command line flags rather -// than `root:password@mysql.kubesphere-system.svc` from configuration file, -// cause command line has higher priority. But if command line flags like following: -// -// --mysql-username root --mysql-password password -// -// we will `root:password@mysql.kubesphere-system.svc` as input, case -// mysql-host is missing in command line flags, all other mysql command line flags -// will be ignored. - -// InstallAPI installs api for config -func InstallAPI(c *restful.Container) { - ws := runtime.NewWebService(schema.GroupVersion{ - Group: "", - Version: "v1alpha1", - }) - - ws.Route(ws.GET("/configz"). - To(func(request *restful.Request, response *restful.Response) { - var conf = *sharedConfig - - conf.stripEmptyOptions() - - response.WriteAsJson(convertToMap(&conf)) - }). - Doc("Get system components configuration"). - Produces(restful.MIME_JSON). - Writes(Config{}). - Returns(http.StatusOK, "ok", Config{})) - - c.Add(ws) -} - -// convertToMap simply converts config to map[string]bool -// to hide sensitive information -func convertToMap(conf *Config) map[string]bool { - result := make(map[string]bool, 0) - - if conf == nil { - return result - } - - c := reflect.Indirect(reflect.ValueOf(conf)) - - for i := 0; i < c.NumField(); i++ { - name := strings.Split(c.Type().Field(i).Tag.Get("json"), ",")[0] - if strings.HasPrefix(name, "-") { - continue - } - - if c.Field(i).IsNil() { - result[name] = false - } else { - result[name] = true - } - } - - return result -} - -// Load loads configuration after setup -func Load() error { - sharedConfig = newConfig() - - viper.SetConfigName(DefaultConfigurationName) - viper.AddConfigPath(DefaultConfigurationPath) - viper.AddConfigPath(".") - - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { - klog.Warning("configuration file not found") - return nil - } else { - panic(fmt.Errorf("error parsing configuration file %s", err)) - } - } - - conf := newConfig() - if err := viper.Unmarshal(conf); err != nil { - klog.Error(fmt.Errorf("error unmarshal configuration %v", err)) - return err - } else { - // make sure kubesphere options always exists - if conf.KubeSphereOptions == nil { - conf.KubeSphereOptions = kubesphere.NewKubeSphereOptions() - } else { - ksOptions := kubesphere.NewKubeSphereOptions() - conf.KubeSphereOptions.ApplyTo(ksOptions) - conf.KubeSphereOptions = ksOptions - } - - conf.Apply(shadowConfig) - sharedConfig = conf - } - - return nil -} - -const ( - // DefaultConfigurationName is the default name of configuration - DefaultConfigurationName = "kubesphere" - - // DefaultConfigurationPath the default location of the configuration file - DefaultConfigurationPath = "/etc/kubesphere" -) - -var ( - // sharedConfig holds configuration across kubesphere - sharedConfig *Config - - // shadowConfig contains options from commandline options - shadowConfig = &Config{} -) - -type Config struct { - MySQLOptions *mysql.MySQLOptions `json:"mysql,omitempty" yaml:"mysql,omitempty" mapstructure:"mysql"` - DevopsOptions *devops.DevopsOptions `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` - SonarQubeOptions *sonarqube.SonarQubeOptions `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` - KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` - ServiceMeshOptions *servicemesh.ServiceMeshOptions `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` - LdapOptions *ldap.LdapOptions `json:"ldap,omitempty" yaml:"ldap,omitempty" mapstructure:"ldap"` - RedisOptions *redis.RedisOptions `json:"redis,omitempty" yaml:"redis,omitempty" mapstructure:"redis"` - S3Options *s2is3.S3Options `json:"s3,omitempty" yaml:"s3,omitempty" mapstructure:"s3"` - OpenPitrixOptions *openpitrix.OpenPitrixOptions `json:"openpitrix,omitempty" yaml:"openpitrix,omitempty" mapstructure:"openpitrix"` - MonitoringOptions *prometheus.PrometheusOptions `json:"monitoring,omitempty" yaml:"monitoring,omitempty" mapstructure:"monitoring"` - LoggingOptions *esclient.ElasticSearchOptions `json:"logging,omitempty" yaml:"logging,omitempty" mapstructure:"logging"` - - // Options below are only loaded from configuration file, no command line flags for these options now. - KubeSphereOptions *kubesphere.KubeSphereOptions `json:"-" yaml:"kubesphere,omitempty" mapstructure:"kubesphere"` - - // Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere, - // we can add these options to kubesphere command lines - AlertingOptions *alerting.AlertingOptions `json:"alerting,omitempty" yaml:"alerting,omitempty" mapstructure:"alerting"` - NotificationOptions *notification.NotificationOptions `json:"notification,omitempty" yaml:"notification,omitempty" mapstructure:"notification"` -} - -func newConfig() *Config { - return &Config{ - MySQLOptions: mysql.NewMySQLOptions(), - DevopsOptions: devops.NewDevopsOptions(), - SonarQubeOptions: sonarqube.NewSonarQubeOptions(), - KubernetesOptions: k8s.NewKubernetesOptions(), - ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), - LdapOptions: ldap.NewLdapOptions(), - RedisOptions: redis.NewRedisOptions(), - S3Options: s2is3.NewS3Options(), - OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), - MonitoringOptions: prometheus.NewPrometheusOptions(), - KubeSphereOptions: kubesphere.NewKubeSphereOptions(), - AlertingOptions: alerting.NewAlertingOptions(), - NotificationOptions: notification.NewNotificationOptions(), - LoggingOptions: esclient.NewElasticSearchOptions(), - } -} - -func Get() *Config { - return sharedConfig -} - -func (c *Config) Apply(conf *Config) { - shadowConfig = conf - - if conf.LoggingOptions != nil { - conf.LoggingOptions.ApplyTo(c.LoggingOptions) - } - - if conf.KubeSphereOptions != nil { - conf.KubeSphereOptions.ApplyTo(c.KubeSphereOptions) - } - - if conf.MonitoringOptions != nil { - conf.MonitoringOptions.ApplyTo(c.MonitoringOptions) - } - if conf.OpenPitrixOptions != nil { - conf.OpenPitrixOptions.ApplyTo(c.OpenPitrixOptions) - } - - if conf.S3Options != nil { - conf.S3Options.ApplyTo(c.S3Options) - } - - if conf.RedisOptions != nil { - conf.RedisOptions.ApplyTo(c.RedisOptions) - } - - if conf.LdapOptions != nil { - conf.LdapOptions.ApplyTo(c.LdapOptions) - } - - if conf.ServiceMeshOptions != nil { - conf.ServiceMeshOptions.ApplyTo(c.ServiceMeshOptions) - } - - if conf.KubernetesOptions != nil { - conf.KubernetesOptions.ApplyTo(c.KubernetesOptions) - } - - if conf.SonarQubeOptions != nil { - conf.SonarQubeOptions.ApplyTo(c.SonarQubeOptions) - } - - if conf.DevopsOptions != nil { - conf.DevopsOptions.ApplyTo(c.DevopsOptions) - } - - if conf.MySQLOptions != nil { - conf.MySQLOptions.ApplyTo(c.MySQLOptions) - } -} - -func (c *Config) stripEmptyOptions() { - if c.MySQLOptions != nil && c.MySQLOptions.Host == "" { - c.MySQLOptions = nil - } - - if c.RedisOptions != nil && c.RedisOptions.RedisURL == "" { - c.RedisOptions = nil - } - - if c.DevopsOptions != nil && c.DevopsOptions.Host == "" { - c.DevopsOptions = nil - } - - if c.MonitoringOptions != nil && c.MonitoringOptions.Endpoint == "" && - c.MonitoringOptions.SecondaryEndpoint == "" { - c.MonitoringOptions = nil - } - - if c.SonarQubeOptions != nil && c.SonarQubeOptions.Host == "" { - c.SonarQubeOptions = nil - } - - if c.LdapOptions != nil && c.LdapOptions.Host == "" { - c.LdapOptions = nil - } - - if c.OpenPitrixOptions != nil && c.OpenPitrixOptions.IsEmpty() { - c.OpenPitrixOptions = nil - } - - if c.ServiceMeshOptions != nil && c.ServiceMeshOptions.IstioPilotHost == "" && - c.ServiceMeshOptions.ServicemeshPrometheusHost == "" && - c.ServiceMeshOptions.JaegerQueryHost == "" { - c.ServiceMeshOptions = nil - } - - if c.S3Options != nil && c.S3Options.Endpoint == "" { - c.S3Options = nil - } - - if c.AlertingOptions != nil && c.AlertingOptions.Endpoint == "" { - c.AlertingOptions = nil - } - - if c.LoggingOptions != nil && c.LoggingOptions.Host == "" { - c.LoggingOptions = nil - } - - if c.NotificationOptions != nil && c.NotificationOptions.Endpoint == "" { - c.NotificationOptions = nil - } - -} diff --git a/pkg/server/config/config_test.go b/pkg/server/config/config_test.go deleted file mode 100644 index f4f266c87..000000000 --- a/pkg/server/config/config_test.go +++ /dev/null @@ -1,191 +0,0 @@ -package config - -import ( - "fmt" - "gopkg.in/yaml.v2" - "io/ioutil" - "kubesphere.io/kubesphere/pkg/simple/client/alerting" - "kubesphere.io/kubesphere/pkg/simple/client/devops" - "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" - "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" - "kubesphere.io/kubesphere/pkg/simple/client/ldap" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" - "kubesphere.io/kubesphere/pkg/simple/client/notification" - "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/simple/client/prometheus" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "kubesphere.io/kubesphere/pkg/simple/client/s2is3" - "kubesphere.io/kubesphere/pkg/simple/client/servicemesh" - "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" - "os" - "testing" - "time" -) - -func newTestConfig() *Config { - conf := &Config{ - MySQLOptions: &mysql.MySQLOptions{ - Host: "10.68.96.5:3306", - Username: "root", - Password: "admin", - MaxIdleConnections: 10, - MaxOpenConnections: 20, - MaxConnectionLifeTime: time.Duration(10) * time.Second, - }, - DevopsOptions: &devops.DevopsOptions{ - Host: "http://ks-devops.kubesphere-devops-system.svc", - Username: "jenkins", - Password: "kubesphere", - MaxConnections: 10, - }, - SonarQubeOptions: &sonarqube.SonarQubeOptions{ - Host: "http://sonarqube.kubesphere-devops-system.svc", - Token: "ABCDEFG", - }, - KubernetesOptions: &k8s.KubernetesOptions{ - KubeConfig: "/Users/zry/.kube/config", - Master: "https://127.0.0.1:6443", - QPS: 1e6, - Burst: 1e6, - }, - ServiceMeshOptions: &servicemesh.ServiceMeshOptions{ - IstioPilotHost: "http://istio-pilot.istio-system.svc:9090", - JaegerQueryHost: "http://jaeger-query.istio-system.svc:80", - ServicemeshPrometheusHost: "http://prometheus-k8s.kubesphere-monitoring-system.svc", - }, - LdapOptions: &ldap.LdapOptions{ - Host: "http://openldap.kubesphere-system.svc", - ManagerDN: "cn=admin,dc=example,dc=org", - ManagerPassword: "P@88w0rd", - UserSearchBase: "ou=Users,dc=example,dc=org", - GroupSearchBase: "ou=Groups,dc=example,dc=org", - }, - RedisOptions: &redis.RedisOptions{ - RedisURL: "redis://:qwerty@localhost:6379/1", - }, - S3Options: &s2is3.S3Options{ - Endpoint: "http://minio.openpitrix-system.svc", - Region: "", - DisableSSL: false, - ForcePathStyle: false, - AccessKeyID: "ABCDEFGHIJKLMN", - SecretAccessKey: "OPQRSTUVWXYZ", - SessionToken: "abcdefghijklmn", - Bucket: "ssss", - }, - OpenPitrixOptions: &openpitrix.OpenPitrixOptions{ - RuntimeManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9103", - ClusterManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9104", - RepoManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9101", - AppManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9102", - CategoryManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9113", - AttachmentManagerEndpoint: "openpitrix-hyperpitrix.openpitrix-system.svc:9122", - }, - MonitoringOptions: &prometheus.PrometheusOptions{ - Endpoint: "http://prometheus.kubesphere-monitoring-system.svc", - SecondaryEndpoint: "http://prometheus.kubesphere-monitoring-system.svc", - }, - LoggingOptions: &esclient.ElasticSearchOptions{ - Host: "http://elasticsearch-logging.kubesphere-logging-system.svc:9200", - IndexPrefix: "elk", - Version: "6", - }, - KubeSphereOptions: &kubesphere.KubeSphereOptions{ - APIServer: "http://ks-apiserver.kubesphere-system.svc", - AccountServer: "http://ks-account.kubesphere-system.svc", - }, - AlertingOptions: &alerting.AlertingOptions{ - Endpoint: "http://alerting.kubesphere-alerting-system.svc:9200", - }, - NotificationOptions: ¬ification.NotificationOptions{ - Endpoint: "http://notification.kubesphere-alerting-system.svc:9200", - }, - } - return conf -} - -func saveTestConfig(t *testing.T, conf *Config) { - content, err := yaml.Marshal(conf) - if err != nil { - t.Fatalf("error marshal config. %v", err) - } - - err = ioutil.WriteFile(fmt.Sprintf("%s.yaml", DefaultConfigurationName), content, 0640) - if err != nil { - t.Fatalf("error write configuration file, %v", err) - } -} - -func cleanTestConfig(t *testing.T) { - file := fmt.Sprintf("%s.yaml", DefaultConfigurationName) - if _, err := os.Stat(file); os.IsNotExist(err) { - t.Log("file not exists, skipping") - return - } - - err := os.Remove(file) - if err != nil { - t.Fatalf("remove %s file failed", file) - } - -} - -func TestGet(t *testing.T) { - conf := newTestConfig() - saveTestConfig(t, conf) - defer cleanTestConfig(t) - - err := Load() - if err != nil { - t.Fatal(err) - } - conf2 := Get() - - if diff := reflectutils.Equal(conf, conf2); diff != nil { - t.Fatal(diff) - } -} - -func TestKubeSphereOptions(t *testing.T) { - conf := newTestConfig() - - t.Run("save nil kubesphere options", func(t *testing.T) { - savedConf := *conf - savedConf.KubeSphereOptions = nil - saveTestConfig(t, &savedConf) - defer cleanTestConfig(t) - - err := Load() - if err != nil { - t.Fatal(err) - } - loadedConf := Get() - - if diff := reflectutils.Equal(conf, loadedConf); diff != nil { - t.Fatal(diff) - } - }) - - t.Run("save partially kubesphere options", func(t *testing.T) { - savedConf := *conf - savedConf.KubeSphereOptions.APIServer = "http://example.com" - savedConf.KubeSphereOptions.AccountServer = "" - - saveTestConfig(t, &savedConf) - defer cleanTestConfig(t) - - err := Load() - if err != nil { - t.Fatal(err) - } - loadedConf := Get() - - savedConf.KubeSphereOptions.AccountServer = "http://ks-account.kubesphere-system.svc" - - if diff := reflectutils.Equal(&savedConf, loadedConf); diff != nil { - t.Fatal(diff) - } - }) -} diff --git a/pkg/server/errors/errors.go b/pkg/server/errors/errors.go index 80daea632..c08138e31 100644 --- a/pkg/server/errors/errors.go +++ b/pkg/server/errors/errors.go @@ -18,6 +18,7 @@ package errors import ( + "fmt" "github.com/emicklei/go-restful" "net/http" ) @@ -28,22 +29,22 @@ type Error struct { var None = Error{Message: "success"} -func (e *Error) Error() string { +func (e Error) Error() string { return e.Message } -func Wrap(err error) Error { +func Wrap(err error) error { return Error{Message: err.Error()} } -func New(message string) Error { - return Error{Message: message} +func New(format string, args ...interface{}) error { + return Error{Message: fmt.Sprintf(format, args...)} } -func ParseSvcErr(err error, resp *restful.Response) { +func GetServiceErrorCode(err error) int { if svcErr, ok := err.(restful.ServiceError); ok { - resp.WriteServiceError(svcErr.Code, svcErr) + return svcErr.Code } else { - resp.WriteHeaderAndEntity(http.StatusInternalServerError, Wrap(err)) + return http.StatusInternalServerError } } diff --git a/pkg/server/filter/logging.go b/pkg/server/filter/logging.go deleted file mode 100644 index 150c0e046..000000000 --- a/pkg/server/filter/logging.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 filter - -import ( - "k8s.io/klog" - "net" - "strings" - "time" - - "github.com/emicklei/go-restful" -) - -func Logging(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { - start := time.Now() - chain.ProcessFilter(req, resp) - klog.V(4).Infof("%s - \"%s %s %s\" %d %d %dms", - getRequestIP(req), - req.Request.Method, - req.Request.RequestURI, - req.Request.Proto, - resp.StatusCode(), - resp.ContentLength(), - time.Since(start)/time.Millisecond, - ) -} - -func getRequestIP(req *restful.Request) string { - address := strings.Trim(req.Request.Header.Get("X-Real-Ip"), " ") - if address != "" { - return address - } - - address = strings.Trim(req.Request.Header.Get("X-Forwarded-For"), " ") - if address != "" { - return address - } - - address, _, err := net.SplitHostPort(req.Request.RemoteAddr) - if err != nil { - return req.Request.RemoteAddr - } - - return address -} diff --git a/pkg/server/options/options.go b/pkg/server/options/options.go index c53d15106..54dca7f56 100644 --- a/pkg/server/options/options.go +++ b/pkg/server/options/options.go @@ -21,6 +21,7 @@ import ( "fmt" "github.com/spf13/pflag" "kubesphere.io/kubesphere/pkg/utils/net" + "os" ) type ServerRunOptions struct { @@ -63,21 +64,29 @@ func (s *ServerRunOptions) Validate() []error { if net.IsValidPort(s.SecurePort) { if s.TlsCertFile == "" { errs = append(errs, fmt.Errorf("tls cert file is empty while secure serving")) + } else { + if _, err := os.Stat(s.TlsCertFile); err != nil { + errs = append(errs, err) + } } if s.TlsPrivateKey == "" { errs = append(errs, fmt.Errorf("tls private key file is empty while secure serving")) + } else { + if _, err := os.Stat(s.TlsPrivateKey); err != nil { + errs = append(errs, err) + } } } return errs } -func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { +func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet, c *ServerRunOptions) { - fs.StringVar(&s.BindAddress, "bind-address", "0.0.0.0", "server bind address") - fs.IntVar(&s.InsecurePort, "insecure-port", 9090, "insecure port number") - fs.IntVar(&s.SecurePort, "secure-port", 0, "secure port number") - fs.StringVar(&s.TlsCertFile, "tls-cert-file", "", "tls cert file") - fs.StringVar(&s.TlsPrivateKey, "tls-private-key", "", "tls private key") + fs.StringVar(&s.BindAddress, "bind-address", c.BindAddress, "server bind address") + fs.IntVar(&s.InsecurePort, "insecure-port", c.InsecurePort, "insecure port number") + fs.IntVar(&s.SecurePort, "secure-port", s.SecurePort, "secure port number") + fs.StringVar(&s.TlsCertFile, "tls-cert-file", c.TlsCertFile, "tls cert file") + fs.StringVar(&s.TlsPrivateKey, "tls-private-key", c.TlsPrivateKey, "tls private key") } diff --git a/pkg/server/params/params.go b/pkg/server/params/params.go index 83ace6438..f558ed3ad 100644 --- a/pkg/server/params/params.go +++ b/pkg/server/params/params.go @@ -30,11 +30,10 @@ const ( OrderByParam = "orderBy" ConditionsParam = "conditions" ReverseParam = "reverse" - NameParam = "name" ) -func ParsePaging(paging string) (limit, offset int) { - +func ParsePaging(req *restful.Request) (limit, offset int) { + paging := req.QueryParameter(PagingParam) limit = 10 offset = 0 if groups := regexp.MustCompile(`^limit=(-?\d+),page=(\d+)$`).FindStringSubmatch(paging); len(groups) == 3 { @@ -45,7 +44,9 @@ func ParsePaging(paging string) (limit, offset int) { return } -func ParseConditions(conditionsStr string) (*Conditions, error) { +func ParseConditions(req *restful.Request) (*Conditions, error) { + + conditionsStr := req.QueryParameter(ConditionsParam) conditions := &Conditions{Match: make(map[string]string, 0), Fuzzy: make(map[string]string, 0)} @@ -76,20 +77,19 @@ func ParseConditions(conditionsStr string) (*Conditions, error) { return conditions, nil } -func ParseReverse(req *restful.Request) bool { - reverse := req.QueryParameter(ReverseParam) - b, err := strconv.ParseBool(reverse) - if err != nil { - return false - } - return b -} - type Conditions struct { Match map[string]string Fuzzy map[string]string } +func GetBoolValueWithDefault(req *restful.Request, name string, dv bool) bool { + reverse := req.QueryParameter(name) + if v, err := strconv.ParseBool(reverse); err == nil { + return v + } + return dv +} + func GetStringValueWithDefault(req *restful.Request, name string, dv string) string { v := req.QueryParameter(name) if v == "" { diff --git a/pkg/server/server.go b/pkg/server/server.go deleted file mode 100644 index d6a1aaea3..000000000 --- a/pkg/server/server.go +++ /dev/null @@ -1,24 +0,0 @@ -package server - -import ( - "bytes" - "fmt" - "k8s.io/klog" - "net/http" - "runtime" -) - -func LogStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) { - var buffer bytes.Buffer - buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) - for i := 2; ; i += 1 { - _, file, line, ok := runtime.Caller(i) - if !ok { - break - } - buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line)) - } - klog.Error(buffer.String()) - httpWriter.WriteHeader(http.StatusInternalServerError) - httpWriter.Write([]byte("recover from panic situation")) -} diff --git a/pkg/simple/client/alerting/options.go b/pkg/simple/client/alerting/options.go index 5cb9edd70..ccf25c564 100644 --- a/pkg/simple/client/alerting/options.go +++ b/pkg/simple/client/alerting/options.go @@ -1,16 +1,16 @@ package alerting -type AlertingOptions struct { +type Options struct { Endpoint string `json:"endpoint" yaml:"endpoint"` } -func NewAlertingOptions() *AlertingOptions { - return &AlertingOptions{ +func NewAlertingOptions() *Options { + return &Options{ Endpoint: "", } } -func (s *AlertingOptions) ApplyTo(options *AlertingOptions) { +func (s *Options) ApplyTo(options *Options) { if options == nil { options = s return diff --git a/pkg/simple/client/cache/cache.go b/pkg/simple/client/cache/cache.go new file mode 100644 index 000000000..4d8712674 --- /dev/null +++ b/pkg/simple/client/cache/cache.go @@ -0,0 +1,25 @@ +package cache + +import "time" + +var NeverExpire = time.Duration(0) + +type Interface interface { + // Keys retrieves all keys match the given pattern + Keys(pattern string) ([]string, error) + + // Get retrieves the value of the given key, return error if key doesn't exist + Get(key string) (string, error) + + // Set sets the value and living duration of the given key, zero duration means never expire + Set(key string, value string, duration time.Duration) error + + // Del deletes the given key, no error returned if the key doesn't exists + Del(keys ...string) error + + // Exists checks the existence of a give key + Exists(keys ...string) (bool, error) + + // Expires updates object's expiration time, return err if key doesn't exist + Expire(key string, duration time.Duration) error +} diff --git a/pkg/simple/client/cache/options.go b/pkg/simple/client/cache/options.go new file mode 100644 index 000000000..3422c1f81 --- /dev/null +++ b/pkg/simple/client/cache/options.go @@ -0,0 +1,46 @@ +package cache + +import ( + "fmt" + "github.com/spf13/pflag" +) + +type Options struct { + Host string `json:"host"` + Port int `json:"port"` + Password string `json:"password"` + DB int `json:"db"` +} + +// NewRedisOptions returns options points to nowhere, +// because redis is not required for some components +func NewRedisOptions() *Options { + return &Options{ + Host: "", + Port: 0, + Password: "", + DB: 0, + } +} + +// Validate check options +func (r *Options) Validate() []error { + errors := make([]error, 0) + + if r.Port == 0 { + errors = append(errors, fmt.Errorf("invalid service port number")) + } + + return errors +} + +// AddFlags add option flags to command line flags, +// if redis-host left empty, the following options will be ignored. +func (r *Options) AddFlags(fs *pflag.FlagSet, s *Options) { + fs.StringVar(&r.Host, "redis-host", s.Host, "Redis connection URL. If left blank, means redis is unnecessary, "+ + "redis will be disabled.") + + fs.IntVar(&r.Port, "redis-port", s.Port, "") + fs.StringVar(&r.Password, "redis-password", s.Password, "") + fs.IntVar(&r.DB, "redis-db", s.DB, "") +} diff --git a/pkg/simple/client/cache/redis.go b/pkg/simple/client/cache/redis.go new file mode 100644 index 000000000..54db227bf --- /dev/null +++ b/pkg/simple/client/cache/redis.go @@ -0,0 +1,91 @@ +/* + + Copyright 2019 The KubeSphere 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 cache + +import ( + "fmt" + "github.com/go-redis/redis" + "k8s.io/klog" + "time" +) + +type Client struct { + client *redis.Client +} + +func NewRedisClient(option *Options, stopCh <-chan struct{}) (Interface, error) { + var r Client + + redisOptions := &redis.Options{ + Addr: fmt.Sprintf("%s:%d", option.Host, option.Port), + Password: option.Password, + DB: option.DB, + } + + if stopCh == nil { + klog.Fatalf("no stop channel passed, redis connections will leak.") + } + + r.client = redis.NewClient(redisOptions) + + if err := r.client.Ping().Err(); err != nil { + r.client.Close() + return nil, err + } + + // close redis in case of connection leak + if stopCh != nil { + go func() { + <-stopCh + if err := r.client.Close(); err != nil { + klog.Error(err) + } + }() + } + + return &r, nil +} + +func (r *Client) Get(key string) (string, error) { + return r.client.Get(key).Result() +} + +func (r *Client) Keys(pattern string) ([]string, error) { + return r.client.Keys(pattern).Result() +} + +func (r *Client) Set(key string, value string, duration time.Duration) error { + return r.client.Set(key, value, duration).Err() +} + +func (r *Client) Del(keys ...string) error { + return r.client.Del(keys...).Err() +} + +func (r *Client) Exists(keys ...string) (bool, error) { + existedKeys, err := r.client.Exists(keys...).Result() + if err != nil { + return false, err + } + + return len(keys) == int(existedKeys), nil +} + +func (r *Client) Expire(key string, duration time.Duration) error { + return r.client.Expire(key, duration).Err() +} diff --git a/pkg/simple/client/cache/simple_cache.go b/pkg/simple/client/cache/simple_cache.go new file mode 100644 index 000000000..55dc54428 --- /dev/null +++ b/pkg/simple/client/cache/simple_cache.go @@ -0,0 +1,111 @@ +package cache + +import ( + "kubesphere.io/kubesphere/pkg/server/errors" + "regexp" + "strings" + "time" +) + +var ErrNoSuchKey = errors.New("no such key") + +type simpleObject struct { + value string + neverExpire bool + expiredAt time.Time +} + +// SimpleCache implements cache.Interface use memory objects, it should be used only for testing +type simpleCache struct { + store map[string]simpleObject +} + +func NewSimpleCache() Interface { + return &simpleCache{store: make(map[string]simpleObject)} +} + +func (s *simpleCache) Keys(pattern string) ([]string, error) { + // There is a little difference between go regexp and redis key pattern + // In redis, * means any character, while in go . means match everything. + pattern = strings.Replace(pattern, "*", ".", -1) + + re, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + var keys []string + for k := range s.store { + if re.MatchString(k) { + keys = append(keys, k) + } + } + + return keys, nil +} + +func (s *simpleCache) Set(key string, value string, duration time.Duration) error { + sobject := simpleObject{ + value: value, + neverExpire: false, + expiredAt: time.Now().Add(duration), + } + + if duration == NeverExpire { + sobject.neverExpire = true + } + + s.store[key] = sobject + return nil +} + +func (s *simpleCache) Del(keys ...string) error { + for _, key := range keys { + if _, ok := s.store[key]; ok { + delete(s.store, key) + } else { + return ErrNoSuchKey + } + } + + return nil +} + +func (s *simpleCache) Get(key string) (string, error) { + if sobject, ok := s.store[key]; ok { + if sobject.neverExpire || time.Now().Before(sobject.expiredAt) { + return sobject.value, nil + } + } + + return "", ErrNoSuchKey +} + +func (s *simpleCache) Exists(keys ...string) (bool, error) { + for _, key := range keys { + if _, ok := s.store[key]; !ok { + return false, ErrNoSuchKey + } + } + + return true, nil +} + +func (s *simpleCache) Expire(key string, duration time.Duration) error { + value, err := s.Get(key) + if err != nil { + return err + } + + sobject := simpleObject{ + value: value, + neverExpire: false, + expiredAt: time.Now().Add(duration), + } + + if duration == NeverExpire { + sobject.neverExpire = true + } + + s.store[key] = sobject + return nil +} diff --git a/pkg/simple/client/cache/simple_cache_test.go b/pkg/simple/client/cache/simple_cache_test.go new file mode 100644 index 000000000..d2fae499b --- /dev/null +++ b/pkg/simple/client/cache/simple_cache_test.go @@ -0,0 +1,123 @@ +package cache + +import ( + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/util/sets" + "testing" + "time" +) + +var dataSet = map[string]string{ + "foo1": "val1", + "foo2": "val2", + "foo3": "val3", + "bar1": "val1", + "bar2": "val2", +} + +// load dataset into cache +func load(client Interface, data map[string]string) error { + for k, v := range data { + err := client.Set(k, v, NeverExpire) + if err != nil { + return err + } + } + + return nil +} + +// dump retrieve all data in simple into a map +func dump(client Interface) (map[string]string, error) { + keys, err := client.Keys("*") + if err != nil { + return nil, err + } + + snapshot := make(map[string]string) + for _, key := range keys { + val, err := client.Get(key) + if err != nil { + continue + } + snapshot[key] = val + } + + return snapshot, nil +} + +func TestDeleteAndExpireCache(t *testing.T) { + var testCases = []struct { + description string + deleteKeys sets.String + expireKeys sets.String + expireDuration time.Duration // never use a 0(NeverExpires) duration with expireKeys, recommend time.Millisecond * 500. + expected map[string]string + }{ + { + description: "Should get all keys", + expected: map[string]string{ + "foo1": "val1", + "foo2": "val2", + "foo3": "val3", + "bar1": "val1", + "bar2": "val2", + }, + }, + { + description: "Test delete should get only keys start with foo", + expected: map[string]string{ + "foo1": "val1", + "foo2": "val2", + "foo3": "val3", + }, + deleteKeys: sets.NewString("bar1", "bar2"), + }, + { + description: "Should get only keys start with bar", + expected: map[string]string{ + "bar1": "val1", + "bar2": "val2", + }, + expireDuration: time.Millisecond * 500, + expireKeys: sets.NewString("foo1", "foo2", "foo3"), + }, + } + + for _, testCase := range testCases { + cacheClient := NewSimpleCache() + + t.Run(testCase.description, func(t *testing.T) { + err := load(cacheClient, dataSet) + if err != nil { + t.Fatalf("Unable to load dataset, got error %v", err) + } + + if len(testCase.deleteKeys) != 0 { + err = cacheClient.Del(testCase.deleteKeys.List()...) + if err != nil { + t.Fatalf("Error delete keys, %v", err) + } + } + + if len(testCase.expireKeys) != 0 && testCase.expireDuration != 0 { + for _, key := range testCase.expireKeys.List() { + err = cacheClient.Expire(key, testCase.expireDuration) + if err != nil { + t.Fatalf("Error expire keys, %v", err) + } + } + time.Sleep(testCase.expireDuration) + } + + got, err := dump(cacheClient) + if err != nil { + t.Fatalf("Error dump data, %v", err) + } + + if diff := cmp.Diff(got, testCase.expected); len(diff) != 0 { + t.Errorf("%T differ (-got, +expected) %v", testCase.expected, diff) + } + }) + } +} diff --git a/pkg/simple/client/db/db.go b/pkg/simple/client/db/db.go new file mode 100644 index 000000000..8ce9a0c7e --- /dev/null +++ b/pkg/simple/client/db/db.go @@ -0,0 +1,5 @@ +package db + +// +type Interface interface { +} diff --git a/pkg/gojenkins/OWNERS b/pkg/simple/client/devops/OWNERS similarity index 100% rename from pkg/gojenkins/OWNERS rename to pkg/simple/client/devops/OWNERS diff --git a/pkg/simple/client/devops/build.go b/pkg/simple/client/devops/build.go new file mode 100644 index 000000000..d1e3f0ada --- /dev/null +++ b/pkg/simple/client/devops/build.go @@ -0,0 +1,115 @@ +package devops + +const ( + LastBuild = "lastBuild" + LastCompletedBuild = "lastCompletedBuild" + LastFailedBuild = "lastFailedBuild" + LastStableBuild = "lastStableBuild" + LastSuccessfulBuild = "lastSuccessfulBuild" + LastUnstableBuild = "lastUnstableBuild" + LastUnsuccessfulBuild = "lastUnsuccessfulBuild" + FirstBuild = "firstBuild" +) + +type GeneralParameter struct { + Name string + Value string +} + +type Branch struct { + SHA1 string `json:",omitempty"` + Name string `json:",omitempty"` +} + +type BuildRevision struct { + SHA1 string `json:"SHA1,omitempty"` + Branch []Branch `json:"Branch,omitempty"` +} + +type Builds struct { + BuildNumber int64 `json:"buildNumber"` + BuildResult interface{} `json:"buildResult"` + Marked BuildRevision `json:"marked"` + Revision BuildRevision `json:"revision"` +} + +type Culprit struct { + AbsoluteUrl string + FullName string +} + +type GeneralAction struct { + Parameters []GeneralParameter `json:"parameters,omitempty"` + Causes []map[string]interface{} `json:"causes,omitempty"` + BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"` + LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"` + RemoteUrls []string `json:"remoteUrls,omitempty"` + ScmName string `json:"scmName,omitempty"` + Subdir interface{} `json:"subdir,omitempty"` + ClassName string `json:"_class,omitempty"` + SonarTaskId string `json:"ceTaskId,omitempty"` + SonarServerUrl string `json:"serverUrl,omitempty"` + SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"` + TotalCount int64 `json:",omitempty"` + UrlName string `json:",omitempty"` +} + +type Build struct { + Actions []GeneralAction + Artifacts []struct { + DisplayPath string `json:"displayPath"` + FileName string `json:"fileName"` + RelativePath string `json:"relativePath"` + } `json:"artifacts"` + Building bool `json:"building"` + BuiltOn string `json:"builtOn"` + ChangeSet struct { + Items []struct { + AffectedPaths []string `json:"affectedPaths"` + Author struct { + AbsoluteUrl string `json:"absoluteUrl"` + FullName string `json:"fullName"` + } `json:"author"` + Comment string `json:"comment"` + CommitID string `json:"commitId"` + Date string `json:"date"` + ID string `json:"id"` + Msg string `json:"msg"` + Paths []struct { + EditType string `json:"editType"` + File string `json:"file"` + } `json:"paths"` + Timestamp int64 `json:"timestamp"` + } `json:"items"` + Kind string `json:"kind"` + Revisions []struct { + Module string + Revision int + } `json:"revision"` + } `json:"changeSet"` + Culprits []Culprit `json:"culprits"` + Description interface{} `json:"description"` + Duration int64 `json:"duration"` + EstimatedDuration int64 `json:"estimatedDuration"` + Executor interface{} `json:"executor"` + FullDisplayName string `json:"fullDisplayName"` + ID string `json:"id"` + KeepLog bool `json:"keepLog"` + Number int64 `json:"number"` + QueueID int64 `json:"queueId"` + Result string `json:"result"` + Timestamp int64 `json:"timestamp"` + URL string `json:"url"` + Runs []struct { + Number int64 + URL string + } `json:"runs"` +} + +type BuildGetter interface { + // GetProjectPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build. + GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*Build, error) + + // GetMultiBranchPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build. + GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*Build, error) +} diff --git a/pkg/simple/client/devops/credential.go b/pkg/simple/client/devops/credential.go new file mode 100644 index 000000000..feb0f877a --- /dev/null +++ b/pkg/simple/client/devops/credential.go @@ -0,0 +1,55 @@ +package devops + +import ( + v1 "k8s.io/api/core/v1" +) + +type Credential struct { + Id string `json:"id" description:"Id of Credential, e.g. dockerhub-id"` + Type string `json:"type" description:"Type of Credential, e.g. ssh/kubeconfig"` + DisplayName string `json:"display_name,omitempty" description:"Credential's display name"` + Fingerprint *struct { + FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` + Hash string `json:"hash,omitempty" description:"Credential's hash"` + Usage []*struct { + Name string `json:"name,omitempty" description:"pipeline full name"` + Ranges struct { + Ranges []*struct { + Start int `json:"start,omitempty" description:"Start build number"` + End int `json:"end,omitempty" description:"End build number"` + } `json:"ranges,omitempty"` + } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` + } `json:"usage,omitempty" description:"all usage of Credential"` + } `json:"fingerprint,omitempty" description:"usage of the Credential"` + Description string `json:"description,omitempty" description:"Credential's description'"` + Domain string `json:"domain,omitempty" description:"Credential's domain,In ks we only use the default domain, default '_''"` +} + +type UsernamePasswordCredential struct { + Username string `json:"username,omitempty" description:"username of username_password credential"` + Password string `json:"password,omitempty" description:"password of username_password credential"` +} + +type SshCredential struct { + Username string `json:"username,omitempty" description:"username of ssh credential"` + Passphrase string `json:"passphrase,omitempty" description:"passphrase of ssh credential, password of ssh credential"` + PrivateKey string `json:"private_key,omitempty" mapstructure:"private_key" description:"private key of ssh credential"` +} + +type SecretTextCredential struct { + Secret string `json:"secret,omitempty" description:"secret content of credential"` +} + +type KubeconfigCredential struct { + Content string `json:"content,omitempty" description:"content of kubeconfig"` +} + +type CredentialOperator interface { + CreateCredentialInProject(projectId string, credential *v1.Secret) (string, error) + + UpdateCredentialInProject(projectId string, credential *v1.Secret) (string, error) + + GetCredentialInProject(projectId, id string) (*Credential, error) + + DeleteCredentialInProject(projectId, id string) (string, error) +} diff --git a/pkg/simple/client/devops/devops.go b/pkg/simple/client/devops/devops.go deleted file mode 100644 index 6b5952499..000000000 --- a/pkg/simple/client/devops/devops.go +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 devops - -import ( - "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins" - "sync" -) - -const ( - JenkinsAllUserRoleName = "kubesphere-user" -) - -type DevopsClient struct { - jenkinsClient *gojenkins.Jenkins -} - -func NewDevopsClient(options *DevopsOptions) (*DevopsClient, error) { - var d DevopsClient - - jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password) - jenkins, err := jenkins.Init() - if err != nil { - klog.Errorf("failed to connecto to jenkins role, %+v", err) - return nil, err - } - - d.jenkinsClient = jenkins - - err = d.initializeJenkins() - if err != nil { - klog.Error(err) - return nil, err - } - - return &d, nil -} - -func NewDevopsClientOrDie(options *DevopsOptions) *DevopsClient { - jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password) - jenkins, err := jenkins.Init() - if err != nil { - klog.Errorf("failed to connecto to jenkins role, %+v", err) - panic(err) - } - - d := &DevopsClient{ - jenkinsClient: jenkins, - } - - err = d.initializeJenkins() - if err != nil { - klog.Error(err) - panic(err) - } - - return d -} - -func (c *DevopsClient) Jenkins() *gojenkins.Jenkins { - return c.jenkinsClient -} - -var mutex = sync.Mutex{} - -func (c *DevopsClient) initializeJenkins() error { - mutex.Lock() - defer mutex.Unlock() - - if c.jenkinsClient == nil { - return fmt.Errorf("jenkins intialization failed") - } - - globalRole, err := c.jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName) - if err != nil { - klog.Error(err) - return err - } - - // Jenkins uninitialized, create global role - if globalRole == nil { - _, err := c.jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{GlobalRead: true}, true) - if err != nil { - klog.Error(err) - return err - } - } - - _, err = c.jenkinsClient.AddProjectRole(JenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{SCMTag: true}, true) - if err != nil { - klog.Error(err) - return err - } - - return nil -} diff --git a/pkg/simple/client/devops/fake/fakedevops.go b/pkg/simple/client/devops/fake/fakedevops.go new file mode 100644 index 000000000..6a28eaf8b --- /dev/null +++ b/pkg/simple/client/devops/fake/fakedevops.go @@ -0,0 +1,542 @@ +package fake + +import ( + "fmt" + "github.com/emicklei/go-restful" + "io/ioutil" + v1 "k8s.io/api/core/v1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" + "net/url" + "strings" +) + +type Devops struct { + Data map[string]interface{} + + Projects map[string]interface{} + + Pipelines map[string]map[string]*devopsv1alpha3.Pipeline + + Credentials map[string]map[string]*v1.Secret +} + +func New(projects ...string) *Devops { + d := &Devops{ + Data: nil, + Projects: map[string]interface{}{}, + Pipelines: map[string]map[string]*devopsv1alpha3.Pipeline{}, + Credentials: map[string]map[string]*v1.Secret{}, + } + for _, p := range projects { + d.Projects[p] = true + } + return d +} +func NewWithPipelines(project string, pipelines ...*devopsv1alpha3.Pipeline) *Devops { + d := &Devops{ + Data: nil, + Projects: map[string]interface{}{}, + Pipelines: map[string]map[string]*devopsv1alpha3.Pipeline{}, + Credentials: map[string]map[string]*v1.Secret{}, + } + + d.Projects[project] = true + d.Pipelines[project] = map[string]*devopsv1alpha3.Pipeline{} + for _, f := range pipelines { + d.Pipelines[project][f.Name] = f + } + return d +} + +func NewWithCredentials(project string, credentials ...*v1.Secret) *Devops { + d := &Devops{ + Data: nil, + Projects: map[string]interface{}{}, + Credentials: map[string]map[string]*v1.Secret{}, + } + + d.Projects[project] = true + d.Credentials[project] = map[string]*v1.Secret{} + for _, f := range credentials { + d.Credentials[project][f.Name] = f + } + return d +} + +func (d *Devops) CreateDevOpsProject(projectId string) (string, error) { + if _, ok := d.Projects[projectId]; ok { + return projectId, nil + } + d.Projects[projectId] = true + d.Pipelines[projectId] = map[string]*devopsv1alpha3.Pipeline{} + d.Credentials[projectId] = map[string]*v1.Secret{} + return projectId, nil +} + +func (d *Devops) DeleteDevOpsProject(projectId string) error { + if _, ok := d.Projects[projectId]; ok { + delete(d.Projects, projectId) + delete(d.Pipelines, projectId) + delete(d.Credentials, projectId) + return nil + } else { + return &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + }, + Message: "", + } + } +} + +func (d *Devops) GetDevOpsProject(projectId string) (string, error) { + if _, ok := d.Projects[projectId]; ok { + return projectId, nil + } else { + return "", &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + } +} + +func NewFakeDevops(data map[string]interface{}) *Devops { + var fakeData Devops + fakeData.Data = data + return &fakeData +} + +// Pipelinne operator interface +func (d *Devops) GetPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.Pipeline, error) { + return nil, nil +} + +func (d *Devops) ListPipelines(httpParameters *devops.HttpParameters) (*devops.PipelineList, error) { + return nil, nil +} +func (d *Devops) GetPipelineRun(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) { + return nil, nil +} +func (d *Devops) ListPipelineRuns(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineRunList, error) { + return nil, nil +} +func (d *Devops) StopPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) { + return nil, nil +} +func (d *Devops) ReplayPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) { + return nil, nil +} +func (d *Devops) RunPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) { + return nil, nil +} +func (d *Devops) GetArtifacts(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) { + return nil, nil +} +func (d *Devops) GetRunLog(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} +func (d *Devops) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) { + return nil, nil, nil +} +func (d *Devops) GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) { + s := []string{projectName, pipelineName, runId, nodeId} + key := strings.Join(s, "-") + res := d.Data[key].([]devops.NodeSteps) + return res, nil +} +func (d *Devops) GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.PipelineRunNodes, error) { + s := []string{projectName, pipelineName, runId} + key := strings.Join(s, "-") + res := d.Data[key].([]devops.PipelineRunNodes) + return res, nil +} +func (d *Devops) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} + +//BranchPipelinne operator interface +func (d *Devops) GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.BranchPipeline, error) { + return nil, nil +} +func (d *Devops) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) { + return nil, nil +} +func (d *Devops) StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) { + return nil, nil +} +func (d *Devops) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) { + return nil, nil +} +func (d *Devops) RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) { + return nil, nil +} +func (d *Devops) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) { + return nil, nil +} +func (d *Devops) GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} +func (d *Devops) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) { + return nil, nil, nil +} +func (d *Devops) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) { + s := []string{projectName, pipelineName, branchName, runId, nodeId} + key := strings.Join(s, "-") + res := d.Data[key].([]devops.NodeSteps) + return res, nil +} +func (d *Devops) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.BranchPipelineRunNodes, error) { + s := []string{projectName, pipelineName, branchName, runId} + key := strings.Join(s, "-") + res := d.Data[key].([]devops.BranchPipelineRunNodes) + return res, nil +} +func (d *Devops) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} +func (d *Devops) GetPipelineBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineBranch, error) { + return nil, nil +} +func (d *Devops) ScanBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} + +// Common pipeline operator interface +func (d *Devops) GetConsoleLog(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} +func (d *Devops) GetCrumb(httpParameters *devops.HttpParameters) (*devops.Crumb, error) { + return nil, nil +} + +// SCM operator interface +func (d *Devops) GetSCMServers(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMServer, error) { + return nil, nil +} +func (d *Devops) GetSCMOrg(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMOrg, error) { + return nil, nil +} +func (d *Devops) GetOrgRepo(scmId, organizationId string, httpParameters *devops.HttpParameters) ([]devops.OrgRepo, error) { + return nil, nil +} +func (d *Devops) CreateSCMServers(scmId string, httpParameters *devops.HttpParameters) (*devops.SCMServer, error) { + return nil, nil +} +func (d *Devops) Validate(scmId string, httpParameters *devops.HttpParameters) (*devops.Validates, error) { + return nil, nil +} + +//Webhook operator interface +func (d *Devops) GetNotifyCommit(httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} +func (d *Devops) GithubWebhook(httpParameters *devops.HttpParameters) ([]byte, error) { + return nil, nil +} + +func (d *Devops) CheckScriptCompile(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.CheckScript, error) { + return nil, nil +} +func (d *Devops) CheckCron(projectName string, httpParameters *devops.HttpParameters) (*devops.CheckCronRes, error) { + return nil, nil +} +func (d *Devops) ToJenkinsfile(httpParameters *devops.HttpParameters) (*devops.ResJenkinsfile, error) { + return nil, nil +} +func (d *Devops) ToJson(httpParameters *devops.HttpParameters) (*devops.ResJson, error) { + return nil, nil +} + +// CredentialOperator +func (d *Devops) CreateCredentialInProject(projectId string, credential *v1.Secret) (string, error) { + if _, ok := d.Credentials[projectId][credential.Name]; ok { + err := fmt.Errorf("credential name [%s] has been used", credential.Name) + return "", restful.NewError(http.StatusConflict, err.Error()) + } + d.Credentials[projectId][credential.Name] = credential + return credential.Name, nil +} +func (d *Devops) UpdateCredentialInProject(projectId string, credential *v1.Secret) (string, error) { + if _, ok := d.Credentials[projectId][credential.Name]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return "", err + } + d.Credentials[projectId][credential.Name] = credential + return credential.Name, nil +} + +func (d *Devops) GetCredentialInProject(projectId, id string) (*devops.Credential, error) { + if _, ok := d.Credentials[projectId][id]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return nil, err + } + return &devops.Credential{Id: id}, nil +} +func (d *Devops) GetCredentialsInProject(projectId string) ([]*devops.Credential, error) { + return nil, nil +} +func (d *Devops) DeleteCredentialInProject(projectId, id string) (string, error) { + if _, ok := d.Credentials[projectId][id]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return "", err + } + delete(d.Credentials[projectId], id) + return "", nil +} + +// BuildGetter +func (d *Devops) GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*devops.Build, error) { + return nil, nil +} +func (d *Devops) GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*devops.Build, error) { + return nil, nil +} + +// ProjectPipelineOperator +func (d *Devops) CreateProjectPipeline(projectId string, pipeline *devopsv1alpha3.Pipeline) (string, error) { + if _, ok := d.Pipelines[projectId][pipeline.Name]; ok { + err := fmt.Errorf("pipeline name [%s] has been used", pipeline.Name) + return "", restful.NewError(http.StatusConflict, err.Error()) + } + d.Pipelines[projectId][pipeline.Name] = pipeline + return "", nil +} + +func (d *Devops) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { + if _, ok := d.Pipelines[projectId][pipelineId]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return "", err + } + delete(d.Pipelines[projectId], pipelineId) + return "", nil +} + +func (d *Devops) UpdateProjectPipeline(projectId string, pipeline *devopsv1alpha3.Pipeline) (string, error) { + if _, ok := d.Pipelines[projectId][pipeline.Name]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return "", err + } + d.Pipelines[projectId][pipeline.Name] = pipeline + return "", nil +} + +func (d *Devops) GetProjectPipelineConfig(projectId, pipelineId string) (*devopsv1alpha3.Pipeline, error) { + if _, ok := d.Pipelines[projectId][pipelineId]; !ok { + err := &devops.ErrorResponse{ + Body: []byte{}, + Response: &http.Response{ + Status: "404 Not Found", + StatusCode: 404, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 50, + Header: http.Header{ + "Foo": []string{"Bar"}, + }, + Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used + Request: &http.Request{ + Method: "", + URL: &url.URL{ + Scheme: "", + Opaque: "", + User: nil, + Host: "", + Path: "", + RawPath: "", + ForceQuery: false, + RawQuery: "", + Fragment: "", + }, + }, + }, + Message: "", + } + return nil, err + } + + return d.Pipelines[projectId][pipelineId], nil +} diff --git a/pkg/simple/client/devops/interface.go b/pkg/simple/client/devops/interface.go new file mode 100644 index 000000000..2cf85ca48 --- /dev/null +++ b/pkg/simple/client/devops/interface.go @@ -0,0 +1,44 @@ +package devops + +import ( + "fmt" + "github.com/asaskevich/govalidator" + "net/http" + "strconv" +) + +type Interface interface { + CredentialOperator + + BuildGetter + + PipelineOperator + + ProjectPipelineOperator + + ProjectOperator +} + +func GetDevOpsStatusCode(devopsErr error) int { + if code, err := strconv.Atoi(devopsErr.Error()); err == nil { + message := http.StatusText(code) + if !govalidator.IsNull(message) { + return code + } + } + if jErr, ok := devopsErr.(*ErrorResponse); ok { + return jErr.Response.StatusCode + } + return http.StatusInternalServerError +} + +type ErrorResponse struct { + Body []byte + Response *http.Response + Message string +} + +func (e *ErrorResponse) Error() string { + u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, e.Response.Request.URL.RequestURI()) + return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message) +} diff --git a/pkg/gojenkins/README.md b/pkg/simple/client/devops/jenkins/README.md similarity index 100% rename from pkg/gojenkins/README.md rename to pkg/simple/client/devops/jenkins/README.md diff --git a/pkg/simple/client/devops/jenkins/build.go b/pkg/simple/client/devops/jenkins/build.go new file mode 100644 index 000000000..657c52061 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/build.go @@ -0,0 +1,414 @@ +// Copyright 2015 Vadim Kravcenko +// +// 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 jenkins + +import ( + "bytes" + "errors" + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" + "net/url" + "strconv" + "time" +) + +const ( + Git = "git" + Hg = "hg" + Svn = "svc" +) + +type Build struct { + Raw *devops.Build + Job *Job + Jenkins *Jenkins + Base string + Depth int +} + +type Parameter struct { + Name string + Value string +} + +type Branch struct { + SHA1 string `json:",omitempty"` + Name string `json:",omitempty"` +} + +type BuildRevision struct { + SHA1 string `json:"SHA1,omitempty"` + Branch []Branch `json:"Branch,omitempty"` +} + +type Builds struct { + BuildNumber int64 `json:"buildNumber"` + BuildResult interface{} `json:"buildResult"` + Marked BuildRevision `json:"marked"` + Revision BuildRevision `json:"revision"` +} + +type Culprit struct { + AbsoluteUrl string + FullName string +} + +type GeneralObj struct { + Parameters []Parameter `json:"parameters,omitempty"` + Causes []map[string]interface{} `json:"causes,omitempty"` + BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"` + LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"` + RemoteUrls []string `json:"remoteUrls,omitempty"` + ScmName string `json:"scmName,omitempty"` + MercurialNodeName string `json:"mercurialNodeName,omitempty"` + MercurialRevisionNumber string `json:"mercurialRevisionNumber,omitempty"` + Subdir interface{} `json:"subdir,omitempty"` + ClassName string `json:"_class,omitempty"` + SonarTaskId string `json:"ceTaskId,omitempty"` + SonarServerUrl string `json:"serverUrl,omitempty"` + SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"` + TotalCount int64 `json:",omitempty"` + UrlName string `json:",omitempty"` +} + +type TestResult struct { + Duration int64 `json:"duration"` + Empty bool `json:"empty"` + FailCount int64 `json:"failCount"` + PassCount int64 `json:"passCount"` + SkipCount int64 `json:"skipCount"` + Suites []struct { + Cases []struct { + Age int64 `json:"age"` + ClassName string `json:"className"` + Duration int64 `json:"duration"` + ErrorDetails interface{} `json:"errorDetails"` + ErrorStackTrace interface{} `json:"errorStackTrace"` + FailedSince int64 `json:"failedSince"` + Name string `json:"name"` + Skipped bool `json:"skipped"` + SkippedMessage interface{} `json:"skippedMessage"` + Status string `json:"status"` + Stderr interface{} `json:"stderr"` + Stdout interface{} `json:"stdout"` + } `json:"cases"` + Duration int64 `json:"duration"` + ID interface{} `json:"id"` + Name string `json:"name"` + Stderr interface{} `json:"stderr"` + Stdout interface{} `json:"stdout"` + Timestamp interface{} `json:"timestamp"` + } `json:"suites"` +} + +type BuildResponse struct { + Actions []devops.GeneralAction + Artifacts []struct { + DisplayPath string `json:"displayPath"` + FileName string `json:"fileName"` + RelativePath string `json:"relativePath"` + } `json:"artifacts"` + Building bool `json:"building"` + BuiltOn string `json:"builtOn"` + ChangeSet struct { + Items []struct { + AffectedPaths []string `json:"affectedPaths"` + Author struct { + AbsoluteUrl string `json:"absoluteUrl"` + FullName string `json:"fullName"` + } `json:"author"` + Comment string `json:"comment"` + CommitID string `json:"commitId"` + Date string `json:"date"` + ID string `json:"id"` + Msg string `json:"msg"` + Paths []struct { + EditType string `json:"editType"` + File string `json:"file"` + } `json:"paths"` + Timestamp int64 `json:"timestamp"` + } `json:"items"` + Kind string `json:"kind"` + Revisions []struct { + Module string + Revision int + } `json:"revision"` + } `json:"changeSet"` + Culprits []devops.Culprit `json:"culprits"` + Description interface{} `json:"description"` + Duration int64 `json:"duration"` + EstimatedDuration int64 `json:"estimatedDuration"` + Executor interface{} `json:"executor"` + FullDisplayName string `json:"fullDisplayName"` + ID string `json:"id"` + KeepLog bool `json:"keepLog"` + Number int64 `json:"number"` + QueueID int64 `json:"queueId"` + Result string `json:"result"` + Timestamp int64 `json:"timestamp"` + URL string `json:"url"` + MavenArtifacts interface{} `json:"mavenArtifacts"` + MavenVersionUsed string `json:"mavenVersionUsed"` + Runs []struct { + Number int64 + URL string + } `json:"runs"` +} + +// Builds +func (b *Build) Info() *devops.Build { + return b.Raw +} + +func (b *Build) GetUrl() string { + return b.Raw.URL +} + +func (b *Build) GetBuildNumber() int64 { + return b.Raw.Number +} +func (b *Build) GetResult() string { + return b.Raw.Result +} + +func (b *Build) Stop() (bool, error) { + if b.IsRunning() { + response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil) + if err != nil { + return false, err + } + if response.StatusCode != http.StatusOK { + return false, errors.New(strconv.Itoa(response.StatusCode)) + } + } + return true, nil +} + +func (b *Build) GetConsoleOutput() string { + url := b.Base + "/consoleText" + var content string + b.Jenkins.Requester.GetXML(url, &content, nil) + return content +} + +func (b *Build) GetCauses() ([]map[string]interface{}, error) { + _, err := b.Poll() + if err != nil { + return nil, err + } + for _, a := range b.Raw.Actions { + if a.Causes != nil { + return a.Causes, nil + } + } + return nil, errors.New("No Causes") +} + +func (b *Build) GetInjectedEnvVars() (map[string]string, error) { + var envVars struct { + EnvMap map[string]string `json:"envMap"` + } + endpoint := b.Base + "/injectedEnvVars" + _, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil) + if err != nil { + return envVars.EnvMap, err + } + return envVars.EnvMap, nil +} + +func (b *Build) GetDownstreamBuilds() ([]*Build, error) { + result := make([]*Build, 0) + downstreamJobs, err := b.Job.GetDownstreamJobs() + if err != nil { + return nil, err + } + for _, job := range downstreamJobs { + allBuildIDs, err := job.GetAllBuildIds() + if err != nil { + return nil, err + } + for _, buildID := range allBuildIDs { + build, err := job.GetBuild(buildID.Number) + if err != nil { + return nil, err + } + upstreamBuild, _ := build.GetUpstreamBuild() + // cannot compare only id, it can be from different job + if b.GetUrl() == upstreamBuild.GetUrl() { + result = append(result, build) + break + } + } + } + return result, nil +} + +func (b *Build) GetUpstreamJob() (*Job, error) { + causes, err := b.GetCauses() + if err != nil { + return nil, err + } + if len(causes) > 0 { + if job, ok := causes[0]["upstreamProject"]; ok { + return b.Jenkins.GetJob(job.(string)) + } + } + return nil, errors.New("Unable to get Upstream Job") +} + +func (b *Build) GetUpstreamBuildNumber() (int64, error) { + causes, err := b.GetCauses() + if err != nil { + return 0, err + } + if len(causes) > 0 { + if build, ok := causes[0]["upstreamBuild"]; ok { + switch t := build.(type) { + default: + return t.(int64), nil + case float64: + return int64(t), nil + } + } + } + return 0, nil +} + +func (b *Build) GetUpstreamBuild() (*Build, error) { + job, err := b.GetUpstreamJob() + if err != nil { + return nil, err + } + if job != nil { + buildNumber, err := b.GetUpstreamBuildNumber() + if err == nil { + return job.GetBuild(buildNumber) + } + } + return nil, errors.New("Build not found") +} + +func (b *Build) GetResultSet() (*TestResult, error) { + + url := b.Base + "/testReport" + var report TestResult + + _, err := b.Jenkins.Requester.GetJSON(url, &report, nil) + if err != nil { + return nil, err + } + + return &report, nil + +} + +func (b *Build) GetTimestamp() time.Time { + msInt := int64(b.Raw.Timestamp) + return time.Unix(0, msInt*int64(time.Millisecond)) +} + +func (b *Build) GetDuration() int64 { + return b.Raw.Duration +} + +func (b *Build) GetRevisionBranch() string { + vcs := b.Raw.ChangeSet.Kind + if vcs == Git { + for _, a := range b.Raw.Actions { + if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" { + return a.LastBuiltRevision.Branch[0].SHA1 + } + } + } else { + panic("Not implemented") + } + return "" +} + +func (b *Build) IsGood() bool { + return !b.IsRunning() && b.Raw.Result == STATUS_SUCCESS +} + +func (b *Build) IsRunning() bool { + _, err := b.Poll() + if err != nil { + return false + } + return b.Raw.Building +} + +func (b *Build) SetDescription(description string) error { + data := url.Values{} + data.Set("description", description) + _, err := b.Jenkins.Requester.Post(b.Base+"/submitDescription", bytes.NewBufferString(data.Encode()), nil, nil) + return err +} +func (b *Build) PauseToggle() error { + response, err := b.Jenkins.Requester.Post(b.Base+"/pause/toggle", nil, nil, nil) + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + return errors.New(strconv.Itoa(response.StatusCode)) + } + return nil +} + +// Poll for current data. Optional Parameter - depth. +// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API +func (b *Build) Poll(options ...interface{}) (int, error) { + depth := "-1" + + for _, o := range options { + switch v := o.(type) { + case string: + depth = v + case int: + depth = strconv.Itoa(v) + case int64: + depth = strconv.FormatInt(v, 10) + } + } + if depth == "-1" { + depth = strconv.Itoa(b.Depth) + } + + qr := map[string]string{ + "depth": depth, + } + response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr) + if err != nil { + return 0, err + } + return response.StatusCode, nil +} + +func (j *Jenkins) GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*devops.Build, error) { + job, err := j.GetJob(pipelineId, projectId) + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + build, err := job.getBuildByType(status) + return build.Raw, nil +} +func (j *Jenkins) GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*devops.Build, error) { + job, err := j.GetJob(pipelineId, projectId, branch) + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + build, err := job.getBuildByType(status) + return build.Raw, nil +} diff --git a/pkg/simple/client/devops/jenkins/constants.go b/pkg/simple/client/devops/jenkins/constants.go new file mode 100644 index 000000000..7c8c2a8aa --- /dev/null +++ b/pkg/simple/client/devops/jenkins/constants.go @@ -0,0 +1,29 @@ +package jenkins + +const ( + STATUS_FAIL = "FAIL" + STATUS_ERROR = "ERROR" + STATUS_ABORTED = "ABORTED" + STATUS_REGRESSION = "REGRESSION" + STATUS_SUCCESS = "SUCCESS" + STATUS_FIXED = "FIXED" + STATUS_PASSED = "PASSED" + RESULT_STATUS_FAILURE = "FAILURE" + RESULT_STATUS_FAILED = "FAILED" + RESULT_STATUS_SKIPPED = "SKIPPED" + STR_RE_SPLIT_VIEW = "(.*)/view/([^/]*)/?" +) + +const ( + GLOBAL_ROLE = "globalRoles" + PROJECT_ROLE = "projectRoles" +) + +var ParameterTypeMap = map[string]string{ + "hudson.model.StringParameterDefinition": "string", + "hudson.model.ChoiceParameterDefinition": "choice", + "hudson.model.TextParameterDefinition": "text", + "hudson.model.BooleanParameterDefinition": "boolean", + "hudson.model.FileParameterDefinition": "file", + "hudson.model.PasswordParameterDefinition": "password", +} diff --git a/pkg/simple/client/devops/jenkins/credential.go b/pkg/simple/client/devops/jenkins/credential.go new file mode 100644 index 000000000..b67c65774 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/credential.go @@ -0,0 +1,288 @@ +/* +Copyright 2018 The KubeSphere 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 jenkins + +import ( + "errors" + "fmt" + "github.com/emicklei/go-restful" + v1 "k8s.io/api/core/v1" + "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" + "strconv" +) + +const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey" +const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource" +const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" +const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" +const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials" +const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource" +const GLOBALScope = "GLOBAL" + +type UsernamePasswordCredential struct { + Scope string `json:"scope"` + Id string `json:"id"` + Username string `json:"username"` + Password string `json:"password"` + Description string `json:"description"` + StaplerClass string `json:"stapler-class"` +} + +type SshCredential struct { + Scope string `json:"scope"` + Id string `json:"id"` + Username string `json:"username"` + Passphrase string `json:"passphrase"` + KeySource PrivateKeySource `json:"privateKeySource"` + Description string `json:"description"` + StaplerClass string `json:"stapler-class"` +} + +type SecretTextCredential struct { + Scope string `json:"scope"` + Id string `json:"id"` + Secret string `json:"secret"` + Description string `json:"description"` + StaplerClass string `json:"stapler-class"` +} + +type KubeconfigCredential struct { + Scope string `json:"scope"` + Id string `json:"id"` + Description string `json:"description"` + KubeconfigSource KubeconfigSource `json:"kubeconfigSource"` + StaplerClass string `json:"stapler-class"` +} + +type PrivateKeySource struct { + StaplerClass string `json:"stapler-class"` + PrivateKey string `json:"privateKey"` +} + +type KubeconfigSource struct { + StaplerClass string `json:"stapler-class"` + Content string `json:"content"` +} + +type CredentialResponse struct { + Id string `json:"id"` + TypeName string `json:"typeName"` + DisplayName string `json:"displayName"` + Fingerprint *struct { + FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` + Hash string `json:"hash,omitempty" description:"Credential's hash"` + Usage []*struct { + Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` + Ranges struct { + Ranges []*struct { + Start int `json:"start,omitempty" description:"Start build number"` + End int `json:"end,omitempty" description:"End build number"` + } `json:"ranges,omitempty"` + } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` + } `json:"usage,omitempty" description:"all usage of Credential"` + } `json:"fingerprint,omitempty" description:"usage of the Credential"` + Description string `json:"description,omitempty"` + Domain string `json:"domain"` +} + +func NewSshCredential(secret *v1.Secret) *SshCredential { + id := secret.Name + username := string(secret.Data[devopsv1alpha3.SSHAuthUsernameKey]) + passphrase := string(secret.Data[devopsv1alpha3.SSHAuthPassphraseKey]) + privatekey := string(secret.Data[devopsv1alpha3.SSHAuthPrivateKey]) + + keySource := PrivateKeySource{ + StaplerClass: DirectSSHCrenditalStaplerClass, + PrivateKey: privatekey, + } + + return &SshCredential{ + Scope: GLOBALScope, + Id: id, + Username: username, + Passphrase: passphrase, + KeySource: keySource, + StaplerClass: SSHCrenditalStaplerClass, + } +} + +func NewUsernamePasswordCredential(secret *v1.Secret) *UsernamePasswordCredential { + id := secret.Name + username := string(secret.Data[devopsv1alpha3.BasicAuthUsernameKey]) + password := string(secret.Data[devopsv1alpha3.BasicAuthPasswordKey]) + return &UsernamePasswordCredential{ + Scope: GLOBALScope, + Id: id, + Username: username, + Password: password, + StaplerClass: UsernamePassswordCredentialStaplerClass, + } +} + +func NewSecretTextCredential(secret *v1.Secret) *SecretTextCredential { + id := secret.Name + secretContent := string(secret.Data[devopsv1alpha3.SecretTextSecretKey]) + return &SecretTextCredential{ + Scope: GLOBALScope, + Id: id, + Secret: secretContent, + StaplerClass: SecretTextCredentialStaplerClass, + } +} + +func NewKubeconfigCredential(secret *v1.Secret) *KubeconfigCredential { + id := secret.Name + secretContent := string(secret.Data[devopsv1alpha3.KubeConfigSecretKey]) + + credentialSource := KubeconfigSource{ + StaplerClass: DirectKubeconfigCredentialStaperClass, + Content: secretContent, + } + + return &KubeconfigCredential{ + Scope: GLOBALScope, + Id: id, + KubeconfigSource: credentialSource, + StaplerClass: KubeconfigCredentialStaplerClass, + } +} + +func (j *Jenkins) GetCredentialInProject(projectId, id string) (*devops.Credential, error) { + responseStruct := &devops.Credential{} + + domain := "_" + + response, err := j.Requester.GetJSON( + fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s", projectId, id), + responseStruct, map[string]string{ + "depth": "2", + }) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + responseStruct.Domain = domain + return responseStruct, nil +} + +func (j *Jenkins) GetCredentialsInProject(projectId string) ([]*devops.Credential, error) { + domain := "_" + var responseStruct = &struct { + Credentials []*devops.Credential `json:"credentials"` + }{} + response, err := j.Requester.GetJSON( + fmt.Sprintf("/job/%s/credentials/store/folder/domain/_", projectId), + responseStruct, map[string]string{ + "depth": "2", + }) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + for _, credential := range responseStruct.Credentials { + credential.Domain = domain + } + return responseStruct.Credentials, nil + +} + +func (j *Jenkins) CreateCredentialInProject(projectId string, credential *v1.Secret) (string, error) { + + var request interface{} + responseString := "" + switch credential.Type { + case devopsv1alpha3.SecretTypeBasicAuth: + request = NewUsernamePasswordCredential(credential) + case devopsv1alpha3.SecretTypeSSHAuth: + request = NewSshCredential(credential) + case devopsv1alpha3.SecretTypeSecretText: + request = NewSecretTextCredential(credential) + case devopsv1alpha3.SecretTypeKubeConfig: + request = NewKubeconfigCredential(credential) + default: + err := fmt.Errorf("error unsupport credential type") + klog.Errorf("%+v", err) + return "", restful.NewError(http.StatusBadRequest, err.Error()) + } + + response, err := j.Requester.Post( + fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/createCredentials", projectId), + nil, &responseString, map[string]string{ + "json": makeJson(map[string]interface{}{ + "credentials": request, + }), + }) + if err != nil { + return "", err + } + if response.StatusCode != http.StatusOK { + return "", errors.New(strconv.Itoa(response.StatusCode)) + } + return credential.Name, nil +} + +func (j *Jenkins) UpdateCredentialInProject(projectId string, credential *v1.Secret) (string, error) { + + requestContent := "" + switch credential.Type { + case devopsv1alpha3.SecretTypeBasicAuth: + requestStruct := NewUsernamePasswordCredential(credential) + requestContent = makeJson(requestStruct) + case devopsv1alpha3.SecretTypeSSHAuth: + requestStruct := NewSshCredential(credential) + requestContent = makeJson(requestStruct) + case devopsv1alpha3.SecretTypeSecretText: + requestStruct := NewSecretTextCredential(credential) + requestContent = makeJson(requestStruct) + case devopsv1alpha3.SecretTypeKubeConfig: + requestStruct := NewKubeconfigCredential(credential) + requestContent = makeJson(requestStruct) + default: + err := fmt.Errorf("error unsupport credential type") + klog.Errorf("%+v", err) + return "", restful.NewError(http.StatusBadRequest, err.Error()) + } + response, err := j.Requester.Post( + fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/updateSubmit", projectId, credential.Name), + nil, nil, map[string]string{ + "json": requestContent, + }) + if err != nil { + return "", err + } + if response.StatusCode != http.StatusOK { + return "", errors.New(strconv.Itoa(response.StatusCode)) + } + return credential.Name, nil +} + +func (j *Jenkins) DeleteCredentialInProject(projectId, id string) (string, error) { + response, err := j.Requester.Post( + fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/doDelete", projectId, id), + nil, nil, nil) + if err != nil { + return "", err + } + if response.StatusCode != http.StatusOK { + return "", errors.New(strconv.Itoa(response.StatusCode)) + } + return id, nil +} diff --git a/pkg/simple/client/devops/jenkins/credential_test.go b/pkg/simple/client/devops/jenkins/credential_test.go new file mode 100644 index 000000000..46e23cb0a --- /dev/null +++ b/pkg/simple/client/devops/jenkins/credential_test.go @@ -0,0 +1,133 @@ +package jenkins + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestNewUsernamePasswordCredential(t *testing.T) { + username := "test-user" + password := "password" + name := "test-secret" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "test", + }, + Data: map[string][]byte{ + "username": []byte(username), + "password": []byte(password), + }, + Type: "credential.devops.kubesphere.io/basic-auth", + } + credential := NewUsernamePasswordCredential(secret) + if credential.StaplerClass != "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" { + t.Fatalf("credential's stapler class should be com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"+ + "other than %s ", credential.StaplerClass) + } + if credential.Id != name { + t.Fatalf("credential's id should be %s "+ + "other than %s ", name, credential.Id) + } + if credential.Username != username { + t.Fatalf("credential's username should be %s "+ + "other than %s ", username, credential.Username) + } + if credential.Password != password { + t.Fatalf("credential's password should be %s "+ + "other than %s ", password, credential.Password) + } +} + +func TestNewSshCredential(t *testing.T) { + username := "test-user" + passphrase := "passphrase" + privatekey := "pk" + name := "test-secret" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "test", + }, + Data: map[string][]byte{ + "username": []byte(username), + "passphrase": []byte(passphrase), + "privatekey": []byte(privatekey), + }, + Type: "credential.devops.kubesphere.io/ssh-auth", + } + credential := NewSshCredential(secret) + if credential.StaplerClass != "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey" { + t.Fatalf("credential's stapler class should be com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"+ + "other than %s ", credential.StaplerClass) + } + if credential.Id != name { + t.Fatalf("credential's id should be %s "+ + "other than %s ", name, credential.Id) + } + if credential.Username != username { + t.Fatalf("credential's username should be %s "+ + "other than %s ", username, credential.Username) + } + if credential.Passphrase != passphrase { + t.Fatalf("credential's passphrase should be %s "+ + "other than %s ", passphrase, credential.Passphrase) + } + if credential.KeySource.PrivateKey != privatekey { + t.Fatalf("credential's privatekey should be %s "+ + "other than %s ", privatekey, credential.KeySource.PrivateKey) + } +} + +func TestNewKubeconfigCredential(t *testing.T) { + content := []byte("test-content") + name := "test-secret" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "test", + }, + Type: "credential.devops.kubesphere.io/kubeconfig", + Data: map[string][]byte{"secret": content}, + } + credential := NewKubeconfigCredential(secret) + if credential.StaplerClass != "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials" { + t.Fatalf("credential's stapler class should be com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials"+ + "other than %s ", credential.StaplerClass) + } + if credential.Id != name { + t.Fatalf("credential's id should be %s "+ + "other than %s ", name, credential.Id) + } + if credential.KubeconfigSource.Content != string(content) { + t.Fatalf("credential's content should be %s "+ + "other than %s ", string(content), credential.KubeconfigSource.Content) + } +} + +func TestNewSecretTextCredential(t *testing.T) { + content := []byte("test-content") + name := "test-secret" + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "test", + }, + Type: "credential.devops.kubesphere.io/secret-text", + Data: map[string][]byte{"secret": content}, + } + credential := NewSecretTextCredential(secret) + if credential.StaplerClass != "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" { + t.Fatalf("credential's stapler class should be org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"+ + "other than %s ", credential.StaplerClass) + } + if credential.Id != name { + t.Fatalf("credential's id should be %s "+ + "other than %s ", name, credential.Id) + } + if credential.Secret != string(content) { + t.Fatalf("credential's content should be %s "+ + "other than %s ", string(content), credential.Secret) + } +} diff --git a/pkg/simple/client/devops/jenkins/devops.go b/pkg/simple/client/devops/jenkins/devops.go new file mode 100644 index 000000000..61e5ec777 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/devops.go @@ -0,0 +1,25 @@ +/* +Copyright 2018 The KubeSphere 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 jenkins + +import ( + "kubesphere.io/kubesphere/pkg/simple/client/devops" +) + +func NewDevopsClient(options *Options) (devops.Interface, error) { + + jenkins := CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password) + + return jenkins, nil +} diff --git a/pkg/simple/client/devops/jenkins/devops_test.go b/pkg/simple/client/devops/jenkins/devops_test.go new file mode 100644 index 000000000..bd11e5d64 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/devops_test.go @@ -0,0 +1,40 @@ +package jenkins + +import ( + "testing" +) + +func Test_parseCronJobTime(t *testing.T) { + type Except struct { + Last string + Next string + } + + Items := []struct { + Input string + Expected Except + }{ + {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 8:59:09 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:14:09 AM UTC.", Except{Last: "2019-09-10T08:59:09Z", Next: "2019-09-10T09:14:09Z"}}, + {"上次è¿è¡Œçš„æ—¶é—´ Thursday, January 3, 2019 11:56:30 PM UTC; 下次è¿è¡Œçš„æ—¶é—´ Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}}, + {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 8:41:34 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, + {"上次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 9:15:26 AM UTC; 下次è¿è¡Œçš„æ—¶é—´ Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, + {"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, + {"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, + } + + for _, item := range Items { + last, next, err := parseCronJobTime(item.Input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + + if last != item.Expected.Last { + t.Errorf("got %#v, expected %#v", last, item.Expected.Last) + } + + if next != item.Expected.Next { + t.Errorf("got %#v, expected %#v", next, item.Expected.Next) + } + + } +} diff --git a/pkg/gojenkins/folder.go b/pkg/simple/client/devops/jenkins/folder.go similarity index 94% rename from pkg/gojenkins/folder.go rename to pkg/simple/client/devops/jenkins/folder.go index 053a8ef33..1646f6c81 100644 --- a/pkg/gojenkins/folder.go +++ b/pkg/simple/client/devops/jenkins/folder.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package gojenkins +package jenkins import ( "errors" @@ -33,8 +33,6 @@ type FolderResponse struct { Name string `json:"name"` URL string `json:"url"` Jobs []InnerJob `json:"jobs"` - PrimaryView *ViewData `json:"primaryView"` - Views []ViewData `json:"views"` } func (f *Folder) parentBase() string { diff --git a/pkg/simple/client/devops/jenkins/jenkins.go b/pkg/simple/client/devops/jenkins/jenkins.go new file mode 100644 index 000000000..c8ca746fd --- /dev/null +++ b/pkg/simple/client/devops/jenkins/jenkins.go @@ -0,0 +1,803 @@ +// Copyright 2015 Vadim Kravcenko +// +// 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. + +// Gojenkins is a Jenkins Client in Go, that exposes the jenkins REST api in a more developer friendly way. +package jenkins + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "log" + "net/http" + "os" + "reflect" + "strconv" + "strings" +) + +// Basic Authentication +type BasicAuth struct { + Username string + Password string +} + +type Jenkins struct { + Server string + Version string + Requester *Requester +} + +// Loggers +var ( + Info *log.Logger + Warning *log.Logger + Error *log.Logger +) + +// Init Method. Should be called after creating a Jenkins Instance. +// e.g jenkins := CreateJenkins("url").Init() +// HTTP Client is set here, Connection to jenkins is tested here. +func (j *Jenkins) Init() (*Jenkins, error) { + j.initLoggers() + + rsp, err := j.Requester.GetJSON("/", nil, nil) + if err != nil { + return nil, err + } + + j.Version = rsp.Header.Get("X-Jenkins") + //if j.Raw == nil { + // return nil, errors.New("Connection Failed, Please verify that the host and credentials are correct.") + //} + + return j, nil +} + +func (j *Jenkins) initLoggers() { + Info = log.New(os.Stdout, + "INFO: ", + log.Ldate|log.Ltime|log.Lshortfile) + + Warning = log.New(os.Stdout, + "WARNING: ", + log.Ldate|log.Ltime|log.Lshortfile) + + Error = log.New(os.Stderr, + "ERROR: ", + log.Ldate|log.Ltime|log.Lshortfile) +} + +// Create a new folder +// This folder can be nested in other parent folders +// Example: jenkins.CreateFolder("newFolder", "grandparentFolder", "parentFolder") +func (j *Jenkins) CreateFolder(name, description string, parents ...string) (*Folder, error) { + folderObj := &Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, name), "/job/")} + folder, err := folderObj.Create(name, description) + if err != nil { + return nil, err + } + return folder, nil +} + +// Create a new job in the folder +// Example: jenkins.CreateJobInFolder("", "newJobName", "myFolder", "parentFolder") +func (j *Jenkins) CreateJobInFolder(config string, jobName string, parentIDs ...string) (*Job, error) { + jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, jobName), "/job/")} + qr := map[string]string{ + "name": jobName, + } + job, err := jobObj.Create(config, qr) + if err != nil { + return nil, err + } + return job, nil +} + +// Create a new job from config File +// Method takes XML string as first Parameter, and if the name is not specified in the config file +// takes name as string as second Parameter +// e.g jenkins.CreateJob("","newJobName") +func (j *Jenkins) CreateJob(config string, options ...interface{}) (*Job, error) { + qr := make(map[string]string) + if len(options) > 0 { + qr["name"] = options[0].(string) + } else { + return nil, errors.New("Error Creating Job, job name is missing") + } + jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + qr["name"]} + job, err := jobObj.Create(config, qr) + if err != nil { + return nil, err + } + return job, nil +} + +// Rename a job. +// First Parameter job old name, Second Parameter job new name. +func (j *Jenkins) RenameJob(job string, name string) *Job { + jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + job} + jobObj.Rename(name) + return &jobObj +} + +// Create a copy of a job. +// First Parameter Name of the job to copy from, Second Parameter new job name. +func (j *Jenkins) CopyJob(copyFrom string, newName string) (*Job, error) { + job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + copyFrom} + _, err := job.Poll() + if err != nil { + return nil, err + } + return job.Copy(newName) +} + +// Delete a job. +func (j *Jenkins) DeleteJob(name string, parentIDs ...string) (bool, error) { + job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, name), "/job/")} + return job.Delete() +} + +// Invoke a job. +// First Parameter job name, second Parameter is optional Build parameters. +func (j *Jenkins) BuildJob(name string, options ...interface{}) (int64, error) { + job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + name} + var params map[string]string + if len(options) > 0 { + params, _ = options[0].(map[string]string) + } + return job.InvokeSimple(params) +} + +func (j *Jenkins) GetBuild(jobName string, number int64) (*Build, error) { + job, err := j.GetJob(jobName) + if err != nil { + return nil, err + } + build, err := job.GetBuild(number) + + if err != nil { + return nil, err + } + return build, nil +} + +func (j *Jenkins) GetJob(id string, parentIDs ...string) (*Job, error) { + job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, id), "/job/")} + status, err := job.Poll() + if err != nil { + return nil, err + } + if status == 200 { + return &job, nil + } + return nil, errors.New(strconv.Itoa(status)) +} + +func (j *Jenkins) GetFolder(id string, parents ...string) (*Folder, error) { + folder := Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, id), "/job/")} + status, err := folder.Poll() + if err != nil { + return nil, fmt.Errorf("trouble polling folder: %v", err) + } + if status == 200 { + return &folder, nil + } + return nil, errors.New(strconv.Itoa(status)) +} + +// Get all builds Numbers and URLS for a specific job. +// There are only build IDs here, +// To get all the other info of the build use jenkins.GetBuild(job,buildNumber) +// or job.GetBuild(buildNumber) + +func (j *Jenkins) Poll() (int, error) { + resp, err := j.Requester.GetJSON("/", nil, nil) + if err != nil { + return 0, err + } + return resp.StatusCode, nil +} + +func (j *Jenkins) GetGlobalRole(roleName string) (*GlobalRole, error) { + roleResponse := &GlobalRoleResponse{ + RoleName: roleName, + } + stringResponse := "" + response, err := j.Requester.Get("/role-strategy/strategy/getRole", + &stringResponse, + map[string]string{ + "roleName": roleName, + "type": GLOBAL_ROLE, + }) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + if stringResponse == "{}" { + return nil, nil + } + err = json.Unmarshal([]byte(stringResponse), roleResponse) + if err != nil { + return nil, err + } + return &GlobalRole{ + Jenkins: j, + Raw: *roleResponse, + }, nil +} + +func (j *Jenkins) GetProjectRole(roleName string) (*ProjectRole, error) { + roleResponse := &ProjectRoleResponse{ + RoleName: roleName, + } + stringResponse := "" + response, err := j.Requester.Get("/role-strategy/strategy/getRole", + &stringResponse, + map[string]string{ + "roleName": roleName, + "type": PROJECT_ROLE, + }) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + if stringResponse == "{}" { + return nil, nil + } + err = json.Unmarshal([]byte(stringResponse), roleResponse) + if err != nil { + return nil, err + } + return &ProjectRole{ + Jenkins: j, + Raw: *roleResponse, + }, nil +} + +func (j *Jenkins) AddGlobalRole(roleName string, ids GlobalPermissionIds, overwrite bool) (*GlobalRole, error) { + responseRole := &GlobalRole{ + Jenkins: j, + Raw: GlobalRoleResponse{ + RoleName: roleName, + PermissionIds: ids, + }} + var idArray []string + values := reflect.ValueOf(ids) + for i := 0; i < values.NumField(); i++ { + field := values.Field(i) + if field.Bool() { + idArray = append(idArray, values.Type().Field(i).Tag.Get("json")) + } + } + param := map[string]string{ + "roleName": roleName, + "type": GLOBAL_ROLE, + "permissionIds": strings.Join(idArray, ","), + "overwrite": strconv.FormatBool(overwrite), + } + responseString := "" + response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + return responseRole, nil +} + +func (j *Jenkins) DeleteProjectRoles(roleName ...string) error { + responseString := "" + + response, err := j.Requester.Post("/role-strategy/strategy/removeRoles", nil, &responseString, map[string]string{ + "type": PROJECT_ROLE, + "roleNames": strings.Join(roleName, ","), + }) + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + fmt.Println(responseString) + return errors.New(strconv.Itoa(response.StatusCode)) + } + return nil +} + +func (j *Jenkins) AddProjectRole(roleName string, pattern string, ids ProjectPermissionIds, overwrite bool) (*ProjectRole, error) { + responseRole := &ProjectRole{ + Jenkins: j, + Raw: ProjectRoleResponse{ + RoleName: roleName, + PermissionIds: ids, + Pattern: pattern, + }} + var idArray []string + values := reflect.ValueOf(ids) + for i := 0; i < values.NumField(); i++ { + field := values.Field(i) + if field.Bool() { + idArray = append(idArray, values.Type().Field(i).Tag.Get("json")) + } + } + param := map[string]string{ + "roleName": roleName, + "type": PROJECT_ROLE, + "permissionIds": strings.Join(idArray, ","), + "overwrite": strconv.FormatBool(overwrite), + "pattern": pattern, + } + responseString := "" + response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, errors.New(strconv.Itoa(response.StatusCode)) + } + return responseRole, nil +} + +func (j *Jenkins) DeleteUserInProject(username string) error { + param := map[string]string{ + "type": PROJECT_ROLE, + "sid": username, + } + responseString := "" + response, err := j.Requester.Post("/role-strategy/strategy/deleteSid", nil, &responseString, param) + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + return errors.New(strconv.Itoa(response.StatusCode)) + } + return nil +} + +func (j *Jenkins) GetPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.Pipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetPipelineUrl, projectName, pipelineName), + } + res, err := PipelineOjb.GetPipeline() + return res, err +} + +func (j *Jenkins) ListPipelines(httpParameters *devops.HttpParameters) (*devops.PipelineList, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: ListPipelinesUrl + httpParameters.Url.RawQuery, + } + res, err := PipelineOjb.ListPipelines() + return res, err +} + +func (j *Jenkins) GetPipelineRun(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetPipelineRunUrl, projectName, pipelineName, runId), + } + res, err := PipelineOjb.GetPipelineRun() + return res, err +} + +func (j *Jenkins) ListPipelineRuns(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineRunList, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: ListPipelineRunUrl + httpParameters.Url.RawQuery, + } + res, err := PipelineOjb.ListPipelineRuns() + return res, err +} + +func (j *Jenkins) StopPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(StopPipelineUrl, projectName, pipelineName, runId), + } + res, err := PipelineOjb.StopPipeline() + return res, err +} + +func (j *Jenkins) ReplayPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(ReplayPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId), + } + res, err := PipelineOjb.ReplayPipeline() + return res, err +} + +func (j *Jenkins) RunPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(RunPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName), + } + res, err := PipelineOjb.RunPipeline() + return res, err +} + +func (j *Jenkins) GetArtifacts(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetArtifactsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId), + } + res, err := PipelineOjb.GetArtifacts() + return res, err +} + +func (j *Jenkins) GetRunLog(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetRunLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId), + } + res, err := PipelineOjb.GetRunLog() + return res, err +} + +func (j *Jenkins) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetStepLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId, stepId), + } + res, header, err := PipelineOjb.GetStepLog() + return res, header, err +} + +func (j *Jenkins) GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetNodeStepsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId), + } + res, err := PipelineOjb.GetNodeSteps() + return res, err +} + +func (j *Jenkins) GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.PipelineRunNodes, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetPipelineRunNodesUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId), + } + res, err := PipelineOjb.GetPipelineRunNodes() + return res, err +} + +func (j *Jenkins) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(SubmitInputStepUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId, stepId), + } + res, err := PipelineOjb.SubmitInputStep() + return res, err +} + +func (j *Jenkins) GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.BranchPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchPipelineUrl, projectName, pipelineName, branchName), + } + res, err := PipelineOjb.GetBranchPipeline() + return res, err +} + +func (j *Jenkins) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchPipelineRunUrl, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.GetBranchPipelineRun() + return res, err +} + +func (j *Jenkins) StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(StopBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.StopBranchPipeline() + return res, err +} + +func (j *Jenkins) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(ReplayBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.ReplayBranchPipeline() + return res, err +} + +func (j *Jenkins) RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(RunBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName), + } + res, err := PipelineOjb.RunBranchPipeline() + return res, err +} + +func (j *Jenkins) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchArtifactsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.GetBranchArtifacts() + return res, err +} + +func (j *Jenkins) GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchRunLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.GetBranchRunLog() + return res, err +} + +func (j *Jenkins) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchStepLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId), + } + res, header, err := PipelineOjb.GetBranchStepLog() + return res, header, err +} + +func (j *Jenkins) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchNodeStepsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId), + } + res, err := PipelineOjb.GetBranchNodeSteps() + return res, err +} + +func (j *Jenkins) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.BranchPipelineRunNodes, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetBranchPipeRunNodesUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId), + } + res, err := PipelineOjb.GetBranchPipelineRunNodes() + return res, err +} + +func (j *Jenkins) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(CheckBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId), + } + res, err := PipelineOjb.SubmitBranchInputStep() + return res, err +} + +func (j *Jenkins) GetPipelineBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineBranch, error) { + path := fmt.Sprintf(GetPipeBranchUrl, projectName, pipelineName) + httpParameters.Url.RawQuery + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: path, + } + res, err := PipelineOjb.GetPipelineBranch() + return res, err +} + +func (j *Jenkins) ScanBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(ScanBranchUrl+httpParameters.Url.RawQuery, projectName, pipelineName), + } + res, err := PipelineOjb.ScanBranch() + return res, err +} + +func (j *Jenkins) GetConsoleLog(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetConsoleLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName), + } + res, err := PipelineOjb.GetConsoleLog() + return res, err +} + +func (j *Jenkins) GetCrumb(httpParameters *devops.HttpParameters) (*devops.Crumb, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: GetCrumbUrl, + } + res, err := PipelineOjb.GetCrumb() + return res, err +} + +func (j *Jenkins) GetSCMServers(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMServer, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: GetSCMServersUrl, + } + res, err := PipelineOjb.GetSCMServers() + return res, err +} + +func (j *Jenkins) GetSCMOrg(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMOrg, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetSCMOrgUrl+httpParameters.Url.RawQuery, scmId), + } + res, err := PipelineOjb.GetSCMOrg() + return res, err +} + +func (j *Jenkins) GetOrgRepo(scmId, organizationId string, httpParameters *devops.HttpParameters) ([]devops.OrgRepo, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(GetOrgRepoUrl+httpParameters.Url.RawQuery, scmId, organizationId), + } + res, err := PipelineOjb.GetOrgRepo() + return res, err +} + +func (j *Jenkins) CreateSCMServers(scmId string, httpParameters *devops.HttpParameters) (*devops.SCMServer, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(CreateSCMServersUrl, scmId), + } + res, err := PipelineOjb.CreateSCMServers() + return res, err +} + +func (j *Jenkins) GetNotifyCommit(httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: GetNotifyCommitUrl + httpParameters.Url.RawQuery, + } + res, err := PipelineOjb.GetNotifyCommit() + return res, err +} + +func (j *Jenkins) GithubWebhook(httpParameters *devops.HttpParameters) ([]byte, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: GithubWebhookUrl + httpParameters.Url.RawQuery, + } + res, err := PipelineOjb.GithubWebhook() + return res, err +} + +func (j *Jenkins) Validate(scmId string, httpParameters *devops.HttpParameters) (*devops.Validates, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(ValidateUrl, scmId), + } + res, err := PipelineOjb.Validate() + return res, err +} + +func (j *Jenkins) CheckScriptCompile(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.CheckScript, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: fmt.Sprintf(CheckScriptCompileUrl, projectName, pipelineName), + } + res, err := PipelineOjb.CheckScriptCompile() + return res, err +} + +func (j *Jenkins) CheckCron(projectName string, httpParameters *devops.HttpParameters) (*devops.CheckCronRes, error) { + var cron = new(devops.CronData) + var reader io.ReadCloser + var path string + + reader = httpParameters.Body + cronData, err := ioutil.ReadAll(reader) + err = json.Unmarshal(cronData, cron) + if err != nil { + klog.Error(err) + return nil, err + } + + if cron.PipelineName != "" { + path = fmt.Sprintf(CheckPipelienCronUrl, projectName, cron.PipelineName, cron.Cron) + } else { + path = fmt.Sprintf(CheckCronUrl, projectName, cron.Cron) + } + + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: path, + } + + res, err := PipelineOjb.CheckCron() + return res, err +} + +func (j *Jenkins) ToJenkinsfile(httpParameters *devops.HttpParameters) (*devops.ResJenkinsfile, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: ToJenkinsfileUrl, + } + res, err := PipelineOjb.ToJenkinsfile() + return res, err +} + +func (j *Jenkins) ToJson(httpParameters *devops.HttpParameters) (*devops.ResJson, error) { + PipelineOjb := &Pipeline{ + HttpParameters: httpParameters, + Jenkins: j, + Path: ToJsonUrl, + } + res, err := PipelineOjb.ToJson() + return res, err +} + +// Creates a new Jenkins Instance +// Optional parameters are: client, username, password +// After creating an instance call init method. +func CreateJenkins(client *http.Client, base string, maxConnection int, auth ...interface{}) *Jenkins { + j := &Jenkins{} + if strings.HasSuffix(base, "/") { + base = base[:len(base)-1] + } + j.Server = base + j.Requester = &Requester{Base: base, SslVerify: true, Client: client, connControl: make(chan struct{}, maxConnection)} + if j.Requester.Client == nil { + j.Requester.Client = http.DefaultClient + } + if len(auth) == 2 { + j.Requester.BasicAuth = &BasicAuth{Username: auth[0].(string), Password: auth[1].(string)} + } + return j +} diff --git a/pkg/simple/client/devops/jenkins/job.go b/pkg/simple/client/devops/jenkins/job.go new file mode 100644 index 000000000..413cd3966 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/job.go @@ -0,0 +1,504 @@ +// Copyright 2015 Vadim Kravcenko +// +// 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 jenkins + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/url" + "path" + "strconv" + "strings" +) + +type Job struct { + Raw *JobResponse + Jenkins *Jenkins + Base string +} + +type JobBuild struct { + Number int64 + URL string +} + +type JobBuildStatus struct { + Number int64 + Building bool + Result string +} + +type InnerJob struct { + Name string `json:"name"` + Url string `json:"url"` + Color string `json:"color"` +} + +type ParameterDefinition struct { + DefaultParameterValue struct { + Name string `json:"name"` + Value interface{} `json:"value"` + } `json:"defaultParameterValue"` + Description string `json:"description"` + Name string `json:"name"` + Type string `json:"type"` +} + +type JobResponse struct { + Class string `json:"_class"` + Actions []devops.GeneralAction + Buildable bool `json:"buildable"` + Builds []JobBuild + Color string `json:"color"` + ConcurrentBuild bool `json:"concurrentBuild"` + Description string `json:"description"` + DisplayName string `json:"displayName"` + DisplayNameOrNull interface{} `json:"displayNameOrNull"` + DownstreamProjects []InnerJob `json:"downstreamProjects"` + FirstBuild JobBuild + HealthReport []struct { + Description string `json:"description"` + IconClassName string `json:"iconClassName"` + IconUrl string `json:"iconUrl"` + Score int64 `json:"score"` + } `json:"healthReport"` + InQueue bool `json:"inQueue"` + KeepDependencies bool `json:"keepDependencies"` + LastBuild JobBuild `json:"lastBuild"` + LastCompletedBuild JobBuild `json:"lastCompletedBuild"` + LastFailedBuild JobBuild `json:"lastFailedBuild"` + LastStableBuild JobBuild `json:"lastStableBuild"` + LastSuccessfulBuild JobBuild `json:"lastSuccessfulBuild"` + LastUnstableBuild JobBuild `json:"lastUnstableBuild"` + LastUnsuccessfulBuild JobBuild `json:"lastUnsuccessfulBuild"` + Name string `json:"name"` + SubJobs []InnerJob `json:"subJobs"` + NextBuildNumber int64 `json:"nextBuildNumber"` + Property []struct { + ParameterDefinitions []ParameterDefinition `json:"parameterDefinitions"` + } `json:"property"` + QueueItem interface{} `json:"queueItem"` + Scm struct{} `json:"scm"` + UpstreamProjects []InnerJob `json:"upstreamProjects"` + URL string `json:"url"` + Jobs []InnerJob `json:"jobs"` +} + +func (j *Job) parentBase() string { + return j.Base[:strings.LastIndex(j.Base, "/job/")] +} + +type History struct { + BuildNumber int + BuildStatus string + BuildTimestamp int64 +} + +func (j *Job) GetName() string { + return j.Raw.Name +} + +func (j *Job) GetDescription() string { + return j.Raw.Description +} + +func (j *Job) GetDetails() *JobResponse { + return j.Raw +} + +func (j *Job) GetBuild(id int64) (*Build, error) { + build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(devops.Build), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)} + status, err := build.Poll() + if err != nil { + return nil, err + } + if status == 200 { + return &build, nil + } + return nil, errors.New(strconv.Itoa(status)) +} + +func (j *Job) getBuildByType(buildType string) (*Build, error) { + allowed := map[string]JobBuild{ + "lastStableBuild": j.Raw.LastStableBuild, + "lastSuccessfulBuild": j.Raw.LastSuccessfulBuild, + "lastBuild": j.Raw.LastBuild, + "lastCompletedBuild": j.Raw.LastCompletedBuild, + "firstBuild": j.Raw.FirstBuild, + "lastFailedBuild": j.Raw.LastFailedBuild, + } + number := "" + if val, ok := allowed[buildType]; ok { + number = strconv.FormatInt(val.Number, 10) + } else { + panic("No Such Build") + } + build := Build{ + Jenkins: j.Jenkins, + Depth: 1, + Job: j, + Raw: new(devops.Build), + Base: j.Base + "/" + number} + status, err := build.Poll() + if err != nil { + return nil, err + } + if status == 200 { + return &build, nil + } + return nil, errors.New(strconv.Itoa(status)) +} + +func (j *Job) GetLastSuccessfulBuild() (*Build, error) { + return j.getBuildByType("lastSuccessfulBuild") +} + +func (j *Job) GetFirstBuild() (*Build, error) { + return j.getBuildByType("firstBuild") +} + +func (j *Job) GetLastBuild() (*Build, error) { + return j.getBuildByType("lastBuild") +} + +func (j *Job) GetLastStableBuild() (*Build, error) { + return j.getBuildByType("lastStableBuild") +} + +func (j *Job) GetLastFailedBuild() (*Build, error) { + return j.getBuildByType("lastFailedBuild") +} + +func (j *Job) GetLastCompletedBuild() (*Build, error) { + return j.getBuildByType("lastCompletedBuild") +} + +// Returns All Builds with Number and URL +func (j *Job) GetAllBuildIds() ([]JobBuild, error) { + var buildsResp struct { + Builds []JobBuild `json:"allBuilds"` + } + _, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,url]"}) + if err != nil { + return nil, err + } + return buildsResp.Builds, nil +} + +func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) { + var buildsResp struct { + Builds []JobBuildStatus `json:"allBuilds"` + } + _, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,building,result]"}) + if err != nil { + return nil, err + } + return buildsResp.Builds, nil +} + +func (j *Job) GetUpstreamJobsMetadata() []InnerJob { + return j.Raw.UpstreamProjects +} + +func (j *Job) GetDownstreamJobsMetadata() []InnerJob { + return j.Raw.DownstreamProjects +} + +func (j *Job) GetInnerJobsMetadata() []InnerJob { + return j.Raw.Jobs +} + +func (j *Job) GetUpstreamJobs() ([]*Job, error) { + jobs := make([]*Job, len(j.Raw.UpstreamProjects)) + for i, job := range j.Raw.UpstreamProjects { + ji, err := j.Jenkins.GetJob(job.Name) + if err != nil { + return nil, err + } + jobs[i] = ji + } + return jobs, nil +} + +func (j *Job) GetDownstreamJobs() ([]*Job, error) { + jobs := make([]*Job, len(j.Raw.DownstreamProjects)) + for i, job := range j.Raw.DownstreamProjects { + ji, err := j.Jenkins.GetJob(job.Name) + if err != nil { + return nil, err + } + jobs[i] = ji + } + return jobs, nil +} + +func (j *Job) GetInnerJob(id string) (*Job, error) { + job := Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: j.Base + "/job/" + id} + status, err := job.Poll() + if err != nil { + return nil, err + } + if status == 200 { + return &job, nil + } + return nil, errors.New(strconv.Itoa(status)) +} + +func (j *Job) GetInnerJobs() ([]*Job, error) { + jobs := make([]*Job, len(j.Raw.Jobs)) + for i, job := range j.Raw.Jobs { + ji, err := j.GetInnerJob(job.Name) + if err != nil { + return nil, err + } + jobs[i] = ji + } + return jobs, nil +} + +func (j *Job) Enable() (bool, error) { + resp, err := j.Jenkins.Requester.Post(j.Base+"/enable", nil, nil, nil) + if err != nil { + return false, err + } + if resp.StatusCode != 200 { + return false, errors.New(strconv.Itoa(resp.StatusCode)) + } + return true, nil +} + +func (j *Job) Disable() (bool, error) { + resp, err := j.Jenkins.Requester.Post(j.Base+"/disable", nil, nil, nil) + if err != nil { + return false, err + } + if resp.StatusCode != 200 { + return false, errors.New(strconv.Itoa(resp.StatusCode)) + } + return true, nil +} + +func (j *Job) Delete() (bool, error) { + resp, err := j.Jenkins.Requester.Post(j.Base+"/doDelete", nil, nil, nil) + if err != nil { + return false, err + } + if resp.StatusCode != 200 { + return false, errors.New(strconv.Itoa(resp.StatusCode)) + } + return true, nil +} + +func (j *Job) Rename(name string) (bool, error) { + data := url.Values{} + data.Set("newName", name) + _, err := j.Jenkins.Requester.Post(j.Base+"/doRename", bytes.NewBufferString(data.Encode()), nil, nil) + if err != nil { + return false, err + } + j.Base = "/job/" + name + j.Poll() + return true, nil +} + +func (j *Job) Create(config string, qr ...interface{}) (*Job, error) { + var querystring map[string]string + if len(qr) > 0 { + querystring = qr[0].(map[string]string) + } + resp, err := j.Jenkins.Requester.PostXML(j.parentBase()+"/createItem", config, j.Raw, querystring) + if err != nil { + return nil, err + } + if resp.StatusCode == 200 { + j.Poll() + return j, nil + } + return nil, errors.New(strconv.Itoa(resp.StatusCode)) +} + +func (j *Job) Copy(destinationName string) (*Job, error) { + qr := map[string]string{"name": destinationName, "from": j.GetName(), "mode": "copy"} + resp, err := j.Jenkins.Requester.Post(j.parentBase()+"/createItem", nil, nil, qr) + if err != nil { + return nil, err + } + if resp.StatusCode == 200 { + newJob := &Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: "/job/" + destinationName} + _, err := newJob.Poll() + if err != nil { + return nil, err + } + return newJob, nil + } + return nil, errors.New(strconv.Itoa(resp.StatusCode)) +} + +func (j *Job) UpdateConfig(config string) error { + + var querystring map[string]string + + resp, err := j.Jenkins.Requester.PostXML(j.Base+"/config.xml", config, nil, querystring) + if err != nil { + return err + } + if resp.StatusCode == 200 { + j.Poll() + return nil + } + return errors.New(strconv.Itoa(resp.StatusCode)) + +} + +func (j *Job) GetConfig() (string, error) { + var data string + _, err := j.Jenkins.Requester.GetXML(j.Base+"/config.xml", &data, nil) + if err != nil { + return "", err + } + return data, nil +} + +func (j *Job) GetParameters() ([]ParameterDefinition, error) { + _, err := j.Poll() + if err != nil { + return nil, err + } + var parameters []ParameterDefinition + for _, property := range j.Raw.Property { + parameters = append(parameters, property.ParameterDefinitions...) + } + return parameters, nil +} + +func (j *Job) IsQueued() (bool, error) { + if _, err := j.Poll(); err != nil { + return false, err + } + return j.Raw.InQueue, nil +} + +func (j *Job) IsRunning() (bool, error) { + if _, err := j.Poll(); err != nil { + return false, err + } + lastBuild, err := j.GetLastBuild() + if err != nil { + return false, err + } + return lastBuild.IsRunning(), nil +} + +func (j *Job) IsEnabled() (bool, error) { + if _, err := j.Poll(); err != nil { + return false, err + } + return j.Raw.Color != "disabled", nil +} + +func (j *Job) HasQueuedBuild() { + panic("Not Implemented yet") +} + +func (j *Job) InvokeSimple(params map[string]string) (int64, error) { + endpoint := "/build" + parameters, err := j.GetParameters() + if err != nil { + return 0, err + } + if len(parameters) > 0 { + endpoint = "/buildWithParameters" + } + data := url.Values{} + for k, v := range params { + data.Set(k, v) + } + resp, err := j.Jenkins.Requester.Post(j.Base+endpoint, bytes.NewBufferString(data.Encode()), nil, nil) + if err != nil { + return 0, err + } + + if resp.StatusCode != 200 && resp.StatusCode != 201 { + return 0, errors.New("Could not invoke job " + j.GetName()) + } + + location := resp.Header.Get("Location") + if location == "" { + return 0, errors.New("Don't have key \"Location\" in response of header") + } + + u, err := url.Parse(location) + if err != nil { + return 0, err + } + + number, err := strconv.ParseInt(path.Base(u.Path), 10, 64) + if err != nil { + return 0, err + } + + return number, nil +} + +func (j *Job) Invoke(files []string, skipIfRunning bool, params map[string]string, cause string, securityToken string) (bool, error) { + isRunning, err := j.IsRunning() + if err != nil { + return false, err + } + if isRunning && skipIfRunning { + return false, fmt.Errorf("Will not request new build because %s is already running", j.GetName()) + } + + base := "/build" + + // If parameters are specified - url is /builWithParameters + if params != nil { + base = "/buildWithParameters" + } else { + params = make(map[string]string) + } + + // If files are specified - url is /build + if files != nil { + base = "/build" + } + reqParams := map[string]string{} + buildParams := map[string]string{} + if securityToken != "" { + reqParams["token"] = securityToken + } + + buildParams["json"] = string(makeJson(params)) + b, _ := json.Marshal(buildParams) + resp, err := j.Jenkins.Requester.PostFiles(j.Base+base, bytes.NewBuffer(b), nil, reqParams, files) + if err != nil { + return false, err + } + if resp.StatusCode == 200 || resp.StatusCode == 201 { + return true, nil + } + return false, errors.New(strconv.Itoa(resp.StatusCode)) +} + +func (j *Job) Poll() (int, error) { + response, err := j.Jenkins.Requester.GetJSON(j.Base, j.Raw, nil) + if err != nil { + return 0, err + } + return response.StatusCode, nil +} diff --git a/pkg/simple/client/devops/jenkins/options.go b/pkg/simple/client/devops/jenkins/options.go new file mode 100644 index 000000000..70280a279 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/options.go @@ -0,0 +1,67 @@ +package jenkins + +import ( + "fmt" + "github.com/spf13/pflag" + "kubesphere.io/kubesphere/pkg/utils/reflectutils" +) + +type Options struct { + Host string `json:",omitempty" yaml:"host" description:"Jenkins service host address"` + Username string `json:",omitempty" yaml:"username" description:"Jenkins admin username"` + Password string `json:",omitempty" yaml:"password" description:"Jenkins admin password"` + MaxConnections int `json:"maxConnections,omitempty" yaml:"maxConnections" description:"Maximum connections allowed to connect to Jenkins"` +} + +// NewDevopsOptions returns a `zero` instance +func NewDevopsOptions() *Options { + return &Options{ + Host: "", + Username: "", + Password: "", + MaxConnections: 100, + } +} + +// ApplyTo apply configuration to another options +func (s *Options) ApplyTo(options *Options) { + if s.Host != "" { + reflectutils.Override(options, s) + } +} + +// Validate check if there is misconfiguration in options +func (s *Options) Validate() []error { + var errors []error + + // devops is not needed, ignore rest options + if s.Host == "" { + return errors + } + + if s.Username == "" || s.Password == "" { + errors = append(errors, fmt.Errorf("jenkins's username or password is empty")) + } + + if s.MaxConnections <= 0 { + errors = append(errors, fmt.Errorf("jenkins's maximum connections should be greater than 0")) + } + + return errors +} + +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.Host, "jenkins-host", c.Host, ""+ + "Jenkins service host address. If left blank, means Jenkins "+ + "is unnecessary.") + + fs.StringVar(&s.Username, "jenkins-username", c.Username, ""+ + "Username for access to Jenkins service. Leave it blank if there isn't any.") + + fs.StringVar(&s.Password, "jenkins-password", c.Password, ""+ + "Password for access to Jenkins service, used pair with username.") + + fs.IntVar(&s.MaxConnections, "jenkins-max-connections", c.MaxConnections, ""+ + "Maximum allowed connections to Jenkins. ") + +} diff --git a/pkg/simple/client/devops/jenkins/pipeline.go b/pkg/simple/client/devops/jenkins/pipeline.go new file mode 100644 index 000000000..08122f46b --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pipeline.go @@ -0,0 +1,722 @@ +package jenkins + +import ( + "encoding/json" + "github.com/PuerkitoBio/goquery" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" + "net/url" + "strings" + "time" +) + +type Pipeline struct { + HttpParameters *devops.HttpParameters + Jenkins *Jenkins + Path string +} + +const ( + GetPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/" + ListPipelinesUrl = "/blue/rest/search/?" + GetPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/" + ListPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/?" + StopPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/stop/?" + ReplayPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/replay/" + RunPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/" + GetArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/artifacts/?" + GetRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/log/?" + GetStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/log/?" + GetPipelineRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/?" + SubmitInputStepUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/" + GetNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/?" + + GetBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/" + GetBranchPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/" + StopBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/stop/?" + ReplayBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/replay/" + RunBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/" + GetBranchArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/artifacts/?" + GetBranchRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/log/?" + GetBranchStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/log/?" + GetBranchNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/?" + GetBranchPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/?" + CheckBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/" + GetPipeBranchUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/?" + ScanBranchUrl = "/job/%s/job/%s/build?" + GetConsoleLogUrl = "/job/%s/job/%s/indexing/consoleText" + GetCrumbUrl = "/crumbIssuer/api/json/" + GetSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/" + GetSCMOrgUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/?" + GetOrgRepoUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/%s/repositories/?" + CreateSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/" + ValidateUrl = "/blue/rest/organizations/jenkins/scm/%s/validate" + + GetNotifyCommitUrl = "/git/notifyCommit/?" + GithubWebhookUrl = "/github-webhook/" + CheckScriptCompileUrl = "/job/%s/job/%s/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile" + + CheckPipelienCronUrl = "/job/%s/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s" + CheckCronUrl = "/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s" + ToJenkinsfileUrl = "/pipeline-model-converter/toJenkinsfile" + ToJsonUrl = "/pipeline-model-converter/toJson" + + cronJobLayout = "Monday, January 2, 2006 15:04:05 PM" +) + +func (p *Pipeline) GetPipeline() (*devops.Pipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var pipeline devops.Pipeline + + err = json.Unmarshal(res, &pipeline) + if err != nil { + klog.Error(err) + return nil, err + } + return &pipeline, err +} + +func (p *Pipeline) ListPipelines() (*devops.PipelineList, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + return nil, err + } + count, err := p.searchPipelineCount() + if err != nil { + klog.Error(err) + return nil, err + } + + pipelienList := devops.PipelineList{Total: count} + err = json.Unmarshal(res, &pipelienList.Items) + if err != nil { + klog.Error(err) + return nil, err + } + return &pipelienList, err +} + +func (p *Pipeline) searchPipelineCount() (int, error) { + query, _ := parseJenkinsQuery(p.HttpParameters.Url.RawQuery) + query.Set("start", "0") + query.Set("limit", "1000") + query.Set("depth", "-1") + + formatUrl := ListPipelinesUrl + query.Encode() + + res, err := p.Jenkins.SendPureRequest(formatUrl, p.HttpParameters) + if err != nil { + klog.Error(err) + return 0, err + } + var pipelines []devops.Pipeline + err = json.Unmarshal(res, &pipelines) + if err != nil { + klog.Error(err) + return 0, err + } + return len(pipelines), nil +} + +func (p *Pipeline) GetPipelineRun() (*devops.PipelineRun, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var pipelineRun devops.PipelineRun + err = json.Unmarshal(res, &pipelineRun) + if err != nil { + klog.Error(err) + return nil, err + } + return &pipelineRun, err +} + +func (p *Pipeline) ListPipelineRuns() (*devops.PipelineRunList, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var pipelineRunList devops.PipelineRunList + err = json.Unmarshal(res, &pipelineRunList) + if err != nil { + klog.Error(err) + return nil, err + } + return &pipelineRunList, err +} + +func (p *Pipeline) searchPipelineRunsCount() (int, error) { + query, _ := parseJenkinsQuery(p.HttpParameters.Url.RawQuery) + query.Set("start", "0") + query.Set("limit", "1000") + query.Set("depth", "-1") + //formatUrl := fmt.Sprintf(SearchPipelineRunUrl, projectName, pipelineName) + + res, err := p.Jenkins.SendPureRequest(ListPipelineRunUrl+query.Encode(), p.HttpParameters) + if err != nil { + klog.Error(err) + return 0, err + } + var runs []devops.PipelineRun + err = json.Unmarshal(res, &runs) + if err != nil { + klog.Error(err) + return 0, err + } + return len(runs), nil +} + +func (p *Pipeline) StopPipeline() (*devops.StopPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var stopPipeline devops.StopPipeline + err = json.Unmarshal(res, &stopPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &stopPipeline, err +} + +func (p *Pipeline) ReplayPipeline() (*devops.ReplayPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var replayPipeline devops.ReplayPipeline + err = json.Unmarshal(res, &replayPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &replayPipeline, err +} + +func (p *Pipeline) RunPipeline() (*devops.RunPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var runPipeline devops.RunPipeline + err = json.Unmarshal(res, &runPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &runPipeline, err +} + +func (p *Pipeline) GetArtifacts() ([]devops.Artifacts, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var artifacts []devops.Artifacts + err = json.Unmarshal(res, &artifacts) + if err != nil { + klog.Error(err) + return nil, err + } + + return artifacts, err +} + +func (p *Pipeline) GetRunLog() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetStepLog() ([]byte, http.Header, error) { + res, header, err := p.Jenkins.SendPureRequestWithHeaderResp(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, header, err +} + +func (p *Pipeline) GetNodeSteps() ([]devops.NodeSteps, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var nodeSteps []devops.NodeSteps + err = json.Unmarshal(res, &nodeSteps) + if err != nil { + klog.Error(err) + return nil, err + } + + return nodeSteps, err +} + +func (p *Pipeline) GetPipelineRunNodes() ([]devops.PipelineRunNodes, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var pipelineRunNodes []devops.PipelineRunNodes + err = json.Unmarshal(res, &pipelineRunNodes) + if err != nil { + klog.Error(err) + return nil, err + } + + return pipelineRunNodes, err +} + +func (p *Pipeline) SubmitInputStep() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetBranchPipeline() (*devops.BranchPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchPipeline devops.BranchPipeline + err = json.Unmarshal(res, &branchPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &branchPipeline, err +} + +func (p *Pipeline) GetBranchPipelineRun() (*devops.PipelineRun, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchPipelineRun devops.PipelineRun + err = json.Unmarshal(res, &branchPipelineRun) + if err != nil { + klog.Error(err) + return nil, err + } + + return &branchPipelineRun, err +} + +func (p *Pipeline) StopBranchPipeline() (*devops.StopPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchStopPipeline devops.StopPipeline + err = json.Unmarshal(res, &branchStopPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &branchStopPipeline, err +} + +func (p *Pipeline) ReplayBranchPipeline() (*devops.ReplayPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchReplayPipeline devops.ReplayPipeline + err = json.Unmarshal(res, &branchReplayPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &branchReplayPipeline, err +} + +func (p *Pipeline) RunBranchPipeline() (*devops.RunPipeline, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchRunPipeline devops.RunPipeline + err = json.Unmarshal(res, &branchRunPipeline) + if err != nil { + klog.Error(err) + return nil, err + } + + return &branchRunPipeline, err +} + +func (p *Pipeline) GetBranchArtifacts() ([]devops.Artifacts, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var artifacts []devops.Artifacts + err = json.Unmarshal(res, &artifacts) + if err != nil { + klog.Error(err) + return nil, err + } + + return artifacts, err +} + +func (p *Pipeline) GetBranchRunLog() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetBranchStepLog() ([]byte, http.Header, error) { + res, header, err := p.Jenkins.SendPureRequestWithHeaderResp(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, header, err +} + +func (p *Pipeline) GetBranchNodeSteps() ([]devops.NodeSteps, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchNodeSteps []devops.NodeSteps + err = json.Unmarshal(res, &branchNodeSteps) + if err != nil { + klog.Error(err) + return nil, err + } + + return branchNodeSteps, err +} + +func (p *Pipeline) GetBranchPipelineRunNodes() ([]devops.BranchPipelineRunNodes, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var branchPipelineRunNodes []devops.BranchPipelineRunNodes + err = json.Unmarshal(res, &branchPipelineRunNodes) + if err != nil { + klog.Error(err) + return nil, err + } + + return branchPipelineRunNodes, err +} + +func (p *Pipeline) SubmitBranchInputStep() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetPipelineBranch() (*devops.PipelineBranch, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var pipelineBranch devops.PipelineBranch + err = json.Unmarshal(res, &pipelineBranch) + if err != nil { + klog.Error(err) + return nil, err + } + + return &pipelineBranch, err +} + +func (p *Pipeline) ScanBranch() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetConsoleLog() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GetCrumb() (*devops.Crumb, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var crumb devops.Crumb + err = json.Unmarshal(res, &crumb) + if err != nil { + klog.Error(err) + return nil, err + } + + return &crumb, err +} + +func (p *Pipeline) GetSCMServers() ([]devops.SCMServer, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var SCMServer []devops.SCMServer + err = json.Unmarshal(res, &SCMServer) + if err != nil { + klog.Error(err) + return nil, err + } + + return SCMServer, err +} + +func (p *Pipeline) GetSCMOrg() ([]devops.SCMOrg, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var SCMOrg []devops.SCMOrg + err = json.Unmarshal(res, &SCMOrg) + if err != nil { + klog.Error(err) + return nil, err + } + + return SCMOrg, err +} + +func (p *Pipeline) GetOrgRepo() ([]devops.OrgRepo, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var OrgRepo []devops.OrgRepo + err = json.Unmarshal(res, &OrgRepo) + if err != nil { + klog.Error(err) + return nil, err + } + + return OrgRepo, err +} + +func (p *Pipeline) CreateSCMServers() (*devops.SCMServer, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + var SCMServer devops.SCMServer + err = json.Unmarshal(res, &SCMServer) + if err != nil { + klog.Error(err) + return nil, err + } + + return &SCMServer, err +} + +func (p *Pipeline) GetNotifyCommit() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) GithubWebhook() ([]byte, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + return res, err +} + +func (p *Pipeline) Validate() (*devops.Validates, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var validates devops.Validates + err = json.Unmarshal(res, &validates) + if err != nil { + klog.Error(err) + return nil, err + } + + return &validates, err +} + +func (p *Pipeline) CheckScriptCompile() (*devops.CheckScript, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + // Jenkins will return different struct according to different results. + var checkScript devops.CheckScript + ok := json.Unmarshal(res, &checkScript) + if ok != nil { + var resJson []*devops.CheckScript + err := json.Unmarshal(res, &resJson) + if err != nil { + klog.Error(err) + return nil, err + } + + return resJson[0], nil + } + + return &checkScript, err + +} + +func (p *Pipeline) CheckCron() (*devops.CheckCronRes, error) { + + var res = new(devops.CheckCronRes) + + Url, err := url.Parse(p.Jenkins.Server + p.Path) + + reqJenkins := &http.Request{ + Method: http.MethodGet, + URL: Url, + Header: p.HttpParameters.Header, + } + + client := &http.Client{Timeout: 30 * time.Second} + + resp, err := client.Do(reqJenkins) + + if resp != nil && resp.StatusCode != http.StatusOK { + resBody, _ := getRespBody(resp) + return &devops.CheckCronRes{ + Result: "error", + Message: string(resBody), + }, err + } + if err != nil { + klog.Error(err) + return nil, err + } + defer resp.Body.Close() + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + klog.Error(err) + return nil, err + } + doc.Find("div").Each(func(i int, selection *goquery.Selection) { + res.Message = selection.Text() + res.Result, _ = selection.Attr("class") + }) + if res.Result == "ok" { + res.LastTime, res.NextTime, err = parseCronJobTime(res.Message) + if err != nil { + klog.Error(err) + return nil, err + } + } + + return res, err +} + +func parseCronJobTime(msg string) (string, string, error) { + + times := strings.Split(msg, ";") + + lastTmp := strings.Split(times[0], " ") + lastCount := len(lastTmp) + lastTmp = lastTmp[lastCount-7 : lastCount-1] + lastTime := strings.Join(lastTmp, " ") + lastUinx, err := time.Parse(cronJobLayout, lastTime) + if err != nil { + klog.Error(err) + return "", "", err + } + last := lastUinx.Format(time.RFC3339) + + nextTmp := strings.Split(times[1], " ") + nextCount := len(nextTmp) + nextTmp = nextTmp[nextCount-7 : nextCount-1] + nextTime := strings.Join(nextTmp, " ") + nextUinx, err := time.Parse(cronJobLayout, nextTime) + if err != nil { + klog.Error(err) + return "", "", err + } + next := nextUinx.Format(time.RFC3339) + + return last, next, nil +} + +func (p *Pipeline) ToJenkinsfile() (*devops.ResJenkinsfile, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var jenkinsfile devops.ResJenkinsfile + err = json.Unmarshal(res, &jenkinsfile) + if err != nil { + klog.Error(err) + return nil, err + } + + return &jenkinsfile, err +} + +func (p *Pipeline) ToJson() (*devops.ResJson, error) { + res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters) + if err != nil { + klog.Error(err) + } + + var toJson devops.ResJson + err = json.Unmarshal(res, &toJson) + if err != nil { + klog.Error(err) + return nil, err + } + + return &toJson, err +} diff --git a/pkg/simple/client/devops/jenkins/pipeline_internal.go b/pkg/simple/client/devops/jenkins/pipeline_internal.go new file mode 100644 index 000000000..e93cb46e0 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pipeline_internal.go @@ -0,0 +1,893 @@ +package jenkins + +import ( + "fmt" + "github.com/beevik/etree" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "strconv" + "strings" + "time" +) + +func replaceXmlVersion(config, oldVersion, targetVersion string) string { + lines := strings.Split(config, "\n") + lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1) + output := strings.Join(lines, "\n") + return output +} + +func createPipelineConfigXml(pipeline *devopsv1alpha3.NoScmPipeline) (string, error) { + doc := etree.NewDocument() + xmlString := ` + + + + + + + + + + + +` + doc.ReadFromString(xmlString) + flow := doc.SelectElement("flow-definition") + flow.CreateElement("description").SetText(pipeline.Description) + properties := flow.CreateElement("properties") + + if pipeline.DisableConcurrent { + properties.CreateElement("org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") + } + + if pipeline.Discarder != nil { + discarder := properties.CreateElement("jenkins.model.BuildDiscarderProperty") + strategy := discarder.CreateElement("strategy") + strategy.CreateAttr("class", "hudson.tasks.LogRotator") + strategy.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) + strategy.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) + strategy.CreateElement("artifactDaysToKeep").SetText("-1") + strategy.CreateElement("artifactNumToKeep").SetText("-1") + } + if pipeline.Parameters != nil { + appendParametersToEtree(properties, pipeline.Parameters) + } + + if pipeline.TimerTrigger != nil { + triggers := properties. + CreateElement("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"). + CreateElement("triggers") + triggers.CreateElement("hudson.triggers.TimerTrigger").CreateElement("spec").SetText(pipeline.TimerTrigger.Cron) + } + + pipelineDefine := flow.CreateElement("definition") + pipelineDefine.CreateAttr("class", "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition") + pipelineDefine.CreateAttr("plugin", "workflow-cps") + pipelineDefine.CreateElement("script").SetText(pipeline.Jenkinsfile) + + pipelineDefine.CreateElement("sandbox").SetText("true") + + flow.CreateElement("triggers") + + if pipeline.RemoteTrigger != nil { + flow.CreateElement("authToken").SetText(pipeline.RemoteTrigger.Token) + } + flow.CreateElement("disabled").SetText("false") + + doc.Indent(2) + stringXml, err := doc.WriteToString() + if err != nil { + return "", err + } + return replaceXmlVersion(stringXml, "1.0", "1.1"), err +} + +func parsePipelineConfigXml(config string) (*devopsv1alpha3.NoScmPipeline, error) { + pipeline := &devopsv1alpha3.NoScmPipeline{} + config = replaceXmlVersion(config, "1.1", "1.0") + doc := etree.NewDocument() + err := doc.ReadFromString(config) + if err != nil { + return nil, err + } + flow := doc.SelectElement("flow-definition") + if flow == nil { + return nil, fmt.Errorf("can not find pipeline definition") + } + pipeline.Description = flow.SelectElement("description").Text() + + properties := flow.SelectElement("properties") + if properties. + SelectElement( + "org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") != nil { + pipeline.DisableConcurrent = true + } + if properties.SelectElement("jenkins.model.BuildDiscarderProperty") != nil { + strategy := properties. + SelectElement("jenkins.model.BuildDiscarderProperty"). + SelectElement("strategy") + pipeline.Discarder = &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: strategy.SelectElement("daysToKeep").Text(), + NumToKeep: strategy.SelectElement("numToKeep").Text(), + } + } + + pipeline.Parameters = getParametersfromEtree(properties) + if len(pipeline.Parameters) == 0 { + pipeline.Parameters = nil + } + + if triggerProperty := properties. + SelectElement( + "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil { + triggers := triggerProperty.SelectElement("triggers") + if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil { + pipeline.TimerTrigger = &devopsv1alpha3.TimerTrigger{ + Cron: timerTrigger.SelectElement("spec").Text(), + } + } + } + if authToken := flow.SelectElement("authToken"); authToken != nil { + pipeline.RemoteTrigger = &devopsv1alpha3.RemoteTrigger{ + Token: authToken.Text(), + } + } + if definition := flow.SelectElement("definition"); definition != nil { + if script := definition.SelectElement("script"); script != nil { + pipeline.Jenkinsfile = script.Text() + } + } + return pipeline, nil +} + +func appendParametersToEtree(properties *etree.Element, parameters []devopsv1alpha3.Parameter) { + parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty"). + CreateElement("parameterDefinitions") + for _, parameter := range parameters { + for className, typeName := range ParameterTypeMap { + if typeName == parameter.Type { + paramDefine := parameterDefinitions.CreateElement(className) + paramDefine.CreateElement("name").SetText(parameter.Name) + paramDefine.CreateElement("description").SetText(parameter.Description) + switch parameter.Type { + case "choice": + choices := paramDefine.CreateElement("choices") + choices.CreateAttr("class", "java.util.Arrays$ArrayList") + a := choices.CreateElement("a") + a.CreateAttr("class", "string-array") + choiceValues := strings.Split(parameter.DefaultValue, "\n") + for _, choiceValue := range choiceValues { + a.CreateElement("string").SetText(choiceValue) + } + case "file": + break + default: + paramDefine.CreateElement("defaultValue").SetText(parameter.DefaultValue) + } + } + } + } +} + +func getParametersfromEtree(properties *etree.Element) []devopsv1alpha3.Parameter { + var parameters []devopsv1alpha3.Parameter + if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil { + params := parametersProperty.SelectElement("parameterDefinitions").ChildElements() + for _, param := range params { + switch param.Tag { + case "hudson.model.StringParameterDefinition": + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + DefaultValue: param.SelectElement("defaultValue").Text(), + Type: ParameterTypeMap["hudson.model.StringParameterDefinition"], + }) + case "hudson.model.BooleanParameterDefinition": + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + DefaultValue: param.SelectElement("defaultValue").Text(), + Type: ParameterTypeMap["hudson.model.BooleanParameterDefinition"], + }) + case "hudson.model.TextParameterDefinition": + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + DefaultValue: param.SelectElement("defaultValue").Text(), + Type: ParameterTypeMap["hudson.model.TextParameterDefinition"], + }) + case "hudson.model.FileParameterDefinition": + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + Type: ParameterTypeMap["hudson.model.FileParameterDefinition"], + }) + case "hudson.model.PasswordParameterDefinition": + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + DefaultValue: param.SelectElement("name").Text(), + Type: ParameterTypeMap["hudson.model.PasswordParameterDefinition"], + }) + case "hudson.model.ChoiceParameterDefinition": + choiceParameter := devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + Type: ParameterTypeMap["hudson.model.ChoiceParameterDefinition"], + } + choices := param.SelectElement("choices").SelectElement("a").SelectElements("string") + for _, choice := range choices { + choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text()) + } + choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue) + parameters = append(parameters, choiceParameter) + default: + parameters = append(parameters, devopsv1alpha3.Parameter{ + Name: param.SelectElement("name").Text(), + Description: param.SelectElement("description").Text(), + DefaultValue: "unknown", + Type: param.Tag, + }) + } + } + } + return parameters +} + +func appendGitSourceToEtree(source *etree.Element, gitSource *devopsv1alpha3.GitSource) { + source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource") + source.CreateAttr("plugin", "git") + source.CreateElement("id").SetText(gitSource.ScmId) + source.CreateElement("remote").SetText(gitSource.Url) + if gitSource.CredentialId != "" { + source.CreateElement("credentialsId").SetText(gitSource.CredentialId) + } + traits := source.CreateElement("traits") + if gitSource.DiscoverBranches { + traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait") + } + if gitSource.CloneOption != nil { + cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") + cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") + cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(gitSource.CloneOption.Shallow)) + cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) + cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) + cloneExtension.CreateElement("reference") + if gitSource.CloneOption.Timeout >= 0 { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(gitSource.CloneOption.Timeout)) + } else { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) + } + + if gitSource.CloneOption.Depth >= 0 { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(gitSource.CloneOption.Depth)) + } else { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) + } + } + + if gitSource.RegexFilter != "" { + regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") + regexTraits.CreateAttr("plugin", "scm-api@2.4.0") + regexTraits.CreateElement("regex").SetText(gitSource.RegexFilter) + } + return +} + +func getGitSourcefromEtree(source *etree.Element) *devopsv1alpha3.GitSource { + var gitSource devopsv1alpha3.GitSource + if credential := source.SelectElement("credentialsId"); credential != nil { + gitSource.CredentialId = credential.Text() + } + if remote := source.SelectElement("remote"); remote != nil { + gitSource.Url = remote.Text() + } + + traits := source.SelectElement("traits") + if branchDiscoverTrait := traits.SelectElement( + "jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil { + gitSource.DiscoverBranches = true + } + if cloneTrait := traits.SelectElement( + "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { + if cloneExtension := cloneTrait.SelectElement( + "extension"); cloneExtension != nil { + gitSource.CloneOption = &devopsv1alpha3.GitCloneOption{} + if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { + gitSource.CloneOption.Shallow = value + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { + gitSource.CloneOption.Timeout = int(value) + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { + gitSource.CloneOption.Depth = int(value) + } + } + } + if regexTrait := traits.SelectElement( + "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { + if regex := regexTrait.SelectElement("regex"); regex != nil { + gitSource.RegexFilter = regex.Text() + } + } + return &gitSource +} + +func getGithubSourcefromEtree(source *etree.Element) *devopsv1alpha3.GithubSource { + var githubSource devopsv1alpha3.GithubSource + if credential := source.SelectElement("credentialsId"); credential != nil { + githubSource.CredentialId = credential.Text() + } + if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { + githubSource.Owner = repoOwner.Text() + } + if repository := source.SelectElement("repository"); repository != nil { + githubSource.Repo = repository.Text() + } + if apiUri := source.SelectElement("apiUri"); apiUri != nil { + githubSource.ApiUri = apiUri.Text() + } + traits := source.SelectElement("traits") + if branchDiscoverTrait := traits.SelectElement( + "org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) + githubSource.DiscoverBranches = strategyId + } + if originPRDiscoverTrait := traits.SelectElement( + "org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) + githubSource.DiscoverPRFromOrigin = strategyId + } + if forkPRDiscoverTrait := traits.SelectElement( + "org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) + trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value + trust := strings.Split(trustClass, "$") + switch trust[1] { + case "TrustContributors": + githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 1, + } + case "TrustEveryone": + githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 2, + } + case "TrustPermission": + githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 3, + } + case "TrustNobody": + githubSource.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 4, + } + } + if cloneTrait := traits.SelectElement( + "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { + if cloneExtension := cloneTrait.SelectElement( + "extension"); cloneExtension != nil { + githubSource.CloneOption = &devopsv1alpha3.GitCloneOption{} + if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { + githubSource.CloneOption.Shallow = value + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { + githubSource.CloneOption.Timeout = int(value) + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { + githubSource.CloneOption.Depth = int(value) + } + } + } + + if regexTrait := traits.SelectElement( + "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { + if regex := regexTrait.SelectElement("regex"); regex != nil { + githubSource.RegexFilter = regex.Text() + } + } + } + return &githubSource +} + +func appendGithubSourceToEtree(source *etree.Element, githubSource *devopsv1alpha3.GithubSource) { + source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource") + source.CreateAttr("plugin", "github-branch-source") + source.CreateElement("id").SetText(githubSource.ScmId) + source.CreateElement("credentialsId").SetText(githubSource.CredentialId) + source.CreateElement("repoOwner").SetText(githubSource.Owner) + source.CreateElement("repository").SetText(githubSource.Repo) + if githubSource.ApiUri != "" { + source.CreateElement("apiUri").SetText(githubSource.ApiUri) + } + traits := source.CreateElement("traits") + if githubSource.DiscoverBranches != 0 { + traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"). + CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverBranches)) + } + if githubSource.DiscoverPRFromOrigin != 0 { + traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"). + CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromOrigin)) + } + if githubSource.DiscoverPRFromForks != nil { + forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait") + forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromForks.Strategy)) + trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$" + switch githubSource.DiscoverPRFromForks.Trust { + case 1: + trustClass += "TrustContributors" + case 2: + trustClass += "TrustEveryone" + case 3: + trustClass += "TrustPermission" + case 4: + trustClass += "TrustNobody" + default: + } + forkTrait.CreateElement("trust").CreateAttr("class", trustClass) + } + if githubSource.CloneOption != nil { + cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") + cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") + cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(githubSource.CloneOption.Shallow)) + cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) + cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) + cloneExtension.CreateElement("reference") + if githubSource.CloneOption.Timeout >= 0 { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(githubSource.CloneOption.Timeout)) + } else { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) + } + + if githubSource.CloneOption.Depth >= 0 { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(githubSource.CloneOption.Depth)) + } else { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) + } + } + if githubSource.RegexFilter != "" { + regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") + regexTraits.CreateAttr("plugin", "scm-api@2.4.0") + regexTraits.CreateElement("regex").SetText(githubSource.RegexFilter) + } + return +} + +func getBitbucketServerSourceFromEtree(source *etree.Element) *devopsv1alpha3.BitbucketServerSource { + var s devopsv1alpha3.BitbucketServerSource + if credential := source.SelectElement("credentialsId"); credential != nil { + s.CredentialId = credential.Text() + } + if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { + s.Owner = repoOwner.Text() + } + if repository := source.SelectElement("repository"); repository != nil { + s.Repo = repository.Text() + } + if apiUri := source.SelectElement("serverUrl"); apiUri != nil { + s.ApiUri = apiUri.Text() + } + traits := source.SelectElement("traits") + if branchDiscoverTrait := traits.SelectElement( + "com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait"); branchDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) + s.DiscoverBranches = strategyId + } + if originPRDiscoverTrait := traits.SelectElement( + "com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) + s.DiscoverPRFromOrigin = strategyId + } + if forkPRDiscoverTrait := traits.SelectElement( + "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { + strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text()) + trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value + trust := strings.Split(trustClass, "$") + switch trust[1] { + case "TrustEveryone": + s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 1, + } + case "TrustTeamForks": + s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 2, + } + case "TrustNobody": + s.DiscoverPRFromForks = &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: strategyId, + Trust: 3, + } + } + if cloneTrait := traits.SelectElement( + "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { + if cloneExtension := cloneTrait.SelectElement( + "extension"); cloneExtension != nil { + s.CloneOption = &devopsv1alpha3.GitCloneOption{} + if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { + s.CloneOption.Shallow = value + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { + s.CloneOption.Timeout = int(value) + } + if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { + s.CloneOption.Depth = int(value) + } + } + } + + if regexTrait := traits.SelectElement( + "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { + if regex := regexTrait.SelectElement("regex"); regex != nil { + s.RegexFilter = regex.Text() + } + } + } + return &s +} + +func appendBitbucketServerSourceToEtree(source *etree.Element, s *devopsv1alpha3.BitbucketServerSource) { + source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource") + source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source") + source.CreateElement("id").SetText(s.ScmId) + source.CreateElement("credentialsId").SetText(s.CredentialId) + source.CreateElement("repoOwner").SetText(s.Owner) + source.CreateElement("repository").SetText(s.Repo) + source.CreateElement("serverUrl").SetText(s.ApiUri) + + traits := source.CreateElement("traits") + if s.DiscoverBranches != 0 { + traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait>"). + CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches)) + } + if s.DiscoverPRFromOrigin != 0 { + traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"). + CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin)) + } + if s.DiscoverPRFromForks != nil { + forkTrait := traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait") + forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy)) + trustClass := "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait$" + switch s.DiscoverPRFromForks.Trust { + case 1: + trustClass += "TrustEveryone" + case 2: + trustClass += "TrustTeamForks" + case 3: + trustClass += "TrustNobody" + default: + trustClass += "TrustEveryone" + } + forkTrait.CreateElement("trust").CreateAttr("class", trustClass) + } + if s.CloneOption != nil { + cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") + cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") + cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) + cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) + cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) + cloneExtension.CreateElement("reference") + if s.CloneOption.Timeout >= 0 { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) + } else { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) + } + + if s.CloneOption.Depth >= 0 { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) + } else { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) + } + } + if s.RegexFilter != "" { + regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") + regexTraits.CreateAttr("plugin", "scm-api@2.4.0") + regexTraits.CreateElement("regex").SetText(s.RegexFilter) + } + return +} + +func getSvnSourcefromEtree(source *etree.Element) *devopsv1alpha3.SvnSource { + var s devopsv1alpha3.SvnSource + if remote := source.SelectElement("remoteBase"); remote != nil { + s.Remote = remote.Text() + } + + if credentialsId := source.SelectElement("credentialsId"); credentialsId != nil { + s.CredentialId = credentialsId.Text() + } + + if includes := source.SelectElement("includes"); includes != nil { + s.Includes = includes.Text() + } + + if excludes := source.SelectElement("excludes"); excludes != nil { + s.Excludes = excludes.Text() + } + return &s +} + +func appendSvnSourceToEtree(source *etree.Element, s *devopsv1alpha3.SvnSource) { + source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource") + source.CreateAttr("plugin", "subversion") + source.CreateElement("id").SetText(s.ScmId) + if s.CredentialId != "" { + source.CreateElement("credentialsId").SetText(s.CredentialId) + } + if s.Remote != "" { + source.CreateElement("remoteBase").SetText(s.Remote) + } + if s.Includes != "" { + source.CreateElement("includes").SetText(s.Includes) + } + if s.Excludes != "" { + source.CreateElement("excludes").SetText(s.Excludes) + } + return +} + +func getSingleSvnSourceFromEtree(source *etree.Element) *devopsv1alpha3.SingleSvnSource { + var s devopsv1alpha3.SingleSvnSource + if scm := source.SelectElement("scm"); scm != nil { + if locations := scm.SelectElement("locations"); locations != nil { + if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil { + if remote := moduleLocations.SelectElement("remote"); remote != nil { + s.Remote = remote.Text() + } + if credentialId := moduleLocations.SelectElement("credentialsId"); credentialId != nil { + s.CredentialId = credentialId.Text() + } + } + } + } + return &s +} + +func appendSingleSvnSourceToEtree(source *etree.Element, s *devopsv1alpha3.SingleSvnSource) { + + source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource") + source.CreateAttr("plugin", "scm-api") + source.CreateElement("id").SetText(s.ScmId) + source.CreateElement("name").SetText("master") + + scm := source.CreateElement("scm") + scm.CreateAttr("class", "hudson.scm.SubversionSCM") + scm.CreateAttr("plugin", "subversion") + + location := scm.CreateElement("locations").CreateElement("hudson.scm.SubversionSCM_-ModuleLocation") + if s.Remote != "" { + location.CreateElement("remote").SetText(s.Remote) + } + if s.CredentialId != "" { + location.CreateElement("credentialsId").SetText(s.CredentialId) + } + location.CreateElement("local").SetText(".") + location.CreateElement("depthOption").SetText("infinity") + location.CreateElement("ignoreExternalsOption").SetText("true") + location.CreateElement("cancelProcessOnExternalsFail").SetText("true") + + source.CreateElement("excludedRegions") + source.CreateElement("includedRegions") + source.CreateElement("excludedUsers") + source.CreateElement("excludedRevprop") + source.CreateElement("excludedCommitMessages") + source.CreateElement("workspaceUpdater").CreateAttr("class", "hudson.scm.subversion.UpdateUpdater") + source.CreateElement("ignoreDirPropChanges").SetText("false") + source.CreateElement("filterChangelog").SetText("false") + source.CreateElement("quietOperation").SetText("true") + + return +} + +func appendMultiBranchJobTriggerToEtree(properties *etree.Element, s *devopsv1alpha3.MultiBranchJobTrigger) { + triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") + triggerProperty.CreateAttr("plugin", "multibranch-action-triggers") + triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger) + triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger) + return +} + +func getMultiBranchJobTriggerfromEtree(properties *etree.Element) *devopsv1alpha3.MultiBranchJobTrigger { + var s devopsv1alpha3.MultiBranchJobTrigger + triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") + if triggerProperty != nil { + s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text() + s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text() + } + return &s +} +func createMultiBranchPipelineConfigXml(projectName string, pipeline *devopsv1alpha3.MultiBranchPipeline) (string, error) { + doc := etree.NewDocument() + xmlString := ` + + + + + + + + + + + + + + + false + + + + + +` + err := doc.ReadFromString(xmlString) + if err != nil { + return "", err + } + + project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") + project.CreateElement("description").SetText(pipeline.Description) + + if pipeline.MultiBranchJobTrigger != nil { + properties := project.SelectElement("properties") + appendMultiBranchJobTriggerToEtree(properties, pipeline.MultiBranchJobTrigger) + } + + if pipeline.Discarder != nil { + discarder := project.CreateElement("orphanedItemStrategy") + discarder.CreateAttr("class", "com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy") + discarder.CreateAttr("plugin", "cloudbees-folder") + discarder.CreateElement("pruneDeadBranches").SetText("true") + discarder.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep) + discarder.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep) + } + + triggers := project.CreateElement("triggers") + if pipeline.TimerTrigger != nil { + timeTrigger := triggers.CreateElement( + "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger") + timeTrigger.CreateAttr("plugin", "cloudbees-folder") + millis, err := strconv.ParseInt(pipeline.TimerTrigger.Interval, 10, 64) + if err != nil { + return "", err + } + timeTrigger.CreateElement("spec").SetText(toCrontab(millis)) + timeTrigger.CreateElement("interval").SetText(pipeline.TimerTrigger.Interval) + + triggers.CreateElement("disabled").SetText("false") + } + + sources := project.CreateElement("sources") + sources.CreateAttr("class", "jenkins.branch.MultiBranchProject$BranchSourceList") + sources.CreateAttr("plugin", "branch-api") + sourcesOwner := sources.CreateElement("owner") + sourcesOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") + sourcesOwner.CreateAttr("reference", "../..") + + branchSource := sources.CreateElement("data").CreateElement("jenkins.branch.BranchSource") + branchSourceStrategy := branchSource.CreateElement("strategy") + branchSourceStrategy.CreateAttr("class", "jenkins.branch.NamedExceptionsBranchPropertyStrategy") + branchSourceStrategy.CreateElement("defaultProperties").CreateAttr("class", "empty-list") + branchSourceStrategy.CreateElement("namedExceptions").CreateAttr("class", "empty-list") + source := branchSource.CreateElement("source") + + switch pipeline.SourceType { + case "git": + appendGitSourceToEtree(source, pipeline.GitSource) + case "github": + appendGithubSourceToEtree(source, pipeline.GitHubSource) + case "svn": + appendSvnSourceToEtree(source, pipeline.SvnSource) + case "single_svn": + appendSingleSvnSourceToEtree(source, pipeline.SingleSvnSource) + case "bitbucket_server": + appendBitbucketServerSourceToEtree(source, pipeline.BitbucketServerSource) + + default: + return "", fmt.Errorf("unsupport source type") + } + + factory := project.CreateElement("factory") + factory.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory") + factoryOwner := factory.CreateElement("owner") + factoryOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") + factoryOwner.CreateAttr("reference", "../..") + factory.CreateElement("scriptPath").SetText(pipeline.ScriptPath) + + doc.Indent(2) + stringXml, err := doc.WriteToString() + return replaceXmlVersion(stringXml, "1.0", "1.1"), err +} + +func parseMultiBranchPipelineConfigXml(config string) (*devopsv1alpha3.MultiBranchPipeline, error) { + pipeline := &devopsv1alpha3.MultiBranchPipeline{} + config = replaceXmlVersion(config, "1.1", "1.0") + doc := etree.NewDocument() + err := doc.ReadFromString(config) + if err != nil { + return nil, err + } + project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") + if project == nil { + return nil, fmt.Errorf("can not parse mutibranch pipeline config") + } + if properties := project.SelectElement("properties"); properties != nil { + if multibranchTrigger := properties.SelectElement( + "org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil { + pipeline.MultiBranchJobTrigger = getMultiBranchJobTriggerfromEtree(properties) + } + } + pipeline.Description = project.SelectElement("description").Text() + + if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil { + pipeline.Discarder = &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: discarder.SelectElement("daysToKeep").Text(), + NumToKeep: discarder.SelectElement("numToKeep").Text(), + } + } + if triggers := project.SelectElement("triggers"); triggers != nil { + if timerTrigger := triggers.SelectElement( + "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil { + pipeline.TimerTrigger = &devopsv1alpha3.TimerTrigger{ + Interval: timerTrigger.SelectElement("interval").Text(), + } + } + } + + if sources := project.SelectElement("sources"); sources != nil { + if sourcesData := sources.SelectElement("data"); sourcesData != nil { + if branchSource := sourcesData.SelectElement("jenkins.branch.BranchSource"); branchSource != nil { + source := branchSource.SelectElement("source") + switch source.SelectAttr("class").Value { + case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": + pipeline.GitHubSource = getGithubSourcefromEtree(source) + pipeline.SourceType = "github" + case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": + pipeline.BitbucketServerSource = getBitbucketServerSourceFromEtree(source) + pipeline.SourceType = "bitbucket_server" + + case "jenkins.plugins.git.GitSCMSource": + pipeline.SourceType = "git" + pipeline.GitSource = getGitSourcefromEtree(source) + + case "jenkins.scm.impl.SingleSCMSource": + pipeline.SourceType = "single_svn" + pipeline.SingleSvnSource = getSingleSvnSourceFromEtree(source) + + case "jenkins.scm.impl.subversion.SubversionSCMSource": + pipeline.SourceType = "svn" + pipeline.SvnSource = getSvnSourcefromEtree(source) + } + } + } + } + + pipeline.ScriptPath = project.SelectElement("factory").SelectElement("scriptPath").Text() + return pipeline, nil +} + +func toCrontab(millis int64) string { + if millis*time.Millisecond.Nanoseconds() <= 5*time.Minute.Nanoseconds() { + return "* * * * *" + } + if millis*time.Millisecond.Nanoseconds() <= 30*time.Minute.Nanoseconds() { + return "H/5 * * * *" + } + if millis*time.Millisecond.Nanoseconds() <= 1*time.Hour.Nanoseconds() { + return "H/15 * * * *" + } + if millis*time.Millisecond.Nanoseconds() <= 8*time.Hour.Nanoseconds() { + return "H/30 * * * *" + } + if millis*time.Millisecond.Nanoseconds() <= 24*time.Hour.Nanoseconds() { + return "H H/4 * * *" + } + if millis*time.Millisecond.Nanoseconds() <= 48*time.Hour.Nanoseconds() { + return "H H/12 * * *" + } + return "H H * * *" + +} diff --git a/pkg/simple/client/devops/jenkins/pipeline_internal_test.go b/pkg/simple/client/devops/jenkins/pipeline_internal_test.go new file mode 100644 index 000000000..7bf233797 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pipeline_internal_test.go @@ -0,0 +1,621 @@ +package jenkins + +import ( + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "reflect" + "testing" +) + +func Test_NoScmPipelineConfig(t *testing.T) { + inputs := []*devopsv1alpha3.NoScmPipeline{ + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + }, + { + Name: "", + Description: "", + Jenkinsfile: "node{echo 'hello'}", + }, + { + Name: "", + Description: "", + Jenkinsfile: "node{echo 'hello'}", + DisableConcurrent: true, + }, + } + for _, input := range inputs { + outputString, err := createPipelineConfigXml(input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parsePipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_NoScmPipelineConfig_Discarder(t *testing.T) { + inputs := []*devopsv1alpha3.NoScmPipeline{ + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Discarder: &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: "3", NumToKeep: "5", + }, + }, + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Discarder: &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: "3", NumToKeep: "", + }, + }, + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Discarder: &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: "", NumToKeep: "21321", + }, + }, + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Discarder: &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: "", NumToKeep: "", + }, + }, + } + for _, input := range inputs { + outputString, err := createPipelineConfigXml(input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parsePipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_NoScmPipelineConfig_Param(t *testing.T) { + inputs := []*devopsv1alpha3.NoScmPipeline{ + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Parameters: []devopsv1alpha3.Parameter{ + { + Name: "d", + DefaultValue: "a\nb", + Type: "choice", + Description: "fortest", + }, + }, + }, + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + Parameters: []devopsv1alpha3.Parameter{ + { + Name: "a", + DefaultValue: "abc", + Type: "string", + Description: "fortest", + }, + { + Name: "b", + DefaultValue: "false", + Type: "boolean", + Description: "fortest", + }, + { + Name: "c", + DefaultValue: "password \n aaa", + Type: "text", + Description: "fortest", + }, + { + Name: "d", + DefaultValue: "a\nb", + Type: "choice", + Description: "fortest", + }, + }, + }, + } + for _, input := range inputs { + outputString, err := createPipelineConfigXml(input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parsePipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_NoScmPipelineConfig_Trigger(t *testing.T) { + inputs := []*devopsv1alpha3.NoScmPipeline{ + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Cron: "1 1 1 * * *", + }, + }, + + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + RemoteTrigger: &devopsv1alpha3.RemoteTrigger{ + Token: "abc", + }, + }, + { + Name: "", + Description: "for test", + Jenkinsfile: "node{echo 'hello'}", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Cron: "1 1 1 * * *", + }, + RemoteTrigger: &devopsv1alpha3.RemoteTrigger{ + Token: "abc", + }, + }, + } + + for _, input := range inputs { + outputString, err := createPipelineConfigXml(input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parsePipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_MultiBranchPipelineConfig(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + GitSource: &devopsv1alpha3.GitSource{}, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{}, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "single_svn", + SingleSvnSource: &devopsv1alpha3.SingleSvnSource{}, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "svn", + SvnSource: &devopsv1alpha3.SvnSource{}, + }, + } + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + Discarder: &devopsv1alpha3.DiscarderProperty{ + DaysToKeep: "1", + NumToKeep: "2", + }, + GitSource: &devopsv1alpha3.GitSource{}, + }, + } + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) { + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + GitSource: &devopsv1alpha3.GitSource{}, + }, + } + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_MultiBranchPipelineConfig_Source(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + GitSource: &devopsv1alpha3.GitSource{ + Url: "https://github.com/kubesphere/devops", + CredentialId: "git", + DiscoverBranches: true, + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "bitbucket_server", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + BitbucketServerSource: &devopsv1alpha3.BitbucketServerSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + }, + }, + + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "svn", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + SvnSource: &devopsv1alpha3.SvnSource{ + Remote: "https://api.svn.com/bcd", + CredentialId: "svn", + Excludes: "truck", + Includes: "tag/*", + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "single_svn", + TimerTrigger: &devopsv1alpha3.TimerTrigger{ + Interval: "12345566", + }, + SingleSvnSource: &devopsv1alpha3.SingleSvnSource{ + Remote: "https://api.svn.com/bcd", + CredentialId: "svn", + }, + }, + } + + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } +} + +func Test_MultiBranchPipelineCloneConfig(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + GitSource: &devopsv1alpha3.GitSource{ + Url: "https://github.com/kubesphere/devops", + CredentialId: "git", + DiscoverBranches: true, + CloneOption: &devopsv1alpha3.GitCloneOption{ + Shallow: false, + Depth: 3, + Timeout: 20, + }, + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + CloneOption: &devopsv1alpha3.GitCloneOption{ + Shallow: false, + Depth: 3, + Timeout: 20, + }, + }, + }, + } + + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } + +} + +func Test_MultiBranchPipelineRegexFilter(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "git", + GitSource: &devopsv1alpha3.GitSource{ + Url: "https://github.com/kubesphere/devops", + CredentialId: "git", + DiscoverBranches: true, + RegexFilter: ".*", + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + RegexFilter: ".*", + }, + }, + } + + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } + +} + +func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { + + inputs := []*devopsv1alpha3.MultiBranchPipeline{ + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + RegexFilter: ".*", + }, + MultiBranchJobTrigger: &devopsv1alpha3.MultiBranchJobTrigger{ + CreateActionJobsToTrigger: "abc", + DeleteActionJobsToTrigger: "ddd", + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + RegexFilter: ".*", + }, + MultiBranchJobTrigger: &devopsv1alpha3.MultiBranchJobTrigger{ + CreateActionJobsToTrigger: "abc", + }, + }, + { + Name: "", + Description: "for test", + ScriptPath: "Jenkinsfile", + SourceType: "github", + GitHubSource: &devopsv1alpha3.GithubSource{ + Owner: "kubesphere", + Repo: "devops", + CredentialId: "github", + ApiUri: "https://api.github.com", + DiscoverBranches: 1, + DiscoverPRFromOrigin: 2, + DiscoverPRFromForks: &devopsv1alpha3.DiscoverPRFromForks{ + Strategy: 1, + Trust: 1, + }, + RegexFilter: ".*", + }, + MultiBranchJobTrigger: &devopsv1alpha3.MultiBranchJobTrigger{ + DeleteActionJobsToTrigger: "ddd", + }, + }, + } + + for _, input := range inputs { + outputString, err := createMultiBranchPipelineConfigXml("", input) + if err != nil { + t.Fatalf("should not get error %+v", err) + } + output, err := parseMultiBranchPipelineConfigXml(outputString) + + if err != nil { + t.Fatalf("should not get error %+v", err) + } + if !reflect.DeepEqual(input, output) { + t.Fatalf("input [%+v] output [%+v] should equal ", input, output) + } + } + +} diff --git a/pkg/gojenkins/pipeline_model_converter.go b/pkg/simple/client/devops/jenkins/pipeline_model_converter.go similarity index 99% rename from pkg/gojenkins/pipeline_model_converter.go rename to pkg/simple/client/devops/jenkins/pipeline_model_converter.go index 12b9003a1..46640a6e0 100644 --- a/pkg/gojenkins/pipeline_model_converter.go +++ b/pkg/simple/client/devops/jenkins/pipeline_model_converter.go @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package gojenkins +package jenkins import ( "errors" diff --git a/pkg/simple/client/devops/jenkins/project.go b/pkg/simple/client/devops/jenkins/project.go new file mode 100644 index 000000000..d1ccea4c1 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/project.go @@ -0,0 +1,42 @@ +package jenkins + +import ( + "github.com/emicklei/go-restful" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" +) + +type DevOpsProjectRoleResponse struct { + ProjectRole *ProjectRole + Err error +} + +func (j *Jenkins) CreateDevOpsProject(projectId string) (string, error) { + _, err := j.CreateFolder(projectId, "") + if err != nil { + klog.Errorf("%+v", err) + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + return projectId, nil +} + +func (j *Jenkins) DeleteDevOpsProject(projectId string) error { + _, err := j.DeleteJob(projectId) + + if err != nil && devops.GetDevOpsStatusCode(err) != http.StatusNotFound { + klog.Errorf("%+v", err) + return restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + return nil +} + +func (j *Jenkins) GetDevOpsProject(projectId string) (string, error) { + job, err := j.GetJob(projectId) + if err != nil { + klog.Errorf("%+v", err) + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + + } + return job.GetName(), nil +} diff --git a/pkg/simple/client/devops/jenkins/project_pipeline.go b/pkg/simple/client/devops/jenkins/project_pipeline.go new file mode 100644 index 000000000..29a3807fe --- /dev/null +++ b/pkg/simple/client/devops/jenkins/project_pipeline.go @@ -0,0 +1,170 @@ +package jenkins + +import ( + "fmt" + "github.com/emicklei/go-restful" + "k8s.io/klog" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" +) + +func (j *Jenkins) CreateProjectPipeline(projectId string, pipeline *devopsv1alpha3.Pipeline) (string, error) { + switch pipeline.Spec.Type { + case devopsv1alpha3.NoScmPipelineType: + + config, err := createPipelineConfigXml(pipeline.Spec.Pipeline) + if err != nil { + return "", restful.NewError(http.StatusInternalServerError, err.Error()) + } + + job, err := j.GetJob(pipeline.Name, projectId) + if job != nil { + err := fmt.Errorf("job name [%s] has been used", job.GetName()) + return "", restful.NewError(http.StatusConflict, err.Error()) + } + + if err != nil && devops.GetDevOpsStatusCode(err) != http.StatusNotFound { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + _, err = j.CreateJobInFolder(config, pipeline.Name, projectId) + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + return pipeline.Name, nil + case devopsv1alpha3.MultiBranchPipelineType: + config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.Spec.MultiBranchPipeline) + if err != nil { + return "", restful.NewError(http.StatusInternalServerError, err.Error()) + } + + job, err := j.GetJob(pipeline.Name, projectId) + if job != nil { + err := fmt.Errorf("job name [%s] has been used", job.GetName()) + return "", restful.NewError(http.StatusConflict, err.Error()) + } + + if err != nil && devops.GetDevOpsStatusCode(err) != http.StatusNotFound { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + _, err = j.CreateJobInFolder(config, pipeline.Name, projectId) + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + return pipeline.Name, nil + + default: + err := fmt.Errorf("error unsupport job type") + klog.Errorf("%+v", err) + return "", restful.NewError(http.StatusBadRequest, err.Error()) + } +} + +func (j *Jenkins) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { + _, err := j.DeleteJob(pipelineId, projectId) + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + return pipelineId, nil + +} +func (j *Jenkins) UpdateProjectPipeline(projectId string, pipeline *devopsv1alpha3.Pipeline) (string, error) { + switch pipeline.Spec.Type { + case devopsv1alpha3.NoScmPipelineType: + + config, err := createPipelineConfigXml(pipeline.Spec.Pipeline) + if err != nil { + return "", restful.NewError(http.StatusInternalServerError, err.Error()) + } + + job, err := j.GetJob(pipeline.Name, projectId) + + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + err = job.UpdateConfig(config) + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + return pipeline.Name, nil + case devopsv1alpha3.MultiBranchPipelineType: + + config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.Spec.MultiBranchPipeline) + if err != nil { + klog.Errorf("%+v", err) + + return "", restful.NewError(http.StatusInternalServerError, err.Error()) + } + + job, err := j.GetJob(pipeline.Spec.MultiBranchPipeline.Name, projectId) + + if err != nil { + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + err = job.UpdateConfig(config) + if err != nil { + klog.Errorf("%+v", err) + return "", restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + + return pipeline.Name, nil + + default: + err := fmt.Errorf("error unsupport job type") + klog.Errorf("%+v", err) + return "", restful.NewError(http.StatusBadRequest, err.Error()) + } +} + +func (j *Jenkins) GetProjectPipelineConfig(projectId, pipelineId string) (*devopsv1alpha3.Pipeline, error) { + job, err := j.GetJob(pipelineId, projectId) + if err != nil { + klog.Errorf("%+v", err) + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + switch job.Raw.Class { + case "org.jenkinsci.plugins.workflow.job.WorkflowJob": + config, err := job.GetConfig() + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + pipeline, err := parsePipelineConfigXml(config) + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + pipeline.Name = pipelineId + return &devopsv1alpha3.Pipeline{ + Spec: devopsv1alpha3.PipelineSpec{ + Type: devopsv1alpha3.NoScmPipelineType, + Pipeline: pipeline, + }, + }, nil + + case "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject": + config, err := job.GetConfig() + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + pipeline, err := parseMultiBranchPipelineConfigXml(config) + if err != nil { + return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error()) + } + pipeline.Name = pipelineId + return &devopsv1alpha3.Pipeline{ + Spec: devopsv1alpha3.PipelineSpec{ + Type: devopsv1alpha3.MultiBranchPipelineType, + MultiBranchPipeline: pipeline, + }, + }, nil + default: + klog.Errorf("%+v", err) + return nil, restful.NewError(http.StatusBadRequest, err.Error()) + } +} diff --git a/pkg/simple/client/devops/jenkins/pure_request.go b/pkg/simple/client/devops/jenkins/pure_request.go new file mode 100644 index 000000000..4943d06ce --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pure_request.go @@ -0,0 +1,54 @@ +package jenkins + +import ( + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "net/http" + "net/url" + "time" +) + +// TODO: deprecated, use SendJenkinsRequestWithHeaderResp() instead +func (j *Jenkins) SendPureRequest(path string, httpParameters *devops.HttpParameters) ([]byte, error) { + resBody, _, err := j.SendPureRequestWithHeaderResp(path, httpParameters) + + return resBody, err +} + +func (j *Jenkins) SendPureRequestWithHeaderResp(path string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) { + Url, err := url.Parse(j.Server + path) + if err != nil { + klog.Error(err) + return nil, nil, err + } + + client := &http.Client{Timeout: 30 * time.Second} + + newRequest := &http.Request{ + Method: httpParameters.Method, + URL: Url, + Header: httpParameters.Header, + Body: httpParameters.Body, + Form: httpParameters.Form, + PostForm: httpParameters.PostForm, + } + + resp, err := client.Do(newRequest) + if err != nil { + klog.Error(err) + return nil, nil, err + } + + resBody, _ := getRespBody(resp) + defer resp.Body.Close() + + if resp.StatusCode >= http.StatusBadRequest { + klog.Errorf("%+v", string(resBody)) + jkerr := new(JkError) + jkerr.Code = resp.StatusCode + jkerr.Message = string(resBody) + return nil, nil, jkerr + } + + return resBody, resp.Header, nil +} diff --git a/pkg/simple/client/devops/jenkins/request.go b/pkg/simple/client/devops/jenkins/request.go new file mode 100644 index 000000000..968d0cc00 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/request.go @@ -0,0 +1,459 @@ +// Copyright 2015 Vadim Kravcenko +// +// 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 jenkins + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" +) + +// Request Methods + +type APIRequest struct { + Method string + Endpoint string + Payload io.Reader + Headers http.Header + Suffix string +} + +func (ar *APIRequest) SetHeader(key string, value string) *APIRequest { + ar.Headers.Set(key, value) + return ar +} + +func NewAPIRequest(method string, endpoint string, payload io.Reader) *APIRequest { + var headers = http.Header{} + var suffix string + ar := &APIRequest{method, endpoint, payload, headers, suffix} + return ar +} + +type Requester struct { + Base string + BasicAuth *BasicAuth + Client *http.Client + CACert []byte + SslVerify bool + connControl chan struct{} +} + +func (r *Requester) SetCrumb(ar *APIRequest) error { + crumbData := map[string]string{} + response, err := r.GetJSON("/crumbIssuer/api/json", &crumbData, nil) + if err != nil { + jenkinsError, ok := err.(*devops.ErrorResponse) + if ok && jenkinsError.Response.StatusCode == http.StatusNotFound { + return nil + } + return err + } + if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" { + ar.SetHeader(crumbData["crumbRequestField"], crumbData["crumb"]) + } + + return nil +} + +func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { + ar := NewAPIRequest("POST", endpoint, payload) + if err := r.SetCrumb(ar); err != nil { + return nil, err + } + ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") + ar.Suffix = "api/json" + return r.Do(ar, responseStruct, querystring) +} + +func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { + ar := NewAPIRequest("POST", endpoint, payload) + if err := r.SetCrumb(ar); err != nil { + return nil, err + } + ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") + ar.Suffix = "" + return r.Do(ar, responseStruct, querystring) +} +func (r *Requester) PostForm(endpoint string, payload io.Reader, responseStruct interface{}, formString map[string]string) (*http.Response, error) { + ar := NewAPIRequest("POST", endpoint, payload) + if err := r.SetCrumb(ar); err != nil { + return nil, err + } + ar.SetHeader("Content-Type", "application/x-www-form-urlencoded") + ar.Suffix = "" + return r.DoPostForm(ar, responseStruct, formString) +} + +func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) { + ar := NewAPIRequest("POST", endpoint, payload) + if err := r.SetCrumb(ar); err != nil { + return nil, err + } + return r.Do(ar, responseStruct, querystring, files) +} + +func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { + payload := bytes.NewBuffer([]byte(xml)) + ar := NewAPIRequest("POST", endpoint, payload) + if err := r.SetCrumb(ar); err != nil { + return nil, err + } + ar.SetHeader("Content-Type", "application/xml;charset=utf-8") + ar.Suffix = "" + return r.Do(ar, responseStruct, querystring) +} + +func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) { + ar := NewAPIRequest("GET", endpoint, nil) + ar.SetHeader("Content-Type", "application/json") + ar.Suffix = "api/json" + return r.Do(ar, responseStruct, query) +} + +func (r *Requester) GetXML(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) { + ar := NewAPIRequest("GET", endpoint, nil) + ar.SetHeader("Content-Type", "application/xml") + ar.Suffix = "" + return r.Do(ar, responseStruct, query) +} + +func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { + ar := NewAPIRequest("GET", endpoint, nil) + ar.Suffix = "" + return r.Do(ar, responseStruct, querystring) +} + +func (r *Requester) GetHtml(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) { + ar := NewAPIRequest("GET", endpoint, nil) + ar.Suffix = "" + return r.DoGet(ar, responseStruct, querystring) +} + +func (r *Requester) SetClient(client *http.Client) *Requester { + r.Client = client + return r +} + +//Add auth on redirect if required. +func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error { + if r.BasicAuth != nil { + req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) + } + return nil +} + +func (r *Requester) DoGet(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) { + fileUpload := false + var files []string + URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) + + if err != nil { + return nil, err + } + + for _, o := range options { + switch v := o.(type) { + case map[string]string: + + querystring := make(url.Values) + for key, val := range v { + querystring.Set(key, val) + } + + URL.RawQuery = querystring.Encode() + case []string: + fileUpload = true + files = v + } + } + var req *http.Request + if fileUpload { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + for _, file := range files { + fileData, err := os.Open(file) + if err != nil { + Error.Println(err.Error()) + return nil, err + } + + part, err := writer.CreateFormFile("file", filepath.Base(file)) + if err != nil { + Error.Println(err.Error()) + return nil, err + } + if _, err = io.Copy(part, fileData); err != nil { + return nil, err + } + defer fileData.Close() + } + var params map[string]string + json.NewDecoder(ar.Payload).Decode(¶ms) + for key, val := range params { + if err = writer.WriteField(key, val); err != nil { + return nil, err + } + } + if err = writer.Close(); err != nil { + return nil, err + } + req, err = http.NewRequest(ar.Method, URL.String(), body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", writer.FormDataContentType()) + } else { + req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload) + if err != nil { + return nil, err + } + } + + if r.BasicAuth != nil { + req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) + } + req.Close = true + req.Header.Add("Accept", "*/*") + for k := range ar.Headers { + req.Header.Add(k, ar.Headers.Get(k)) + } + r.connControl <- struct{}{} + if response, err := r.Client.Do(req); err != nil { + <-r.connControl + return nil, err + } else { + <-r.connControl + errorText := response.Header.Get("X-Error") + if errorText != "" { + return nil, errors.New(errorText) + } + err := CheckResponse(response) + if err != nil { + return nil, err + } + switch responseStruct.(type) { + case *string: + return r.ReadRawResponse(response, responseStruct) + default: + return r.ReadJSONResponse(response, responseStruct) + } + + } + +} + +func (r *Requester) Do(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) { + if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" { + ar.Endpoint += "/" + } + + fileUpload := false + var files []string + URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) + + if err != nil { + return nil, err + } + + for _, o := range options { + switch v := o.(type) { + case map[string]string: + + querystring := make(url.Values) + for key, val := range v { + querystring.Set(key, val) + } + + URL.RawQuery = querystring.Encode() + case []string: + fileUpload = true + files = v + } + } + var req *http.Request + if fileUpload { + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + for _, file := range files { + fileData, err := os.Open(file) + if err != nil { + Error.Println(err.Error()) + return nil, err + } + + part, err := writer.CreateFormFile("file", filepath.Base(file)) + if err != nil { + Error.Println(err.Error()) + return nil, err + } + if _, err = io.Copy(part, fileData); err != nil { + return nil, err + } + defer fileData.Close() + } + var params map[string]string + json.NewDecoder(ar.Payload).Decode(¶ms) + for key, val := range params { + if err = writer.WriteField(key, val); err != nil { + return nil, err + } + } + if err = writer.Close(); err != nil { + return nil, err + } + req, err = http.NewRequest(ar.Method, URL.String(), body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", writer.FormDataContentType()) + } else { + req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload) + if err != nil { + return nil, err + } + } + + if r.BasicAuth != nil { + req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) + } + req.Close = true + req.Header.Add("Accept", "*/*") + for k := range ar.Headers { + req.Header.Add(k, ar.Headers.Get(k)) + } + r.connControl <- struct{}{} + if response, err := r.Client.Do(req); err != nil { + <-r.connControl + return nil, err + } else { + <-r.connControl + errorText := response.Header.Get("X-Error") + if errorText != "" { + return nil, errors.New(errorText) + } + err := CheckResponse(response) + if err != nil { + return nil, err + } + switch responseStruct.(type) { + case *string: + return r.ReadRawResponse(response, responseStruct) + default: + return r.ReadJSONResponse(response, responseStruct) + } + + } + +} + +func (r *Requester) DoPostForm(ar *APIRequest, responseStruct interface{}, form map[string]string) (*http.Response, error) { + + if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" { + ar.Endpoint += "/" + } + URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix) + + if err != nil { + return nil, err + } + formValue := make(url.Values) + for k, v := range form { + formValue.Set(k, v) + } + req, err := http.NewRequest("POST", URL.String(), strings.NewReader(formValue.Encode())) + if r.BasicAuth != nil { + req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password) + } + req.Close = true + req.Header.Add("Accept", "*/*") + for k := range ar.Headers { + req.Header.Add(k, ar.Headers.Get(k)) + } + r.connControl <- struct{}{} + if response, err := r.Client.Do(req); err != nil { + <-r.connControl + return nil, err + } else { + <-r.connControl + errorText := response.Header.Get("X-Error") + if errorText != "" { + return nil, errors.New(errorText) + } + err := CheckResponse(response) + if err != nil { + return nil, err + } + switch responseStruct.(type) { + case *string: + return r.ReadRawResponse(response, responseStruct) + default: + return r.ReadJSONResponse(response, responseStruct) + } + + } +} + +func (r *Requester) ReadRawResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) { + defer response.Body.Close() + + content, err := ioutil.ReadAll(response.Body) + if err != nil { + return nil, err + } + if str, ok := responseStruct.(*string); ok { + *str = string(content) + } else { + return nil, fmt.Errorf("Could not cast responseStruct to *string") + } + + return response, nil +} + +func (r *Requester) ReadJSONResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) { + defer response.Body.Close() + err := json.NewDecoder(response.Body).Decode(responseStruct) + if err != nil && err.Error() == "EOF" { + return response, nil + } + return response, nil +} + +func CheckResponse(r *http.Response) error { + + switch r.StatusCode { + case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent, http.StatusFound, http.StatusNotModified: + return nil + } + defer r.Body.Close() + errorResponse := &devops.ErrorResponse{Response: r} + data, err := ioutil.ReadAll(r.Body) + if err == nil && data != nil { + errorResponse.Body = data + errorResponse.Message = string(data) + } + + return errorResponse +} diff --git a/pkg/gojenkins/role.go b/pkg/simple/client/devops/jenkins/role.go similarity index 99% rename from pkg/gojenkins/role.go rename to pkg/simple/client/devops/jenkins/role.go index 872d32655..f8a7e643f 100644 --- a/pkg/gojenkins/role.go +++ b/pkg/simple/client/devops/jenkins/role.go @@ -1,4 +1,4 @@ -package gojenkins +package jenkins import ( "errors" diff --git a/pkg/simple/client/devops/jenkins/utils.go b/pkg/simple/client/devops/jenkins/utils.go new file mode 100644 index 000000000..e7893fee4 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/utils.go @@ -0,0 +1,131 @@ +// Copyright 2015 Vadim Kravcenko +// +// 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 jenkins + +import ( + "compress/gzip" + "encoding/json" + "io" + "io/ioutil" + "k8s.io/klog" + "net/http" + "net/url" + "strings" + "time" + "unicode/utf8" +) + +func makeJson(data interface{}) string { + str, err := json.Marshal(data) + if err != nil { + return "" + } + return string(json.RawMessage(str)) +} + +func Reverse(s string) string { + size := len(s) + buf := make([]byte, size) + for start := 0; start < size; { + r, n := utf8.DecodeRuneInString(s[start:]) + start += n + utf8.EncodeRune(buf[size-start:], r) + } + return string(buf) +} + +type JkError struct { + Message string `json:"message"` + Code int `json:"code"` +} + +func (err *JkError) Error() string { + return err.Message +} + +// Decompress response.body of JenkinsAPIResponse +func getRespBody(resp *http.Response) ([]byte, error) { + var reader io.ReadCloser + if resp.Header.Get("Content-Encoding") == "gzip" { + reader, _ = gzip.NewReader(resp.Body) + } else { + reader = resp.Body + } + resBody, err := ioutil.ReadAll(reader) + if err != nil { + klog.Error(err) + return nil, err + } + return resBody, err + +} + +// parseJenkinsQuery Parse the special query of jenkins. +// ParseQuery in the standard library makes the query not re-encode +func parseJenkinsQuery(query string) (url.Values, error) { + m := make(url.Values) + err := error(nil) + for query != "" { + key := query + if i := strings.IndexAny(key, "&"); i >= 0 { + key, query = key[:i], key[i+1:] + } else { + query = "" + } + if key == "" { + continue + } + value := "" + if i := strings.Index(key, "="); i >= 0 { + key, value = key[:i], key[i+1:] + } + key, err1 := url.QueryUnescape(key) + if err1 != nil { + if err == nil { + err = err1 + } + continue + } + value, err1 = url.QueryUnescape(value) + if err1 != nil { + if err == nil { + err = err1 + } + continue + } + m[key] = append(m[key], value) + } + return m, err +} + +type JenkinsBlueTime time.Time + +func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error { + if b == nil || strings.Trim(string(b), "\"") == "null" { + *t = JenkinsBlueTime(time.Time{}) + return nil + } + j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\"")) + + if err != nil { + return err + } + *t = JenkinsBlueTime(j) + return nil +} + +func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) { + return json.Marshal(time.Time(t)) +} diff --git a/pkg/simple/client/devops/options.go b/pkg/simple/client/devops/options.go deleted file mode 100644 index 3919fadd8..000000000 --- a/pkg/simple/client/devops/options.go +++ /dev/null @@ -1,67 +0,0 @@ -package devops - -import ( - "fmt" - "github.com/spf13/pflag" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" -) - -type DevopsOptions struct { - Host string `json:",omitempty" yaml:"host" description:"Jenkins service host address"` - Username string `json:",omitempty" yaml:"username" description:"Jenkins admin username"` - Password string `json:",omitempty" yaml:"password" description:"Jenkins admin password"` - MaxConnections int `json:"maxConnections,omitempty" yaml:"maxConnections" description:"Maximum connections allowed to connect to Jenkins"` -} - -// NewDevopsOptions returns a `zero` instance -func NewDevopsOptions() *DevopsOptions { - return &DevopsOptions{ - Host: "", - Username: "", - Password: "", - MaxConnections: 100, - } -} - -// ApplyTo apply configuration to another options -func (s *DevopsOptions) ApplyTo(options *DevopsOptions) { - if s.Host != "" { - reflectutils.Override(options, s) - } -} - -// Validate check if there is misconfiguration in options -func (s *DevopsOptions) Validate() []error { - errors := []error{} - - // devops is not needed, ignore rest options - if s.Host == "" { - return errors - } - - if s.Username == "" || s.Password == "" { - errors = append(errors, fmt.Errorf("jenkins's username or password is empty")) - } - - if s.MaxConnections <= 0 { - errors = append(errors, fmt.Errorf("jenkins's maximum connections should be greater than 0")) - } - - return errors -} - -func (s *DevopsOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Host, "jenkins-host", s.Host, ""+ - "Jenkins service host address. If left blank, means Jenkins "+ - "is unnecessary.") - - fs.StringVar(&s.Username, "jenkins-username", s.Username, ""+ - "Username for access to Jenkins service. Leave it blank if there isn't any.") - - fs.StringVar(&s.Password, "jenkins-password", s.Password, ""+ - "Password for access to Jenkins service, used pair with username.") - - fs.IntVar(&s.MaxConnections, "jenkins-max-connections", s.MaxConnections, ""+ - "Maximum allowed connections to Jenkins. ") - -} diff --git a/pkg/simple/client/devops/pipeline.go b/pkg/simple/client/devops/pipeline.go new file mode 100644 index 000000000..8043a50fe --- /dev/null +++ b/pkg/simple/client/devops/pipeline.go @@ -0,0 +1,1199 @@ +package devops + +import ( + "io" + "net/http" + "net/url" +) + +type PipelineList struct { + Items []Pipeline `json:"items"` + Total int `json:"total_count"` +} + +// GetPipeline & SearchPipelines +type Pipeline struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability." ` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Scm struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"scm,omitempty"` + Branches struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"branches,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Runs struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"runs,omitempty"` + Trends struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"trends,omitempty"` + Queue struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"queue,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource."` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` + Disabled interface{} `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action."` + DisplayName string `json:"displayName,omitempty" description:"display name"` + FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` + FullName string `json:"fullName,omitempty" description:"full name"` + Name string `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Parameters interface{} `json:"parameters,omitempty" description:"parameters of pipeline, a pipeline can define list of parameters pipeline job expects."` + Permissions struct { + Create bool `json:"create,omitempty" description:"create action"` + Configure bool `json:"configure,omitempty" description:"configure action"` + Read bool `json:"read,omitempty" description:"read action"` + Start bool `json:"start,omitempty" description:"start action"` + Stop bool `json:"stop,omitempty" description:"stop action"` + } `json:"permissions,omitempty" description:"permissions"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` + NumberOfFolders int `json:"numberOfFolders,omitempty" description:"number of folders"` + NumberOfPipelines int `json:"numberOfPipelines,omitempty" description:"number of pipelines"` + PipelineFolderNames []interface{} `json:"pipelineFolderNames,omitempty" description:"pipeline folder names"` + WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline activity"` + BranchNames []string `json:"branchNames,omitempty" description:"branch names"` + NumberOfFailingBranches int `json:"numberOfFailingBranches,omitempty" description:"number of failing branches"` + NumberOfFailingPullRequests int `json:"numberOfFailingPullRequests,omitempty" description:"number of failing pull requests"` + NumberOfSuccessfulBranches int `json:"numberOfSuccessfulBranches,omitempty" description:"number of successful pull requests"` + NumberOfSuccessfulPullRequests int `json:"numberOfSuccessfulPullRequests,omitempty" description:"number of successful pull requests"` + ScmSource struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + APIURL interface{} `json:"apiUrl,omitempty" description:"api url"` + ID string `json:"id,omitempty" description:"The id of the source configuration management (SCM)."` + } `json:"scmSource,omitempty"` + TotalNumberOfBranches int `json:"totalNumberOfBranches,omitempty" description:"total number of branches"` + TotalNumberOfPullRequests int `json:"totalNumberOfPullRequests,omitempty" description:"total number of pull requests"` +} + +// GetPipeBranchRun & SearchPipelineRuns +type PipelineRunList struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + PrevRun struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"prevRun,omitempty"` + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Nodes struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"nodes,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + NextRun struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"nextRun,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + UserID string `json:"userId,omitempty" description:"user id"` + UserName string `json:"userName,omitempty" description:"user name"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description of resource"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime string `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline name"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"source type, such as \"WorkflowRun\""` + Branch struct { + IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` + Issues []interface{} `json:"issues,omitempty" description:"issues"` + URL string `json:"url,omitempty" description:"url"` + } `json:"branch,omitempty"` + CommitID string `json:"commitId,omitempty" description:"commit id"` + CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url "` + PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` +} + +// GetBranchPipeRunNodes +type BranchPipelineRunNodes struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"source type, e.g. \"WorkflowRun\""` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Edges []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ID string `json:"id,omitempty" description:"id"` + Type string `json:"type,omitempty" description:"source type"` + } `json:"edges,omitempty"` + FirstParent interface{} `json:"firstParent,omitempty" description:"first parent resource"` + Restartable bool `json:"restartable,omitempty" description:"restartable or not"` + Steps []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + } `json:"_links,omitempty"` + Actions []struct { + Class string `json:"_class,omitempty"` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty"` + URLName string `json:"urlName,omitempty"` + } `json:"actions,omitempty" description:"references the reachable path to this resource"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"result"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"source type"` + } `json:"steps,omitempty"` +} + +// Validate +type Validates struct { + CredentialID string `json:"credentialId,omitempty" description:"the id of credential"` +} + +// GetSCMOrg +type SCMOrg struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Repositories struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Href string `json:"href,omitempty" description:"url in api"` + } `json:"repositories,omitempty"` + Self struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Href string `json:"href,omitempty" description:"self url in api"` + } `json:"self,omitempty" description:"scm org self info"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Avatar string `json:"avatar,omitempty" description:"the url of organization avatar"` + JenkinsOrganizationPipeline bool `json:"jenkinsOrganizationPipeline,omitempty" description:"weather or not already have jenkins pipeline."` + Name string `json:"name,omitempty" description:"organization name"` +} + +type SCMServer struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Href string `json:"href,omitempty" description:"self url in api"` + } `json:"self,omitempty" description:"scm server self info"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + ID string `json:"id,omitempty" description:"server id of scm server"` + Name string `json:"name,omitempty" description:"name of scm server"` + ApiURL string `json:"apiUrl,omitempty" description:"url of scm server"` +} + +// GetOrgRepo +type OrgRepo struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Repositories struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Items []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + DefaultBranch string `json:"defaultBranch,omitempty" description:"default branch"` + Description string `json:"description,omitempty" description:"description"` + Name string `json:"name,omitempty" description:"name"` + Permissions struct { + Admin bool `json:"admin,omitempty" description:"admin"` + Push bool `json:"push,omitempty" description:"push action"` + Pull bool `json:"pull,omitempty" description:"pull action"` + } `json:"permissions,omitempty"` + Private bool `json:"private,omitempty" description:"private or not"` + FullName string `json:"fullName,omitempty" description:"full name"` + } `json:"items,omitempty"` + LastPage interface{} `json:"lastPage,omitempty" description:"last page"` + NextPage interface{} `json:"nextPage,omitempty" description:"next page"` + PageSize int `json:"pageSize,omitempty" description:"page size"` + } `json:"repositories,omitempty"` +} + +// StopPipeline +type StopPipeline struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Nodes struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"nodes,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` + ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime string `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + Branch struct { + IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` + Issues []interface{} `json:"issues,omitempty" description:"issues"` + URL string `json:"url,omitempty" description:"url"` + } `json:"branch,omitempty"` + CommitID string `json:"commitId,omitempty" description:"commit id"` + CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url"` + PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` +} + +// ReplayPipeline +type ReplayPipeline struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` + ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage string `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + UserID string `json:"userId,omitempty" description:"user id"` + UserName string `json:"userName,omitempty" description:"user name"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis interface{} `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime interface{} `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime interface{} `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis interface{} `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary interface{} `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime interface{} `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + QueueID string `json:"queueId,omitempty" description:"queue id"` +} + +// GetArtifacts +type Artifacts struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Downloadable bool `json:"downloadable,omitempty" description:"downloadable or not"` + ID string `json:"id,omitempty" description:"id"` + Name string `json:"name,omitempty" description:"name"` + Path string `json:"path,omitempty" description:"path"` + Size int `json:"size,omitempty" description:"size"` + URL string `json:"url,omitempty" description:"The url for Download artifacts"` +} + +// GetPipeBranch +type PipelineBranch struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Scm struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"scm,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Runs struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"runs,omitempty"` + Trends struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"trends,omitempty"` + Queue struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"queue,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions."` + Disabled bool `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time, unit is millis"` + FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` + FullName string `json:"fullName,omitempty" description:"full name"` + LatestRun struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + PrevRun struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"prevRun,omitempty"` + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + ArtifactsZipFile string `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime string `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime string `json:"startTime,omitempty" description:"start run"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + } `json:"latestRun,omitempty"` + Name string `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Parameters []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + DefaultParameterValue struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Name string `json:"name,omitempty" description:"name"` + Value string `json:"value,omitempty" description:"value"` + } `json:"defaultParameterValue,omitempty"` + Description string `json:"description,omitempty" description:"description"` + Name string `json:"name,omitempty" description:"name"` + Type string `json:"type,omitempty" description:"type"` + } `json:"parameters,omitempty"` + Permissions struct { + Create bool `json:"create,omitempty" description:"create action"` + Configure bool `json:"configure,omitempty" description:"configure action"` + Read bool `json:"read,omitempty" description:"read action"` + Start bool `json:"start,omitempty" description:"start action"` + Stop bool `json:"stop,omitempty" description:"stop action"` + } `json:"permissions,omitempty"` + WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline"` + Branch struct { + IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` + Issues []interface{} `json:"issues,omitempty" description:"issues"` + URL string `json:"url,omitempty" description:"url"` + } `json:"branch,omitempty"` +} + +// RunPipeline +type RunPayload struct { + Parameters []struct { + Name string `json:"name,omitempty" description:"name"` + Value string `json:"value,omitempty" description:"value"` + } `json:"parameters,omitempty"` +} + +type RunPipeline struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage string `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + UserID string `json:"userId,omitempty" description:"user id"` + UserName string `json:"userName,omitempty" description:"user name"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis interface{} `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime interface{} `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime interface{} `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis interface{} `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary interface{} `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime interface{} `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + QueueID string `json:"queueId,omitempty" description:"queue id"` +} + +// GetNodeStatus +type NodeStatus struct { + Class string `json:"_class,omitempty" description:""` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Edges []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ID string `json:"id,omitempty" description:"id"` + Type string `json:"type,omitempty" description:"type"` + } `json:"edges,omitempty"` + FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` + Restartable bool `json:"restartable,omitempty" description:"restartable or not"` + Steps []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []struct { + Class string `json:"_class,omitempty" description:"references the reachable path to this resource"` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty" description:""` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + URLName string `json:"urlName,omitempty" description:"url name"` + } `json:"actions,omitempty"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + } `json:"steps,omitempty"` +} + +// CheckPipeline +type CheckPlayload struct { + ID string `json:"id,omitempty" description:"id"` + Parameters []CheckPlayloadParameters `json:"parameters,omitempty"` + Abort bool `json:"abort,omitempty" description:"abort or not"` +} + +type CreateScmServerReq struct { + Name string `json:"name,omitempty" description:"name of scm server"` + ApiURL string `json:"apiUrl,omitempty" description:"url of scm server"` +} + +type CheckPlayloadParameters struct { + Name string `json:"name,omitempty" description:"name"` + Value interface{} `json:"value,omitempty" description:"value"` +} + +// Getcrumb +type Crumb struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Crumb string `json:"crumb,omitempty" description:"crumb data"` + CrumbRequestField string `json:"crumbRequestField,omitempty" description:"crumb request field"` +} + +// CheckScriptCompile +type CheckScript struct { + Column int `json:"column,omitempty" description:"column e.g. 0"` + Line int `json:"line,omitempty" description:"line e.g. 0"` + Message string `json:"message,omitempty" description:"message e.g. unexpected char: '#'"` + Status string `json:"status,omitempty" description:"status e.g. fail"` +} + +// CheckCron +type CronData struct { + PipelineName string `json:"pipelineName,omitempty" description:"Pipeline name, if pipeline haven't created, not required'"` + Cron string `json:"cron" description:"Cron script data."` +} + +type CheckCronRes struct { + Result string `json:"result,omitempty" description:"result e.g. ok, error"` + Message string `json:"message,omitempty" description:"message"` + LastTime string `json:"lastTime,omitempty" description:"last run time."` + NextTime string `json:"nextTime,omitempty" description:"next run time."` +} + +// GetPipelineRun +type PipelineRun struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + PrevRun struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"prevRun,omitempty"` + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Nodes struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"nodes,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + ArtifactsZipFile interface{} `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + UserID string `json:"userId,omitempty" description:"user id"` + UserName string `json:"userName,omitempty" description:"user name"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime string `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"the name of pipeline"` + Replayable bool `json:"replayable,omitempty" description:"replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + Branch interface{} `json:"branch,omitempty" description:"branch"` + CommitID interface{} `json:"commitId,omitempty" description:"commit id"` + CommitURL interface{} `json:"commitUrl,omitempty" description:"commit url"` + PullRequest interface{} `json:"pullRequest,omitempty" description:"pull request"` +} + +// GetBranchPipeRun +type BranchPipeline struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Scm struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"scm,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Runs struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"runs,omitempty"` + Trends struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"trends,omitempty"` + Queue struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"queue,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + Disabled bool `json:"disabled,omitempty" description:"disable or not, if disabled, can not do any action"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + FullDisplayName string `json:"fullDisplayName,omitempty" description:"full display name"` + FullName string `json:"fullName,omitempty" description:"full name"` + LatestRun struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + PrevRun struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"prevRun,omitempty"` + Parent struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"parent,omitempty"` + Tests struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"tests,omitempty"` + Log struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"log,omitempty"` + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + BlueTestSummary struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"blueTestSummary,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Artifacts struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"artifacts,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + ArtifactsZipFile string `json:"artifactsZipFile,omitempty" description:"the artifacts zip file"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Causes []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + ShortDescription string `json:"shortDescription,omitempty" description:"short description"` + UserID string `json:"userId,omitempty" description:"user id"` + UserName string `json:"userName,omitempty" description:"user name"` + } `json:"causes,omitempty"` + ChangeSet []interface{} `json:"changeSet,omitempty" description:"changeset information"` + Description interface{} `json:"description,omitempty" description:"description"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` + EnQueueTime string `json:"enQueueTime,omitempty" description:"the time of enter the queue"` + EndTime string `json:"endTime,omitempty" description:"the time of end"` + EstimatedDurationInMillis int `json:"estimatedDurationInMillis,omitempty" description:"estimated duration time in millis"` + ID string `json:"id,omitempty" description:"id"` + Name interface{} `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Pipeline string `json:"pipeline,omitempty" description:"pipeline"` + Replayable bool `json:"replayable,omitempty" description:"Replayable or not"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + RunSummary string `json:"runSummary,omitempty" description:"pipeline run summary"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. RUNNING"` + Type string `json:"type,omitempty" description:"type"` + } `json:"latestRun,omitempty"` + Name string `json:"name,omitempty" description:"name"` + Organization string `json:"organization,omitempty" description:"the name of organization"` + Parameters []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + DefaultParameterValue struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Name string `json:"name,omitempty" description:"name"` + Value string `json:"value,omitempty" description:"value"` + } `json:"defaultParameterValue,omitempty" description:""` + Description string `json:"description,omitempty" description:"description"` + Name string `json:"name,omitempty" description:"name"` + Type string `json:"type,omitempty" description:"type"` + } `json:"parameters,omitempty"` + Permissions struct { + Create bool `json:"create,omitempty" description:"create action"` + Configure bool `json:"configure,omitempty" description:"configure action"` + Read bool `json:"read,omitempty" description:"read action"` + Start bool `json:"start,omitempty" description:"start action"` + Stop bool `json:"stop,omitempty" description:"stop action"` + } `json:"permissions,omitempty"` + WeatherScore int `json:"weatherScore,omitempty" description:"the score to description the result of pipeline"` + Branch struct { + IsPrimary bool `json:"isPrimary,omitempty" description:"primary or not"` + Issues []interface{} `json:"issues,omitempty" description:"issues"` + URL string `json:"url,omitempty" description:"url"` + } `json:"branch,omitempty"` +} + +// GetPipelineRunNodes +type PipelineRunNodes struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. FINISHED"` + Type string `json:"type,omitempty" description:"type"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Edges []interface{} `json:"edges,omitempty" description:"edges"` + FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` + Restartable bool `json:"restartable,omitempty" description:"restartable or not"` +} + +// GetNodeSteps +type NodeSteps struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + URLName string `json:"urlName,omitempty" description:"url name"` + } `json:"actions,omitempty"` + DisplayDescription string `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of starts"` + State string `json:"state,omitempty" description:"run state. e.g. SKIPPED"` + Type string `json:"type,omitempty" description:"type"` +} + +// CheckScriptCompile +type ReqScript struct { + Value string `json:"value,omitempty" description:"Pipeline script data"` +} + +// ToJenkinsfile requests +type ReqJson struct { + Json string `json:"json,omitempty" description:"json data"` +} + +// ToJenkinsfile response +type ResJenkinsfile struct { + Status string `json:"status,omitempty" description:"status e.g. ok"` + Data struct { + Result string `json:"result,omitempty" description:"result e.g. success"` + Jenkinsfile string `json:"jenkinsfile,omitempty" description:"jenkinsfile"` + Errors []struct { + Location []string `json:"location,omitempty" description:"err location"` + Error string `json:"error,omitempty" description:"error message"` + } `json:"errors,omitempty"` + } `json:"data,omitempty"` +} + +type ReqJenkinsfile struct { + Jenkinsfile string `json:"jenkinsfile,omitempty" description:"jenkinsfile"` +} + +type ResJson struct { + Status string `json:"status,omitempty" description:"status e.g. ok"` + Data struct { + Result string `json:"result,omitempty" description:"result e.g. success"` + JSON struct { + Pipeline struct { + Stages []interface{} `json:"stages,omitempty" description:"stages"` + Agent struct { + Type string `json:"type,omitempty" description:"type"` + Arguments []struct { + Key string `json:"key,omitempty" description:"key"` + Value struct { + IsLiteral bool `json:"isLiteral,omitempty" description:"is literal or not"` + Value string `json:"value,omitempty" description:"value"` + } `json:"value,omitempty"` + } `json:"arguments,omitempty"` + } `json:"agent,omitempty"` + } `json:"pipeline,omitempty"` + } `json:"json,omitempty"` + } `json:"data,omitempty"` +} + +type NodesDetail struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links struct { + Self struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Actions struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"actions,omitempty"` + Steps struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"steps,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` + DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` + DisplayName string `json:"displayName,omitempty" description:"display name"` + DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"` + ID string `json:"id,omitempty" description:"id"` + Input *Input `json:"input,omitempty" description:"the action should user input"` + Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` + StartTime string `json:"startTime,omitempty" description:"the time of start"` + State string `json:"state,omitempty" description:"run state. e.g. FINISHED"` + Type string `json:"type,omitempty" description:"type"` + CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` + Edges []interface{} `json:"edges,omitempty" description:"edges"` + FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"` + Restartable bool `json:"restartable,omitempty" description:"restartable or not"` + Steps []NodeSteps `json:"steps,omitempty" description:"steps"` +} + +type NodesStepsIndex struct { + Id int `json:"id,omitempty" description:"id"` + Steps []NodeSteps `json:"steps,omitempty" description:"steps"` +} + +type Input struct { + Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` + Links *struct { + Self *struct { + Class string `json:"_class,omitempty"` + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"_links,omitempty" description:"references the reachable path to this resource"` + ID string `json:"id,omitempty" description:"the id of check action"` + Message string `json:"message,omitempty" description:"the message of check action"` + Ok string `json:"ok,omitempty" description:"check status. e.g. \"Proceed\""` + Parameters []interface{} `json:"parameters,omitempty" description:"the parameters of check action"` + Submitter interface{} `json:"submitter,omitempty" description:"check submitter"` +} + +type HttpParameters struct { + Method string `json:"method,omitempty"` + Header http.Header `json:"header,omitempty"` + Body io.ReadCloser `json:"body,omitempty"` + Form url.Values `json:"form,omitempty"` + PostForm url.Values `json:"postForm,omitempty"` + Url *url.URL `json:"url,omitempty"` +} + +type PipelineOperator interface { + + // Pipelinne operator interface + GetPipeline(projectName, pipelineName string, httpParameters *HttpParameters) (*Pipeline, error) + ListPipelines(httpParameters *HttpParameters) (*PipelineList, error) + GetPipelineRun(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*PipelineRun, error) + ListPipelineRuns(projectName, pipelineName string, httpParameters *HttpParameters) (*PipelineRunList, error) + StopPipeline(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*StopPipeline, error) + ReplayPipeline(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*ReplayPipeline, error) + RunPipeline(projectName, pipelineName string, httpParameters *HttpParameters) (*RunPipeline, error) + GetArtifacts(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]Artifacts, error) + GetRunLog(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]byte, error) + GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, http.Header, error) + GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *HttpParameters) ([]NodeSteps, error) + GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]PipelineRunNodes, error) + SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, error) + + //BranchPipelinne operator interface + GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *HttpParameters) (*BranchPipeline, error) + GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*PipelineRun, error) + StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*StopPipeline, error) + ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*ReplayPipeline, error) + RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *HttpParameters) (*RunPipeline, error) + GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]Artifacts, error) + GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]byte, error) + GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, http.Header, error) + GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *HttpParameters) ([]NodeSteps, error) + GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]BranchPipelineRunNodes, error) + SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, error) + GetPipelineBranch(projectName, pipelineName string, httpParameters *HttpParameters) (*PipelineBranch, error) + ScanBranch(projectName, pipelineName string, httpParameters *HttpParameters) ([]byte, error) + + // Common pipeline operator interface + GetConsoleLog(projectName, pipelineName string, httpParameters *HttpParameters) ([]byte, error) + GetCrumb(httpParameters *HttpParameters) (*Crumb, error) + + // SCM operator interface + GetSCMServers(scmId string, httpParameters *HttpParameters) ([]SCMServer, error) + GetSCMOrg(scmId string, httpParameters *HttpParameters) ([]SCMOrg, error) + GetOrgRepo(scmId, organizationId string, httpParameters *HttpParameters) ([]OrgRepo, error) + CreateSCMServers(scmId string, httpParameters *HttpParameters) (*SCMServer, error) + Validate(scmId string, httpParameters *HttpParameters) (*Validates, error) + + //Webhook operator interface + GetNotifyCommit(httpParameters *HttpParameters) ([]byte, error) + GithubWebhook(httpParameters *HttpParameters) ([]byte, error) + + CheckScriptCompile(projectName, pipelineName string, httpParameters *HttpParameters) (*CheckScript, error) + CheckCron(projectName string, httpParameters *HttpParameters) (*CheckCronRes, error) + ToJenkinsfile(httpParameters *HttpParameters) (*ResJenkinsfile, error) + ToJson(httpParameters *HttpParameters) (*ResJson, error) +} diff --git a/pkg/simple/client/devops/project.go b/pkg/simple/client/devops/project.go new file mode 100644 index 000000000..bd0a0252b --- /dev/null +++ b/pkg/simple/client/devops/project.go @@ -0,0 +1,12 @@ +package devops + +/** +project operator, providing API for creating/getting/deleting projects +The actual data of the project is stored in the CRD, +so we only need to create the project with the corresponding ID in the CI/CD system. +*/ +type ProjectOperator interface { + CreateDevOpsProject(projectId string) (string, error) + DeleteDevOpsProject(projectId string) error + GetDevOpsProject(projectId string) (string, error) +} diff --git a/pkg/simple/client/devops/project_pipeline.go b/pkg/simple/client/devops/project_pipeline.go new file mode 100644 index 000000000..f5610fd5b --- /dev/null +++ b/pkg/simple/client/devops/project_pipeline.go @@ -0,0 +1,10 @@ +package devops + +import "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" + +type ProjectPipelineOperator interface { + CreateProjectPipeline(projectId string, pipeline *v1alpha3.Pipeline) (string, error) + DeleteProjectPipeline(projectId string, pipelineId string) (string, error) + UpdateProjectPipeline(projectId string, pipeline *v1alpha3.Pipeline) (string, error) + GetProjectPipelineConfig(projectId, pipelineId string) (*v1alpha3.Pipeline, error) +} diff --git a/pkg/simple/client/elasticsearch/esclient.go b/pkg/simple/client/elasticsearch/esclient.go deleted file mode 100644 index cb5fd1711..000000000 --- a/pkg/simple/client/elasticsearch/esclient.go +++ /dev/null @@ -1,385 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 esclient - -import ( - "context" - "encoding/json" - "fmt" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" - v5 "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch/versions/v5" - v6 "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch/versions/v6" - v7 "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch/versions/v7" - "strings" - "time" - - "github.com/json-iterator/go" -) - -const ( - matchPhrase = iota - matchPhrasePrefix - regexpQuery - - podNameMaxLength = 63 - // max 10 characters + 1 hyphen - replicaSetSuffixMaxLength = 11 - // a unique random string as suffix, 5 characters + 1 hyphen - randSuffixLength = 6 - - fieldPodName = "kubernetes.pod_name" - fieldContainerName = "kubernetes.container_name" - fieldLog = "log" - - fieldNamespaceNameKeyword = "kubernetes.namespace_name.keyword" - fieldPodNameKeyword = "kubernetes.pod_name.keyword" - fieldContainerNameKeyword = "kubernetes.container_name.keyword" -) - -const ( - ElasticV5 = "5" - ElasticV6 = "6" - ElasticV7 = "7" -) - -var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary - -type ElasticSearchClient struct { - client Client -} - -func NewLoggingClient(options *ElasticSearchOptions) (*ElasticSearchClient, error) { - var version, index string - esClient := &ElasticSearchClient{} - - if options.Version == "" { - var err error - version, err = detectVersionMajor(options.Host) - if err != nil { - return nil, err - } - } else { - version = options.Version - } - - if options.IndexPrefix != "" { - index = options.IndexPrefix - } else { - index = "logstash" - } - - switch version { - case ElasticV5: - esClient.client = v5.New(options.Host, index) - case ElasticV6: - esClient.client = v6.New(options.Host, index) - case ElasticV7: - esClient.client = v7.New(options.Host, index) - default: - return nil, fmt.Errorf("unsupported elasticsearch version %s", version) - } - - return esClient, nil -} - -func (c *ElasticSearchClient) ES() *Client { - return &c.client -} - -func detectVersionMajor(host string) (string, error) { - - // Info APIs are backward compatible with versions of v5.x, v6.x and v7.x - es := v6.New(host, "") - res, err := es.Client.Info( - es.Client.Info.WithContext(context.Background()), - ) - if err != nil { - return "", err - } - - defer res.Body.Close() - - var b map[string]interface{} - if err = json.NewDecoder(res.Body).Decode(&b); err != nil { - return "", err - } - if res.IsError() { - // Print the response status and error information. - e, _ := b["error"].(map[string]interface{}) - return "", fmt.Errorf("[%s] type: %v, reason: %v", res.Status(), e["type"], e["reason"]) - } - - // get the major version - version, _ := b["version"].(map[string]interface{}) - number, _ := version["number"].(string) - if number == "" { - return "", fmt.Errorf("failed to detect elastic version number") - } - - v := strings.Split(number, ".")[0] - return v, nil -} - -func createQueryRequest(param v1alpha2.QueryParameters) ([]byte, error) { - var request v1alpha2.Request - var mainBoolQuery v1alpha2.BoolFilter - - if len(param.NamespaceWithCreationTime) != 0 { - var boolShould v1alpha2.BoolShould - for namespace, creationTime := range param.NamespaceWithCreationTime { - var boolFilter v1alpha2.BoolFilter - - matchPhrase := v1alpha2.MatchPhrase{MatchPhrase: map[string]string{fieldNamespaceNameKeyword: namespace}} - rangeQuery := v1alpha2.RangeQuery{RangeSpec: v1alpha2.RangeSpec{TimeRange: v1alpha2.TimeRange{Gte: creationTime, Lte: ""}}} - - boolFilter.Filter = append(boolFilter.Filter, matchPhrase) - boolFilter.Filter = append(boolFilter.Filter, rangeQuery) - - boolShould.Should = append(boolShould.Should, v1alpha2.BoolQuery{Bool: boolFilter}) - } - boolShould.MinimumShouldMatch = 1 - mainBoolQuery.Filter = append(mainBoolQuery.Filter, v1alpha2.BoolQuery{Bool: boolShould}) - } - if param.WorkloadFilter != nil { - boolQuery := makeBoolShould(regexpQuery, fieldPodNameKeyword, param.WorkloadFilter) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - if param.PodFilter != nil { - boolQuery := makeBoolShould(matchPhrase, fieldPodNameKeyword, param.PodFilter) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - if param.ContainerFilter != nil { - boolQuery := makeBoolShould(matchPhrase, fieldContainerNameKeyword, param.ContainerFilter) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - - if param.WorkloadQuery != nil { - boolQuery := makeBoolShould(matchPhrasePrefix, fieldPodName, param.WorkloadQuery) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - if param.PodQuery != nil { - boolQuery := makeBoolShould(matchPhrasePrefix, fieldPodName, param.PodQuery) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - if param.ContainerQuery != nil { - boolQuery := makeBoolShould(matchPhrasePrefix, fieldContainerName, param.ContainerQuery) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - if param.LogQuery != nil { - boolQuery := makeBoolShould(matchPhrasePrefix, fieldLog, param.LogQuery) - mainBoolQuery.Filter = append(mainBoolQuery.Filter, boolQuery) - } - - rangeQuery := v1alpha2.RangeQuery{RangeSpec: v1alpha2.RangeSpec{TimeRange: v1alpha2.TimeRange{Gte: param.StartTime, Lte: param.EndTime}}} - mainBoolQuery.Filter = append(mainBoolQuery.Filter, rangeQuery) - - if param.Operation == v1alpha2.OperationStatistics { - containerAgg := v1alpha2.AggField{Field: "kubernetes.docker_id.keyword"} - statisticAggs := v1alpha2.StatisticsAggs{ContainerAgg: v1alpha2.ContainerAgg{Cardinality: containerAgg}} - request.Aggs = statisticAggs - request.Size = 0 - } else if param.Operation == v1alpha2.OperationHistogram { - var interval string - if param.Interval != "" { - interval = param.Interval - } else { - interval = "15m" - } - param.Interval = interval - request.Aggs = v1alpha2.HistogramAggs{HistogramAgg: v1alpha2.HistogramAgg{DateHistogram: v1alpha2.DateHistogram{Field: "time", Interval: interval}}} - request.Size = 0 - } else { - request.From = param.From - request.Size = param.Size - var order string - if strings.Compare(strings.ToLower(param.Sort), "asc") == 0 { - order = "asc" - } else { - order = "desc" - } - request.Sorts = append(request.Sorts, v1alpha2.Sort{Order: v1alpha2.Order{Order: order}}) - } - - request.MainQuery = v1alpha2.BoolQuery{Bool: mainBoolQuery} - - return json.Marshal(request) -} - -func makeBoolShould(queryType int, field string, list []string) v1alpha2.BoolQuery { - var should []interface{} - for _, phrase := range list { - - var q interface{} - - switch queryType { - case matchPhrase: - q = v1alpha2.MatchPhrase{MatchPhrase: map[string]string{field: phrase}} - case matchPhrasePrefix: - q = v1alpha2.MatchPhrasePrefix{MatchPhrasePrefix: map[string]string{field: phrase}} - case regexpQuery: - q = v1alpha2.RegexpQuery{Regexp: map[string]string{field: makePodNameRegexp(phrase)}} - } - - should = append(should, q) - } - - return v1alpha2.BoolQuery{ - Bool: v1alpha2.BoolShould{ - Should: should, - MinimumShouldMatch: 1, - }, - } -} - -func makePodNameRegexp(workloadName string) string { - var regexp string - if len(workloadName) <= podNameMaxLength-replicaSetSuffixMaxLength-randSuffixLength { - // match deployment pods, eg. -579dfbcddd-24znw - // replicaset rand string is limited to vowels - // https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83 - regexp += workloadName + "-[bcdfghjklmnpqrstvwxz2456789]{1,10}-[a-z0-9]{5}|" - // match statefulset pods, eg. -0 - regexp += workloadName + "-[0-9]+|" - // match pods of daemonset or job, eg. -29tdk, -5xqvl - regexp += workloadName + "-[a-z0-9]{5}" - } else if len(workloadName) <= podNameMaxLength-randSuffixLength { - replicaSetSuffixLength := podNameMaxLength - randSuffixLength - len(workloadName) - regexp += fmt.Sprintf("%s%d%s", workloadName+"-[bcdfghjklmnpqrstvwxz2456789]{", replicaSetSuffixLength, "}[a-z0-9]{5}|") - regexp += workloadName + "-[0-9]+|" - regexp += workloadName + "-[a-z0-9]{5}" - } else { - // Rand suffix may overwrites the workload name if the name is too long - // This won't happen for StatefulSet because a statefulset pod will fail to create - regexp += workloadName[:podNameMaxLength-randSuffixLength+1] + "[a-z0-9]{5}|" - regexp += workloadName + "-[0-9]+" - } - return regexp -} - -func (c *ElasticSearchClient) parseQueryResult(operation int, body []byte) (*v1alpha2.QueryResult, error) { - var queryResult v1alpha2.QueryResult - - var response v1alpha2.Response - err := jsonIter.Unmarshal(body, &response) - if err != nil { - klog.Error(err) - return nil, err - } - - if response.Shards.Successful != response.Shards.Total { - //Elastic some shards error - klog.Warningf("Not all shards succeed, successful shards: %d, skipped shards: %d, failed shards: %d", - response.Shards.Successful, response.Shards.Skipped, response.Shards.Failed) - } - - switch operation { - case v1alpha2.OperationQuery: - var readResult v1alpha2.ReadResult - readResult.Total = c.client.GetTotalHitCount(response.Hits.Total) - for _, hit := range response.Hits.Hits { - var logRecord v1alpha2.LogRecord - logRecord.Time = hit.Source.Time - logRecord.Log = hit.Source.Log - logRecord.Namespace = hit.Source.Kubernetes.Namespace - logRecord.Pod = hit.Source.Kubernetes.Pod - logRecord.Container = hit.Source.Kubernetes.Container - logRecord.Host = hit.Source.Kubernetes.Host - readResult.Records = append(readResult.Records, logRecord) - } - queryResult.Read = &readResult - case v1alpha2.OperationStatistics: - var statisticsResponse v1alpha2.StatisticsResponseAggregations - err := jsonIter.Unmarshal(response.Aggregations, &statisticsResponse) - if err != nil && response.Aggregations != nil { - klog.Error(err) - return nil, err - } - queryResult.Statistics = &v1alpha2.StatisticsResult{Containers: statisticsResponse.ContainerCount.Value, Logs: c.client.GetTotalHitCount(response.Hits.Total)} - case v1alpha2.OperationHistogram: - var histogramResult v1alpha2.HistogramResult - histogramResult.Total = c.client.GetTotalHitCount(response.Hits.Total) - - var histogramAggregations v1alpha2.HistogramAggregations - err = jsonIter.Unmarshal(response.Aggregations, &histogramAggregations) - if err != nil && response.Aggregations != nil { - klog.Error(err) - return nil, err - } - for _, histogram := range histogramAggregations.HistogramAggregation.Histograms { - var histogramRecord v1alpha2.HistogramRecord - histogramRecord.Time = histogram.Time - histogramRecord.Count = histogram.Count - - histogramResult.Histograms = append(histogramResult.Histograms, histogramRecord) - } - - queryResult.Histogram = &histogramResult - case v1alpha2.OperationExport: - var readResult v1alpha2.ReadResult - readResult.ScrollID = response.ScrollId - for _, hit := range response.Hits.Hits { - var logRecord v1alpha2.LogRecord - logRecord.Log = hit.Source.Log - readResult.Records = append(readResult.Records, logRecord) - } - queryResult.Read = &readResult - } - - return &queryResult, nil -} - -func (c *ElasticSearchClient) Query(param v1alpha2.QueryParameters) (*v1alpha2.QueryResult, error) { - - var queryResult = new(v1alpha2.QueryResult) - - if param.NamespaceNotFound { - queryResult = new(v1alpha2.QueryResult) - switch param.Operation { - case v1alpha2.OperationStatistics: - queryResult.Statistics = new(v1alpha2.StatisticsResult) - case v1alpha2.OperationHistogram: - queryResult.Histogram = new(v1alpha2.HistogramResult) - default: - queryResult.Read = new(v1alpha2.ReadResult) - } - return queryResult, nil - } - - query, err := createQueryRequest(param) - if err != nil { - klog.Error(err) - return nil, err - } - - body, err := c.client.Search(query, param.ScrollTimeout) - if err != nil { - klog.Error(err) - return nil, err - } - - return c.parseQueryResult(param.Operation, body) -} - -func (c *ElasticSearchClient) Scroll(scrollId string) (*v1alpha2.QueryResult, error) { - body, err := c.client.Scroll(scrollId, time.Minute) - if err != nil { - klog.Error(err) - return nil, err - } - return c.parseQueryResult(v1alpha2.OperationExport, body) -} - -func (c *ElasticSearchClient) ClearScroll(scrollId string) { - c.client.ClearScroll(scrollId) -} diff --git a/pkg/simple/client/elasticsearch/interface.go b/pkg/simple/client/elasticsearch/interface.go deleted file mode 100644 index de278c8ae..000000000 --- a/pkg/simple/client/elasticsearch/interface.go +++ /dev/null @@ -1,11 +0,0 @@ -package esclient - -import "time" - -type Client interface { - // Perform Search API - Search(body []byte, scrollTimeout time.Duration) ([]byte, error) - Scroll(scrollId string, scrollTimeout time.Duration) ([]byte, error) - ClearScroll(scrollId string) - GetTotalHitCount(v interface{}) int64 -} diff --git a/pkg/simple/client/elasticsearch/options.go b/pkg/simple/client/elasticsearch/options.go deleted file mode 100644 index 0ae93442d..000000000 --- a/pkg/simple/client/elasticsearch/options.go +++ /dev/null @@ -1,46 +0,0 @@ -package esclient - -import ( - "github.com/spf13/pflag" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" -) - -type ElasticSearchOptions struct { - Host string `json:"host" yaml:"host"` - IndexPrefix string `json:"indexPrefix,omitempty" yaml:"indexPrefix"` - Version string `json:"version" yaml:"version"` -} - -func NewElasticSearchOptions() *ElasticSearchOptions { - return &ElasticSearchOptions{ - Host: "", - IndexPrefix: "fluentbit", - Version: "", - } -} - -func (s *ElasticSearchOptions) ApplyTo(options *ElasticSearchOptions) { - if s.Host != "" { - reflectutils.Override(options, s) - } -} - -func (s *ElasticSearchOptions) Validate() []error { - errs := []error{} - - return errs -} - -func (s *ElasticSearchOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Host, "elasticsearch-host", s.Host, ""+ - "ElasticSearch logging service host. KubeSphere is using elastic as log store, "+ - "if this filed left blank, KubeSphere will use kubernetes builtin log API instead, and"+ - " the following elastic search options will be ignored.") - - fs.StringVar(&s.IndexPrefix, "index-prefix", s.IndexPrefix, ""+ - "Index name prefix. KubeSphere will retrieve logs against indices matching the prefix.") - - fs.StringVar(&s.Version, "elasticsearch-version", s.Version, ""+ - "ElasticSearch major version, e.g. 5/6/7, if left blank, will detect automatically."+ - "Currently, minimum supported version is 5.x") -} diff --git a/pkg/simple/client/elasticsearch/versions/v5/elasticsearch.go b/pkg/simple/client/elasticsearch/versions/v5/elasticsearch.go deleted file mode 100644 index 48d0f5fe0..000000000 --- a/pkg/simple/client/elasticsearch/versions/v5/elasticsearch.go +++ /dev/null @@ -1,88 +0,0 @@ -package v5 - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/elastic/go-elasticsearch/v5" - "github.com/elastic/go-elasticsearch/v5/esapi" - "io/ioutil" - "k8s.io/klog" - "time" -) - -type Elastic struct { - client *elasticsearch.Client - index string -} - -func New(address string, index string) *Elastic { - client, err := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{address}, - }) - if err != nil { - klog.Error(err) - return nil - } - - return &Elastic{client: client, index: index} -} - -func (e *Elastic) Search(body []byte, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.client.Search( - e.client.Search.WithContext(context.Background()), - e.client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), - e.client.Search.WithBody(bytes.NewBuffer(body)), - e.client.Search.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - return ioutil.ReadAll(response.Body) -} - -func (e *Elastic) Scroll(scrollId string, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.client.Scroll( - e.client.Scroll.WithContext(context.Background()), - e.client.Scroll.WithScrollID(scrollId), - e.client.Scroll.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - return ioutil.ReadAll(response.Body) -} - -func (e *Elastic) ClearScroll(scrollId string) { - response, _ := e.client.ClearScroll( - e.client.ClearScroll.WithContext(context.Background()), - e.client.ClearScroll.WithScrollID(scrollId)) - defer response.Body.Close() -} - -func (e *Elastic) GetTotalHitCount(v interface{}) int64 { - f, _ := v.(float64) - return int64(f) -} - -func parseError(response *esapi.Response) error { - var e map[string]interface{} - if err := json.NewDecoder(response.Body).Decode(&e); err != nil { - return err - } else { - // Print the response status and error information. - e, _ := e["error"].(map[string]interface{}) - return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) - } -} diff --git a/pkg/simple/client/elasticsearch/versions/v6/elasticsearch.go b/pkg/simple/client/elasticsearch/versions/v6/elasticsearch.go deleted file mode 100644 index c6ec6590c..000000000 --- a/pkg/simple/client/elasticsearch/versions/v6/elasticsearch.go +++ /dev/null @@ -1,88 +0,0 @@ -package v6 - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/elastic/go-elasticsearch/v6" - "github.com/elastic/go-elasticsearch/v6/esapi" - "io/ioutil" - "k8s.io/klog" - "time" -) - -type Elastic struct { - Client *elasticsearch.Client - index string -} - -func New(address string, index string) *Elastic { - client, err := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{address}, - }) - if err != nil { - klog.Error(err) - return nil - } - - return &Elastic{Client: client, index: index} -} - -func (e *Elastic) Search(body []byte, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.Client.Search( - e.Client.Search.WithContext(context.Background()), - e.Client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), - e.Client.Search.WithBody(bytes.NewBuffer(body)), - e.Client.Search.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - return ioutil.ReadAll(response.Body) -} - -func (e *Elastic) Scroll(scrollId string, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.Client.Scroll( - e.Client.Scroll.WithContext(context.Background()), - e.Client.Scroll.WithScrollID(scrollId), - e.Client.Scroll.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - return ioutil.ReadAll(response.Body) -} - -func (e *Elastic) ClearScroll(scrollId string) { - response, _ := e.Client.ClearScroll( - e.Client.ClearScroll.WithContext(context.Background()), - e.Client.ClearScroll.WithScrollID(scrollId)) - defer response.Body.Close() -} - -func (e *Elastic) GetTotalHitCount(v interface{}) int64 { - f, _ := v.(float64) - return int64(f) -} - -func parseError(response *esapi.Response) error { - var e map[string]interface{} - if err := json.NewDecoder(response.Body).Decode(&e); err != nil { - return err - } else { - // Print the response status and error information. - e, _ := e["error"].(map[string]interface{}) - return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) - } -} diff --git a/pkg/simple/client/elasticsearch/versions/v7/elasticsearch.go b/pkg/simple/client/elasticsearch/versions/v7/elasticsearch.go deleted file mode 100644 index 030cf126b..000000000 --- a/pkg/simple/client/elasticsearch/versions/v7/elasticsearch.go +++ /dev/null @@ -1,91 +0,0 @@ -package v7 - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/elastic/go-elasticsearch/v7" - "github.com/elastic/go-elasticsearch/v7/esapi" - "io/ioutil" - "k8s.io/klog" - "time" -) - -type Elastic struct { - client *elasticsearch.Client - index string -} - -func New(address string, index string) *Elastic { - client, err := elasticsearch.NewClient(elasticsearch.Config{ - Addresses: []string{address}, - }) - if err != nil { - klog.Error(err) - return nil - } - - return &Elastic{client: client, index: index} -} - -func (e *Elastic) Search(body []byte, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.client.Search( - e.client.Search.WithContext(context.Background()), - e.client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), - e.client.Search.WithTrackTotalHits(true), - e.client.Search.WithBody(bytes.NewBuffer(body)), - e.client.Search.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - return ioutil.ReadAll(response.Body) -} - -func (e *Elastic) Scroll(scrollId string, scrollTimeout time.Duration) ([]byte, error) { - response, err := e.client.Scroll( - e.client.Scroll.WithContext(context.Background()), - e.client.Scroll.WithScrollID(scrollId), - e.client.Scroll.WithScroll(scrollTimeout)) - if err != nil { - return nil, err - } - defer response.Body.Close() - - if response.IsError() { - return nil, parseError(response) - } - - b, err := ioutil.ReadAll(response.Body) - return b, err -} - -func (e *Elastic) ClearScroll(scrollId string) { - response, _ := e.client.ClearScroll( - e.client.ClearScroll.WithContext(context.Background()), - e.client.ClearScroll.WithScrollID(scrollId)) - defer response.Body.Close() -} - -func (e *Elastic) GetTotalHitCount(v interface{}) int64 { - m, _ := v.(map[string]interface{}) - f, _ := m["value"].(float64) - return int64(f) -} - -func parseError(response *esapi.Response) error { - var e map[string]interface{} - if err := json.NewDecoder(response.Body).Decode(&e); err != nil { - return err - } else { - // Print the response status and error information. - e, _ := e["error"].(map[string]interface{}) - return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) - } -} diff --git a/pkg/simple/client/factory.go b/pkg/simple/client/factory.go deleted file mode 100644 index 6fadb2c58..000000000 --- a/pkg/simple/client/factory.go +++ /dev/null @@ -1,377 +0,0 @@ -package client - -import ( - "fmt" - goredis "github.com/go-redis/redis" - "kubesphere.io/kubesphere/pkg/simple/client/devops" - esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" - "kubesphere.io/kubesphere/pkg/simple/client/k8s" - "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" - "kubesphere.io/kubesphere/pkg/simple/client/ldap" - "kubesphere.io/kubesphere/pkg/simple/client/mysql" - "kubesphere.io/kubesphere/pkg/simple/client/openpitrix" - "kubesphere.io/kubesphere/pkg/simple/client/prometheus" - "kubesphere.io/kubesphere/pkg/simple/client/redis" - "kubesphere.io/kubesphere/pkg/simple/client/s2is3" - "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" - "sync" -) - -type ClientSetNotEnabledError struct { - err error -} - -func (e ClientSetNotEnabledError) Error() string { - return fmt.Sprintf("client set not enabled: %v", e.err) -} - -type ClientSetOptions struct { - mySQLOptions *mysql.MySQLOptions - redisOptions *redis.RedisOptions - kubernetesOptions *k8s.KubernetesOptions - devopsOptions *devops.DevopsOptions - sonarqubeOptions *sonarqube.SonarQubeOptions - ldapOptions *ldap.LdapOptions - s3Options *s2is3.S3Options - openPitrixOptions *openpitrix.OpenPitrixOptions - prometheusOptions *prometheus.PrometheusOptions - kubesphereOptions *kubesphere.KubeSphereOptions - elasticSearhOptions *esclient.ElasticSearchOptions -} - -func NewClientSetOptions() *ClientSetOptions { - return &ClientSetOptions{ - mySQLOptions: mysql.NewMySQLOptions(), - redisOptions: redis.NewRedisOptions(), - kubernetesOptions: k8s.NewKubernetesOptions(), - ldapOptions: ldap.NewLdapOptions(), - devopsOptions: devops.NewDevopsOptions(), - sonarqubeOptions: sonarqube.NewSonarQubeOptions(), - s3Options: s2is3.NewS3Options(), - openPitrixOptions: openpitrix.NewOpenPitrixOptions(), - prometheusOptions: prometheus.NewPrometheusOptions(), - kubesphereOptions: kubesphere.NewKubeSphereOptions(), - elasticSearhOptions: esclient.NewElasticSearchOptions(), - } -} - -func (c *ClientSetOptions) SetMySQLOptions(options *mysql.MySQLOptions) *ClientSetOptions { - c.mySQLOptions = options - return c -} - -func (c *ClientSetOptions) SetRedisOptions(options *redis.RedisOptions) *ClientSetOptions { - c.redisOptions = options - return c -} - -func (c *ClientSetOptions) SetKubernetesOptions(options *k8s.KubernetesOptions) *ClientSetOptions { - c.kubernetesOptions = options - return c -} - -func (c *ClientSetOptions) SetDevopsOptions(options *devops.DevopsOptions) *ClientSetOptions { - c.devopsOptions = options - return c -} - -func (c *ClientSetOptions) SetLdapOptions(options *ldap.LdapOptions) *ClientSetOptions { - c.ldapOptions = options - return c -} - -func (c *ClientSetOptions) SetS3Options(options *s2is3.S3Options) *ClientSetOptions { - c.s3Options = options - return c -} - -func (c *ClientSetOptions) SetOpenPitrixOptions(options *openpitrix.OpenPitrixOptions) *ClientSetOptions { - c.openPitrixOptions = options - return c -} - -func (c *ClientSetOptions) SetPrometheusOptions(options *prometheus.PrometheusOptions) *ClientSetOptions { - c.prometheusOptions = options - return c -} - -func (c *ClientSetOptions) SetSonarQubeOptions(options *sonarqube.SonarQubeOptions) *ClientSetOptions { - c.sonarqubeOptions = options - return c -} - -func (c *ClientSetOptions) SetKubeSphereOptions(options *kubesphere.KubeSphereOptions) *ClientSetOptions { - c.kubesphereOptions = options - return c -} - -func (c *ClientSetOptions) SetElasticSearchOptions(options *esclient.ElasticSearchOptions) *ClientSetOptions { - c.elasticSearhOptions = options - return c -} - -// ClientSet provide best of effort service to initialize clients, -// but there is no guarantee to return a valid client instance, -// so do validity check before use -type ClientSet struct { - csoptions *ClientSetOptions - stopCh <-chan struct{} - - mySQLClient *mysql.MySQLClient - - k8sClient *k8s.KubernetesClient - ldapClient *ldap.LdapClient - devopsClient *devops.DevopsClient - sonarQubeClient *sonarqube.SonarQubeClient - redisClient *redis.RedisClient - s3Client *s2is3.S3Client - prometheusClient *prometheus.PrometheusClient - openpitrixClient *openpitrix.OpenPitrixClient - kubesphereClient *kubesphere.KubeSphereClient - elasticSearchClient *esclient.ElasticSearchClient -} - -var mutex sync.Mutex - -// global clientsets instance -var sharedClientSet *ClientSet - -func ClientSets() *ClientSet { - return sharedClientSet -} - -func NewClientSetFactory(c *ClientSetOptions, stopCh <-chan struct{}) *ClientSet { - sharedClientSet = &ClientSet{csoptions: c, stopCh: stopCh} - - if c.kubernetesOptions != nil { - sharedClientSet.k8sClient = k8s.NewKubernetesClientOrDie(c.kubernetesOptions) - } - - if c.kubesphereOptions != nil { - sharedClientSet.kubesphereClient = kubesphere.NewKubeSphereClient(c.kubesphereOptions) - } - - return sharedClientSet -} - -// lazy creating -func (cs *ClientSet) MySQL() (*mysql.Database, error) { - var err error - - if cs.csoptions.mySQLOptions == nil || cs.csoptions.mySQLOptions.Host == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.mySQLClient != nil { - return cs.mySQLClient.Database(), nil - } else { - mutex.Lock() - defer mutex.Unlock() - if cs.mySQLClient == nil { - cs.mySQLClient, err = mysql.NewMySQLClient(cs.csoptions.mySQLOptions, cs.stopCh) - if err != nil { - return nil, err - } - } - - return cs.mySQLClient.Database(), nil - } -} - -func (cs *ClientSet) Redis() (*goredis.Client, error) { - var err error - - if cs.csoptions.redisOptions == nil || cs.csoptions.redisOptions.RedisURL == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.redisClient != nil { - return cs.redisClient.Redis(), nil - } else { - mutex.Lock() - defer mutex.Unlock() - if cs.redisClient == nil { - cs.redisClient, err = redis.NewRedisClient(cs.csoptions.redisOptions, cs.stopCh) - if err != nil { - return nil, err - } - } - - return cs.redisClient.Redis(), nil - } -} - -func (cs *ClientSet) Devops() (*devops.DevopsClient, error) { - var err error - - if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.devopsClient != nil { - return cs.devopsClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.devopsClient == nil { - cs.devopsClient, err = devops.NewDevopsClient(cs.csoptions.devopsOptions) - if err != nil { - return nil, err - } - } - return cs.devopsClient, nil - } -} - -func (cs *ClientSet) SonarQube() (*sonarqube.SonarQubeClient, error) { - var err error - - if cs.csoptions.sonarqubeOptions == nil || cs.csoptions.sonarqubeOptions.Host == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.sonarQubeClient != nil { - return cs.sonarQubeClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.sonarQubeClient == nil { - cs.sonarQubeClient, err = sonarqube.NewSonarQubeClient(cs.csoptions.sonarqubeOptions) - if err != nil { - return nil, err - } - } - return cs.sonarQubeClient, nil - } -} - -func (cs *ClientSet) Ldap() (*ldap.LdapClient, error) { - var err error - - if cs.csoptions.ldapOptions == nil || cs.csoptions.ldapOptions.Host == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.ldapClient != nil { - return cs.ldapClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.ldapClient == nil { - cs.ldapClient, err = ldap.NewLdapClient(cs.csoptions.ldapOptions, cs.stopCh) - if err != nil { - return nil, err - } - } - return cs.ldapClient, nil - } -} - -// since kubernetes client is required, we will -// create it on setup -func (cs *ClientSet) K8s() *k8s.KubernetesClient { - return cs.k8sClient -} - -func (cs *ClientSet) S3() (*s2is3.S3Client, error) { - var err error - - if cs.csoptions.s3Options == nil || cs.csoptions.s3Options.Endpoint == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.s3Client != nil { - return cs.s3Client, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.s3Client == nil { - cs.s3Client, err = s2is3.NewS3Client(cs.csoptions.s3Options) - if err != nil { - return nil, err - } - } - return cs.s3Client, nil - } -} - -func (cs *ClientSet) OpenPitrix() (*openpitrix.OpenPitrixClient, error) { - var err error - - if cs.csoptions.openPitrixOptions == nil || - cs.csoptions.openPitrixOptions.RepoManagerEndpoint == "" || - cs.csoptions.openPitrixOptions.RuntimeManagerEndpoint == "" || - cs.csoptions.openPitrixOptions.ClusterManagerEndpoint == "" || - cs.csoptions.openPitrixOptions.AppManagerEndpoint == "" || - cs.csoptions.openPitrixOptions.AttachmentManagerEndpoint == "" || - cs.csoptions.openPitrixOptions.RepoIndexerEndpoint == "" || - cs.csoptions.openPitrixOptions.CategoryManagerEndpoint == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.openpitrixClient != nil { - return cs.openpitrixClient, nil - } else { - cs.openpitrixClient, err = openpitrix.NewOpenPitrixClient(cs.csoptions.openPitrixOptions) - if err != nil { - return nil, err - } - - return cs.openpitrixClient, nil - } -} - -func (cs *ClientSet) Prometheus() (*prometheus.PrometheusClient, error) { - var err error - - if cs.csoptions.prometheusOptions == nil || cs.csoptions.prometheusOptions.Endpoint == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.prometheusClient != nil { - return cs.prometheusClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.prometheusClient == nil { - cs.prometheusClient, err = prometheus.NewPrometheusClient(cs.csoptions.prometheusOptions) - if err != nil { - return nil, err - } - } - return cs.prometheusClient, nil - } -} - -func (cs *ClientSet) KubeSphere() *kubesphere.KubeSphereClient { - return cs.kubesphereClient -} - -func (cs *ClientSet) ElasticSearch() (*esclient.ElasticSearchClient, error) { - var err error - - if cs.csoptions.elasticSearhOptions == nil || cs.csoptions.elasticSearhOptions.Host == "" { - return nil, ClientSetNotEnabledError{} - } - - if cs.elasticSearchClient != nil { - return cs.elasticSearchClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.elasticSearchClient == nil { - cs.elasticSearchClient, err = esclient.NewLoggingClient(cs.csoptions.elasticSearhOptions) - if err != nil { - return nil, err - } - } - - return cs.elasticSearchClient, nil - } -} diff --git a/pkg/simple/client/fluentbit/fluentbitcrdclient.go b/pkg/simple/client/fluentbit/fluentbitcrdclient.go deleted file mode 100644 index 9cb26dab4..000000000 --- a/pkg/simple/client/fluentbit/fluentbitcrdclient.go +++ /dev/null @@ -1,288 +0,0 @@ -/* -Copyright 2018 The KubeSphere 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 fluentbitclient - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "time" -) - -const ( - CRDPlural string = "fluentbits" - CRDGroup string = "logging.kubesphere.io" - CRDVersion string = "v1alpha1" - FullCRDName string = CRDPlural + "." + CRDGroup -) - -// FluentBitList auto generated by the sdk -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type FluentBitList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - Items []FluentBit `json:"items"` -} - -// FluentBit auto generated by the sdk -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type FluentBit struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata"` - Spec FluentBitSpec `json:"spec"` - Status FluentBitStatus `json:"status,omitempty"` -} - -// FluentBitSpec holds the spec for the operator -type FluentBitSpec struct { - Service []Plugin `json:"service"` - Input []Plugin `json:"input"` - Filter []Plugin `json:"filter"` - Output []Plugin `json:"output"` - Settings []Plugin `json:"settings"` -} - -// FluentBitStatus holds the status info for the operator -type FluentBitStatus struct { - // Fill me -} - -// Plugin struct for fluent-bit plugins -type Plugin struct { - Type string `json:"type" description:"output plugin type, eg. fluentbit-output-es"` - Name string `json:"name" description:"output plugin name, eg. fluentbit-output-es"` - Parameters []Parameter `json:"parameters" description:"output plugin configuration parameters"` -} - -// Fluent-bit output plugins -type OutputPlugin struct { - Plugin - Id string `json:"id,omitempty" description:"output uuid"` - Enable bool `json:"enable" description:"active status, one of true, false"` - Updatetime time.Time `json:"updatetime,omitempty" description:"last updatetime"` -} - -// Parameter generic parameter type to handle values from different sources -type Parameter struct { - Name string `json:"name" description:"configuration parameter key, eg. Name. refer to Fluent bit's Output Plugins Section for more configuration parameters."` - ValueFrom *ValueFrom `json:"valueFrom,omitempty"` - Value string `json:"value" description:"configuration parameter value, eg. es. refer to Fluent bit's Output Plugins Section for more configuration parameters."` -} - -// ValueFrom generic type to determine value origin -type ValueFrom struct { - SecretKeyRef KubernetesSecret `json:"secretKeyRef"` -} - -// KubernetesSecret is a ValueFrom type -type KubernetesSecret struct { - Name string `json:"name"` - Key string `json:"key"` - Namespace string `json:"namespace"` -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FluentBit) DeepCopyInto(out *FluentBit) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - out.Status = in.Status - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FluentBit. -func (in *FluentBit) DeepCopy() *FluentBit { - if in == nil { - return nil - } - out := new(FluentBit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *FluentBit) 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 *FluentBitList) DeepCopyInto(out *FluentBitList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]FluentBit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FluentBitList. -func (in *FluentBitList) DeepCopy() *FluentBitList { - if in == nil { - return nil - } - out := new(FluentBitList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *FluentBitList) 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 *FluentBitSpec) DeepCopyInto(out *FluentBitSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FluentBitSpec. -func (in *FluentBitSpec) DeepCopy() *FluentBitSpec { - if in == nil { - return nil - } - out := new(FluentBitSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FluentBitStatus) DeepCopyInto(out *FluentBitStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FluentBitStatus. -func (in *FluentBitStatus) DeepCopy() *FluentBitStatus { - if in == nil { - return nil - } - out := new(FluentBitStatus) - in.DeepCopyInto(out) - return out -} - -// Create a Rest client with the new CRD Schema -var SchemeGroupVersion = schema.GroupVersion{Group: CRDGroup, Version: CRDVersion} - -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &FluentBit{}, - &FluentBitList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} - -func NewFluentbitCRDClient(cfg *rest.Config) (*rest.RESTClient, *runtime.Scheme, error) { - scheme := runtime.NewScheme() - SchemeBuilder := runtime.NewSchemeBuilder(addKnownTypes) - if err := SchemeBuilder.AddToScheme(scheme); err != nil { - return nil, nil, err - } - config := *cfg - config.GroupVersion = &SchemeGroupVersion - config.APIPath = "/apis" - config.ContentType = runtime.ContentTypeJSON - config.NegotiatedSerializer = serializer.NewCodecFactory(runtime.NewScheme()).WithoutConversion() - - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, nil, err - } - return client, scheme, nil -} - -// This file implement all the (CRUD) client methods we need to access our CRD object - -func CrdClient(cl *rest.RESTClient, scheme *runtime.Scheme, namespace string) *crdclient { - return &crdclient{cl: cl, ns: namespace, plural: CRDPlural, - codec: runtime.NewParameterCodec(scheme)} -} - -type crdclient struct { - cl *rest.RESTClient - ns string - plural string - codec runtime.ParameterCodec -} - -func (f *crdclient) Create(obj *FluentBit) (*FluentBit, error) { - var result FluentBit - err := f.cl.Post(). - Namespace(f.ns).Resource(f.plural). - Body(obj).Do().Into(&result) - return &result, err -} - -func (f *crdclient) Update(name string, obj *FluentBit) (*FluentBit, error) { - var result FluentBit - err := f.cl.Put(). - Namespace(f.ns).Resource(f.plural). - Name(name).Body(obj).Do().Into(&result) - return &result, err -} - -func (f *crdclient) Delete(name string, options *metav1.DeleteOptions) error { - return f.cl.Delete(). - Namespace(f.ns).Resource(f.plural). - Name(name).Body(options).Do(). - Error() -} - -func (f *crdclient) Get(name string) (*FluentBit, error) { - var result FluentBit - err := f.cl.Get(). - Namespace(f.ns).Resource(f.plural). - Name(name).Do().Into(&result) - return &result, err -} - -func (f *crdclient) List(opts metav1.ListOptions) (*FluentBitList, error) { - var result FluentBitList - err := f.cl.Get(). - Namespace(f.ns).Resource(f.plural). - VersionedParams(&opts, f.codec). - Do().Into(&result) - return &result, err -} - -// Create a new List watch for our TPR -func (f *crdclient) NewListWatch() *cache.ListWatch { - return cache.NewListWatchFromClient(f.cl, f.plural, f.ns, fields.Everything()) -} - -// return rest config, if path not specified assume in cluster config -func GetClientConfig(kubeconfig string) (*rest.Config, error) { - if kubeconfig != "" { - return clientcmd.BuildConfigFromFlags("", kubeconfig) - } - return rest.InClusterConfig() -} diff --git a/pkg/simple/client/k8s/kubernetes.go b/pkg/simple/client/k8s/kubernetes.go index a203eb61e..c46e51b18 100644 --- a/pkg/simple/client/k8s/kubernetes.go +++ b/pkg/simple/client/k8s/kubernetes.go @@ -2,7 +2,7 @@ package k8s import ( applicationclientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned" - s2i "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" + istioclient "istio.io/client-go/pkg/clientset/versioned" "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -11,19 +11,29 @@ import ( "strings" ) -type KubernetesClient struct { +type Client interface { + Kubernetes() kubernetes.Interface + KubeSphere() kubesphere.Interface + Istio() istioclient.Interface + Application() applicationclientset.Interface + Discovery() discovery.DiscoveryInterface + Master() string + Config() *rest.Config +} + +type kubernetesClient struct { // kubernetes client interface - k8s *kubernetes.Clientset + k8s kubernetes.Interface // discovery client discoveryClient *discovery.DiscoveryClient // generated clientset - ks *kubesphere.Clientset + ks kubesphere.Interface - s2i *s2i.Clientset + application applicationclientset.Interface - application *applicationclientset.Clientset + istio istioclient.Interface master string @@ -31,7 +41,7 @@ type KubernetesClient struct { } // NewKubernetesClientOrDie creates KubernetesClient and panic if there is an error -func NewKubernetesClientOrDie(options *KubernetesOptions) *KubernetesClient { +func NewKubernetesClientOrDie(options *KubernetesOptions) Client { config, err := clientcmd.BuildConfigFromFlags("", options.KubeConfig) if err != nil { panic(err) @@ -40,11 +50,11 @@ func NewKubernetesClientOrDie(options *KubernetesOptions) *KubernetesClient { config.QPS = options.QPS config.Burst = options.Burst - k := &KubernetesClient{ + k := &kubernetesClient{ k8s: kubernetes.NewForConfigOrDie(config), discoveryClient: discovery.NewDiscoveryClientForConfigOrDie(config), ks: kubesphere.NewForConfigOrDie(config), - s2i: s2i.NewForConfigOrDie(config), + istio: istioclient.NewForConfigOrDie(config), application: applicationclientset.NewForConfigOrDie(config), master: config.Host, config: config, @@ -63,7 +73,7 @@ func NewKubernetesClientOrDie(options *KubernetesOptions) *KubernetesClient { } // NewKubernetesClient creates a KubernetesClient -func NewKubernetesClient(options *KubernetesOptions) (*KubernetesClient, error) { +func NewKubernetesClient(options *KubernetesOptions) (Client, error) { config, err := clientcmd.BuildConfigFromFlags("", options.KubeConfig) if err != nil { return nil, err @@ -72,7 +82,7 @@ func NewKubernetesClient(options *KubernetesOptions) (*KubernetesClient, error) config.QPS = options.QPS config.Burst = options.Burst - var k KubernetesClient + var k kubernetesClient k.k8s, err = kubernetes.NewForConfig(config) if err != nil { return nil, err @@ -83,12 +93,13 @@ func NewKubernetesClient(options *KubernetesOptions) (*KubernetesClient, error) return nil, err } - k.s2i, err = s2i.NewForConfig(config) + k.application, err = applicationclientset.NewForConfig(config) if err != nil { return nil, err } - k.application, err = applicationclientset.NewForConfig(config) + k.istio, err = istioclient.NewForConfig(config) + if err != nil { return nil, err } @@ -99,31 +110,31 @@ func NewKubernetesClient(options *KubernetesOptions) (*KubernetesClient, error) return &k, nil } -func (k *KubernetesClient) Kubernetes() kubernetes.Interface { +func (k *kubernetesClient) Kubernetes() kubernetes.Interface { return k.k8s } -func (k *KubernetesClient) Discovery() discovery.DiscoveryInterface { +func (k *kubernetesClient) Discovery() discovery.DiscoveryInterface { return k.discoveryClient } -func (k *KubernetesClient) KubeSphere() kubesphere.Interface { +func (k *kubernetesClient) KubeSphere() kubesphere.Interface { return k.ks } -func (k *KubernetesClient) S2i() s2i.Interface { - return k.s2i -} - -func (k *KubernetesClient) Application() applicationclientset.Interface { +func (k *kubernetesClient) Application() applicationclientset.Interface { return k.application } +func (k *kubernetesClient) Istio() istioclient.Interface { + return k.istio +} + // master address used to generate kubeconfig for downloading -func (k *KubernetesClient) Master() string { +func (k *kubernetesClient) Master() string { return k.master } -func (k *KubernetesClient) Config() *rest.Config { +func (k *kubernetesClient) Config() *rest.Config { return k.config } diff --git a/pkg/simple/client/k8s/null_client.go b/pkg/simple/client/k8s/null_client.go new file mode 100644 index 000000000..69dee0f48 --- /dev/null +++ b/pkg/simple/client/k8s/null_client.go @@ -0,0 +1,45 @@ +package k8s + +import ( + application "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned" + istio "istio.io/client-go/pkg/clientset/versioned" + "k8s.io/client-go/discovery" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" +) + +type nullClient struct { +} + +func NewNullClient() Client { + return &nullClient{} +} + +func (n nullClient) Kubernetes() kubernetes.Interface { + return nil +} + +func (n nullClient) KubeSphere() kubesphere.Interface { + return nil +} + +func (n nullClient) Istio() istio.Interface { + return nil +} + +func (n nullClient) Application() application.Interface { + return nil +} + +func (n nullClient) Discovery() discovery.DiscoveryInterface { + return nil +} + +func (n nullClient) Master() string { + return "" +} + +func (n nullClient) Config() *rest.Config { + return nil +} diff --git a/pkg/simple/client/k8s/options.go b/pkg/simple/client/k8s/options.go index 6b0a12bdf..e167d5ca5 100644 --- a/pkg/simple/client/k8s/options.go +++ b/pkg/simple/client/k8s/options.go @@ -49,11 +49,11 @@ func (k *KubernetesOptions) ApplyTo(options *KubernetesOptions) { reflectutils.Override(options, k) } -func (k *KubernetesOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&k.KubeConfig, "kubeconfig", k.KubeConfig, ""+ +func (k *KubernetesOptions) AddFlags(fs *pflag.FlagSet, c *KubernetesOptions) { + fs.StringVar(&k.KubeConfig, "kubeconfig", c.KubeConfig, ""+ "Path for kubernetes kubeconfig file, if left blank, will use "+ "in cluster way.") - fs.StringVar(&k.Master, "master", k.Master, ""+ + fs.StringVar(&k.Master, "master", c.Master, ""+ "Used to generate kubeconfig for downloading, if not specified, will use host in kubeconfig.") } diff --git a/pkg/simple/client/kubesphere/kubesphere.go b/pkg/simple/client/kubesphere/kubesphere.go deleted file mode 100644 index c7024e77f..000000000 --- a/pkg/simple/client/kubesphere/kubesphere.go +++ /dev/null @@ -1,349 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 kubesphere - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" - "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models" - "net/http" - "strings" -) - -type Interface interface { - CreateGroup(group *models.Group) (*models.Group, error) - UpdateGroup(group *models.Group) (*models.Group, error) - DescribeGroup(name string) (*models.Group, error) - DeleteGroup(name string) error - ListUsers() (*models.PageableResponse, error) - ListWorkspaceDevOpsProjects(workspace string) (*v1alpha2.PageableDevOpsProject, error) - DeleteWorkspaceDevOpsProjects(workspace, devops string) error -} - -type KubeSphereClient struct { - client *http.Client - - apiServer string - accountServer string -} - -func NewKubeSphereClient(options *KubeSphereOptions) *KubeSphereClient { - return &KubeSphereClient{ - client: &http.Client{}, - apiServer: options.APIServer, - accountServer: options.AccountServer, - } -} - -type Error struct { - status int - message string -} - -func (e Error) Error() string { - return fmt.Sprintf("status: %d,message: %s", e.status, e.message) -} - -func (c *KubeSphereClient) CreateGroup(group *models.Group) (*models.Group, error) { - data, err := json.Marshal(group) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups", c.accountServer), bytes.NewReader(data)) - - if err != nil { - klog.Error(err) - return nil, err - } - req.Header.Add("Content-Type", "application/json") - - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - - if resp.StatusCode > http.StatusOK { - return nil, Error{resp.StatusCode, string(data)} - } - - err = json.Unmarshal(data, group) - - if err != nil { - klog.Error(err) - return nil, err - } - - return group, nil -} - -func (c *KubeSphereClient) UpdateGroup(group *models.Group) (*models.Group, error) { - data, err := json.Marshal(group) - - if err != nil { - klog.Error(err) - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, group.Name), bytes.NewReader(data)) - - if err != nil { - klog.Error(err) - return nil, err - } - - req.Header.Add("Content-Type", "application/json") - resp, err := c.client.Do(req) - - if err != nil { - return nil, err - } - defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - - if resp.StatusCode > http.StatusOK { - return nil, Error{resp.StatusCode, string(data)} - } - - err = json.Unmarshal(data, group) - - if err != nil { - klog.Error(err) - return nil, err - } - - return group, nil -} - -func (c *KubeSphereClient) DeleteGroup(name string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, name), nil) - - if err != nil { - klog.Error(err) - return err - } - - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return err - } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return err - } - - if resp.StatusCode > http.StatusOK { - return Error{resp.StatusCode, string(data)} - } - - return nil -} - -func (c *KubeSphereClient) DescribeGroup(name string) (*models.Group, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/groups/%s", c.accountServer, name), nil) - - if err != nil { - klog.Error(err) - return nil, err - } - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - - if resp.StatusCode > http.StatusOK { - return nil, Error{resp.StatusCode, string(data)} - } - - var group models.Group - err = json.Unmarshal(data, &group) - - if err != nil { - klog.Error(err) - return nil, err - } - - return &group, nil -} - -func (c *KubeSphereClient) ListUsers() (*models.PageableResponse, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/iam.kubesphere.io/v1alpha2/users", c.accountServer), nil) - - if err != nil { - return nil, err - } - req.Header.Add("Authorization", c.accountServer) - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - - if resp.StatusCode > http.StatusOK { - return nil, Error{resp.StatusCode, string(data)} - } - - var result models.PageableResponse - err = json.Unmarshal(data, &result) - - if err != nil { - klog.Error(err) - return nil, err - } - - return &result, nil -} - -func (c *KubeSphereClient) ListWorkspaceDevOpsProjects(workspace string) (*v1alpha2.PageableDevOpsProject, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/kapis/tenant.kubesphere.io/v1alpha2/workspaces/%s/devops", c.apiServer, workspace), nil) - - if err != nil { - klog.Error(err) - return nil, err - } - - req.Header.Add(constants.UserNameHeader, constants.AdminUserName) - - klog.Info(req.Method, req.URL) - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return nil, err - } - if resp.StatusCode > http.StatusOK { - klog.Error(req.Method, req.URL, resp.StatusCode, string(data)) - return nil, Error{resp.StatusCode, string(data)} - } - - var result v1alpha2.PageableDevOpsProject - err = json.Unmarshal(data, &result) - - if err != nil { - klog.Error(err) - return nil, err - } - return &result, nil - -} - -func (c *KubeSphereClient) DeleteWorkspaceDevOpsProjects(workspace, devops string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/kapis/tenant.kubesphere.io/v1alpha2/workspaces/%s/devops/%s", c.apiServer, workspace, devops), nil) - - if err != nil { - klog.Error(err) - return err - } - req.Header.Add(constants.UserNameHeader, constants.AdminUserName) - - klog.Info(req.Method, req.URL) - resp, err := c.client.Do(req) - - if err != nil { - klog.Error(err) - return err - } - defer resp.Body.Close() - data, err := ioutil.ReadAll(resp.Body) - - if err != nil { - klog.Error(err) - return err - } - if resp.StatusCode > http.StatusOK { - klog.Error(req.Method, req.URL, resp.StatusCode, string(data)) - return Error{resp.StatusCode, string(data)} - } - - return nil -} - -func IsNotFound(err error) bool { - if e, ok := err.(Error); ok { - if e.status == http.StatusNotFound { - return true - } - if strings.Contains(e.message, "not exist") { - return true - } - if strings.Contains(e.message, "not found") { - return true - } - } - return false -} - -func IsExist(err error) bool { - if e, ok := err.(Error); ok { - if e.status == http.StatusConflict { - return true - } - if strings.Contains(e.message, "Already Exists") { - return true - } - } - return false -} diff --git a/pkg/simple/client/kubesphere/options.go b/pkg/simple/client/kubesphere/options.go deleted file mode 100644 index 442e44ed9..000000000 --- a/pkg/simple/client/kubesphere/options.go +++ /dev/null @@ -1,40 +0,0 @@ -package kubesphere - -import "github.com/spf13/pflag" - -type KubeSphereOptions struct { - APIServer string `json:"apiServer" yaml:"apiServer"` - AccountServer string `json:"accountServer" yaml:"accountServer"` -} - -// NewKubeSphereOptions create a default options -func NewKubeSphereOptions() *KubeSphereOptions { - return &KubeSphereOptions{ - APIServer: "http://ks-apiserver.kubesphere-system.svc", - AccountServer: "http://ks-account.kubesphere-system.svc", - } -} - -func (s *KubeSphereOptions) ApplyTo(options *KubeSphereOptions) { - if s.AccountServer != "" { - options.AccountServer = s.AccountServer - } - - if s.APIServer != "" { - options.APIServer = s.APIServer - } -} - -func (s *KubeSphereOptions) Validate() []error { - errs := []error{} - - return errs -} - -func (s *KubeSphereOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.APIServer, "kubesphere-apiserver-host", s.APIServer, ""+ - "KubeSphere apiserver host address.") - - fs.StringVar(&s.AccountServer, "kubesphere-account-host", s.AccountServer, ""+ - "KubeSphere account server host address.") -} diff --git a/pkg/simple/client/ldap/channel.go b/pkg/simple/client/ldap/channel.go index 6f91393db..04f72530f 100644 --- a/pkg/simple/client/ldap/channel.go +++ b/pkg/simple/client/ldap/channel.go @@ -25,7 +25,7 @@ type channelPool struct { // PoolFactory is a function to create new connections. type PoolFactory func(string) (ldap.Client, error) -// NewChannelPool returns a new pool based on buffered channels with an initial +// newChannelPool returns a new pool based on buffered channels with an initial // capacity and maximum capacity. Factory is used when initial capacity is // greater than zero to fill the pool. A zero initialCap doesn't fill the Pool // until a new Get() is called. During a Get(), If there is no new connection @@ -36,7 +36,7 @@ type PoolFactory func(string) (ldap.Client, error) // of the call is one of those passed, most likely you want to set this to something // like // []uint8{ldap.LDAPResultTimeLimitExceeded, ldap.ErrorNetwork} -func NewChannelPool(initialCap, maxCap int, name string, factory PoolFactory, closeAt []uint16) (Pool, error) { +func newChannelPool(initialCap, maxCap int, name string, factory PoolFactory, closeAt []uint16) (Pool, error) { if initialCap < 0 || maxCap <= 0 || initialCap > maxCap { return nil, errors.New("invalid capacity settings") } diff --git a/pkg/simple/client/ldap/interface.go b/pkg/simple/client/ldap/interface.go new file mode 100644 index 000000000..ea83b769b --- /dev/null +++ b/pkg/simple/client/ldap/interface.go @@ -0,0 +1,27 @@ +package ldap + +import ( + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" +) + +// Interface defines CRUD behaviors of manipulating users +type Interface interface { + // Create create a new user in ldap + Create(user *iamv1alpha2.User) error + + // Update updates a user information, return error if user not exists + Update(user *iamv1alpha2.User) error + + // Delete deletes a user from ldap, return nil if user not exists + Delete(name string) error + + // Get gets a user by its username from ldap, return ErrUserNotExists if user not exists + Get(name string) (*iamv1alpha2.User, error) + + // Authenticate checks if (name, password) is valid, return ErrInvalidCredentials if not + Authenticate(name string, password string) error + + List(query *query.Query) (*api.ListResult, error) +} diff --git a/pkg/simple/client/ldap/ldap.go b/pkg/simple/client/ldap/ldap.go index 7c0e15814..1372551e2 100644 --- a/pkg/simple/client/ldap/ldap.go +++ b/pkg/simple/client/ldap/ldap.go @@ -20,71 +20,458 @@ package ldap import ( "fmt" "github.com/go-ldap/ldap" + "github.com/google/uuid" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/server/errors" + "sort" + "strings" + "sync" + "time" ) -type LdapClient struct { - pool Pool - options *LdapOptions +const ( + ldapAttributeObjectClass = "objectClass" + ldapAttributeCommonName = "cn" + ldapAttributeSerialNumber = "sn" + ldapAttributeGlobalIDNumber = "gidNumber" + ldapAttributeHomeDirectory = "homeDirectory" + ldapAttributeUserID = "uid" + ldapAttributeUserIDNumber = "uidNumber" + ldapAttributeMail = "mail" + ldapAttributeUserPassword = "userPassword" + ldapAttributePreferredLanguage = "preferredLanguage" + ldapAttributeDescription = "description" + ldapAttributeCreateTimestamp = "createTimestamp" + ldapAttributeOrganizationUnit = "ou" + + // ldap create timestamp attribute layout + ldapAttributeCreateTimestampLayout = "20060102150405Z" +) + +var ErrUserAlreadyExisted = errors.New("user already existed") +var ErrUserNotExists = errors.New("user not exists") +var ErrInvalidCredentials = errors.New("invalid credentials") + +type ldapInterfaceImpl struct { + pool Pool + userSearchBase string + groupSearchBase string + managerDN string + managerPassword string + + once sync.Once } -// panic if cannot connect to ldap service -func NewLdapClient(options *LdapOptions, stopCh <-chan struct{}) (*LdapClient, error) { - pool, err := NewChannelPool(8, 64, "kubesphere", func(s string) (ldap.Client, error) { +var _ Interface = &ldapInterfaceImpl{} + +func NewLdapClient(options *Options, stopCh <-chan struct{}) (Interface, error) { + + poolFactory := func(s string) (ldap.Client, error) { conn, err := ldap.Dial("tcp", options.Host) if err != nil { return nil, err } return conn, nil - }, []uint16{ldap.LDAPResultAdminLimitExceeded, ldap.ErrorNetwork}) + } + + pool, err := newChannelPool(options.InitialCap, + options.MaxCap, + options.PoolName, + poolFactory, + []uint16{ldap.LDAPResultAdminLimitExceeded, ldap.ErrorNetwork}) if err != nil { - klog.Error(err) - pool.Close() return nil, err } - client := &LdapClient{ - pool: pool, - options: options, + client := &ldapInterfaceImpl{ + pool: pool, + userSearchBase: options.UserSearchBase, + groupSearchBase: options.GroupSearchBase, + managerDN: options.ManagerDN, + managerPassword: options.ManagerPassword, + once: sync.Once{}, } go func() { <-stopCh - if client.pool != nil { - client.pool.Close() - } + client.close() }() + client.once.Do(func() { + _ = client.createSearchBase() + }) + return client, nil } -func (l *LdapClient) NewConn() (ldap.Client, error) { - if l.pool == nil { - err := fmt.Errorf("ldap connection pool is not initialized") - klog.Errorln(err) - return nil, err +func (l *ldapInterfaceImpl) createSearchBase() error { + conn, err := l.newConn() + if err != nil { + return err + } + defer conn.Close() + + createIfNotExistsFunc := func(request *ldap.AddRequest) error { + searchRequest := &ldap.SearchRequest{ + BaseDN: request.DN, + Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldap.NeverDerefAliases, + SizeLimit: 0, + TimeLimit: 0, + TypesOnly: false, + Filter: "(objectClass=*)", + } + + _, err = conn.Search(searchRequest) + if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) { + return conn.Add(request) + } + return nil } - conn, err := l.pool.Get() - // cannot connect to ldap server or pool is closed + userSearchBaseAddRequest := &ldap.AddRequest{ + DN: l.userSearchBase, + Attributes: []ldap.Attribute{ + { + Type: ldapAttributeObjectClass, + Vals: []string{"organizationalUnit", "top"}, + }, + { + Type: ldapAttributeOrganizationUnit, + Vals: []string{"Users"}, + }, + }, + } + + err = createIfNotExistsFunc(userSearchBaseAddRequest) + if err != nil { + return err + } + + groupSearchBaseAddRequest := *userSearchBaseAddRequest + groupSearchBaseAddRequest.DN = l.groupSearchBase + + return createIfNotExistsFunc(&groupSearchBaseAddRequest) + +} + +func (l *ldapInterfaceImpl) close() { + if l.pool != nil { + l.pool.Close() + } +} + +func (l *ldapInterfaceImpl) newConn() (ldap.Client, error) { + conn, err := l.pool.Get() if err != nil { - klog.Errorln(err) return nil, err } - err = conn.Bind(l.options.ManagerDN, l.options.ManagerPassword) + defer conn.Close() + + err = conn.Bind(l.managerDN, l.managerPassword) if err != nil { - conn.Close() - klog.Error(err) return nil, err } return conn, nil } -func (l *LdapClient) GroupSearchBase() string { - return l.options.GroupSearchBase +func (l *ldapInterfaceImpl) dnForUsername(username string) string { + return fmt.Sprintf("uid=%s,%s", username, l.userSearchBase) } -func (l *LdapClient) UserSearchBase() string { - return l.options.UserSearchBase +func (l *ldapInterfaceImpl) filterForUsername(username string) string { + return fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username) +} + +func (l *ldapInterfaceImpl) Get(name string) (*iamv1alpha2.User, error) { + conn, err := l.newConn() + if err != nil { + return nil, err + } + defer conn.Close() + + searchRequest := &ldap.SearchRequest{ + BaseDN: l.userSearchBase, + Scope: ldap.ScopeWholeSubtree, + DerefAliases: ldap.NeverDerefAliases, + SizeLimit: 0, + TimeLimit: 0, + TypesOnly: false, + Filter: l.filterForUsername(name), + Attributes: []string{ + ldapAttributeMail, + ldapAttributeDescription, + ldapAttributePreferredLanguage, + ldapAttributeCreateTimestamp, + }, + } + + searchResults, err := conn.Search(searchRequest) + if err != nil { + return nil, err + } + + if len(searchResults.Entries) != 1 { + return nil, ErrUserNotExists + } + + userEntry := searchResults.Entries[0] + + user := &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: userEntry.GetAttributeValue(ldapAttributeUserID), + }, + Spec: iamv1alpha2.UserSpec{ + Email: userEntry.GetAttributeValue(ldapAttributeMail), + Lang: userEntry.GetAttributeValue(ldapAttributePreferredLanguage), + Description: userEntry.GetAttributeValue(ldapAttributeDescription), + }, + } + + createTimestamp, _ := time.Parse(ldapAttributeCreateTimestampLayout, userEntry.GetAttributeValue(ldapAttributeCreateTimestamp)) + user.ObjectMeta.CreationTimestamp.Time = createTimestamp + + return user, nil +} + +func (l *ldapInterfaceImpl) Create(user *iamv1alpha2.User) error { + if _, err := l.Get(user.Name); err != nil { + return ErrUserAlreadyExisted + } + + createRequest := &ldap.AddRequest{ + DN: l.dnForUsername(user.Name), + Attributes: []ldap.Attribute{ + { + Type: ldapAttributeObjectClass, + Vals: []string{"inetOrgPerson", "posixAccount", "top"}, + }, + { + Type: ldapAttributeCommonName, + Vals: []string{user.Name}, + }, + { + Type: ldapAttributeSerialNumber, + Vals: []string{" "}, + }, + { + Type: ldapAttributeGlobalIDNumber, + Vals: []string{"500"}, + }, + { + Type: ldapAttributeHomeDirectory, + Vals: []string{"/home/" + user.Name}, + }, + { + Type: ldapAttributeUserID, + Vals: []string{user.Name}, + }, + { + Type: ldapAttributeUserIDNumber, + Vals: []string{uuid.New().String()}, + }, + { + Type: ldapAttributeMail, + Vals: []string{user.Spec.Email}, + }, + { + Type: ldapAttributeUserPassword, + Vals: []string{user.Spec.EncryptedPassword}, + }, + { + Type: ldapAttributePreferredLanguage, + Vals: []string{user.Spec.Lang}, + }, + { + Type: ldapAttributeDescription, + Vals: []string{user.Spec.Description}, + }, + }, + } + + conn, err := l.newConn() + if err != nil { + return err + } + defer conn.Close() + + return conn.Add(createRequest) +} + +func (l *ldapInterfaceImpl) Delete(name string) error { + conn, err := l.newConn() + if err != nil { + return err + } + defer conn.Close() + + _, err = l.Get(name) + if err != nil { + if err == ErrUserNotExists { + return nil + } + return err + } + + deleteRequest := &ldap.DelRequest{ + DN: l.dnForUsername(name), + } + + return conn.Del(deleteRequest) +} + +func (l *ldapInterfaceImpl) Update(newUser *iamv1alpha2.User) error { + conn, err := l.newConn() + if err != nil { + return err + } + defer conn.Close() + + // check user existed + _, err = l.Get(newUser.Name) + if err != nil { + return err + } + + modifyRequest := &ldap.ModifyRequest{ + DN: l.dnForUsername(newUser.Name), + } + + if newUser.Spec.Description != "" { + modifyRequest.Replace(ldapAttributeDescription, []string{newUser.Spec.Description}) + } + + if newUser.Spec.Lang != "" { + modifyRequest.Replace(ldapAttributePreferredLanguage, []string{newUser.Spec.Lang}) + } + + if newUser.Spec.EncryptedPassword != "" { + modifyRequest.Replace(ldapAttributeUserPassword, []string{newUser.Spec.EncryptedPassword}) + } + + return conn.Modify(modifyRequest) + +} + +func (l *ldapInterfaceImpl) Authenticate(username, password string) error { + conn, err := l.newConn() + if err != nil { + return err + } + defer conn.Close() + + dn := l.dnForUsername(username) + err = conn.Bind(dn, password) + if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) { + return ErrInvalidCredentials + } + return err +} + +func (l *ldapInterfaceImpl) List(query *query.Query) (*api.ListResult, error) { + conn, err := l.newConn() + if err != nil { + return nil, err + } + defer conn.Close() + + pageControl := ldap.NewControlPaging(1000) + + users := make([]iamv1alpha2.User, 0) + + filter := "(&(objectClass=inetOrgPerson))" + + if keyword := query.Filters["keyword"]; keyword != "" { + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword) + } + + if username := query.Filters["username"]; username != "" { + uidFilter := "" + for _, username := range strings.Split(string(username), "|") { + uidFilter += fmt.Sprintf("(uid=%s)", username) + } + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter) + } + + if email := query.Filters["email"]; email != "" { + emailFilter := "" + for _, username := range strings.Split(string(email), "|") { + emailFilter += fmt.Sprintf("(mail=%s)", username) + } + filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter) + } + + for { + userSearchRequest := ldap.NewSearchRequest( + l.userSearchBase, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + filter, + []string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"}, + []ldap.Control{pageControl}, + ) + + response, err := conn.Search(userSearchRequest) + + if err != nil { + klog.Errorln("search user", err) + return nil, err + } + + for _, entry := range response.Entries { + + uid := entry.GetAttributeValue("uid") + email := entry.GetAttributeValue("mail") + description := entry.GetAttributeValue("description") + lang := entry.GetAttributeValue("preferredLanguage") + createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp")) + + user := iamv1alpha2.User{ObjectMeta: metav1.ObjectMeta{Name: uid, CreationTimestamp: metav1.Time{Time: createTimestamp}}, Spec: iamv1alpha2.UserSpec{ + Email: email, + Lang: lang, + Description: description, + }} + + users = append(users, user) + } + + updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging) + if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 { + pageControl.SetCookie(ctrl.Cookie) + continue + } + + break + } + + sort.Slice(users, func(i, j int) bool { + if !query.Ascending { + i, j = j, i + } + switch query.SortBy { + case "username": + return strings.Compare(users[i].Name, users[j].Name) <= 0 + case "createTime": + fallthrough + default: + return users[i].CreationTimestamp.Before(&users[j].CreationTimestamp) + } + }) + + items := make([]interface{}, 0) + + for i, user := range users { + if i >= query.Pagination.Offset && len(items) < query.Pagination.Limit { + items = append(items, user) + } + } + + return &api.ListResult{ + Items: items, + TotalItems: len(users), + }, nil } diff --git a/pkg/simple/client/ldap/options.go b/pkg/simple/client/ldap/options.go index 7f9408eac..cc631aa8e 100644 --- a/pkg/simple/client/ldap/options.go +++ b/pkg/simple/client/ldap/options.go @@ -5,51 +5,57 @@ import ( "kubesphere.io/kubesphere/pkg/utils/reflectutils" ) -type LdapOptions struct { +type Options struct { Host string `json:"host,omitempty" yaml:"host"` ManagerDN string `json:"managerDN,omitempty" yaml:"managerDN"` ManagerPassword string `json:"managerPassword,omitempty" yaml:"managerPassword"` UserSearchBase string `json:"userSearchBase,omitempty" yaml:"userSearchBase"` GroupSearchBase string `json:"groupSearchBase,omitempty" yaml:"groupSearchBase"` + InitialCap int `json:"initialCap,omitempty" yaml:"initialCap"` + MaxCap int `json:"maxCap,omitempty" yaml:"maxCap"` + PoolName string `json:"poolName,omitempty" yaml:"poolName"` } -// NewLdapOptions return a default option +// NewOptions return a default option // which host field point to nowhere. -func NewLdapOptions() *LdapOptions { - return &LdapOptions{ +func NewOptions() *Options { + return &Options{ Host: "", ManagerDN: "cn=admin,dc=example,dc=org", UserSearchBase: "ou=Users,dc=example,dc=org", GroupSearchBase: "ou=Groups,dc=example,dc=org", + InitialCap: 10, + MaxCap: 100, + PoolName: "ldap", } } -func (l *LdapOptions) Validate() []error { - errors := []error{} +func (l *Options) Validate() []error { + var errors []error return errors } -func (l *LdapOptions) ApplyTo(options *LdapOptions) { +func (l *Options) ApplyTo(options *Options) { if l.Host != "" { reflectutils.Override(options, l) } } -func (l *LdapOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&l.Host, "ldap-host", l.Host, ""+ +func (l *Options) AddFlags(fs *pflag.FlagSet, s *Options) { + fs.StringVar(&l.Host, "ldap-host", s.Host, ""+ "Ldap service host, if left blank, all of the following ldap options will "+ "be ignored and ldap will be disabled.") - fs.StringVar(&l.ManagerDN, "ldap-manager-dn", l.ManagerDN, ""+ + fs.StringVar(&l.ManagerDN, "ldap-manager-dn", s.ManagerDN, ""+ "Ldap manager account domain name.") - fs.StringVar(&l.ManagerPassword, "ldap-manager-password", l.ManagerPassword, ""+ + fs.StringVar(&l.ManagerPassword, "ldap-manager-password", s.ManagerPassword, ""+ "Ldap manager account password.") - fs.StringVar(&l.UserSearchBase, "ldap-user-search-base", l.UserSearchBase, ""+ + fs.StringVar(&l.UserSearchBase, "ldap-user-search-base", s.UserSearchBase, ""+ "Ldap user search base.") - fs.StringVar(&l.GroupSearchBase, "ldap-group-search-base", l.GroupSearchBase, ""+ + fs.StringVar(&l.GroupSearchBase, "ldap-group-search-base", s.GroupSearchBase, ""+ "Ldap group search base.") } diff --git a/pkg/simple/client/ldap/simple_ldap.go b/pkg/simple/client/ldap/simple_ldap.go new file mode 100644 index 000000000..38a4ab855 --- /dev/null +++ b/pkg/simple/client/ldap/simple_ldap.go @@ -0,0 +1,91 @@ +package ldap + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/query" +) + +// simpleLdap is a implementation of ldap.Interface, you should never use this in production env! +type simpleLdap struct { + store map[string]*iamv1alpha2.User +} + +func NewSimpleLdap() Interface { + sl := &simpleLdap{ + store: map[string]*iamv1alpha2.User{}, + } + + // initialize with a admin user + admin := &iamv1alpha2.User{ + ObjectMeta: metav1.ObjectMeta{ + Name: "admin", + }, + Spec: iamv1alpha2.UserSpec{ + Email: "admin@kubesphere.io", + Lang: "eng", + Description: "administrator", + Groups: nil, + EncryptedPassword: "P@88w0rd", + }, + } + sl.store[admin.Name] = admin + return sl +} + +func (s simpleLdap) Create(user *iamv1alpha2.User) error { + s.store[user.Name] = user + return nil +} + +func (s simpleLdap) Update(user *iamv1alpha2.User) error { + _, err := s.Get(user.Name) + if err != nil { + return err + } + s.store[user.Name] = user + return nil +} + +func (s simpleLdap) Delete(name string) error { + _, err := s.Get(name) + if err != nil { + return err + } + delete(s.store, name) + return nil +} + +func (s simpleLdap) Get(name string) (*iamv1alpha2.User, error) { + if user, ok := s.store[name]; !ok { + return nil, ErrUserNotExists + } else { + return user, nil + } +} + +func (s simpleLdap) Authenticate(name string, password string) error { + if user, err := s.Get(name); err != nil { + return err + } else { + if user.Spec.EncryptedPassword != password { + return ErrInvalidCredentials + } + } + + return nil +} + +func (l *simpleLdap) List(query *query.Query) (*api.ListResult, error) { + items := make([]interface{}, 0) + + for _, user := range l.store { + items = append(items, user) + } + + return &api.ListResult{ + Items: items, + TotalItems: len(items), + }, nil +} diff --git a/pkg/simple/client/ldap/simple_ldap_test.go b/pkg/simple/client/ldap/simple_ldap_test.go new file mode 100644 index 000000000..62dff8764 --- /dev/null +++ b/pkg/simple/client/ldap/simple_ldap_test.go @@ -0,0 +1,102 @@ +package ldap + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "testing" +) + +func TestSimpleLdap(t *testing.T) { + ldapClient := NewSimpleLdap() + + foo := &iamv1alpha2.User{ + TypeMeta: metav1.TypeMeta{APIVersion: iamv1alpha2.SchemeGroupVersion.String()}, + ObjectMeta: metav1.ObjectMeta{ + Name: "jerry", + }, + Spec: iamv1alpha2.UserSpec{ + Email: "jerry@kubesphere.io", + Lang: "en", + Description: "Jerry is kind and gentle.", + Groups: []string{}, + EncryptedPassword: "P@88w0rd", + }, + } + + t.Run("should create user", func(t *testing.T) { + err := ldapClient.Create(foo) + if err != nil { + t.Fatal(err) + } + + // check if user really created + user, err := ldapClient.Get(foo.Name) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(user, foo); len(diff) != 0 { + t.Fatalf("%T differ (-got, +want): %s", user, diff) + } + + _ = ldapClient.Delete(foo.Name) + }) + + t.Run("should update user", func(t *testing.T) { + err := ldapClient.Create(foo) + if err != nil { + t.Fatal(err) + } + + foo.Spec.Description = "Jerry needs some drinks." + err = ldapClient.Update(foo) + if err != nil { + t.Fatal(err) + } + + // check if user really created + user, err := ldapClient.Get(foo.Name) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(user, foo); len(diff) != 0 { + t.Fatalf("%T differ (-got, +want): %s", user, diff) + } + + _ = ldapClient.Delete(foo.Name) + }) + + t.Run("should delete user", func(t *testing.T) { + err := ldapClient.Create(foo) + if err != nil { + t.Fatal(err) + } + + err = ldapClient.Delete(foo.Name) + if err != nil { + t.Fatal(err) + } + + _, err = ldapClient.Get(foo.Name) + if err == nil || err != ErrUserNotExists { + t.Fatalf("expected ErrUserNotExists error, got %v", err) + } + }) + + t.Run("should verify username and password", func(t *testing.T) { + err := ldapClient.Create(foo) + if err != nil { + t.Fatal(err) + } + + err = ldapClient.Authenticate(foo.Name, foo.Spec.EncryptedPassword) + if err != nil { + t.Fatalf("should pass but got an error %v", err) + } + + err = ldapClient.Authenticate(foo.Name, "gibberish") + if err == nil || err != ErrInvalidCredentials { + t.Fatalf("expected error ErrInvalidCrenentials but got %v", err) + } + }) +} diff --git a/pkg/simple/client/logging/elasticsearch/api_body.go b/pkg/simple/client/logging/elasticsearch/api_body.go new file mode 100644 index 000000000..72b9d6fed --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/api_body.go @@ -0,0 +1,277 @@ +package elasticsearch + +import ( + "fmt" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/logging" + "time" +) + +const ( + podNameMaxLength = 63 + podNameSuffixLength = 6 // 5 characters + 1 hyphen + replicaSetSuffixMaxLength = 11 // max 10 characters + 1 hyphen +) + +type bodyBuilder struct { + Body +} + +func newBodyBuilder() *bodyBuilder { + return &bodyBuilder{} +} + +func (bb *bodyBuilder) bytes() ([]byte, error) { + return json.Marshal(bb.Body) +} + +// The mainBody func builds api body for query. +// TODO: Should use an elegant pakcage for building query body, but `elastic/go-elasticsearch` doesn't provide it currently. +// +// Example: +// GET kapis/logging.kubesphere.io/v1alpha2/cluster?start_time=0&end_time=156576063993&namespaces=kubesphere-system&pod_query=ks-apiserver +// ----- +//{ +// "from":0, +// "size":10, +// "sort":[ +// { +// "time": "desc" +// } +// ], +// "query":{ +// "bool":{ +// "filter":[ +// { +// "bool":{ +// "should":[ +// { +// "bool":{ +// "filter":[ +// { +// "match_phrase":{ +// "kubernetes.namespace_name.keyword":"kubesphere-system" +// } +// }, +// { +// "range":{ +// "time":{ +// "gte":"1572315987000" +// } +// } +// } +// ] +// } +// } +// ], +// "minimum_should_match":1 +// } +// }, +// { +// "bool":{ +// "should":[ +// { +// "match_phrase_prefix":{ +// "kubernetes.pod_name":"ks-apiserver" +// } +// } +// ], +// "minimum_should_match":1 +// } +// }, +// { +// "range":{ +// "time":{ +// "gte":"0", +// "lte":"156576063993" +// } +// } +// } +// ] +// } +// } +//} +func (bb *bodyBuilder) mainBool(sf logging.SearchFilter) *bodyBuilder { + var ms []Match + + // literal matching + if len(sf.NamespaceFilter) != 0 { + var b Bool + for ns := range sf.NamespaceFilter { + match := Match{ + Bool: &Bool{ + Filter: []Match{ + { + MatchPhrase: map[string]string{ + "kubernetes.namespace_name.keyword": ns, + }, + }, + { + Range: &Range{ + Time: &Time{ + Gte: func() *time.Time { t := sf.NamespaceFilter[ns]; return &t }(), + }, + }, + }, + }, + }, + } + b.Should = append(b.Should, match) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.WorkloadFilter != nil { + var b Bool + for _, wk := range sf.WorkloadFilter { + b.Should = append(b.Should, Match{Regexp: map[string]string{"kubernetes.pod_name.keyword": podNameRegexp(wk)}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.PodFilter != nil { + var b Bool + for _, po := range sf.PodFilter { + b.Should = append(b.Should, Match{MatchPhrase: map[string]string{"kubernetes.pod_name.keyword": po}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.ContainerFilter != nil { + var b Bool + for _, c := range sf.ContainerFilter { + b.Should = append(b.Should, Match{MatchPhrase: map[string]string{"kubernetes.container_name.keyword": c}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + + // fuzzy matching + if sf.WorkloadSearch != nil { + var b Bool + for _, wk := range sf.WorkloadSearch { + b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.pod_name": wk}}) + } + + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.PodSearch != nil { + var b Bool + for _, po := range sf.PodSearch { + b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.pod_name": po}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.ContainerSearch != nil { + var b Bool + for _, c := range sf.ContainerSearch { + b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"kubernetes.container_name": c}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + if sf.LogSearch != nil { + var b Bool + for _, l := range sf.LogSearch { + b.Should = append(b.Should, Match{MatchPhrasePrefix: map[string]string{"log": l}}) + } + b.MinimumShouldMatch = 1 + ms = append(ms, Match{Bool: &b}) + } + + if !sf.Starttime.IsZero() || !sf.Endtime.IsZero() { + fromTo := Match{ + Range: &Range{&Time{ + Gte: &sf.Starttime, + Lte: &sf.Endtime, + }}, + } + ms = append(ms, fromTo) + } + + bb.Body.Query = &Query{Bool{Filter: ms}} + return bb +} + +func (bb *bodyBuilder) cardinalityAggregation() *bodyBuilder { + bb.Body.Aggs = &Aggs{ + CardinalityAggregation: &CardinalityAggregation{ + &Cardinality{ + Field: "kubernetes.docker_id.keyword", + }, + }, + } + return bb +} + +func (bb *bodyBuilder) dateHistogramAggregation(interval string) *bodyBuilder { + if interval == "" { + interval = "15m" + } + + bb.Body.Aggs = &Aggs{ + DateHistogramAggregation: &DateHistogramAggregation{ + &DateHistogram{ + Field: "time", + Interval: interval, + }, + }, + } + return bb +} + +func (bb *bodyBuilder) from(n int64) *bodyBuilder { + bb.From = n + return bb +} + +func (bb *bodyBuilder) size(n int64) *bodyBuilder { + bb.Size = n + return bb +} + +func (bb *bodyBuilder) sort(o string) *bodyBuilder { + if o != "asc" { + o = "desc" + } + + bb.Sorts = []map[string]string{{"time": o}} + return bb +} + +func podNameRegexp(workloadName string) string { + var regexp string + if len(workloadName) <= podNameMaxLength-replicaSetSuffixMaxLength-podNameSuffixLength { + // match deployment pods, eg. -579dfbcddd-24znw + // replicaset rand string is limited to vowels + // https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83 + regexp += workloadName + "-[bcdfghjklmnpqrstvwxz2456789]{1,10}-[a-z0-9]{5}|" + // match statefulset pods, eg. -0 + regexp += workloadName + "-[0-9]+|" + // match pods of daemonset or job, eg. -29tdk, -5xqvl + regexp += workloadName + "-[a-z0-9]{5}" + } else if len(workloadName) <= podNameMaxLength-podNameSuffixLength { + replicaSetSuffixLength := podNameMaxLength - podNameSuffixLength - len(workloadName) + regexp += fmt.Sprintf("%s%d%s", workloadName+"-[bcdfghjklmnpqrstvwxz2456789]{", replicaSetSuffixLength, "}[a-z0-9]{5}|") + regexp += workloadName + "-[0-9]+|" + regexp += workloadName + "-[a-z0-9]{5}" + } else { + // Rand suffix may overwrites the workload name if the name is too long + // This won't happen for StatefulSet because long name will cause ReplicaSet fails during StatefulSet creation. + regexp += workloadName[:podNameMaxLength-podNameSuffixLength+1] + "[a-z0-9]{5}|" + regexp += workloadName + "-[0-9]+" + } + return regexp +} + +func parseResponse(body []byte) (Response, error) { + var res Response + err := json.Unmarshal(body, &res) + if err != nil { + klog.Error(err) + return Response{}, err + } + return res, nil +} diff --git a/pkg/simple/client/logging/elasticsearch/api_body_test.go b/pkg/simple/client/logging/elasticsearch/api_body_test.go new file mode 100644 index 000000000..6e160f358 --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/api_body_test.go @@ -0,0 +1,50 @@ +package elasticsearch + +import ( + "github.com/google/go-cmp/cmp" + "kubesphere.io/kubesphere/pkg/simple/client/logging" + "testing" +) + +func TestCardinalityAggregation(t *testing.T) { + var test = struct { + description string + searchFilter logging.SearchFilter + expected *bodyBuilder + }{ + description: "add cardinality aggregation", + searchFilter: logging.SearchFilter{ + LogSearch: []string{"info"}, + }, + expected: &bodyBuilder{Body{ + Query: &Query{ + Bool: Bool{ + Filter: []Match{ + { + Bool: &Bool{ + Should: []Match{ + { + MatchPhrasePrefix: map[string]string{"log": "info"}, + }, + }, + MinimumShouldMatch: 1, + }, + }, + }, + }, + }, + Aggs: &Aggs{ + CardinalityAggregation: &CardinalityAggregation{ + Cardinality: &Cardinality{Field: "kubernetes.docker_id.keyword"}, + }, + }, + }}, + } + + t.Run(test.description, func(t *testing.T) { + body := newBodyBuilder().mainBool(test.searchFilter).cardinalityAggregation() + if diff := cmp.Diff(body, test.expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", test.expected, diff) + } + }) +} diff --git a/pkg/simple/client/logging/elasticsearch/api_body_types.go b/pkg/simple/client/logging/elasticsearch/api_body_types.go new file mode 100644 index 000000000..8371f6efa --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/api_body_types.go @@ -0,0 +1,127 @@ +package elasticsearch + +import "time" + +// --------------------------------------------- Request Body --------------------------------------------- + +// More info: https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search-API.html +type Body struct { + From int64 `json:"from,omitempty"` + Size int64 `json:"size,omitempty"` + Sorts []map[string]string `json:"sort,omitempty"` + *Query `json:"query,omitempty"` + *Aggs `json:"aggs,omitempty"` +} + +type Query struct { + Bool `json:"bool,omitempty"` +} + +// Example: +// {bool: {filter: <[]Match>}} +// {bool: {should: <[]Match>, minimum_should_match: 1}} +type Bool struct { + Filter []Match `json:"filter,omitempty"` + Should []Match `json:"should,omitempty"` + MinimumShouldMatch int32 `json:"minimum_should_match,omitempty"` +} + +// Example: []Match +// [ +// { +// bool: +// }, +// { +// match_phrase: { +// : +// } +// }, +// ... +// ] +type Match struct { + *Bool `json:"bool,omitempty"` + MatchPhrase map[string]string `json:"match_phrase,omitempty"` + MatchPhrasePrefix map[string]string `json:"match_phrase_prefix,omitempty"` + Regexp map[string]string `json:"regexp,omitempty"` + *Range `json:"range,omitempty"` +} + +type Range struct { + *Time `json:"time,omitempty"` +} + +type Time struct { + Gte *time.Time `json:"gte,omitempty"` + Lte *time.Time `json:"lte,omitempty"` +} + +type Aggs struct { + *CardinalityAggregation `json:"container_count,omitempty"` + *DateHistogramAggregation `json:"log_count_over_time,omitempty"` +} + +type CardinalityAggregation struct { + *Cardinality `json:"cardinality,omitempty"` +} + +type Cardinality struct { + Field string `json:"field,omitempty"` +} + +type DateHistogramAggregation struct { + *DateHistogram `json:"date_histogram,omitempty"` +} + +type DateHistogram struct { + Field string `json:"field,omitempty"` + Interval string `json:"interval,omitempty"` +} + +// --------------------------------------------- Response Body --------------------------------------------- + +type Response struct { + ScrollId string `json:"_scroll_id,omitempty"` + Hits `json:"hits,omitempty"` + Aggregations `json:"aggregations,omitempty"` +} + +type Hits struct { + Total interface{} `json:"total"` // `As of Elasticsearch v7.x, hits.total is changed incompatibly + AllHits []Hit `json:"hits"` +} + +type Hit struct { + Source `json:"_source"` + Sort []int64 `json:"sort"` +} + +type Source struct { + Log string `json:"log"` + Time string `json:"time"` + Kubernetes `json:"kubernetes"` +} + +type Kubernetes struct { + Namespace string `json:"namespace_name"` + Pod string `json:"pod_name"` + Container string `json:"container_name"` + Host string `json:"host"` +} + +type Aggregations struct { + ContainerCount `json:"container_count"` + LogCountOverTime `json:"log_count_over_time"` +} + +type ContainerCount struct { + Value int64 `json:"value"` +} + +type LogCountOverTime struct { + Buckets []Bucket `json:"buckets"` +} + +type Bucket struct { + Time int64 `json:"key"` + Count int64 `json:"doc_count"` +} diff --git a/pkg/simple/client/logging/elasticsearch/elasticsearch.go b/pkg/simple/client/logging/elasticsearch/elasticsearch.go new file mode 100644 index 000000000..30a3b2619 --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/elasticsearch.go @@ -0,0 +1,263 @@ +package elasticsearch + +import ( + "bytes" + "context" + "fmt" + jsoniter "github.com/json-iterator/go" + "io" + "kubesphere.io/kubesphere/pkg/simple/client/logging" + v5 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v5" + v6 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v6" + v7 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v7" + "kubesphere.io/kubesphere/pkg/utils/stringutils" + "strings" +) + +const ( + ElasticV5 = "5" + ElasticV6 = "6" + ElasticV7 = "7" +) + +var json = jsoniter.ConfigCompatibleWithStandardLibrary + +// Elasticsearch implement logging interface +type Elasticsearch struct { + c client +} + +// versioned es client interface +type client interface { + // Perform Search API + Search(body []byte) ([]byte, error) + Scroll(id string) ([]byte, error) + ClearScroll(id string) + GetTotalHitCount(v interface{}) int64 +} + +func NewElasticsearch(options *Options) (*Elasticsearch, error) { + var version, index string + es := &Elasticsearch{} + + if options.Version == "" { + var err error + version, err = detectVersionMajor(options.Host) + if err != nil { + return nil, err + } + } else { + version = options.Version + } + + if options.IndexPrefix != "" { + index = options.IndexPrefix + } else { + index = "logstash" + } + + switch version { + case ElasticV5: + es.c = v5.New(options.Host, index) + case ElasticV6: + es.c = v6.New(options.Host, index) + case ElasticV7: + es.c = v7.New(options.Host, index) + default: + return nil, fmt.Errorf("unsupported elasticsearch version %s", version) + } + + return es, nil +} + +func (es *Elasticsearch) ES() *client { + return &es.c +} + +func detectVersionMajor(host string) (string, error) { + // Info APIs are backward compatible with versions of v5.x, v6.x and v7.x + es := v6.New(host, "") + res, err := es.Client.Info( + es.Client.Info.WithContext(context.Background()), + ) + if err != nil { + return "", err + } + + defer res.Body.Close() + + var b map[string]interface{} + if err = json.NewDecoder(res.Body).Decode(&b); err != nil { + return "", err + } + if res.IsError() { + // Print the response status and error information. + e, _ := b["error"].(map[string]interface{}) + return "", fmt.Errorf("[%s] type: %v, reason: %v", res.Status(), e["type"], e["reason"]) + } + + // get the major version + version, _ := b["version"].(map[string]interface{}) + number, _ := version["number"].(string) + if number == "" { + return "", fmt.Errorf("failed to detect elastic version number") + } + + v := strings.Split(number, ".")[0] + return v, nil +} + +func (es Elasticsearch) GetCurrentStats(sf logging.SearchFilter) (logging.Statistics, error) { + body, err := newBodyBuilder(). + mainBool(sf). + cardinalityAggregation(). + bytes() + if err != nil { + return logging.Statistics{}, err + } + + b, err := es.c.Search(body) + if err != nil { + return logging.Statistics{}, err + } + + res, err := parseResponse(b) + if err != nil { + return logging.Statistics{}, err + } + + return logging.Statistics{ + Containers: res.Value, + Logs: es.c.GetTotalHitCount(res.Total), + }, + nil +} + +func (es Elasticsearch) CountLogsByInterval(sf logging.SearchFilter, interval string) (logging.Histogram, error) { + body, err := newBodyBuilder(). + mainBool(sf). + dateHistogramAggregation(interval). + bytes() + if err != nil { + return logging.Histogram{}, err + } + + b, err := es.c.Search(body) + if err != nil { + return logging.Histogram{}, err + } + + res, err := parseResponse(b) + if err != nil { + return logging.Histogram{}, err + } + + var h logging.Histogram + h.Total = es.c.GetTotalHitCount(res.Total) + for _, b := range res.Buckets { + h.Buckets = append(h.Buckets, logging.Bucket{ + Time: b.Time, + Count: b.Count, + }) + } + return h, nil +} + +func (es Elasticsearch) SearchLogs(sf logging.SearchFilter, f, s int64, o string) (logging.Logs, error) { + body, err := newBodyBuilder(). + mainBool(sf). + from(f). + size(s). + sort(o). + bytes() + if err != nil { + return logging.Logs{}, err + } + + b, err := es.c.Search(body) + if err != nil { + return logging.Logs{}, err + } + + res, err := parseResponse(b) + if err != nil { + return logging.Logs{}, err + } + + var l logging.Logs + l.Total = es.c.GetTotalHitCount(res.Total) + for _, hit := range res.AllHits { + l.Records = append(l.Records, logging.Record{ + Log: hit.Log, + Time: hit.Time, + Namespace: hit.Namespace, + Pod: hit.Pod, + Container: hit.Container, + }) + } + return l, nil +} + +func (es Elasticsearch) ExportLogs(sf logging.SearchFilter, w io.Writer) error { + var id string + var from int64 = 0 + var size int64 = 1000 + + res, err := es.SearchLogs(sf, from, size, "desc") + defer es.ClearScroll(id) + if err != nil { + return err + } + + if res.Records == nil || len(res.Records) == 0 { + return nil + } + + // limit to retrieve max 100k records + for i := 0; i < 100; i++ { + res, id, err = es.scroll(id) + if err != nil { + return err + } + + if res.Records == nil || len(res.Records) == 0 { + return nil + } + + output := new(bytes.Buffer) + for _, r := range res.Records { + output.WriteString(fmt.Sprintf(`%s`, stringutils.StripAnsi(r.Log))) + } + _, err = io.Copy(w, output) + if err != nil { + return err + } + } + return nil +} + +func (es *Elasticsearch) scroll(id string) (logging.Logs, string, error) { + b, err := es.c.Scroll(id) + if err != nil { + return logging.Logs{}, id, err + } + + res, err := parseResponse(b) + if err != nil { + return logging.Logs{}, id, err + } + + var l logging.Logs + for _, hit := range res.AllHits { + l.Records = append(l.Records, logging.Record{ + Log: hit.Log, + }) + } + return l, res.ScrollId, nil +} + +func (es *Elasticsearch) ClearScroll(id string) { + if id != "" { + es.c.ClearScroll(id) + } +} diff --git a/pkg/simple/client/logging/elasticsearch/elasticsearch_test.go b/pkg/simple/client/logging/elasticsearch/elasticsearch_test.go new file mode 100644 index 000000000..2907ee159 --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/elasticsearch_test.go @@ -0,0 +1,368 @@ +package elasticsearch + +import ( + "github.com/google/go-cmp/cmp" + "kubesphere.io/kubesphere/pkg/simple/client/logging" + v5 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v5" + v6 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v6" + v7 "kubesphere.io/kubesphere/pkg/simple/client/logging/elasticsearch/versions/v7" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func MockElasticsearchService(pattern string, fakeResp string) *httptest.Server { + mux := http.NewServeMux() + mux.HandleFunc(pattern, func(res http.ResponseWriter, req *http.Request) { + res.Write([]byte(fakeResp)) + }) + return httptest.NewServer(mux) +} + +func TestDetectVersionMajor(t *testing.T) { + var tests = []struct { + description string + fakeResp string + expected string + expectedError bool + }{ + { + description: "detect es 6.x version number", + fakeResp: `{ + "name" : "elasticsearch-logging-data-0", + "cluster_name" : "elasticsearch", + "cluster_uuid" : "uLm0838MSd60T1XEh5P2Qg", + "version" : { + "number" : "6.7.0", + "build_flavor" : "oss", + "build_type" : "docker", + "build_hash" : "8453f77", + "build_date" : "2019-03-21T15:32:29.844721Z", + "build_snapshot" : false, + "lucene_version" : "7.7.0", + "minimum_wire_compatibility_version" : "5.6.0", + "minimum_index_compatibility_version" : "5.0.0" + }, + "tagline" : "You Know, for Search" +}`, + expected: ElasticV6, + expectedError: false, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + es := MockElasticsearchService("/", test.fakeResp) + defer es.Close() + + v, err := detectVersionMajor(es.URL) + if err == nil && test.expectedError { + t.Fatalf("expected error while got nothing") + } else if err != nil && !test.expectedError { + t.Fatal(err) + } + + if v != test.expected { + t.Fatalf("expected get version %s, but got %s", test.expected, v) + } + }) + } +} + +func TestGetCurrentStats(t *testing.T) { + var tests = []struct { + description string + searchFilter logging.SearchFilter + fakeVersion string + fakeResp string + expected logging.Statistics + expectedError bool + }{ + { + description: "[es 6.x] run as admin", + searchFilter: logging.SearchFilter{}, + fakeVersion: ElasticV6, + fakeResp: `{ + "took": 171, + "timed_out": false, + "_shards": { + "total": 10, + "successful": 10, + "skipped": 0, + "failed": 0 + }, + "hits": { + "total": 241222, + "max_score": 1.0, + "hits": [ + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "Hn1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:29.015Z", + "log": " value: \"hostpath\"\n", + "time": "2020-02-28T19:25:29.015492329Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "I31GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:33.103Z", + "log": "I0228 19:25:33.102631 1 controller.go:1040] provision \"kubesphere-system/redis-pvc\" class \"local\": trying to save persistentvolume \"pvc-be6d127d-9366-4ea8-b1ce-f30c1b3a447b\"\n", + "time": "2020-02-28T19:25:33.103075891Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "JX1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:33.113Z", + "log": "I0228 19:25:33.112200 1 controller.go:1088] provision \"kubesphere-system/redis-pvc\" class \"local\": succeeded\n", + "time": "2020-02-28T19:25:33.113110332Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "Kn1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:34.168Z", + "log": " value: \"hostpath\"\n", + "time": "2020-02-28T19:25:34.168983384Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "LH1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:34.168Z", + "log": " value: \"/var/openebs/local/\"\n", + "time": "2020-02-28T19:25:34.168997393Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "NX1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:25:42.868Z", + "log": "I0228 19:25:42.868413 1 config.go:83] SC local has config:- name: StorageType\n", + "time": "2020-02-28T19:25:42.868578188Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "Q31GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:26:13.881Z", + "log": "- name: BasePath\n", + "time": "2020-02-28T19:26:13.881180681Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "S31GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:26:14.597Z", + "log": " value: \"/var/openebs/local/\"\n", + "time": "2020-02-28T19:26:14.597702238Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "TH1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:26:14.597Z", + "log": "I0228 19:26:14.597007 1 provisioner_hostpath.go:42] Creating volume pvc-c3b1e67f-00d2-407d-8c45-690bb273c16a at ks-allinone:/var/openebs/local/pvc-c3b1e67f-00d2-407d-8c45-690bb273c16a\n", + "time": "2020-02-28T19:26:14.597708432Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + }, + { + "_index": "ks-logstash-log-2020.02.28", + "_type": "flb_type", + "_id": "UX1GjXABMO5aQxyNsyxy", + "_score": 1.0, + "_source": { + "@timestamp": "2020-02-28T19:26:15.920Z", + "log": "I0228 19:26:15.915071 1 event.go:221] Event(v1.ObjectReference{Kind:\"PersistentVolumeClaim\", Namespace:\"kubesphere-system\", Name:\"mysql-pvc\", UID:\"1e87deb5-eaec-475f-8eb6-8613b3be80a4\", APIVersion:\"v1\", ResourceVersion:\"2397\", FieldPath:\"\"}): type: 'Normal' reason: 'ProvisioningSucceeded' Successfully provisioned volume pvc-1e87deb5-eaec-475f-8eb6-8613b3be80a4\n", + "time": "2020-02-28T19:26:15.920650572Z", + "kubernetes": { + "pod_name": "openebs-localpv-provisioner-55c66b57b4-jgtjc", + "namespace_name": "kube-system", + "host": "ks-allinone", + "container_name": "openebs-localpv-provisioner", + "docker_id": "cac01cd01cc79d8a8903ddbe6fbde9ac7497919a3f33c61861443703a9e08b39", + "container_hash": "25d789bcd3d12a4ba50bbb56eed1de33279d04352adbba8fd7e3b7b938aec806" + } + } + } + ] + }, + "aggregations": { + "container_count": { + "value": 93 + } + } +}`, + expected: logging.Statistics{ + Containers: 93, + Logs: 241222, + }, + expectedError: false, + }, + { + description: "[es 6.x] index not found", + searchFilter: logging.SearchFilter{ + NamespaceFilter: map[string]time.Time{ + "workspace-1-project-a": time.Unix(1582000000, 0), + "workspace-1-project-b": time.Unix(1582333333, 0), + }, + }, + fakeVersion: ElasticV6, + fakeResp: `{ + "error": { + "root_cause": [ + { + "type": "index_not_found_exception", + "reason": "no such index", + "resource.type": "index_or_alias", + "resource.id": "ks-lsdfsdfsdfs", + "index_uuid": "_na_", + "index": "ks-lsdfsdfsdfs" + } + ], + "type": "index_not_found_exception", + "reason": "no such index", + "resource.type": "index_or_alias", + "resource.id": "ks-lsdfsdfsdfs", + "index_uuid": "_na_", + "index": "ks-lsdfsdfsdfs" + }, + "status": 404 +}`, + expected: logging.Statistics{ + Containers: 0, + Logs: 0, + }, + expectedError: true, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + es := MockElasticsearchService("/", test.fakeResp) + defer es.Close() + + clientv5 := Elasticsearch{c: v5.New(es.URL, "ks-logstash-log")} + clientv6 := Elasticsearch{c: v6.New(es.URL, "ks-logstash-log")} + clientv7 := Elasticsearch{c: v7.New(es.URL, "ks-logstash-log")} + + var stats logging.Statistics + var err error + switch test.fakeVersion { + case ElasticV5: + stats, err = clientv5.GetCurrentStats(test.searchFilter) + case ElasticV6: + stats, err = clientv6.GetCurrentStats(test.searchFilter) + case ElasticV7: + stats, err = clientv7.GetCurrentStats(test.searchFilter) + } + + if err != nil && !test.expectedError { + t.Fatal(err) + } else if diff := cmp.Diff(stats, test.expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", test.expected, diff) + } + }) + } +} diff --git a/pkg/simple/client/logging/elasticsearch/options.go b/pkg/simple/client/logging/elasticsearch/options.go new file mode 100644 index 000000000..07b141bfc --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/options.go @@ -0,0 +1,46 @@ +package elasticsearch + +import ( + "github.com/spf13/pflag" + "kubesphere.io/kubesphere/pkg/utils/reflectutils" +) + +type Options struct { + Host string `json:"host" yaml:"host"` + IndexPrefix string `json:"indexPrefix,omitempty" yaml:"indexPrefix"` + Version string `json:"version" yaml:"version"` +} + +func NewElasticSearchOptions() *Options { + return &Options{ + Host: "", + IndexPrefix: "fluentbit", + Version: "", + } +} + +func (s *Options) ApplyTo(options *Options) { + if s.Host != "" { + reflectutils.Override(options, s) + } +} + +func (s *Options) Validate() []error { + errs := []error{} + + return errs +} + +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.Host, "elasticsearch-host", c.Host, ""+ + "Elasticsearch logging service host. KubeSphere is using elastic as log store, "+ + "if this filed left blank, KubeSphere will use kubernetes builtin log API instead, and"+ + " the following elastic search options will be ignored.") + + fs.StringVar(&s.IndexPrefix, "index-prefix", c.IndexPrefix, ""+ + "Index name prefix. KubeSphere will retrieve logs against indices matching the prefix.") + + fs.StringVar(&s.Version, "elasticsearch-version", c.Version, ""+ + "Elasticsearch major version, e.g. 5/6/7, if left blank, will detect automatically."+ + "Currently, minimum supported version is 5.x") +} diff --git a/pkg/simple/client/logging/elasticsearch/versions/v5/v5.go b/pkg/simple/client/logging/elasticsearch/versions/v5/v5.go new file mode 100644 index 000000000..e53a0c03c --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/versions/v5/v5.go @@ -0,0 +1,88 @@ +package v5 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/elastic/go-elasticsearch/v5" + "github.com/elastic/go-elasticsearch/v5/esapi" + "io/ioutil" + "k8s.io/klog" + "time" +) + +type Elastic struct { + client *elasticsearch.Client + index string +} + +func New(address string, index string) *Elastic { + client, err := elasticsearch.NewClient(elasticsearch.Config{ + Addresses: []string{address}, + }) + if err != nil { + klog.Error(err) + return nil + } + + return &Elastic{client: client, index: index} +} + +func (e *Elastic) Search(body []byte) ([]byte, error) { + response, err := e.client.Search( + e.client.Search.WithContext(context.Background()), + e.client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), + e.client.Search.WithBody(bytes.NewBuffer(body)), + e.client.Search.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + return ioutil.ReadAll(response.Body) +} + +func (e *Elastic) Scroll(id string) ([]byte, error) { + response, err := e.client.Scroll( + e.client.Scroll.WithContext(context.Background()), + e.client.Scroll.WithScrollID(id), + e.client.Scroll.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + return ioutil.ReadAll(response.Body) +} + +func (e *Elastic) ClearScroll(scrollId string) { + response, _ := e.client.ClearScroll( + e.client.ClearScroll.WithContext(context.Background()), + e.client.ClearScroll.WithScrollID(scrollId)) + defer response.Body.Close() +} + +func (e *Elastic) GetTotalHitCount(v interface{}) int64 { + f, _ := v.(float64) + return int64(f) +} + +func parseError(response *esapi.Response) error { + var e map[string]interface{} + if err := json.NewDecoder(response.Body).Decode(&e); err != nil { + return err + } else { + // Print the response status and error information. + e, _ := e["error"].(map[string]interface{}) + return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) + } +} diff --git a/pkg/simple/client/logging/elasticsearch/versions/v6/v6.go b/pkg/simple/client/logging/elasticsearch/versions/v6/v6.go new file mode 100644 index 000000000..8c584aadb --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/versions/v6/v6.go @@ -0,0 +1,88 @@ +package v6 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/elastic/go-elasticsearch/v6" + "github.com/elastic/go-elasticsearch/v6/esapi" + "io/ioutil" + "k8s.io/klog" + "time" +) + +type Elastic struct { + Client *elasticsearch.Client + index string +} + +func New(address string, index string) *Elastic { + client, err := elasticsearch.NewClient(elasticsearch.Config{ + Addresses: []string{address}, + }) + if err != nil { + klog.Error(err) + return nil + } + + return &Elastic{Client: client, index: index} +} + +func (e *Elastic) Search(body []byte) ([]byte, error) { + response, err := e.Client.Search( + e.Client.Search.WithContext(context.Background()), + e.Client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), + e.Client.Search.WithBody(bytes.NewBuffer(body)), + e.Client.Search.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + return ioutil.ReadAll(response.Body) +} + +func (e *Elastic) Scroll(id string) ([]byte, error) { + response, err := e.Client.Scroll( + e.Client.Scroll.WithContext(context.Background()), + e.Client.Scroll.WithScrollID(id), + e.Client.Scroll.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + return ioutil.ReadAll(response.Body) +} + +func (e *Elastic) ClearScroll(scrollId string) { + response, _ := e.Client.ClearScroll( + e.Client.ClearScroll.WithContext(context.Background()), + e.Client.ClearScroll.WithScrollID(scrollId)) + defer response.Body.Close() +} + +func (e *Elastic) GetTotalHitCount(v interface{}) int64 { + f, _ := v.(float64) + return int64(f) +} + +func parseError(response *esapi.Response) error { + var e map[string]interface{} + if err := json.NewDecoder(response.Body).Decode(&e); err != nil { + return err + } else { + // Print the response status and error information. + e, _ := e["error"].(map[string]interface{}) + return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) + } +} diff --git a/pkg/simple/client/logging/elasticsearch/versions/v7/v7.go b/pkg/simple/client/logging/elasticsearch/versions/v7/v7.go new file mode 100644 index 000000000..1051ffc66 --- /dev/null +++ b/pkg/simple/client/logging/elasticsearch/versions/v7/v7.go @@ -0,0 +1,91 @@ +package v7 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/elastic/go-elasticsearch/v7" + "github.com/elastic/go-elasticsearch/v7/esapi" + "io/ioutil" + "k8s.io/klog" + "time" +) + +type Elastic struct { + client *elasticsearch.Client + index string +} + +func New(address string, index string) *Elastic { + client, err := elasticsearch.NewClient(elasticsearch.Config{ + Addresses: []string{address}, + }) + if err != nil { + klog.Error(err) + return nil + } + + return &Elastic{client: client, index: index} +} + +func (e *Elastic) Search(body []byte) ([]byte, error) { + response, err := e.client.Search( + e.client.Search.WithContext(context.Background()), + e.client.Search.WithIndex(fmt.Sprintf("%s*", e.index)), + e.client.Search.WithTrackTotalHits(true), + e.client.Search.WithBody(bytes.NewBuffer(body)), + e.client.Search.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + return ioutil.ReadAll(response.Body) +} + +func (e *Elastic) Scroll(id string) ([]byte, error) { + response, err := e.client.Scroll( + e.client.Scroll.WithContext(context.Background()), + e.client.Scroll.WithScrollID(id), + e.client.Scroll.WithScroll(time.Minute)) + if err != nil { + return nil, err + } + defer response.Body.Close() + + if response.IsError() { + return nil, parseError(response) + } + + b, err := ioutil.ReadAll(response.Body) + return b, err +} + +func (e *Elastic) ClearScroll(scrollId string) { + response, _ := e.client.ClearScroll( + e.client.ClearScroll.WithContext(context.Background()), + e.client.ClearScroll.WithScrollID(scrollId)) + defer response.Body.Close() +} + +func (e *Elastic) GetTotalHitCount(v interface{}) int64 { + m, _ := v.(map[string]interface{}) + f, _ := m["value"].(float64) + return int64(f) +} + +func parseError(response *esapi.Response) error { + var e map[string]interface{} + if err := json.NewDecoder(response.Body).Decode(&e); err != nil { + return err + } else { + // Print the response status and error information. + e, _ := e["error"].(map[string]interface{}) + return fmt.Errorf("type: %v, reason: %v", e["type"], e["reason"]) + } +} diff --git a/pkg/simple/client/logging/interface.go b/pkg/simple/client/logging/interface.go new file mode 100644 index 000000000..69a20909d --- /dev/null +++ b/pkg/simple/client/logging/interface.go @@ -0,0 +1,69 @@ +package logging + +import ( + "io" + "time" +) + +type Interface interface { + // Current stats about log store, eg. total number of logs and containers + GetCurrentStats(sf SearchFilter) (Statistics, error) + + CountLogsByInterval(sf SearchFilter, interval string) (Histogram, error) + + SearchLogs(sf SearchFilter, from, size int64, order string) (Logs, error) + + ExportLogs(sf SearchFilter, w io.Writer) error +} + +// Log search result +type Logs struct { + Total int64 `json:"total" description:"total number of matched results"` + Records []Record `json:"records,omitempty" description:"actual array of results"` +} + +type Record struct { + Log string `json:"log,omitempty" description:"log message"` + Time string `json:"time,omitempty" description:"log timestamp"` + Namespace string `json:"namespace,omitempty" description:"namespace"` + Pod string `json:"pod,omitempty" description:"pod name"` + Container string `json:"container,omitempty" description:"container name"` +} + +// Log statistics result +type Statistics struct { + Containers int64 `json:"containers" description:"total number of containers"` + Logs int64 `json:"logs" description:"total number of logs"` +} + +// Log count result by interval +type Histogram struct { + Total int64 `json:"total" description:"total number of logs"` + Buckets []Bucket `json:"histograms" description:"actual array of histogram results"` +} + +type Bucket struct { + Time int64 `json:"time" description:"timestamp"` + Count int64 `json:"count" description:"total number of logs at intervals"` +} + +// General query conditions +type SearchFilter struct { + // xxxSearch for literal matching + // xxxfilter for fuzzy matching + + // To prevent disclosing archived logs of a reopened namespace, + // NamespaceFilter records the namespace creation time. + // Any query to this namespace must begin after its creation. + NamespaceFilter map[string]time.Time + WorkloadSearch []string + WorkloadFilter []string + PodSearch []string + PodFilter []string + ContainerSearch []string + ContainerFilter []string + LogSearch []string + + Starttime time.Time + Endtime time.Time +} diff --git a/pkg/simple/client/monitoring/interface.go b/pkg/simple/client/monitoring/interface.go new file mode 100644 index 000000000..68a471317 --- /dev/null +++ b/pkg/simple/client/monitoring/interface.go @@ -0,0 +1,11 @@ +package monitoring + +import "time" + +type Interface interface { + GetMetric(expr string, time time.Time) Metric + GetMetricOverTime(expr string, start, end time.Time, step time.Duration) Metric + GetNamedMetrics(metrics []string, time time.Time, opt QueryOption) []Metric + GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, opt QueryOption) []Metric + GetMetadata(namespace string) []Metadata +} diff --git a/pkg/simple/client/monitoring/prometheus/prometheus.go b/pkg/simple/client/monitoring/prometheus/prometheus.go new file mode 100644 index 000000000..86e5662da --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/prometheus.go @@ -0,0 +1,202 @@ +package prometheus + +import ( + "context" + "fmt" + "github.com/prometheus/client_golang/api" + apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "sync" + "time" +) + +// prometheus implements monitoring interface backed by Prometheus +type prometheus struct { + client apiv1.API +} + +func NewPrometheus(options *Options) (monitoring.Interface, error) { + cfg := api.Config{ + Address: options.Endpoint, + } + + client, err := api.NewClient(cfg) + return prometheus{client: apiv1.NewAPI(client)}, err +} + +func (p prometheus) GetMetric(expr string, ts time.Time) monitoring.Metric { + var parsedResp monitoring.Metric + + value, err := p.client.Query(context.Background(), expr, ts) + if err != nil { + parsedResp.Error = err.Error() + } else { + parsedResp.MetricData = parseQueryResp(value) + } + + return parsedResp +} + +func (p prometheus) GetMetricOverTime(expr string, start, end time.Time, step time.Duration) monitoring.Metric { + timeRange := apiv1.Range{ + Start: start, + End: end, + Step: step, + } + + value, err := p.client.QueryRange(context.Background(), expr, timeRange) + + var parsedResp monitoring.Metric + if err != nil { + parsedResp.Error = err.Error() + } else { + parsedResp.MetricData = parseQueryRangeResp(value) + } + return parsedResp +} + +func (p prometheus) GetNamedMetrics(metrics []string, ts time.Time, o monitoring.QueryOption) []monitoring.Metric { + var res []monitoring.Metric + var mtx sync.Mutex + var wg sync.WaitGroup + + opts := monitoring.NewQueryOptions() + o.Apply(opts) + + for _, metric := range metrics { + wg.Add(1) + go func(metric string) { + parsedResp := monitoring.Metric{MetricName: metric} + + value, err := p.client.Query(context.Background(), makeExpr(metric, *opts), ts) + if err != nil { + parsedResp.Error = err.Error() + } else { + parsedResp.MetricData = parseQueryResp(value) + } + + mtx.Lock() + res = append(res, parsedResp) + mtx.Unlock() + + wg.Done() + }(metric) + } + + wg.Wait() + + return res +} + +func (p prometheus) GetNamedMetricsOverTime(metrics []string, start, end time.Time, step time.Duration, o monitoring.QueryOption) []monitoring.Metric { + var res []monitoring.Metric + var mtx sync.Mutex + var wg sync.WaitGroup + + opts := monitoring.NewQueryOptions() + o.Apply(opts) + + timeRange := apiv1.Range{ + Start: start, + End: end, + Step: step, + } + + for _, metric := range metrics { + wg.Add(1) + go func(metric string) { + parsedResp := monitoring.Metric{MetricName: metric} + + value, err := p.client.QueryRange(context.Background(), makeExpr(metric, *opts), timeRange) + if err != nil { + parsedResp.Error = err.Error() + } else { + parsedResp.MetricData = parseQueryRangeResp(value) + } + + mtx.Lock() + res = append(res, parsedResp) + mtx.Unlock() + + wg.Done() + }(metric) + } + + wg.Wait() + + return res +} + +func (p prometheus) GetMetadata(namespace string) []monitoring.Metadata { + var meta []monitoring.Metadata + + // Filter metrics available to members of this namespace + matchTarget := fmt.Sprintf("{namespace=\"%s\"}", namespace) + items, err := p.client.TargetsMetadata(context.Background(), matchTarget, "", "") + if err != nil { + return meta + } + + // Deduplication + set := make(map[string]bool) + for _, item := range items { + _, ok := set[item.Metric] + if !ok { + set[item.Metric] = true + meta = append(meta, monitoring.Metadata{ + Metric: item.Metric, + Type: string(item.Type), + Help: item.Help, + }) + } + } + + return meta +} + +func parseQueryRangeResp(value model.Value) monitoring.MetricData { + res := monitoring.MetricData{MetricType: monitoring.MetricTypeMatrix} + + data, _ := value.(model.Matrix) + + for _, v := range data { + mv := monitoring.MetricValue{ + Metadata: make(map[string]string), + } + + for k, v := range v.Metric { + mv.Metadata[string(k)] = string(v) + } + + for _, k := range v.Values { + mv.Series = append(mv.Series, monitoring.Point{float64(k.Timestamp) / 1000, float64(k.Value)}) + } + + res.MetricValues = append(res.MetricValues, mv) + } + + return res +} + +func parseQueryResp(value model.Value) monitoring.MetricData { + res := monitoring.MetricData{MetricType: monitoring.MetricTypeVector} + + data, _ := value.(model.Vector) + + for _, v := range data { + mv := monitoring.MetricValue{ + Metadata: make(map[string]string), + } + + for k, v := range v.Metric { + mv.Metadata[string(k)] = string(v) + } + + mv.Sample = &monitoring.Point{float64(v.Timestamp) / 1000, float64(v.Value)} + + res.MetricValues = append(res.MetricValues, mv) + } + + return res +} diff --git a/pkg/simple/client/monitoring/prometheus/prometheus_options.go b/pkg/simple/client/monitoring/prometheus/prometheus_options.go new file mode 100644 index 000000000..8b46fbff5 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/prometheus_options.go @@ -0,0 +1,41 @@ +package prometheus + +import ( + "github.com/spf13/pflag" +) + +type Options struct { + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` + SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"` +} + +func NewPrometheusOptions() *Options { + return &Options{ + Endpoint: "", + SecondaryEndpoint: "", + } +} + +func (s *Options) Validate() []error { + var errs []error + return errs +} + +func (s *Options) ApplyTo(options *Options) { + if s.Endpoint != "" { + options.Endpoint = s.Endpoint + } + + if s.SecondaryEndpoint != "" { + options.SecondaryEndpoint = s.SecondaryEndpoint + } +} + +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.Endpoint, "prometheus-endpoint", c.Endpoint, ""+ + "Prometheus service endpoint which stores KubeSphere monitoring data, if left "+ + "blank, will use builtin metrics-server as data source.") + + fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", c.SecondaryEndpoint, ""+ + "Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.") +} diff --git a/pkg/simple/client/monitoring/prometheus/prometheus_test.go b/pkg/simple/client/monitoring/prometheus/prometheus_test.go new file mode 100644 index 000000000..35d018c82 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/prometheus_test.go @@ -0,0 +1,143 @@ +package prometheus + +import ( + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/json-iterator/go" + "io/ioutil" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestGetNamedMetrics(t *testing.T) { + tests := []struct { + fakeResp string + expected string + }{ + { + fakeResp: "metrics-vector-type-prom.json", + expected: "metrics-vector-type-res.json", + }, + { + fakeResp: "metrics-error-prom.json", + expected: "metrics-error-res.json", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + expected := make([]monitoring.Metric, 0) + err := jsonFromFile(tt.expected, &expected) + if err != nil { + t.Fatal(err) + } + + srv := mockPrometheusService("/api/v1/query", tt.fakeResp) + defer srv.Close() + + client, _ := NewPrometheus(&Options{Endpoint: srv.URL}) + result := client.GetNamedMetrics([]string{"cluster_cpu_utilisation"}, time.Now(), monitoring.ClusterOption{}) + if diff := cmp.Diff(result, expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} + +func TestGetNamedMetricsOverTime(t *testing.T) { + tests := []struct { + fakeResp string + expected string + }{ + { + fakeResp: "metrics-matrix-type-prom.json", + expected: "metrics-matrix-type-res.json", + }, + { + fakeResp: "metrics-error-prom.json", + expected: "metrics-error-res.json", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + expected := make([]monitoring.Metric, 0) + err := jsonFromFile(tt.expected, &expected) + if err != nil { + t.Fatal(err) + } + + srv := mockPrometheusService("/api/v1/query_range", tt.fakeResp) + defer srv.Close() + + client, _ := NewPrometheus(&Options{Endpoint: srv.URL}) + result := client.GetNamedMetricsOverTime([]string{"cluster_cpu_utilisation"}, time.Now().Add(-time.Minute*3), time.Now(), time.Minute, monitoring.ClusterOption{}) + if diff := cmp.Diff(result, expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} + +func TestGetMetadata(t *testing.T) { + tests := []struct { + fakeResp string + expected string + }{ + { + fakeResp: "metadata-prom.json", + expected: "metadata-res.json", + }, + { + fakeResp: "metadata-notfound-prom.json", + expected: "metadata-notfound-res.json", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + expected := make([]monitoring.Metadata, 0) + err := jsonFromFile(tt.expected, &expected) + if err != nil { + t.Fatal(err) + } + if len(expected) == 0 { + expected = nil + } + + srv := mockPrometheusService("/api/v1/targets/metadata", tt.fakeResp) + defer srv.Close() + + client, _ := NewPrometheus(&Options{Endpoint: srv.URL}) + result := client.GetMetadata("default") + if diff := cmp.Diff(result, expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} + +func mockPrometheusService(pattern, fakeResp string) *httptest.Server { + mux := http.NewServeMux() + mux.HandleFunc(pattern, func(res http.ResponseWriter, req *http.Request) { + b, _ := ioutil.ReadFile(fmt.Sprintf("./testdata/%s", fakeResp)) + res.Write(b) + }) + return httptest.NewServer(mux) +} + +func jsonFromFile(expectedFile string, expectedJsonPtr interface{}) error { + json, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s", expectedFile)) + if err != nil { + return err + } + err = jsoniter.Unmarshal(json, expectedJsonPtr) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/simple/client/monitoring/prometheus/promql.go b/pkg/simple/client/monitoring/prometheus/promql.go new file mode 100644 index 000000000..ed411ca23 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/promql.go @@ -0,0 +1,424 @@ +/* +Copyright 2019 The KubeSphere 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 prometheus + +import ( + "fmt" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "strings" +) + +const ( + StatefulSet = "StatefulSet" + DaemonSet = "DaemonSet" + Deployment = "Deployment" +) + +var promQLTemplates = map[string]string{ + //cluster + "cluster_cpu_utilisation": ":node_cpu_utilisation:avg1m", + "cluster_cpu_usage": `round(:node_cpu_utilisation:avg1m * sum(node:node_num_cpu:sum), 0.001)`, + "cluster_cpu_total": "sum(node:node_num_cpu:sum)", + "cluster_memory_utilisation": ":node_memory_utilisation:", + "cluster_memory_available": "sum(node:node_memory_bytes_available:sum)", + "cluster_memory_total": "sum(node:node_memory_bytes_total:sum)", + "cluster_memory_usage_wo_cache": "sum(node:node_memory_bytes_total:sum) - sum(node:node_memory_bytes_available:sum)", + "cluster_net_utilisation": ":node_net_utilisation:sum_irate", + "cluster_net_bytes_transmitted": "sum(node:node_net_bytes_transmitted:sum_irate)", + "cluster_net_bytes_received": "sum(node:node_net_bytes_received:sum_irate)", + "cluster_disk_read_iops": "sum(node:data_volume_iops_reads:sum)", + "cluster_disk_write_iops": "sum(node:data_volume_iops_writes:sum)", + "cluster_disk_read_throughput": "sum(node:data_volume_throughput_bytes_read:sum)", + "cluster_disk_write_throughput": "sum(node:data_volume_throughput_bytes_written:sum)", + "cluster_disk_size_usage": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} - node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, + "cluster_disk_size_utilisation": `cluster:disk_utilization:ratio`, + "cluster_disk_size_capacity": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, + "cluster_disk_size_available": `sum(max(node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) by (device, instance))`, + "cluster_disk_inode_total": `sum(node:node_inodes_total:)`, + "cluster_disk_inode_usage": `sum(node:node_inodes_total:) - sum(node:node_inodes_free:)`, + "cluster_disk_inode_utilisation": `cluster:disk_inode_utilization:ratio`, + "cluster_namespace_count": `count(kube_namespace_labels)`, + "cluster_pod_count": `cluster:pod:sum`, + "cluster_pod_quota": `sum(max(kube_node_status_capacity_pods) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`, + "cluster_pod_utilisation": `cluster:pod_utilization:ratio`, + "cluster_pod_running_count": `cluster:pod_running:count`, + "cluster_pod_succeeded_count": `count(kube_pod_info unless on (pod) (kube_pod_status_phase{phase=~"Failed|Pending|Unknown|Running"} > 0) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0))`, + "cluster_pod_abnormal_count": `cluster:pod_abnormal:sum`, + "cluster_node_online": `sum(kube_node_status_condition{condition="Ready",status="true"})`, + "cluster_node_offline": `cluster:node_offline:sum`, + "cluster_node_total": `sum(kube_node_status_condition{condition="Ready"})`, + "cluster_cronjob_count": `sum(kube_cronjob_labels)`, + "cluster_pvc_count": `sum(kube_persistentvolumeclaim_info)`, + "cluster_daemonset_count": `sum(kube_daemonset_labels)`, + "cluster_deployment_count": `sum(kube_deployment_labels)`, + "cluster_endpoint_count": `sum(kube_endpoint_labels)`, + "cluster_hpa_count": `sum(kube_hpa_labels)`, + "cluster_job_count": `sum(kube_job_labels)`, + "cluster_statefulset_count": `sum(kube_statefulset_labels)`, + "cluster_replicaset_count": `count(kube_replicaset_labels)`, + "cluster_service_count": `sum(kube_service_info)`, + "cluster_secret_count": `sum(kube_secret_info)`, + "cluster_pv_count": `sum(kube_persistentvolume_labels)`, + "cluster_ingresses_extensions_count": `sum(kube_ingress_labels)`, + "cluster_load1": `sum(node_load1{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, + "cluster_load5": `sum(node_load5{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, + "cluster_load15": `sum(node_load15{job="node-exporter"}) / sum(node:node_num_cpu:sum)`, + "cluster_pod_abnormal_ratio": `cluster:pod_abnormal:ratio`, + "cluster_node_offline_ratio": `cluster:node_offline:ratio`, + + //node + "node_cpu_utilisation": "node:node_cpu_utilisation:avg1m{$1}", + "node_cpu_total": "node:node_num_cpu:sum{$1}", + "node_memory_utilisation": "node:node_memory_utilisation:{$1}", + "node_memory_available": "node:node_memory_bytes_available:sum{$1}", + "node_memory_total": "node:node_memory_bytes_total:sum{$1}", + "node_memory_usage_wo_cache": "node:node_memory_bytes_total:sum{$1} - node:node_memory_bytes_available:sum{$1}", + "node_net_utilisation": "node:node_net_utilisation:sum_irate{$1}", + "node_net_bytes_transmitted": "node:node_net_bytes_transmitted:sum_irate{$1}", + "node_net_bytes_received": "node:node_net_bytes_received:sum_irate{$1}", + "node_disk_read_iops": "node:data_volume_iops_reads:sum{$1}", + "node_disk_write_iops": "node:data_volume_iops_writes:sum{$1}", + "node_disk_read_throughput": "node:data_volume_throughput_bytes_read:sum{$1}", + "node_disk_write_throughput": "node:data_volume_throughput_bytes_written:sum{$1}", + "node_disk_size_capacity": `sum(max(node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{$1}) by (device, node)) by (node)`, + "node_disk_size_available": `node:disk_space_available:{$1}`, + "node_disk_size_usage": `sum(max((node_filesystem_size_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"} - node_filesystem_avail_bytes{device=~"/dev/.*", device!~"/dev/loop\\d+", job="node-exporter"}) * on (namespace, pod) group_left(node) node_namespace_pod:kube_pod_info:{$1}) by (device, node)) by (node)`, + "node_disk_size_utilisation": `node:disk_space_utilization:ratio{$1}`, + "node_disk_inode_total": `node:node_inodes_total:{$1}`, + "node_disk_inode_usage": `node:node_inodes_total:{$1} - node:node_inodes_free:{$1}`, + "node_disk_inode_utilisation": `node:disk_inode_utilization:ratio{$1}`, + "node_pod_count": `node:pod_count:sum{$1}`, + "node_pod_quota": `max(kube_node_status_capacity_pods{$1}) by (node) unless on (node) (kube_node_status_condition{condition="Ready",status=~"unknown|false"} > 0)`, + "node_pod_utilisation": `node:pod_utilization:ratio{$1}`, + "node_pod_running_count": `node:pod_running:count{$1}`, + "node_pod_succeeded_count": `node:pod_succeeded:count{$1}`, + "node_pod_abnormal_count": `node:pod_abnormal:count{$1}`, + "node_cpu_usage": `round(node:node_cpu_utilisation:avg1m{$1} * node:node_num_cpu:sum{$1}, 0.001)`, + "node_load1": `node:load1:ratio{$1}`, + "node_load5": `node:load5:ratio{$1}`, + "node_load15": `node:load15:ratio{$1}`, + "node_pod_abnormal_ratio": `node:pod_abnormal:ratio{$1}`, + + // workspace + "workspace_cpu_usage": `round(sum by (label_kubesphere_io_workspace) (namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", $1}), 0.001)`, + "workspace_memory_usage": `sum by (label_kubesphere_io_workspace) (namespace:container_memory_usage_bytes:sum{namespace!="", $1})`, + "workspace_memory_usage_wo_cache": `sum by (label_kubesphere_io_workspace) (namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", $1})`, + "workspace_net_bytes_transmitted": `sum by (label_kubesphere_io_workspace) (sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "workspace_net_bytes_received": `sum by (label_kubesphere_io_workspace) (sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "workspace_pod_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_pod_running_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase="Running", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_pod_succeeded_count": `sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_pod_abnormal_count": `count by (label_kubesphere_io_workspace) ((kube_pod_info{node!=""} unless on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Succeeded"}>0) unless on (pod, namespace) ((kube_pod_status_ready{job="kube-state-metrics", condition="true"}>0) and on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Running"}>0)) unless on (pod, namespace) (kube_pod_container_status_waiting_reason{job="kube-state-metrics", reason="ContainerCreating"}>0)) * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_ingresses_extensions_count": `sum by (label_kubesphere_io_workspace) (kube_ingress_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_cronjob_count": `sum by (label_kubesphere_io_workspace) (kube_cronjob_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_pvc_count": `sum by (label_kubesphere_io_workspace) (kube_persistentvolumeclaim_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_daemonset_count": `sum by (label_kubesphere_io_workspace) (kube_daemonset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_deployment_count": `sum by (label_kubesphere_io_workspace) (kube_deployment_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_endpoint_count": `sum by (label_kubesphere_io_workspace) (kube_endpoint_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_hpa_count": `sum by (label_kubesphere_io_workspace) (kube_hpa_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_job_count": `sum by (label_kubesphere_io_workspace) (kube_job_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_statefulset_count": `sum by (label_kubesphere_io_workspace) (kube_statefulset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_replicaset_count": `count by (label_kubesphere_io_workspace) (kube_replicaset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_service_count": `sum by (label_kubesphere_io_workspace) (kube_service_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_secret_count": `sum by (label_kubesphere_io_workspace) (kube_secret_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + "workspace_pod_abnormal_ratio": `count by (label_kubesphere_io_workspace) ((kube_pod_info{node!=""} unless on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Succeeded"}>0) unless on (pod, namespace) ((kube_pod_status_ready{job="kube-state-metrics", condition="true"}>0) and on (pod, namespace) (kube_pod_status_phase{job="kube-state-metrics", phase="Running"}>0)) unless on (pod, namespace) (kube_pod_container_status_waiting_reason{job="kube-state-metrics", reason="ContainerCreating"}>0)) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1}) / sum by (label_kubesphere_io_workspace) (kube_pod_status_phase{phase!="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace)(kube_namespace_labels{$1}))`, + + //namespace + "namespace_cpu_usage": `round(namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", $1}, 0.001)`, + "namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace!="", $1}`, + "namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", $1}`, + "namespace_net_bytes_transmitted": `sum by (namespace) (irate(container_network_transmit_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_net_bytes_received": `sum by (namespace) (irate(container_network_receive_bytes_total{namespace!="", pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m]) * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pod_count": `sum by (namespace) (kube_pod_status_phase{phase!~"Failed|Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pod_running_count": `sum by (namespace) (kube_pod_status_phase{phase="Running", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pod_succeeded_count": `sum by (namespace) (kube_pod_status_phase{phase="Succeeded", namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pod_abnormal_count": `namespace:pod_abnormal:count{namespace!="", $1}`, + "namespace_pod_abnormal_ratio": `namespace:pod_abnormal:ratio{namespace!="", $1}`, + "namespace_memory_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.memory"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_cpu_limit_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="limits.cpu"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pod_count_hard": `min by (namespace) (kube_resourcequota{resourcequota!="quota", type="hard", namespace!="", resource="count/pods"} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_cronjob_count": `sum by (namespace) (kube_cronjob_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_pvc_count": `sum by (namespace) (kube_persistentvolumeclaim_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_daemonset_count": `sum by (namespace) (kube_daemonset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_deployment_count": `sum by (namespace) (kube_deployment_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_endpoint_count": `sum by (namespace) (kube_endpoint_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_hpa_count": `sum by (namespace) (kube_hpa_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_job_count": `sum by (namespace) (kube_job_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_statefulset_count": `sum by (namespace) (kube_statefulset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_replicaset_count": `count by (namespace) (kube_replicaset_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_service_count": `sum by (namespace) (kube_service_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_secret_count": `sum by (namespace) (kube_secret_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_configmap_count": `sum by (namespace) (kube_configmap_info{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_ingresses_extensions_count": `sum by (namespace) (kube_ingress_labels{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + "namespace_s2ibuilder_count": `sum by (namespace) (s2i_s2ibuilder_created{namespace!=""} * on (namespace) group_left(label_kubesphere_io_workspace) kube_namespace_labels{$1})`, + + // workload + "workload_cpu_usage": `round(namespace:workload_cpu_usage:sum{$1}, 0.001)`, + "workload_memory_usage": `namespace:workload_memory_usage:sum{$1}`, + "workload_memory_usage_wo_cache": `namespace:workload_memory_usage_wo_cache:sum{$1}`, + "workload_net_bytes_transmitted": `namespace:workload_net_bytes_transmitted:sum_irate{$1}`, + "workload_net_bytes_received": `namespace:workload_net_bytes_received:sum_irate{$1}`, + + "workload_deployment_replica": `label_join(sum (label_join(label_replace(kube_deployment_spec_replicas{$2}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_deployment_replica_available": `label_join(sum (label_join(label_replace(kube_deployment_status_replicas_available{$2}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_statefulset_replica": `label_join(sum (label_join(label_replace(kube_statefulset_replicas{$2}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_statefulset_replica_available": `label_join(sum (label_join(label_replace(kube_statefulset_status_replicas_current{$2}, "owner_kind", "StatefulSet", "", ""), "workload", "", "statefulset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_daemonset_replica": `label_join(sum (label_join(label_replace(kube_daemonset_status_desired_number_scheduled{$2}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_daemonset_replica_available": `label_join(sum (label_join(label_replace(kube_daemonset_status_number_available{$2}, "owner_kind", "DaemonSet", "", ""), "workload", "", "daemonset")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "workload_deployment_unavailable_replicas_ratio": `namespace:deployment_unavailable_replicas:ratio{$1}`, + "workload_daemonset_unavailable_replicas_ratio": `namespace:daemonset_unavailable_replicas:ratio{$1}`, + "workload_statefulset_unavailable_replicas_ratio": `namespace:statefulset_unavailable_replicas:ratio{$1}`, + + // pod + "pod_cpu_usage": `round(sum by (namespace, pod) (irate(container_cpu_usage_seconds_total{job="kubelet", pod!="", image!=""}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}, 0.001)`, + "pod_memory_usage": `sum by (namespace, pod) (container_memory_usage_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, + "pod_memory_usage_wo_cache": `sum by (namespace, pod) (container_memory_working_set_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, + "pod_net_bytes_transmitted": `sum by (namespace, pod) (irate(container_network_transmit_bytes_total{pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, + "pod_net_bytes_received": `sum by (namespace, pod) (irate(container_network_receive_bytes_total{pod!="", interface!~"^(cali.+|tunl.+|dummy.+|kube.+|flannel.+|cni.+|docker.+|veth.+|lo.*)", job="kubelet"}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{$1} * on (namespace, pod) group_left(node) kube_pod_info{$2}`, + + // container + "container_cpu_usage": `round(sum by (namespace, pod, container) (irate(container_cpu_usage_seconds_total{job="kubelet", container!="POD", container!="", image!="", $1}[5m])), 0.001)`, + "container_memory_usage": `sum by (namespace, pod, container) (container_memory_usage_bytes{job="kubelet", container!="POD", container!="", image!="", $1})`, + "container_memory_usage_wo_cache": `sum by (namespace, pod, container) (container_memory_working_set_bytes{job="kubelet", container!="POD", container!="", image!="", $1})`, + + // pvc + "pvc_inodes_available": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_free) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_inodes_used": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_used) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_inodes_total": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_inodes_utilisation": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_used / kubelet_volume_stats_inodes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_bytes_available": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_available_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_bytes_used": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_used_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_bytes_total": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_capacity_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + "pvc_bytes_utilisation": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_used_bytes / kubelet_volume_stats_capacity_bytes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{$1}`, + + // component + "etcd_server_list": `label_replace(up{job="etcd"}, "node_ip", "$1", "instance", "(.*):.*")`, + "etcd_server_total": `count(up{job="etcd"})`, + "etcd_server_up_total": `etcd:up:sum`, + "etcd_server_has_leader": `label_replace(etcd_server_has_leader, "node_ip", "$1", "instance", "(.*):.*")`, + "etcd_server_leader_changes": `label_replace(etcd:etcd_server_leader_changes_seen:sum_changes, "node_ip", "$1", "node", "(.*)")`, + "etcd_server_proposals_failed_rate": `avg(etcd:etcd_server_proposals_failed:sum_irate)`, + "etcd_server_proposals_applied_rate": `avg(etcd:etcd_server_proposals_applied:sum_irate)`, + "etcd_server_proposals_committed_rate": `avg(etcd:etcd_server_proposals_committed:sum_irate)`, + "etcd_server_proposals_pending_count": `avg(etcd:etcd_server_proposals_pending:sum)`, + "etcd_mvcc_db_size": `avg(etcd:etcd_debugging_mvcc_db_total_size:sum)`, + "etcd_network_client_grpc_received_bytes": `sum(etcd:etcd_network_client_grpc_received_bytes:sum_irate)`, + "etcd_network_client_grpc_sent_bytes": `sum(etcd:etcd_network_client_grpc_sent_bytes:sum_irate)`, + "etcd_grpc_call_rate": `sum(etcd:grpc_server_started:sum_irate)`, + "etcd_grpc_call_failed_rate": `sum(etcd:grpc_server_handled:sum_irate)`, + "etcd_grpc_server_msg_received_rate": `sum(etcd:grpc_server_msg_received:sum_irate)`, + "etcd_grpc_server_msg_sent_rate": `sum(etcd:grpc_server_msg_sent:sum_irate)`, + "etcd_disk_wal_fsync_duration": `avg(etcd:etcd_disk_wal_fsync_duration:avg)`, + "etcd_disk_wal_fsync_duration_quantile": `avg(etcd:etcd_disk_wal_fsync_duration:histogram_quantile) by (quantile)`, + "etcd_disk_backend_commit_duration": `avg(etcd:etcd_disk_backend_commit_duration:avg)`, + "etcd_disk_backend_commit_duration_quantile": `avg(etcd:etcd_disk_backend_commit_duration:histogram_quantile) by (quantile)`, + + "apiserver_up_sum": `apiserver:up:sum`, + "apiserver_request_rate": `apiserver:apiserver_request_count:sum_irate`, + "apiserver_request_by_verb_rate": `apiserver:apiserver_request_count:sum_verb_irate`, + "apiserver_request_latencies": `apiserver:apiserver_request_latencies:avg`, + "apiserver_request_by_verb_latencies": `apiserver:apiserver_request_latencies:avg_by_verb`, + + "scheduler_up_sum": `scheduler:up:sum`, + "scheduler_schedule_attempts": `scheduler:scheduler_schedule_attempts:sum`, + "scheduler_schedule_attempt_rate": `scheduler:scheduler_schedule_attempts:sum_rate`, + "scheduler_e2e_scheduling_latency": `scheduler:scheduler_e2e_scheduling_latency:avg`, + "scheduler_e2e_scheduling_latency_quantile": `scheduler:scheduler_e2e_scheduling_latency:histogram_quantile`, + + "controller_manager_up_sum": `controller_manager:up:sum`, + + "coredns_up_sum": `coredns:up:sum`, + "coredns_cache_hits": `coredns:coredns_cache_hits_total:sum_irate`, + "coredns_cache_misses": `coredns:coredns_cache_misses:sum_irate`, + "coredns_dns_request_rate": `coredns:coredns_dns_request_count:sum_irate`, + "coredns_dns_request_duration": `coredns:coredns_dns_request_duration:avg`, + "coredns_dns_request_duration_quantile": `coredns:coredns_dns_request_duration:histogram_quantile`, + "coredns_dns_request_by_type_rate": `coredns:coredns_dns_request_type_count:sum_irate`, + "coredns_dns_request_by_rcode_rate": `coredns:coredns_dns_response_rcode_count:sum_irate`, + "coredns_panic_rate": `coredns:coredns_panic_count:sum_irate`, + "coredns_proxy_request_rate": `coredns:coredns_proxy_request_count:sum_irate`, + "coredns_proxy_request_duration": `coredns:coredns_proxy_request_duration:avg`, + "coredns_proxy_request_duration_quantile": `coredns:coredns_proxy_request_duration:histogram_quantile`, + + "prometheus_up_sum": `prometheus:up:sum`, + "prometheus_tsdb_head_samples_appended_rate": `prometheus:prometheus_tsdb_head_samples_appended:sum_rate`, +} + +func makeExpr(metric string, opts monitoring.QueryOptions) string { + tmpl := promQLTemplates[metric] + switch opts.Level { + case monitoring.LevelCluster: + return tmpl + case monitoring.LevelNode: + return makeNodeMetricExpr(tmpl, opts) + case monitoring.LevelWorkspace: + return makeWorkspaceMetricExpr(tmpl, opts) + case monitoring.LevelNamespace: + return makeNamespaceMetricExpr(tmpl, opts) + case monitoring.LevelWorkload: + return makeWorkloadMetricExpr(metric, tmpl, opts) + case monitoring.LevelPod: + return makePodMetricExpr(tmpl, opts) + case monitoring.LevelContainer: + return makeContainerMetricExpr(tmpl, opts) + case monitoring.LevelPVC: + return makePVCMetricExpr(tmpl, opts) + case monitoring.LevelComponent: + return tmpl + default: + return tmpl + } +} + +func makeNodeMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var nodeSelector string + if o.NodeName != "" { + nodeSelector = fmt.Sprintf(`node="%s"`, o.NodeName) + } else { + nodeSelector = fmt.Sprintf(`node=~"%s"`, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", nodeSelector, -1) +} + +func makeWorkspaceMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var workspaceSelector string + if o.WorkspaceName != "" { + workspaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace="%s"`, o.WorkspaceName) + } else { + workspaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace=~"%s", label_kubesphere_io_workspace!=""`, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", workspaceSelector, -1) +} + +func makeNamespaceMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var namespaceSelector string + + // For monitoring namespaces in the specific workspace + // GET /workspaces/{workspace}/namespaces + if o.WorkspaceName != "" { + namespaceSelector = fmt.Sprintf(`label_kubesphere_io_workspace="%s", namespace=~"%s"`, o.WorkspaceName, o.ResourceFilter) + return strings.Replace(tmpl, "$1", namespaceSelector, -1) + } + + // For monitoring the specific namespaces + // GET /namespaces/{namespace} or + // GET /namespaces + if o.NamespaceName != "" { + namespaceSelector = fmt.Sprintf(`namespace="%s"`, o.NamespaceName) + } else { + namespaceSelector = fmt.Sprintf(`namespace=~"%s"`, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", namespaceSelector, -1) +} + +func makeWorkloadMetricExpr(metric, tmpl string, o monitoring.QueryOptions) string { + var kindSelector, workloadSelector string + + switch o.WorkloadKind { + case "deployment": + o.WorkloadKind = Deployment + case "statefulset": + o.WorkloadKind = StatefulSet + case "daemonset": + o.WorkloadKind = DaemonSet + default: + o.WorkloadKind = ".*" + } + workloadSelector = fmt.Sprintf(`namespace="%s", workload=~"%s:%s"`, o.NamespaceName, o.WorkloadKind, o.ResourceFilter) + + if strings.Contains(metric, "deployment") { + kindSelector = fmt.Sprintf(`namespace="%s", deployment!="", deployment=~"%s"`, o.NamespaceName, o.ResourceFilter) + } + if strings.Contains(metric, "statefulset") { + kindSelector = fmt.Sprintf(`namespace="%s", statefulset!="", statefulset=~"%s"`, o.NamespaceName, o.ResourceFilter) + } + if strings.Contains(metric, "daemonset") { + kindSelector = fmt.Sprintf(`namespace="%s", daemonset!="", daemonset=~"%s"`, o.NamespaceName, o.ResourceFilter) + } + + return strings.NewReplacer("$1", workloadSelector, "$2", kindSelector).Replace(tmpl) +} + +func makePodMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var podSelector, workloadSelector string + + // For monitoriong pods of the specific workload + // GET /namespaces/{namespace}/workloads/{kind}/{workload}/pods + if o.WorkloadName != "" { + switch o.WorkloadKind { + case "deployment": + workloadSelector = fmt.Sprintf(`owner_kind="ReplicaSet", owner_name=~"^%s-[^-]{1,10}$"`, o.WorkloadName) + case "statefulset": + workloadSelector = fmt.Sprintf(`owner_kind="StatefulSet", owner_name="%s"`, o.WorkloadName) + case "daemonset": + workloadSelector = fmt.Sprintf(`owner_kind="DaemonSet", owner_name="%s"`, o.WorkloadName) + } + } + + // For monitoring pods in the specific namespace + // GET /namespaces/{namespace}/workloads/{kind}/{workload}/pods or + // GET /namespaces/{namespace}/pods/{pod} or + // GET /namespaces/{namespace}/pods + if o.NamespaceName != "" { + if o.PodName != "" { + podSelector = fmt.Sprintf(`pod="%s", namespace="%s"`, o.PodName, o.NamespaceName) + } else { + podSelector = fmt.Sprintf(`pod=~"%s", namespace="%s"`, o.ResourceFilter, o.NamespaceName) + } + } + + // For monitoring pods on the specific node + // GET /nodes/{node}/pods/{pod} + if o.NodeName != "" { + if o.PodName != "" { + podSelector = fmt.Sprintf(`pod="%s", node="%s"`, o.PodName, o.NodeName) + } else { + podSelector = fmt.Sprintf(`pod=~"%s", node="%s"`, o.ResourceFilter, o.NodeName) + } + } + return strings.NewReplacer("$1", workloadSelector, "$2", podSelector).Replace(tmpl) +} + +func makeContainerMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var containerSelector string + if o.ContainerName != "" { + containerSelector = fmt.Sprintf(`pod="%s", namespace="%s", container="%s"`, o.PodName, o.NamespaceName, o.ContainerName) + } else { + containerSelector = fmt.Sprintf(`pod="%s", namespace="%s", container=~"%s"`, o.PodName, o.NamespaceName, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", containerSelector, -1) +} + +func makePVCMetricExpr(tmpl string, o monitoring.QueryOptions) string { + var pvcSelector string + + // For monitoring persistentvolumeclaims in the specific namespace + // GET /namespaces/{namespace}/persistentvolumeclaims/{persistentvolumeclaim} or + // GET /namespaces/{namespace}/persistentvolumeclaims + if o.NamespaceName != "" { + if o.PersistentVolumeClaimName != "" { + pvcSelector = fmt.Sprintf(`namespace="%s", persistentvolumeclaim="%s"`, o.NamespaceName, o.PersistentVolumeClaimName) + } else { + pvcSelector = fmt.Sprintf(`namespace="%s", persistentvolumeclaim=~"%s"`, o.NamespaceName, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", pvcSelector, -1) + } + + // For monitoring persistentvolumeclaims of the specific storageclass + // GET /storageclasses/{storageclass}/persistentvolumeclaims + if o.StorageClassName != "" { + pvcSelector = fmt.Sprintf(`storageclass="%s", persistentvolumeclaim=~"%s"`, o.StorageClassName, o.ResourceFilter) + } + return strings.Replace(tmpl, "$1", pvcSelector, -1) +} diff --git a/pkg/simple/client/monitoring/prometheus/promql_test.go b/pkg/simple/client/monitoring/prometheus/promql_test.go new file mode 100644 index 000000000..dc66e6d07 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/promql_test.go @@ -0,0 +1,174 @@ +package prometheus + +import ( + "github.com/google/go-cmp/cmp" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring" + "kubesphere.io/kubesphere/pkg/simple/client/monitoring/prometheus/testdata" + "testing" +) + +func TestMakeExpr(t *testing.T) { + tests := []struct { + name string + opts monitoring.QueryOptions + }{ + { + name: "cluster_cpu_utilisation", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelCluster, + }, + }, + { + name: "node_cpu_utilisation", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelNode, + NodeName: "i-2dazc1d6", + }, + }, + { + name: "node_cpu_total", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelNode, + ResourceFilter: "i-2dazc1d6|i-ezjb7gsk", + }, + }, + { + name: "workspace_cpu_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelWorkspace, + WorkspaceName: "system-workspace", + }, + }, + { + name: "workspace_memory_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelWorkspace, + ResourceFilter: "system-workspace|demo", + }, + }, + { + name: "namespace_cpu_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelNamespace, + NamespaceName: "kube-system", + }, + }, + { + name: "namespace_memory_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelNamespace, + ResourceFilter: "kube-system|default", + }, + }, + { + name: "namespace_memory_usage_wo_cache", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelNamespace, + WorkspaceName: "system-workspace", + ResourceFilter: "kube-system|default", + }, + }, + { + name: "workload_cpu_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelWorkload, + WorkloadKind: "deployment", + NamespaceName: "default", + ResourceFilter: "apiserver|coredns", + }, + }, + { + name: "workload_deployment_replica_available", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelWorkload, + WorkloadKind: ".*", + NamespaceName: "default", + ResourceFilter: "apiserver|coredns", + }, + }, + { + name: "pod_cpu_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPod, + NamespaceName: "default", + WorkloadKind: "deployment", + WorkloadName: "elasticsearch", + ResourceFilter: "elasticsearch-0", + }, + }, + { + name: "pod_memory_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPod, + NamespaceName: "default", + PodName: "elasticsearch-12345", + }, + }, + { + name: "pod_memory_usage_wo_cache", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPod, + NodeName: "i-2dazc1d6", + PodName: "elasticsearch-12345", + }, + }, + { + name: "container_cpu_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelContainer, + NamespaceName: "default", + PodName: "elasticsearch-12345", + ContainerName: "syscall", + }, + }, + { + name: "container_memory_usage", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelContainer, + NamespaceName: "default", + PodName: "elasticsearch-12345", + ResourceFilter: "syscall", + }, + }, + { + name: "pvc_inodes_available", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPVC, + NamespaceName: "default", + PersistentVolumeClaimName: "db-123", + }, + }, + { + name: "pvc_inodes_used", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPVC, + NamespaceName: "default", + ResourceFilter: "db-123", + }, + }, + { + name: "pvc_inodes_total", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelPVC, + StorageClassName: "default", + ResourceFilter: "db-123", + }, + }, + { + name: "etcd_server_list", + opts: monitoring.QueryOptions{ + Level: monitoring.LevelComponent, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expected := testdata.PromQLs[tt.name] + result := makeExpr(tt.name, tt.opts) + if diff := cmp.Diff(result, expected); diff != "" { + t.Fatalf("%T differ (-got, +want): %s", expected, diff) + } + }) + } +} diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-prom.json b/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-prom.json new file mode 100644 index 000000000..1ffb57de7 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-prom.json @@ -0,0 +1,5 @@ +{ + "status":"error", + "errorType":"not_found", + "error":"specified metadata not found" +} \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-res.json b/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-res.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metadata-notfound-res.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metadata-prom.json b/pkg/simple/client/monitoring/prometheus/testdata/metadata-prom.json new file mode 100644 index 000000000..1ada466c0 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metadata-prom.json @@ -0,0 +1,35 @@ +{ + "status": "success", + "data": [ + { + "target": { + "instance": "127.0.0.1:9090", + "job": "prometheus" + }, + "metric": "prometheus_treecache_zookeeper_failures_total", + "type": "counter", + "help": "The total number of ZooKeeper failures.", + "unit": "" + }, + { + "target": { + "instance": "127.0.0.1:9090", + "job": "prometheus" + }, + "metric": "prometheus_tsdb_reloads_total", + "type": "counter", + "help": "Number of times the database reloaded block data from disk.", + "unit": "" + }, + { + "target": { + "instance": "127.0.0.1:9090", + "job": "prometheus" + }, + "metric": "prometheus_tsdb_reloads_total", + "type": "counter", + "help": "Number of times the database reloaded block data from disk.", + "unit": "" + } + ] +} \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metadata-res.json b/pkg/simple/client/monitoring/prometheus/testdata/metadata-res.json new file mode 100644 index 000000000..915a0646b --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metadata-res.json @@ -0,0 +1,12 @@ +[ + { + "metric": "prometheus_treecache_zookeeper_failures_total", + "type": "counter", + "help": "The total number of ZooKeeper failures." + }, + { + "metric": "prometheus_tsdb_reloads_total", + "type": "counter", + "help": "Number of times the database reloaded block data from disk." + } +] \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-prom.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-prom.json new file mode 100644 index 000000000..4a9dddf9b --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-prom.json @@ -0,0 +1,5 @@ +{ + "status":"error", + "errorType":"internal", + "error":"inconsistent body for response code" +} \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-res.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-res.json new file mode 100644 index 000000000..5bd92c3a5 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-error-res.json @@ -0,0 +1,6 @@ +[ + { + "metric_name": "cluster_cpu_utilisation", + "error": "bad_response: inconsistent body for response code" + } +] \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-prom.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-prom.json new file mode 100644 index 000000000..82ef6cb12 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-prom.json @@ -0,0 +1,206 @@ +{ + "status":"success", + "data":{ + "resultType":"matrix", + "result":[ + { + "metric":{ + "__name__":"up", + "endpoint":"https", + "instance":"192.168.2.2:9100", + "job":"node-exporter", + "namespace":"kubesphere-monitoring-system", + "pod":"node-exporter-nxpld", + "service":"node-exporter" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-main", + "instance":"10.233.99.18:8443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-metrics", + "instance":"192.168.2.2:10250", + "job":"kubelet", + "namespace":"kube-system", + "node":"ks-allinone", + "service":"kubelet" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-self", + "instance":"10.233.99.18:9443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"mysql-exporter", + "instance":"10.233.99.71:9104", + "job":"mysql-sz197k-prometheus-mysql-exporter", + "namespace":"exporter", + "pod":"mysql-sz197k-prometheus-mysql-exporter-5d58bc7d94-dh6r9", + "service":"mysql-sz197k-prometheus-mysql-exporter" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"web", + "instance":"10.233.99.22:9090", + "job":"prometheus-k8s-system", + "namespace":"kubesphere-monitoring-system", + "pod":"prometheus-k8s-system-0", + "service":"prometheus-k8s-system" + }, + "values":[ + [ + 1585743925, + "1.123456" + ], + [ + 1585744045, + "1.123456" + ], + [ + 1585744165, + "1.123456" + ], + [ + 1585744285, + "1.123456" + ], + [ + 1585744405, + "1.123456" + ] + ] + } + ] + } +} \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-res.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-res.json new file mode 100644 index 000000000..12ac864df --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-matrix-type-res.json @@ -0,0 +1,208 @@ +[ + { + "metric_name":"cluster_cpu_utilisation", + "data":{ + "resultType":"matrix", + "result":[ + { + "metric":{ + "__name__":"up", + "endpoint":"https", + "instance":"192.168.2.2:9100", + "job":"node-exporter", + "namespace":"kubesphere-monitoring-system", + "pod":"node-exporter-nxpld", + "service":"node-exporter" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-main", + "instance":"10.233.99.18:8443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-metrics", + "instance":"192.168.2.2:10250", + "job":"kubelet", + "namespace":"kube-system", + "node":"ks-allinone", + "service":"kubelet" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-self", + "instance":"10.233.99.18:9443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"mysql-exporter", + "instance":"10.233.99.71:9104", + "job":"mysql-sz197k-prometheus-mysql-exporter", + "namespace":"exporter", + "pod":"mysql-sz197k-prometheus-mysql-exporter-5d58bc7d94-dh6r9", + "service":"mysql-sz197k-prometheus-mysql-exporter" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"web", + "instance":"10.233.99.22:9090", + "job":"prometheus-k8s-system", + "namespace":"kubesphere-monitoring-system", + "pod":"prometheus-k8s-system-0", + "service":"prometheus-k8s-system" + }, + "values":[ + [ + 1585743925, + 1.123456 + ], + [ + 1585744045, + 1.123456 + ], + [ + 1585744165, + 1.123456 + ], + [ + 1585744285, + 1.123456 + ], + [ + 1585744405, + 1.123456 + ] + ] + } + ] + } + } +] \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-prom.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-prom.json new file mode 100644 index 000000000..9bc79077a --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-prom.json @@ -0,0 +1,68 @@ +{ + "status":"success", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "__name__":"up", + "endpoint":"https", + "instance":"192.168.2.2:9100", + "job":"node-exporter", + "namespace":"kubesphere-monitoring-system", + "pod":"node-exporter-nxpld", + "service":"node-exporter" + }, + "value":[ + 1585743854.077, + "1.123456" + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-main", + "instance":"10.233.99.18:8443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "value":[ + 1585743854.077, + "1.123456" + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-metrics", + "instance":"192.168.2.2:10250", + "job":"kubelet", + "namespace":"kube-system", + "node":"ks-allinone", + "service":"kubelet" + }, + "value":[ + 1585743854.077, + "1.123456" + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-self", + "instance":"10.233.99.18:9443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "value":[ + 1585743854.077, + "1.123456" + ] + } + ] + } +} \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-res.json b/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-res.json new file mode 100644 index 000000000..ff9e7cdff --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/metrics-vector-type-res.json @@ -0,0 +1,70 @@ +[ + { + "metric_name":"cluster_cpu_utilisation", + "data":{ + "resultType":"vector", + "result":[ + { + "metric":{ + "__name__":"up", + "endpoint":"https", + "instance":"192.168.2.2:9100", + "job":"node-exporter", + "namespace":"kubesphere-monitoring-system", + "pod":"node-exporter-nxpld", + "service":"node-exporter" + }, + "value":[ + 1585743854.077, + 1.123456 + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-main", + "instance":"10.233.99.18:8443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "value":[ + 1585743854.077, + 1.123456 + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-metrics", + "instance":"192.168.2.2:10250", + "job":"kubelet", + "namespace":"kube-system", + "node":"ks-allinone", + "service":"kubelet" + }, + "value":[ + 1585743854.077, + 1.123456 + ] + }, + { + "metric":{ + "__name__":"up", + "endpoint":"https-self", + "instance":"10.233.99.18:9443", + "job":"kube-state-metrics", + "namespace":"kubesphere-monitoring-system", + "pod":"kube-state-metrics-566cdbcb48-98brh", + "service":"kube-state-metrics" + }, + "value":[ + 1585743854.077, + 1.123456 + ] + } + ] + } + } +] \ No newline at end of file diff --git a/pkg/simple/client/monitoring/prometheus/testdata/promqls.go b/pkg/simple/client/monitoring/prometheus/testdata/promqls.go new file mode 100644 index 000000000..7a1e69401 --- /dev/null +++ b/pkg/simple/client/monitoring/prometheus/testdata/promqls.go @@ -0,0 +1,23 @@ +package testdata + +var PromQLs = map[string]string{ + "cluster_cpu_utilisation": `:node_cpu_utilisation:avg1m`, + "node_cpu_utilisation": `node:node_cpu_utilisation:avg1m{node="i-2dazc1d6"}`, + "node_cpu_total": `node:node_num_cpu:sum{node=~"i-2dazc1d6|i-ezjb7gsk"}`, + "workspace_cpu_usage": `round(sum by (label_kubesphere_io_workspace) (namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", label_kubesphere_io_workspace="system-workspace"}), 0.001)`, + "workspace_memory_usage": `sum by (label_kubesphere_io_workspace) (namespace:container_memory_usage_bytes:sum{namespace!="", label_kubesphere_io_workspace=~"system-workspace|demo", label_kubesphere_io_workspace!=""})`, + "namespace_cpu_usage": `round(namespace:container_cpu_usage_seconds_total:sum_rate{namespace!="", namespace="kube-system"}, 0.001)`, + "namespace_memory_usage": `namespace:container_memory_usage_bytes:sum{namespace!="", namespace=~"kube-system|default"}`, + "namespace_memory_usage_wo_cache": `namespace:container_memory_usage_bytes_wo_cache:sum{namespace!="", label_kubesphere_io_workspace="system-workspace", namespace=~"kube-system|default"}`, + "workload_cpu_usage": `round(namespace:workload_cpu_usage:sum{namespace="default", workload=~"Deployment:apiserver|coredns"}, 0.001)`, + "workload_deployment_replica_available": `label_join(sum (label_join(label_replace(kube_deployment_status_replicas_available{namespace="default", deployment!="", deployment=~"apiserver|coredns"}, "owner_kind", "Deployment", "", ""), "workload", "", "deployment")) by (namespace, owner_kind, workload), "workload", ":", "owner_kind", "workload")`, + "pod_cpu_usage": `round(sum by (namespace, pod) (irate(container_cpu_usage_seconds_total{job="kubelet", pod!="", image!=""}[5m])) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{owner_kind="ReplicaSet", owner_name=~"^elasticsearch-[^-]{1,10}$"} * on (namespace, pod) group_left(node) kube_pod_info{pod=~"elasticsearch-0", namespace="default"}, 0.001)`, + "pod_memory_usage": `sum by (namespace, pod) (container_memory_usage_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{} * on (namespace, pod) group_left(node) kube_pod_info{pod="elasticsearch-12345", namespace="default"}`, + "pod_memory_usage_wo_cache": `sum by (namespace, pod) (container_memory_working_set_bytes{job="kubelet", pod!="", image!=""}) * on (namespace, pod) group_left(owner_kind, owner_name) kube_pod_owner{} * on (namespace, pod) group_left(node) kube_pod_info{pod="elasticsearch-12345", node="i-2dazc1d6"}`, + "container_cpu_usage": `round(sum by (namespace, pod, container) (irate(container_cpu_usage_seconds_total{job="kubelet", container!="POD", container!="", image!="", pod="elasticsearch-12345", namespace="default", container="syscall"}[5m])), 0.001)`, + "container_memory_usage": `sum by (namespace, pod, container) (container_memory_usage_bytes{job="kubelet", container!="POD", container!="", image!="", pod="elasticsearch-12345", namespace="default", container=~"syscall"})`, + "pvc_inodes_available": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_free) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{namespace="default", persistentvolumeclaim="db-123"}`, + "pvc_inodes_used": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes_used) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{namespace="default", persistentvolumeclaim=~"db-123"}`, + "pvc_inodes_total": `max by (namespace, persistentvolumeclaim) (kubelet_volume_stats_inodes) * on (namespace, persistentvolumeclaim) group_left (storageclass) kube_persistentvolumeclaim_info{storageclass="default", persistentvolumeclaim=~"db-123"}`, + "etcd_server_list": `label_replace(up{job="etcd"}, "node_ip", "$1", "instance", "(.*):.*")`, +} diff --git a/pkg/simple/client/monitoring/query_options.go b/pkg/simple/client/monitoring/query_options.go new file mode 100644 index 000000000..089cd3be4 --- /dev/null +++ b/pkg/simple/client/monitoring/query_options.go @@ -0,0 +1,147 @@ +package monitoring + +type Level int + +const ( + LevelCluster = 1 << iota + LevelNode + LevelWorkspace + LevelNamespace + LevelWorkload + LevelPod + LevelContainer + LevelPVC + LevelComponent +) + +type QueryOption interface { + Apply(*QueryOptions) +} + +type QueryOptions struct { + Level Level + + ResourceFilter string + NodeName string + WorkspaceName string + NamespaceName string + WorkloadKind string + WorkloadName string + PodName string + ContainerName string + StorageClassName string + PersistentVolumeClaimName string +} + +func NewQueryOptions() *QueryOptions { + return &QueryOptions{} +} + +type ClusterOption struct{} + +func (_ ClusterOption) Apply(o *QueryOptions) { + o.Level = LevelCluster +} + +type NodeOption struct { + ResourceFilter string + NodeName string +} + +func (no NodeOption) Apply(o *QueryOptions) { + o.Level = LevelNode + o.ResourceFilter = no.ResourceFilter + o.NodeName = no.NodeName +} + +type WorkspaceOption struct { + ResourceFilter string + WorkspaceName string +} + +func (wo WorkspaceOption) Apply(o *QueryOptions) { + o.Level = LevelWorkspace + o.ResourceFilter = wo.ResourceFilter + o.WorkspaceName = wo.WorkspaceName +} + +type NamespaceOption struct { + ResourceFilter string + WorkspaceName string + NamespaceName string +} + +func (no NamespaceOption) Apply(o *QueryOptions) { + o.Level = LevelNamespace + o.ResourceFilter = no.ResourceFilter + o.WorkspaceName = no.WorkspaceName + o.NamespaceName = no.NamespaceName +} + +type WorkloadOption struct { + ResourceFilter string + NamespaceName string + WorkloadKind string +} + +func (wo WorkloadOption) Apply(o *QueryOptions) { + o.Level = LevelWorkload + o.ResourceFilter = wo.ResourceFilter + o.NamespaceName = wo.NamespaceName + o.WorkloadKind = wo.WorkloadKind +} + +type PodOption struct { + ResourceFilter string + NodeName string + NamespaceName string + WorkloadKind string + WorkloadName string + PodName string +} + +func (po PodOption) Apply(o *QueryOptions) { + o.Level = LevelPod + o.ResourceFilter = po.ResourceFilter + o.NodeName = po.NodeName + o.NamespaceName = po.NamespaceName + o.WorkloadKind = po.WorkloadKind + o.WorkloadName = po.WorkloadName + o.PodName = po.PodName +} + +type ContainerOption struct { + ResourceFilter string + NamespaceName string + PodName string + ContainerName string +} + +func (co ContainerOption) Apply(o *QueryOptions) { + o.Level = LevelContainer + o.ResourceFilter = co.ResourceFilter + o.NamespaceName = co.NamespaceName + o.PodName = co.PodName + o.ContainerName = co.ContainerName +} + +type PVCOption struct { + ResourceFilter string + NamespaceName string + StorageClassName string + PersistentVolumeClaimName string +} + +func (po PVCOption) Apply(o *QueryOptions) { + o.Level = LevelPVC + o.ResourceFilter = po.ResourceFilter + o.NamespaceName = po.NamespaceName + o.StorageClassName = po.StorageClassName + o.PersistentVolumeClaimName = po.PersistentVolumeClaimName +} + +type ComponentOption struct{} + +func (_ ComponentOption) Apply(o *QueryOptions) { + o.Level = LevelComponent +} diff --git a/pkg/simple/client/monitoring/types.go b/pkg/simple/client/monitoring/types.go new file mode 100644 index 000000000..238289427 --- /dev/null +++ b/pkg/simple/client/monitoring/types.go @@ -0,0 +1,42 @@ +package monitoring + +const ( + MetricTypeMatrix = "matrix" + MetricTypeVector = "vector" +) + +type Metadata struct { + Metric string `json:"metric,omitempty" description:"metric name"` + Type string `json:"type,omitempty" description:"metric type"` + Help string `json:"help,omitempty" description:"metric description"` +} + +type Metric struct { + MetricName string `json:"metric_name,omitempty" description:"metric name, eg. scheduler_up_sum"` + MetricData `json:"data,omitempty" description:"actual metric result"` + Error string `json:"error,omitempty"` +} + +type MetricData struct { + MetricType string `json:"resultType,omitempty" description:"result type, one of matrix, vector"` + MetricValues []MetricValue `json:"result,omitempty" description:"metric data including labels, time series and values"` +} + +type Point [2]float64 + +type MetricValue struct { + Metadata map[string]string `json:"metric,omitempty" description:"time series labels"` + // The type of Point is a float64 array with fixed length of 2. + // So Point will always be initialized as [0, 0], rather than nil. + // To allow empty Sample, we should declare Sample to type *Point + Sample *Point `json:"value,omitempty" description:"time series, values of vector type"` + Series []Point `json:"values,omitempty" description:"time series, values of matrix type"` +} + +func (p Point) Timestamp() float64 { + return p[0] +} + +func (p Point) Value() float64 { + return p[1] +} diff --git a/pkg/simple/client/multicluster/options.go b/pkg/simple/client/multicluster/options.go new file mode 100644 index 000000000..9d09febbc --- /dev/null +++ b/pkg/simple/client/multicluster/options.go @@ -0,0 +1,26 @@ +package multicluster + +import "github.com/spf13/pflag" + +type Options struct { + // Enable + Enable bool `json:"enable"` + EnableFederation bool `json:"enableFederation,omitempty"` +} + +// NewOptions() returns a default nil options +func NewOptions() *Options { + return &Options{ + Enable: false, + EnableFederation: false, + } +} + +func (o *Options) Validate() []error { + return nil +} + +func (o *Options) AddFlags(fs *pflag.FlagSet, s *Options) { + fs.BoolVar(&o.Enable, "multiple-clusters", s.Enable, ""+ + "This field instructs KubeSphere to enter multiple-cluster mode or not.") +} diff --git a/pkg/simple/client/mysql/mysql.go b/pkg/simple/client/mysql/mysql.go index dfedf2beb..1a68d50b7 100644 --- a/pkg/simple/client/mysql/mysql.go +++ b/pkg/simple/client/mysql/mysql.go @@ -19,12 +19,12 @@ import ( "k8s.io/klog" ) -type MySQLClient struct { +type Client struct { database *Database } -func NewMySQLClient(options *MySQLOptions, stopCh <-chan struct{}) (*MySQLClient, error) { - var m MySQLClient +func NewMySQLClient(options *Options, stopCh <-chan struct{}) (*Client, error) { + var m Client conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/devops?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", options.Username, options.Password, options.Host), nil) if err != nil { @@ -50,8 +50,8 @@ func NewMySQLClient(options *MySQLOptions, stopCh <-chan struct{}) (*MySQLClient return &m, nil } -func NewMySQLClientOrDie(options *MySQLOptions, stopCh <-chan struct{}) *MySQLClient { - var m MySQLClient +func NewMySQLClientOrDie(options *Options, stopCh <-chan struct{}) *Client { + var m Client conn, err := dbr.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/devops?parseTime=1&multiStatements=1&charset=utf8mb4&collation=utf8mb4_unicode_ci", options.Username, options.Password, options.Host), nil) if err != nil { @@ -77,6 +77,6 @@ func NewMySQLClientOrDie(options *MySQLOptions, stopCh <-chan struct{}) *MySQLCl return &m } -func (m *MySQLClient) Database() *Database { +func (m *Client) Database() *Database { return m.database } diff --git a/pkg/simple/client/mysql/options.go b/pkg/simple/client/mysql/options.go index 359b6ad9d..e17b27300 100644 --- a/pkg/simple/client/mysql/options.go +++ b/pkg/simple/client/mysql/options.go @@ -2,11 +2,11 @@ package mysql import ( "github.com/spf13/pflag" - reflectutils "kubesphere.io/kubesphere/pkg/utils/reflectutils" + "kubesphere.io/kubesphere/pkg/utils/reflectutils" "time" ) -type MySQLOptions struct { +type Options struct { Host string `json:"host,omitempty" yaml:"host" description:"MySQL service host address"` Username string `json:"username,omitempty" yaml:"username"` Password string `json:"-" yaml:"password"` @@ -16,8 +16,8 @@ type MySQLOptions struct { } // NewMySQLOptions create a `zero` value instance -func NewMySQLOptions() *MySQLOptions { - return &MySQLOptions{ +func NewMySQLOptions() *Options { + return &Options{ Host: "", Username: "", Password: "", @@ -27,33 +27,33 @@ func NewMySQLOptions() *MySQLOptions { } } -func (m *MySQLOptions) Validate() []error { - errors := []error{} +func (m *Options) Validate() []error { + var errors []error return errors } -func (m *MySQLOptions) ApplyTo(options *MySQLOptions) { +func (m *Options) ApplyTo(options *Options) { reflectutils.Override(options, m) } -func (m *MySQLOptions) AddFlags(fs *pflag.FlagSet) { +func (m *Options) AddFlags(fs *pflag.FlagSet, c *Options) { - fs.StringVar(&m.Host, "mysql-host", m.Host, ""+ + fs.StringVar(&m.Host, "mysql-host", c.Host, ""+ "MySQL service host address. If left blank, the following related mysql options will be ignored.") - fs.StringVar(&m.Username, "mysql-username", m.Username, ""+ + fs.StringVar(&m.Username, "mysql-username", c.Username, ""+ "Username for access to mysql service.") - fs.StringVar(&m.Password, "mysql-password", m.Password, ""+ + fs.StringVar(&m.Password, "mysql-password", c.Password, ""+ "Password for access to mysql, should be used pair with password.") - fs.IntVar(&m.MaxIdleConnections, "mysql-max-idle-connections", m.MaxOpenConnections, ""+ + fs.IntVar(&m.MaxIdleConnections, "mysql-max-idle-connections", c.MaxOpenConnections, ""+ "Maximum idle connections allowed to connect to mysql.") - fs.IntVar(&m.MaxOpenConnections, "mysql-max-open-connections", m.MaxOpenConnections, ""+ + fs.IntVar(&m.MaxOpenConnections, "mysql-max-open-connections", c.MaxOpenConnections, ""+ "Maximum open connections allowed to connect to mysql.") - fs.DurationVar(&m.MaxConnectionLifeTime, "mysql-max-connection-life-time", m.MaxConnectionLifeTime, ""+ + fs.DurationVar(&m.MaxConnectionLifeTime, "mysql-max-connection-life-time", c.MaxConnectionLifeTime, ""+ "Maximum connection life time allowed to connecto to mysql.") } diff --git a/pkg/simple/client/network/options.go b/pkg/simple/client/network/options.go new file mode 100644 index 000000000..3bc42e1d6 --- /dev/null +++ b/pkg/simple/client/network/options.go @@ -0,0 +1,32 @@ +package network + +import "github.com/spf13/pflag" + +type Options struct { + + // weave scope service host + WeaveScopeHost string `json:"weaveScopeHost,omitempty" yaml:"weaveScopeHost"` +} + +// NewNetworkOptions returns a `zero` instance +func NewNetworkOptions() *Options { + return &Options{ + WeaveScopeHost: "weave-scope-app.weave.svc", + } +} + +func (s *Options) Validate() []error { + var errors []error + return errors +} + +func (s *Options) ApplyTo(options *Options) { + if s.WeaveScopeHost != "" { + options.WeaveScopeHost = s.WeaveScopeHost + } +} + +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.WeaveScopeHost, "weave-scope-host", c.WeaveScopeHost, ""+ + "weave scope service host") +} diff --git a/pkg/simple/client/notification/options.go b/pkg/simple/client/notification/options.go index 29c97906a..e4df715f9 100644 --- a/pkg/simple/client/notification/options.go +++ b/pkg/simple/client/notification/options.go @@ -1,16 +1,16 @@ package notification -type NotificationOptions struct { +type Options struct { Endpoint string } -func NewNotificationOptions() *NotificationOptions { - return &NotificationOptions{ +func NewNotificationOptions() *Options { + return &Options{ Endpoint: "", } } -func (s *NotificationOptions) ApplyTo(options *NotificationOptions) { +func (s *Options) ApplyTo(options *Options) { if options == nil { options = s return diff --git a/pkg/simple/client/openpitrix/mock.go b/pkg/simple/client/openpitrix/mock.go new file mode 100644 index 000000000..dc4d51e50 --- /dev/null +++ b/pkg/simple/client/openpitrix/mock.go @@ -0,0 +1,2017 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/simple/client/openpitrix/openpitrix.go + +// Package openpitrix is a generated GoMock package. +package openpitrix + +import ( + context "context" + gomock "github.com/golang/mock/gomock" + empty "github.com/golang/protobuf/ptypes/empty" + grpc "google.golang.org/grpc" + pb "openpitrix.io/openpitrix/pkg/pb" + reflect "reflect" +) + +// MockClient is a mock of Client interface +type MockClient struct { + ctrl *gomock.Controller + recorder *MockClientMockRecorder +} + +// MockClientMockRecorder is the mock recorder for MockClient +type MockClientMockRecorder struct { + mock *MockClient +} + +// NewMockClient creates a new mock instance +func NewMockClient(ctrl *gomock.Controller) *MockClient { + mock := &MockClient{ctrl: ctrl} + mock.recorder = &MockClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockClient) EXPECT() *MockClientMockRecorder { + return m.recorder +} + +// CreateRuntime mocks base method +func (m *MockClient) CreateRuntime(ctx context.Context, in *pb.CreateRuntimeRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRuntime", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRuntime indicates an expected call of CreateRuntime +func (mr *MockClientMockRecorder) CreateRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRuntime", reflect.TypeOf((*MockClient)(nil).CreateRuntime), varargs...) +} + +// CreateDebugRuntime mocks base method +func (m *MockClient) CreateDebugRuntime(ctx context.Context, in *pb.CreateRuntimeRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugRuntime", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugRuntime indicates an expected call of CreateDebugRuntime +func (mr *MockClientMockRecorder) CreateDebugRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugRuntime", reflect.TypeOf((*MockClient)(nil).CreateDebugRuntime), varargs...) +} + +// DescribeRuntimeDetails mocks base method +func (m *MockClient) DescribeRuntimeDetails(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeDetailsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeDetails", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeDetailsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeDetails indicates an expected call of DescribeRuntimeDetails +func (mr *MockClientMockRecorder) DescribeRuntimeDetails(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeDetails", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeDetails), varargs...) +} + +// DescribeRuntimes mocks base method +func (m *MockClient) DescribeRuntimes(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimes indicates an expected call of DescribeRuntimes +func (mr *MockClientMockRecorder) DescribeRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimes", reflect.TypeOf((*MockClient)(nil).DescribeRuntimes), varargs...) +} + +// DescribeDebugRuntimes mocks base method +func (m *MockClient) DescribeDebugRuntimes(ctx context.Context, in *pb.DescribeRuntimesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugRuntimes indicates an expected call of DescribeDebugRuntimes +func (mr *MockClientMockRecorder) DescribeDebugRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugRuntimes", reflect.TypeOf((*MockClient)(nil).DescribeDebugRuntimes), varargs...) +} + +// ModifyRuntime mocks base method +func (m *MockClient) ModifyRuntime(ctx context.Context, in *pb.ModifyRuntimeRequest, opts ...grpc.CallOption) (*pb.ModifyRuntimeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRuntime", varargs...) + ret0, _ := ret[0].(*pb.ModifyRuntimeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRuntime indicates an expected call of ModifyRuntime +func (mr *MockClientMockRecorder) ModifyRuntime(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRuntime", reflect.TypeOf((*MockClient)(nil).ModifyRuntime), varargs...) +} + +// DeleteRuntimes mocks base method +func (m *MockClient) DeleteRuntimes(ctx context.Context, in *pb.DeleteRuntimesRequest, opts ...grpc.CallOption) (*pb.DeleteRuntimesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRuntimes", varargs...) + ret0, _ := ret[0].(*pb.DeleteRuntimesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRuntimes indicates an expected call of DeleteRuntimes +func (mr *MockClientMockRecorder) DeleteRuntimes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRuntimes", reflect.TypeOf((*MockClient)(nil).DeleteRuntimes), varargs...) +} + +// CreateRuntimeCredential mocks base method +func (m *MockClient) CreateRuntimeCredential(ctx context.Context, in *pb.CreateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRuntimeCredential indicates an expected call of CreateRuntimeCredential +func (mr *MockClientMockRecorder) CreateRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRuntimeCredential", reflect.TypeOf((*MockClient)(nil).CreateRuntimeCredential), varargs...) +} + +// CreateDebugRuntimeCredential mocks base method +func (m *MockClient) CreateDebugRuntimeCredential(ctx context.Context, in *pb.CreateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.CreateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.CreateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugRuntimeCredential indicates an expected call of CreateDebugRuntimeCredential +func (mr *MockClientMockRecorder) CreateDebugRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugRuntimeCredential", reflect.TypeOf((*MockClient)(nil).CreateDebugRuntimeCredential), varargs...) +} + +// DescribeRuntimeCredentials mocks base method +func (m *MockClient) DescribeRuntimeCredentials(ctx context.Context, in *pb.DescribeRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeCredentials indicates an expected call of DescribeRuntimeCredentials +func (mr *MockClientMockRecorder) DescribeRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeCredentials), varargs...) +} + +// DescribeDebugRuntimeCredentials mocks base method +func (m *MockClient) DescribeDebugRuntimeCredentials(ctx context.Context, in *pb.DescribeRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugRuntimeCredentials indicates an expected call of DescribeDebugRuntimeCredentials +func (mr *MockClientMockRecorder) DescribeDebugRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DescribeDebugRuntimeCredentials), varargs...) +} + +// ModifyRuntimeCredential mocks base method +func (m *MockClient) ModifyRuntimeCredential(ctx context.Context, in *pb.ModifyRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.ModifyRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.ModifyRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRuntimeCredential indicates an expected call of ModifyRuntimeCredential +func (mr *MockClientMockRecorder) ModifyRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRuntimeCredential", reflect.TypeOf((*MockClient)(nil).ModifyRuntimeCredential), varargs...) +} + +// DeleteRuntimeCredentials mocks base method +func (m *MockClient) DeleteRuntimeCredentials(ctx context.Context, in *pb.DeleteRuntimeCredentialsRequest, opts ...grpc.CallOption) (*pb.DeleteRuntimeCredentialsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRuntimeCredentials", varargs...) + ret0, _ := ret[0].(*pb.DeleteRuntimeCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRuntimeCredentials indicates an expected call of DeleteRuntimeCredentials +func (mr *MockClientMockRecorder) DeleteRuntimeCredentials(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRuntimeCredentials", reflect.TypeOf((*MockClient)(nil).DeleteRuntimeCredentials), varargs...) +} + +// ValidateRuntimeCredential mocks base method +func (m *MockClient) ValidateRuntimeCredential(ctx context.Context, in *pb.ValidateRuntimeCredentialRequest, opts ...grpc.CallOption) (*pb.ValidateRuntimeCredentialResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateRuntimeCredential", varargs...) + ret0, _ := ret[0].(*pb.ValidateRuntimeCredentialResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateRuntimeCredential indicates an expected call of ValidateRuntimeCredential +func (mr *MockClientMockRecorder) ValidateRuntimeCredential(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRuntimeCredential", reflect.TypeOf((*MockClient)(nil).ValidateRuntimeCredential), varargs...) +} + +// DescribeRuntimeProviderZones mocks base method +func (m *MockClient) DescribeRuntimeProviderZones(ctx context.Context, in *pb.DescribeRuntimeProviderZonesRequest, opts ...grpc.CallOption) (*pb.DescribeRuntimeProviderZonesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRuntimeProviderZones", varargs...) + ret0, _ := ret[0].(*pb.DescribeRuntimeProviderZonesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRuntimeProviderZones indicates an expected call of DescribeRuntimeProviderZones +func (mr *MockClientMockRecorder) DescribeRuntimeProviderZones(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRuntimeProviderZones", reflect.TypeOf((*MockClient)(nil).DescribeRuntimeProviderZones), varargs...) +} + +// GetRuntimeStatistics mocks base method +func (m *MockClient) GetRuntimeStatistics(ctx context.Context, in *pb.GetRuntimeStatisticsRequest, opts ...grpc.CallOption) (*pb.GetRuntimeStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetRuntimeStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetRuntimeStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRuntimeStatistics indicates an expected call of GetRuntimeStatistics +func (mr *MockClientMockRecorder) GetRuntimeStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRuntimeStatistics", reflect.TypeOf((*MockClient)(nil).GetRuntimeStatistics), varargs...) +} + +// AddNodeKeyPairs mocks base method +func (m *MockClient) AddNodeKeyPairs(ctx context.Context, in *pb.AddNodeKeyPairsRequest, opts ...grpc.CallOption) (*pb.AddNodeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddNodeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.AddNodeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddNodeKeyPairs indicates an expected call of AddNodeKeyPairs +func (mr *MockClientMockRecorder) AddNodeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddNodeKeyPairs", reflect.TypeOf((*MockClient)(nil).AddNodeKeyPairs), varargs...) +} + +// DeleteNodeKeyPairs mocks base method +func (m *MockClient) DeleteNodeKeyPairs(ctx context.Context, in *pb.DeleteNodeKeyPairsRequest, opts ...grpc.CallOption) (*pb.DeleteNodeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteNodeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DeleteNodeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteNodeKeyPairs indicates an expected call of DeleteNodeKeyPairs +func (mr *MockClientMockRecorder) DeleteNodeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteNodeKeyPairs", reflect.TypeOf((*MockClient)(nil).DeleteNodeKeyPairs), varargs...) +} + +// CreateKeyPair mocks base method +func (m *MockClient) CreateKeyPair(ctx context.Context, in *pb.CreateKeyPairRequest, opts ...grpc.CallOption) (*pb.CreateKeyPairResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateKeyPair", varargs...) + ret0, _ := ret[0].(*pb.CreateKeyPairResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateKeyPair indicates an expected call of CreateKeyPair +func (mr *MockClientMockRecorder) CreateKeyPair(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateKeyPair", reflect.TypeOf((*MockClient)(nil).CreateKeyPair), varargs...) +} + +// DescribeKeyPairs mocks base method +func (m *MockClient) DescribeKeyPairs(ctx context.Context, in *pb.DescribeKeyPairsRequest, opts ...grpc.CallOption) (*pb.DescribeKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DescribeKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeKeyPairs indicates an expected call of DescribeKeyPairs +func (mr *MockClientMockRecorder) DescribeKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeKeyPairs", reflect.TypeOf((*MockClient)(nil).DescribeKeyPairs), varargs...) +} + +// DeleteKeyPairs mocks base method +func (m *MockClient) DeleteKeyPairs(ctx context.Context, in *pb.DeleteKeyPairsRequest, opts ...grpc.CallOption) (*pb.DeleteKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DeleteKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteKeyPairs indicates an expected call of DeleteKeyPairs +func (mr *MockClientMockRecorder) DeleteKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteKeyPairs", reflect.TypeOf((*MockClient)(nil).DeleteKeyPairs), varargs...) +} + +// AttachKeyPairs mocks base method +func (m *MockClient) AttachKeyPairs(ctx context.Context, in *pb.AttachKeyPairsRequest, opts ...grpc.CallOption) (*pb.AttachKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AttachKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.AttachKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AttachKeyPairs indicates an expected call of AttachKeyPairs +func (mr *MockClientMockRecorder) AttachKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachKeyPairs", reflect.TypeOf((*MockClient)(nil).AttachKeyPairs), varargs...) +} + +// DetachKeyPairs mocks base method +func (m *MockClient) DetachKeyPairs(ctx context.Context, in *pb.DetachKeyPairsRequest, opts ...grpc.CallOption) (*pb.DetachKeyPairsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DetachKeyPairs", varargs...) + ret0, _ := ret[0].(*pb.DetachKeyPairsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DetachKeyPairs indicates an expected call of DetachKeyPairs +func (mr *MockClientMockRecorder) DetachKeyPairs(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachKeyPairs", reflect.TypeOf((*MockClient)(nil).DetachKeyPairs), varargs...) +} + +// DescribeSubnets mocks base method +func (m *MockClient) DescribeSubnets(ctx context.Context, in *pb.DescribeSubnetsRequest, opts ...grpc.CallOption) (*pb.DescribeSubnetsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeSubnets", varargs...) + ret0, _ := ret[0].(*pb.DescribeSubnetsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeSubnets indicates an expected call of DescribeSubnets +func (mr *MockClientMockRecorder) DescribeSubnets(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeSubnets", reflect.TypeOf((*MockClient)(nil).DescribeSubnets), varargs...) +} + +// CreateCluster mocks base method +func (m *MockClient) CreateCluster(ctx context.Context, in *pb.CreateClusterRequest, opts ...grpc.CallOption) (*pb.CreateClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateCluster", varargs...) + ret0, _ := ret[0].(*pb.CreateClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCluster indicates an expected call of CreateCluster +func (mr *MockClientMockRecorder) CreateCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCluster", reflect.TypeOf((*MockClient)(nil).CreateCluster), varargs...) +} + +// CreateDebugCluster mocks base method +func (m *MockClient) CreateDebugCluster(ctx context.Context, in *pb.CreateClusterRequest, opts ...grpc.CallOption) (*pb.CreateClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateDebugCluster", varargs...) + ret0, _ := ret[0].(*pb.CreateClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateDebugCluster indicates an expected call of CreateDebugCluster +func (mr *MockClientMockRecorder) CreateDebugCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDebugCluster", reflect.TypeOf((*MockClient)(nil).CreateDebugCluster), varargs...) +} + +// ModifyCluster mocks base method +func (m *MockClient) ModifyCluster(ctx context.Context, in *pb.ModifyClusterRequest, opts ...grpc.CallOption) (*pb.ModifyClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyCluster", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyCluster indicates an expected call of ModifyCluster +func (mr *MockClientMockRecorder) ModifyCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyCluster", reflect.TypeOf((*MockClient)(nil).ModifyCluster), varargs...) +} + +// ModifyClusterNode mocks base method +func (m *MockClient) ModifyClusterNode(ctx context.Context, in *pb.ModifyClusterNodeRequest, opts ...grpc.CallOption) (*pb.ModifyClusterNodeResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterNode", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterNodeResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterNode indicates an expected call of ModifyClusterNode +func (mr *MockClientMockRecorder) ModifyClusterNode(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterNode", reflect.TypeOf((*MockClient)(nil).ModifyClusterNode), varargs...) +} + +// ModifyClusterAttributes mocks base method +func (m *MockClient) ModifyClusterAttributes(ctx context.Context, in *pb.ModifyClusterAttributesRequest, opts ...grpc.CallOption) (*pb.ModifyClusterAttributesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterAttributes", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterAttributesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterAttributes indicates an expected call of ModifyClusterAttributes +func (mr *MockClientMockRecorder) ModifyClusterAttributes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterAttributes", reflect.TypeOf((*MockClient)(nil).ModifyClusterAttributes), varargs...) +} + +// ModifyClusterNodeAttributes mocks base method +func (m *MockClient) ModifyClusterNodeAttributes(ctx context.Context, in *pb.ModifyClusterNodeAttributesRequest, opts ...grpc.CallOption) (*pb.ModifyClusterNodeAttributesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyClusterNodeAttributes", varargs...) + ret0, _ := ret[0].(*pb.ModifyClusterNodeAttributesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyClusterNodeAttributes indicates an expected call of ModifyClusterNodeAttributes +func (mr *MockClientMockRecorder) ModifyClusterNodeAttributes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyClusterNodeAttributes", reflect.TypeOf((*MockClient)(nil).ModifyClusterNodeAttributes), varargs...) +} + +// AddTableClusterNodes mocks base method +func (m *MockClient) AddTableClusterNodes(ctx context.Context, in *pb.AddTableClusterNodesRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddTableClusterNodes", varargs...) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddTableClusterNodes indicates an expected call of AddTableClusterNodes +func (mr *MockClientMockRecorder) AddTableClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTableClusterNodes", reflect.TypeOf((*MockClient)(nil).AddTableClusterNodes), varargs...) +} + +// DeleteTableClusterNodes mocks base method +func (m *MockClient) DeleteTableClusterNodes(ctx context.Context, in *pb.DeleteTableClusterNodesRequest, opts ...grpc.CallOption) (*empty.Empty, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteTableClusterNodes", varargs...) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteTableClusterNodes indicates an expected call of DeleteTableClusterNodes +func (mr *MockClientMockRecorder) DeleteTableClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTableClusterNodes", reflect.TypeOf((*MockClient)(nil).DeleteTableClusterNodes), varargs...) +} + +// DeleteClusters mocks base method +func (m *MockClient) DeleteClusters(ctx context.Context, in *pb.DeleteClustersRequest, opts ...grpc.CallOption) (*pb.DeleteClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteClusters", varargs...) + ret0, _ := ret[0].(*pb.DeleteClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteClusters indicates an expected call of DeleteClusters +func (mr *MockClientMockRecorder) DeleteClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClusters", reflect.TypeOf((*MockClient)(nil).DeleteClusters), varargs...) +} + +// UpgradeCluster mocks base method +func (m *MockClient) UpgradeCluster(ctx context.Context, in *pb.UpgradeClusterRequest, opts ...grpc.CallOption) (*pb.UpgradeClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpgradeCluster", varargs...) + ret0, _ := ret[0].(*pb.UpgradeClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpgradeCluster indicates an expected call of UpgradeCluster +func (mr *MockClientMockRecorder) UpgradeCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpgradeCluster", reflect.TypeOf((*MockClient)(nil).UpgradeCluster), varargs...) +} + +// RollbackCluster mocks base method +func (m *MockClient) RollbackCluster(ctx context.Context, in *pb.RollbackClusterRequest, opts ...grpc.CallOption) (*pb.RollbackClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RollbackCluster", varargs...) + ret0, _ := ret[0].(*pb.RollbackClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RollbackCluster indicates an expected call of RollbackCluster +func (mr *MockClientMockRecorder) RollbackCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RollbackCluster", reflect.TypeOf((*MockClient)(nil).RollbackCluster), varargs...) +} + +// ResizeCluster mocks base method +func (m *MockClient) ResizeCluster(ctx context.Context, in *pb.ResizeClusterRequest, opts ...grpc.CallOption) (*pb.ResizeClusterResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ResizeCluster", varargs...) + ret0, _ := ret[0].(*pb.ResizeClusterResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ResizeCluster indicates an expected call of ResizeCluster +func (mr *MockClientMockRecorder) ResizeCluster(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResizeCluster", reflect.TypeOf((*MockClient)(nil).ResizeCluster), varargs...) +} + +// AddClusterNodes mocks base method +func (m *MockClient) AddClusterNodes(ctx context.Context, in *pb.AddClusterNodesRequest, opts ...grpc.CallOption) (*pb.AddClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AddClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.AddClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AddClusterNodes indicates an expected call of AddClusterNodes +func (mr *MockClientMockRecorder) AddClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddClusterNodes", reflect.TypeOf((*MockClient)(nil).AddClusterNodes), varargs...) +} + +// DeleteClusterNodes mocks base method +func (m *MockClient) DeleteClusterNodes(ctx context.Context, in *pb.DeleteClusterNodesRequest, opts ...grpc.CallOption) (*pb.DeleteClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.DeleteClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteClusterNodes indicates an expected call of DeleteClusterNodes +func (mr *MockClientMockRecorder) DeleteClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteClusterNodes", reflect.TypeOf((*MockClient)(nil).DeleteClusterNodes), varargs...) +} + +// UpdateClusterEnv mocks base method +func (m *MockClient) UpdateClusterEnv(ctx context.Context, in *pb.UpdateClusterEnvRequest, opts ...grpc.CallOption) (*pb.UpdateClusterEnvResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UpdateClusterEnv", varargs...) + ret0, _ := ret[0].(*pb.UpdateClusterEnvResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateClusterEnv indicates an expected call of UpdateClusterEnv +func (mr *MockClientMockRecorder) UpdateClusterEnv(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateClusterEnv", reflect.TypeOf((*MockClient)(nil).UpdateClusterEnv), varargs...) +} + +// DescribeClusters mocks base method +func (m *MockClient) DescribeClusters(ctx context.Context, in *pb.DescribeClustersRequest, opts ...grpc.CallOption) (*pb.DescribeClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeClusters indicates an expected call of DescribeClusters +func (mr *MockClientMockRecorder) DescribeClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeClusters", reflect.TypeOf((*MockClient)(nil).DescribeClusters), varargs...) +} + +// DescribeDebugClusters mocks base method +func (m *MockClient) DescribeDebugClusters(ctx context.Context, in *pb.DescribeClustersRequest, opts ...grpc.CallOption) (*pb.DescribeClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugClusters indicates an expected call of DescribeDebugClusters +func (mr *MockClientMockRecorder) DescribeDebugClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugClusters", reflect.TypeOf((*MockClient)(nil).DescribeDebugClusters), varargs...) +} + +// DescribeAppClusters mocks base method +func (m *MockClient) DescribeAppClusters(ctx context.Context, in *pb.DescribeAppClustersRequest, opts ...grpc.CallOption) (*pb.DescribeAppClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppClusters indicates an expected call of DescribeAppClusters +func (mr *MockClientMockRecorder) DescribeAppClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppClusters", reflect.TypeOf((*MockClient)(nil).DescribeAppClusters), varargs...) +} + +// DescribeDebugAppClusters mocks base method +func (m *MockClient) DescribeDebugAppClusters(ctx context.Context, in *pb.DescribeAppClustersRequest, opts ...grpc.CallOption) (*pb.DescribeAppClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeDebugAppClusters", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeDebugAppClusters indicates an expected call of DescribeDebugAppClusters +func (mr *MockClientMockRecorder) DescribeDebugAppClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeDebugAppClusters", reflect.TypeOf((*MockClient)(nil).DescribeDebugAppClusters), varargs...) +} + +// DescribeClusterNodes mocks base method +func (m *MockClient) DescribeClusterNodes(ctx context.Context, in *pb.DescribeClusterNodesRequest, opts ...grpc.CallOption) (*pb.DescribeClusterNodesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeClusterNodes", varargs...) + ret0, _ := ret[0].(*pb.DescribeClusterNodesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeClusterNodes indicates an expected call of DescribeClusterNodes +func (mr *MockClientMockRecorder) DescribeClusterNodes(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeClusterNodes", reflect.TypeOf((*MockClient)(nil).DescribeClusterNodes), varargs...) +} + +// StopClusters mocks base method +func (m *MockClient) StopClusters(ctx context.Context, in *pb.StopClustersRequest, opts ...grpc.CallOption) (*pb.StopClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StopClusters", varargs...) + ret0, _ := ret[0].(*pb.StopClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StopClusters indicates an expected call of StopClusters +func (mr *MockClientMockRecorder) StopClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StopClusters", reflect.TypeOf((*MockClient)(nil).StopClusters), varargs...) +} + +// StartClusters mocks base method +func (m *MockClient) StartClusters(ctx context.Context, in *pb.StartClustersRequest, opts ...grpc.CallOption) (*pb.StartClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "StartClusters", varargs...) + ret0, _ := ret[0].(*pb.StartClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StartClusters indicates an expected call of StartClusters +func (mr *MockClientMockRecorder) StartClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartClusters", reflect.TypeOf((*MockClient)(nil).StartClusters), varargs...) +} + +// RecoverClusters mocks base method +func (m *MockClient) RecoverClusters(ctx context.Context, in *pb.RecoverClustersRequest, opts ...grpc.CallOption) (*pb.RecoverClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RecoverClusters", varargs...) + ret0, _ := ret[0].(*pb.RecoverClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecoverClusters indicates an expected call of RecoverClusters +func (mr *MockClientMockRecorder) RecoverClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecoverClusters", reflect.TypeOf((*MockClient)(nil).RecoverClusters), varargs...) +} + +// CeaseClusters mocks base method +func (m *MockClient) CeaseClusters(ctx context.Context, in *pb.CeaseClustersRequest, opts ...grpc.CallOption) (*pb.CeaseClustersResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CeaseClusters", varargs...) + ret0, _ := ret[0].(*pb.CeaseClustersResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CeaseClusters indicates an expected call of CeaseClusters +func (mr *MockClientMockRecorder) CeaseClusters(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CeaseClusters", reflect.TypeOf((*MockClient)(nil).CeaseClusters), varargs...) +} + +// GetClusterStatistics mocks base method +func (m *MockClient) GetClusterStatistics(ctx context.Context, in *pb.GetClusterStatisticsRequest, opts ...grpc.CallOption) (*pb.GetClusterStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetClusterStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetClusterStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetClusterStatistics indicates an expected call of GetClusterStatistics +func (mr *MockClientMockRecorder) GetClusterStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClusterStatistics", reflect.TypeOf((*MockClient)(nil).GetClusterStatistics), varargs...) +} + +// SyncRepo mocks base method +func (m *MockClient) SyncRepo(ctx context.Context, in *pb.SyncRepoRequest, opts ...grpc.CallOption) (*pb.SyncRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SyncRepo", varargs...) + ret0, _ := ret[0].(*pb.SyncRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SyncRepo indicates an expected call of SyncRepo +func (mr *MockClientMockRecorder) SyncRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SyncRepo", reflect.TypeOf((*MockClient)(nil).SyncRepo), varargs...) +} + +// CreateApp mocks base method +func (m *MockClient) CreateApp(ctx context.Context, in *pb.CreateAppRequest, opts ...grpc.CallOption) (*pb.CreateAppResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateApp", varargs...) + ret0, _ := ret[0].(*pb.CreateAppResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateApp indicates an expected call of CreateApp +func (mr *MockClientMockRecorder) CreateApp(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateApp", reflect.TypeOf((*MockClient)(nil).CreateApp), varargs...) +} + +// ValidatePackage mocks base method +func (m *MockClient) ValidatePackage(ctx context.Context, in *pb.ValidatePackageRequest, opts ...grpc.CallOption) (*pb.ValidatePackageResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidatePackage", varargs...) + ret0, _ := ret[0].(*pb.ValidatePackageResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidatePackage indicates an expected call of ValidatePackage +func (mr *MockClientMockRecorder) ValidatePackage(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidatePackage", reflect.TypeOf((*MockClient)(nil).ValidatePackage), varargs...) +} + +// GetAppStatistics mocks base method +func (m *MockClient) GetAppStatistics(ctx context.Context, in *pb.GetAppStatisticsRequest, opts ...grpc.CallOption) (*pb.GetAppStatisticsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppStatistics", varargs...) + ret0, _ := ret[0].(*pb.GetAppStatisticsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppStatistics indicates an expected call of GetAppStatistics +func (mr *MockClientMockRecorder) GetAppStatistics(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppStatistics", reflect.TypeOf((*MockClient)(nil).GetAppStatistics), varargs...) +} + +// DescribeApps mocks base method +func (m *MockClient) DescribeApps(ctx context.Context, in *pb.DescribeAppsRequest, opts ...grpc.CallOption) (*pb.DescribeAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeApps", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeApps indicates an expected call of DescribeApps +func (mr *MockClientMockRecorder) DescribeApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeApps", reflect.TypeOf((*MockClient)(nil).DescribeApps), varargs...) +} + +// DescribeActiveApps mocks base method +func (m *MockClient) DescribeActiveApps(ctx context.Context, in *pb.DescribeAppsRequest, opts ...grpc.CallOption) (*pb.DescribeAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeActiveApps", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeActiveApps indicates an expected call of DescribeActiveApps +func (mr *MockClientMockRecorder) DescribeActiveApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeActiveApps", reflect.TypeOf((*MockClient)(nil).DescribeActiveApps), varargs...) +} + +// ModifyApp mocks base method +func (m *MockClient) ModifyApp(ctx context.Context, in *pb.ModifyAppRequest, opts ...grpc.CallOption) (*pb.ModifyAppResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyApp", varargs...) + ret0, _ := ret[0].(*pb.ModifyAppResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyApp indicates an expected call of ModifyApp +func (mr *MockClientMockRecorder) ModifyApp(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyApp", reflect.TypeOf((*MockClient)(nil).ModifyApp), varargs...) +} + +// UploadAppAttachment mocks base method +func (m *MockClient) UploadAppAttachment(ctx context.Context, in *pb.UploadAppAttachmentRequest, opts ...grpc.CallOption) (*pb.UploadAppAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "UploadAppAttachment", varargs...) + ret0, _ := ret[0].(*pb.UploadAppAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UploadAppAttachment indicates an expected call of UploadAppAttachment +func (mr *MockClientMockRecorder) UploadAppAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadAppAttachment", reflect.TypeOf((*MockClient)(nil).UploadAppAttachment), varargs...) +} + +// DeleteApps mocks base method +func (m *MockClient) DeleteApps(ctx context.Context, in *pb.DeleteAppsRequest, opts ...grpc.CallOption) (*pb.DeleteAppsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteApps", varargs...) + ret0, _ := ret[0].(*pb.DeleteAppsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteApps indicates an expected call of DeleteApps +func (mr *MockClientMockRecorder) DeleteApps(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteApps", reflect.TypeOf((*MockClient)(nil).DeleteApps), varargs...) +} + +// CreateAppVersion mocks base method +func (m *MockClient) CreateAppVersion(ctx context.Context, in *pb.CreateAppVersionRequest, opts ...grpc.CallOption) (*pb.CreateAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateAppVersion", varargs...) + ret0, _ := ret[0].(*pb.CreateAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateAppVersion indicates an expected call of CreateAppVersion +func (mr *MockClientMockRecorder) CreateAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAppVersion", reflect.TypeOf((*MockClient)(nil).CreateAppVersion), varargs...) +} + +// DescribeAppVersions mocks base method +func (m *MockClient) DescribeAppVersions(ctx context.Context, in *pb.DescribeAppVersionsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersions", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersions indicates an expected call of DescribeAppVersions +func (mr *MockClientMockRecorder) DescribeAppVersions(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersions", reflect.TypeOf((*MockClient)(nil).DescribeAppVersions), varargs...) +} + +// DescribeActiveAppVersions mocks base method +func (m *MockClient) DescribeActiveAppVersions(ctx context.Context, in *pb.DescribeAppVersionsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeActiveAppVersions", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeActiveAppVersions indicates an expected call of DescribeActiveAppVersions +func (mr *MockClientMockRecorder) DescribeActiveAppVersions(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeActiveAppVersions", reflect.TypeOf((*MockClient)(nil).DescribeActiveAppVersions), varargs...) +} + +// DescribeAppVersionAudits mocks base method +func (m *MockClient) DescribeAppVersionAudits(ctx context.Context, in *pb.DescribeAppVersionAuditsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionAuditsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersionAudits", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionAuditsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersionAudits indicates an expected call of DescribeAppVersionAudits +func (mr *MockClientMockRecorder) DescribeAppVersionAudits(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersionAudits", reflect.TypeOf((*MockClient)(nil).DescribeAppVersionAudits), varargs...) +} + +// DescribeAppVersionReviews mocks base method +func (m *MockClient) DescribeAppVersionReviews(ctx context.Context, in *pb.DescribeAppVersionReviewsRequest, opts ...grpc.CallOption) (*pb.DescribeAppVersionReviewsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeAppVersionReviews", varargs...) + ret0, _ := ret[0].(*pb.DescribeAppVersionReviewsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeAppVersionReviews indicates an expected call of DescribeAppVersionReviews +func (mr *MockClientMockRecorder) DescribeAppVersionReviews(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAppVersionReviews", reflect.TypeOf((*MockClient)(nil).DescribeAppVersionReviews), varargs...) +} + +// ModifyAppVersion mocks base method +func (m *MockClient) ModifyAppVersion(ctx context.Context, in *pb.ModifyAppVersionRequest, opts ...grpc.CallOption) (*pb.ModifyAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ModifyAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyAppVersion indicates an expected call of ModifyAppVersion +func (mr *MockClientMockRecorder) ModifyAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyAppVersion", reflect.TypeOf((*MockClient)(nil).ModifyAppVersion), varargs...) +} + +// GetAppVersionPackage mocks base method +func (m *MockClient) GetAppVersionPackage(ctx context.Context, in *pb.GetAppVersionPackageRequest, opts ...grpc.CallOption) (*pb.GetAppVersionPackageResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppVersionPackage", varargs...) + ret0, _ := ret[0].(*pb.GetAppVersionPackageResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppVersionPackage indicates an expected call of GetAppVersionPackage +func (mr *MockClientMockRecorder) GetAppVersionPackage(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppVersionPackage", reflect.TypeOf((*MockClient)(nil).GetAppVersionPackage), varargs...) +} + +// GetAppVersionPackageFiles mocks base method +func (m *MockClient) GetAppVersionPackageFiles(ctx context.Context, in *pb.GetAppVersionPackageFilesRequest, opts ...grpc.CallOption) (*pb.GetAppVersionPackageFilesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAppVersionPackageFiles", varargs...) + ret0, _ := ret[0].(*pb.GetAppVersionPackageFilesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAppVersionPackageFiles indicates an expected call of GetAppVersionPackageFiles +func (mr *MockClientMockRecorder) GetAppVersionPackageFiles(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAppVersionPackageFiles", reflect.TypeOf((*MockClient)(nil).GetAppVersionPackageFiles), varargs...) +} + +// SubmitAppVersion mocks base method +func (m *MockClient) SubmitAppVersion(ctx context.Context, in *pb.SubmitAppVersionRequest, opts ...grpc.CallOption) (*pb.SubmitAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SubmitAppVersion", varargs...) + ret0, _ := ret[0].(*pb.SubmitAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitAppVersion indicates an expected call of SubmitAppVersion +func (mr *MockClientMockRecorder) SubmitAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAppVersion", reflect.TypeOf((*MockClient)(nil).SubmitAppVersion), varargs...) +} + +// CancelAppVersion mocks base method +func (m *MockClient) CancelAppVersion(ctx context.Context, in *pb.CancelAppVersionRequest, opts ...grpc.CallOption) (*pb.CancelAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CancelAppVersion", varargs...) + ret0, _ := ret[0].(*pb.CancelAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CancelAppVersion indicates an expected call of CancelAppVersion +func (mr *MockClientMockRecorder) CancelAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelAppVersion", reflect.TypeOf((*MockClient)(nil).CancelAppVersion), varargs...) +} + +// ReleaseAppVersion mocks base method +func (m *MockClient) ReleaseAppVersion(ctx context.Context, in *pb.ReleaseAppVersionRequest, opts ...grpc.CallOption) (*pb.ReleaseAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReleaseAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReleaseAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReleaseAppVersion indicates an expected call of ReleaseAppVersion +func (mr *MockClientMockRecorder) ReleaseAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseAppVersion", reflect.TypeOf((*MockClient)(nil).ReleaseAppVersion), varargs...) +} + +// DeleteAppVersion mocks base method +func (m *MockClient) DeleteAppVersion(ctx context.Context, in *pb.DeleteAppVersionRequest, opts ...grpc.CallOption) (*pb.DeleteAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAppVersion", varargs...) + ret0, _ := ret[0].(*pb.DeleteAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAppVersion indicates an expected call of DeleteAppVersion +func (mr *MockClientMockRecorder) DeleteAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAppVersion", reflect.TypeOf((*MockClient)(nil).DeleteAppVersion), varargs...) +} + +// IsvReviewAppVersion mocks base method +func (m *MockClient) IsvReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvReviewAppVersion indicates an expected call of IsvReviewAppVersion +func (mr *MockClientMockRecorder) IsvReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvReviewAppVersion", reflect.TypeOf((*MockClient)(nil).IsvReviewAppVersion), varargs...) +} + +// IsvPassAppVersion mocks base method +func (m *MockClient) IsvPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvPassAppVersion indicates an expected call of IsvPassAppVersion +func (mr *MockClientMockRecorder) IsvPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvPassAppVersion", reflect.TypeOf((*MockClient)(nil).IsvPassAppVersion), varargs...) +} + +// IsvRejectAppVersion mocks base method +func (m *MockClient) IsvRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IsvRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IsvRejectAppVersion indicates an expected call of IsvRejectAppVersion +func (mr *MockClientMockRecorder) IsvRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsvRejectAppVersion", reflect.TypeOf((*MockClient)(nil).IsvRejectAppVersion), varargs...) +} + +// BusinessReviewAppVersion mocks base method +func (m *MockClient) BusinessReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessReviewAppVersion indicates an expected call of BusinessReviewAppVersion +func (mr *MockClientMockRecorder) BusinessReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessReviewAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessReviewAppVersion), varargs...) +} + +// BusinessPassAppVersion mocks base method +func (m *MockClient) BusinessPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessPassAppVersion indicates an expected call of BusinessPassAppVersion +func (mr *MockClientMockRecorder) BusinessPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessPassAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessPassAppVersion), varargs...) +} + +// BusinessRejectAppVersion mocks base method +func (m *MockClient) BusinessRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BusinessRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// BusinessRejectAppVersion indicates an expected call of BusinessRejectAppVersion +func (mr *MockClientMockRecorder) BusinessRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BusinessRejectAppVersion", reflect.TypeOf((*MockClient)(nil).BusinessRejectAppVersion), varargs...) +} + +// TechnicalReviewAppVersion mocks base method +func (m *MockClient) TechnicalReviewAppVersion(ctx context.Context, in *pb.ReviewAppVersionRequest, opts ...grpc.CallOption) (*pb.ReviewAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalReviewAppVersion", varargs...) + ret0, _ := ret[0].(*pb.ReviewAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalReviewAppVersion indicates an expected call of TechnicalReviewAppVersion +func (mr *MockClientMockRecorder) TechnicalReviewAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalReviewAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalReviewAppVersion), varargs...) +} + +// TechnicalPassAppVersion mocks base method +func (m *MockClient) TechnicalPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalPassAppVersion indicates an expected call of TechnicalPassAppVersion +func (mr *MockClientMockRecorder) TechnicalPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalPassAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalPassAppVersion), varargs...) +} + +// TechnicalRejectAppVersion mocks base method +func (m *MockClient) TechnicalRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TechnicalRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TechnicalRejectAppVersion indicates an expected call of TechnicalRejectAppVersion +func (mr *MockClientMockRecorder) TechnicalRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TechnicalRejectAppVersion", reflect.TypeOf((*MockClient)(nil).TechnicalRejectAppVersion), varargs...) +} + +// AdminPassAppVersion mocks base method +func (m *MockClient) AdminPassAppVersion(ctx context.Context, in *pb.PassAppVersionRequest, opts ...grpc.CallOption) (*pb.PassAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AdminPassAppVersion", varargs...) + ret0, _ := ret[0].(*pb.PassAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AdminPassAppVersion indicates an expected call of AdminPassAppVersion +func (mr *MockClientMockRecorder) AdminPassAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdminPassAppVersion", reflect.TypeOf((*MockClient)(nil).AdminPassAppVersion), varargs...) +} + +// AdminRejectAppVersion mocks base method +func (m *MockClient) AdminRejectAppVersion(ctx context.Context, in *pb.RejectAppVersionRequest, opts ...grpc.CallOption) (*pb.RejectAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AdminRejectAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RejectAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AdminRejectAppVersion indicates an expected call of AdminRejectAppVersion +func (mr *MockClientMockRecorder) AdminRejectAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdminRejectAppVersion", reflect.TypeOf((*MockClient)(nil).AdminRejectAppVersion), varargs...) +} + +// SuspendAppVersion mocks base method +func (m *MockClient) SuspendAppVersion(ctx context.Context, in *pb.SuspendAppVersionRequest, opts ...grpc.CallOption) (*pb.SuspendAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SuspendAppVersion", varargs...) + ret0, _ := ret[0].(*pb.SuspendAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SuspendAppVersion indicates an expected call of SuspendAppVersion +func (mr *MockClientMockRecorder) SuspendAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SuspendAppVersion", reflect.TypeOf((*MockClient)(nil).SuspendAppVersion), varargs...) +} + +// RecoverAppVersion mocks base method +func (m *MockClient) RecoverAppVersion(ctx context.Context, in *pb.RecoverAppVersionRequest, opts ...grpc.CallOption) (*pb.RecoverAppVersionResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "RecoverAppVersion", varargs...) + ret0, _ := ret[0].(*pb.RecoverAppVersionResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RecoverAppVersion indicates an expected call of RecoverAppVersion +func (mr *MockClientMockRecorder) RecoverAppVersion(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecoverAppVersion", reflect.TypeOf((*MockClient)(nil).RecoverAppVersion), varargs...) +} + +// CreateRepo mocks base method +func (m *MockClient) CreateRepo(ctx context.Context, in *pb.CreateRepoRequest, opts ...grpc.CallOption) (*pb.CreateRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateRepo", varargs...) + ret0, _ := ret[0].(*pb.CreateRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateRepo indicates an expected call of CreateRepo +func (mr *MockClientMockRecorder) CreateRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRepo", reflect.TypeOf((*MockClient)(nil).CreateRepo), varargs...) +} + +// DescribeRepos mocks base method +func (m *MockClient) DescribeRepos(ctx context.Context, in *pb.DescribeReposRequest, opts ...grpc.CallOption) (*pb.DescribeReposResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRepos", varargs...) + ret0, _ := ret[0].(*pb.DescribeReposResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRepos indicates an expected call of DescribeRepos +func (mr *MockClientMockRecorder) DescribeRepos(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRepos", reflect.TypeOf((*MockClient)(nil).DescribeRepos), varargs...) +} + +// ModifyRepo mocks base method +func (m *MockClient) ModifyRepo(ctx context.Context, in *pb.ModifyRepoRequest, opts ...grpc.CallOption) (*pb.ModifyRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyRepo", varargs...) + ret0, _ := ret[0].(*pb.ModifyRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyRepo indicates an expected call of ModifyRepo +func (mr *MockClientMockRecorder) ModifyRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyRepo", reflect.TypeOf((*MockClient)(nil).ModifyRepo), varargs...) +} + +// DeleteRepos mocks base method +func (m *MockClient) DeleteRepos(ctx context.Context, in *pb.DeleteReposRequest, opts ...grpc.CallOption) (*pb.DeleteReposResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteRepos", varargs...) + ret0, _ := ret[0].(*pb.DeleteReposResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteRepos indicates an expected call of DeleteRepos +func (mr *MockClientMockRecorder) DeleteRepos(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRepos", reflect.TypeOf((*MockClient)(nil).DeleteRepos), varargs...) +} + +// ValidateRepo mocks base method +func (m *MockClient) ValidateRepo(ctx context.Context, in *pb.ValidateRepoRequest, opts ...grpc.CallOption) (*pb.ValidateRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ValidateRepo", varargs...) + ret0, _ := ret[0].(*pb.ValidateRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ValidateRepo indicates an expected call of ValidateRepo +func (mr *MockClientMockRecorder) ValidateRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRepo", reflect.TypeOf((*MockClient)(nil).ValidateRepo), varargs...) +} + +// DescribeCategories mocks base method +func (m *MockClient) DescribeCategories(ctx context.Context, in *pb.DescribeCategoriesRequest, opts ...grpc.CallOption) (*pb.DescribeCategoriesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeCategories", varargs...) + ret0, _ := ret[0].(*pb.DescribeCategoriesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeCategories indicates an expected call of DescribeCategories +func (mr *MockClientMockRecorder) DescribeCategories(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeCategories", reflect.TypeOf((*MockClient)(nil).DescribeCategories), varargs...) +} + +// CreateCategory mocks base method +func (m *MockClient) CreateCategory(ctx context.Context, in *pb.CreateCategoryRequest, opts ...grpc.CallOption) (*pb.CreateCategoryResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateCategory", varargs...) + ret0, _ := ret[0].(*pb.CreateCategoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCategory indicates an expected call of CreateCategory +func (mr *MockClientMockRecorder) CreateCategory(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCategory", reflect.TypeOf((*MockClient)(nil).CreateCategory), varargs...) +} + +// ModifyCategory mocks base method +func (m *MockClient) ModifyCategory(ctx context.Context, in *pb.ModifyCategoryRequest, opts ...grpc.CallOption) (*pb.ModifyCategoryResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ModifyCategory", varargs...) + ret0, _ := ret[0].(*pb.ModifyCategoryResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ModifyCategory indicates an expected call of ModifyCategory +func (mr *MockClientMockRecorder) ModifyCategory(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ModifyCategory", reflect.TypeOf((*MockClient)(nil).ModifyCategory), varargs...) +} + +// DeleteCategories mocks base method +func (m *MockClient) DeleteCategories(ctx context.Context, in *pb.DeleteCategoriesRequest, opts ...grpc.CallOption) (*pb.DeleteCategoriesResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteCategories", varargs...) + ret0, _ := ret[0].(*pb.DeleteCategoriesResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteCategories indicates an expected call of DeleteCategories +func (mr *MockClientMockRecorder) DeleteCategories(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCategories", reflect.TypeOf((*MockClient)(nil).DeleteCategories), varargs...) +} + +// CreateAttachment mocks base method +func (m *MockClient) CreateAttachment(ctx context.Context, in *pb.CreateAttachmentRequest, opts ...grpc.CallOption) (*pb.CreateAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateAttachment", varargs...) + ret0, _ := ret[0].(*pb.CreateAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateAttachment indicates an expected call of CreateAttachment +func (mr *MockClientMockRecorder) CreateAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAttachment", reflect.TypeOf((*MockClient)(nil).CreateAttachment), varargs...) +} + +// AppendAttachment mocks base method +func (m *MockClient) AppendAttachment(ctx context.Context, in *pb.AppendAttachmentRequest, opts ...grpc.CallOption) (*pb.AppendAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "AppendAttachment", varargs...) + ret0, _ := ret[0].(*pb.AppendAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AppendAttachment indicates an expected call of AppendAttachment +func (mr *MockClientMockRecorder) AppendAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendAttachment", reflect.TypeOf((*MockClient)(nil).AppendAttachment), varargs...) +} + +// ReplaceAttachment mocks base method +func (m *MockClient) ReplaceAttachment(ctx context.Context, in *pb.ReplaceAttachmentRequest, opts ...grpc.CallOption) (*pb.ReplaceAttachmentResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ReplaceAttachment", varargs...) + ret0, _ := ret[0].(*pb.ReplaceAttachmentResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ReplaceAttachment indicates an expected call of ReplaceAttachment +func (mr *MockClientMockRecorder) ReplaceAttachment(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceAttachment", reflect.TypeOf((*MockClient)(nil).ReplaceAttachment), varargs...) +} + +// GetAttachments mocks base method +func (m *MockClient) GetAttachments(ctx context.Context, in *pb.GetAttachmentsRequest, opts ...grpc.CallOption) (*pb.GetAttachmentsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetAttachments", varargs...) + ret0, _ := ret[0].(*pb.GetAttachmentsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetAttachments indicates an expected call of GetAttachments +func (mr *MockClientMockRecorder) GetAttachments(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttachments", reflect.TypeOf((*MockClient)(nil).GetAttachments), varargs...) +} + +// DeleteAttachments mocks base method +func (m *MockClient) DeleteAttachments(ctx context.Context, in *pb.DeleteAttachmentsRequest, opts ...grpc.CallOption) (*pb.DeleteAttachmentsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteAttachments", varargs...) + ret0, _ := ret[0].(*pb.DeleteAttachmentsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteAttachments indicates an expected call of DeleteAttachments +func (mr *MockClientMockRecorder) DeleteAttachments(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAttachments", reflect.TypeOf((*MockClient)(nil).DeleteAttachments), varargs...) +} + +// IndexRepo mocks base method +func (m *MockClient) IndexRepo(ctx context.Context, in *pb.IndexRepoRequest, opts ...grpc.CallOption) (*pb.IndexRepoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "IndexRepo", varargs...) + ret0, _ := ret[0].(*pb.IndexRepoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IndexRepo indicates an expected call of IndexRepo +func (mr *MockClientMockRecorder) IndexRepo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IndexRepo", reflect.TypeOf((*MockClient)(nil).IndexRepo), varargs...) +} + +// DescribeRepoEvents mocks base method +func (m *MockClient) DescribeRepoEvents(ctx context.Context, in *pb.DescribeRepoEventsRequest, opts ...grpc.CallOption) (*pb.DescribeRepoEventsResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DescribeRepoEvents", varargs...) + ret0, _ := ret[0].(*pb.DescribeRepoEventsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DescribeRepoEvents indicates an expected call of DescribeRepoEvents +func (mr *MockClientMockRecorder) DescribeRepoEvents(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeRepoEvents", reflect.TypeOf((*MockClient)(nil).DescribeRepoEvents), varargs...) +} diff --git a/pkg/simple/client/openpitrix/openpitrix.go b/pkg/simple/client/openpitrix/openpitrix.go new file mode 100644 index 000000000..28e497659 --- /dev/null +++ b/pkg/simple/client/openpitrix/openpitrix.go @@ -0,0 +1,230 @@ +/* + + Copyright 2019 The KubeSphere 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 openpitrix + +import ( + "context" + "fmt" + "k8s.io/klog" + "openpitrix.io/openpitrix/pkg/manager" + "openpitrix.io/openpitrix/pkg/pb" + "openpitrix.io/openpitrix/pkg/sender" + "openpitrix.io/openpitrix/pkg/util/ctxutil" + "strconv" + "strings" +) + +const ( + RuntimeAnnotationKey = "openpitrix_runtime" + KubernetesProvider = "kubernetes" + Unknown = "-" + DeploySuffix = "-Deployment" + DaemonSuffix = "-DaemonSet" + StateSuffix = "-StatefulSet" + SystemUsername = "system" + SystemUserPath = ":system" +) + +type Client interface { + pb.RuntimeManagerClient + pb.ClusterManagerClient + pb.AppManagerClient + pb.RepoManagerClient + pb.CategoryManagerClient + pb.AttachmentManagerClient + pb.RepoIndexerClient +} + +type client struct { + pb.RuntimeManagerClient + pb.ClusterManagerClient + pb.AppManagerClient + pb.RepoManagerClient + pb.CategoryManagerClient + pb.AttachmentManagerClient + pb.RepoIndexerClient +} + +func parseToHostPort(endpoint string) (string, int, error) { + args := strings.Split(endpoint, ":") + if len(args) != 2 { + return "", 0, fmt.Errorf("invalid server host: %s", endpoint) + } + host := args[0] + port, err := strconv.Atoi(args[1]) + if err != nil { + return "", 0, err + } + return host, port, nil +} + +func newRuntimeManagerClient(endpoint string) (pb.RuntimeManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRuntimeManagerClient(conn), nil +} +func newClusterManagerClient(endpoint string) (pb.ClusterManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewClusterManagerClient(conn), nil +} +func newCategoryManagerClient(endpoint string) (pb.CategoryManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewCategoryManagerClient(conn), nil +} + +func newAttachmentManagerClient(endpoint string) (pb.AttachmentManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewAttachmentManagerClient(conn), nil +} + +func newRepoManagerClient(endpoint string) (pb.RepoManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRepoManagerClient(conn), nil +} + +func newRepoIndexer(endpoint string) (pb.RepoIndexerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewRepoIndexerClient(conn), nil +} + +func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) { + host, port, err := parseToHostPort(endpoint) + conn, err := manager.NewClient(host, port) + if err != nil { + return nil, err + } + return pb.NewAppManagerClient(conn), nil +} + +func NewClient(options *Options) (Client, error) { + + runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + clusterManagerClient, err := newClusterManagerClient(options.ClusterManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + repoManagerClient, err := newRepoManagerClient(options.RepoManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + repoIndexerClient, err := newRepoIndexer(options.RepoIndexerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + appManagerClient, err := newAppManagerClient(options.AppManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + categoryManagerClient, err := newCategoryManagerClient(options.CategoryManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + attachmentManagerClient, err := newAttachmentManagerClient(options.AttachmentManagerEndpoint) + + if err != nil { + klog.Error(err) + return nil, err + } + + client := client{ + RuntimeManagerClient: runtimeMangerClient, + ClusterManagerClient: clusterManagerClient, + RepoManagerClient: repoManagerClient, + AppManagerClient: appManagerClient, + CategoryManagerClient: categoryManagerClient, + AttachmentManagerClient: attachmentManagerClient, + RepoIndexerClient: repoIndexerClient, + } + + return &client, nil +} + +func SystemContext() context.Context { + ctx := context.Background() + ctx = ctxutil.ContextWithSender(ctx, sender.New(SystemUsername, SystemUserPath, "")) + return ctx +} +func ContextWithUsername(username string) context.Context { + ctx := context.Background() + if username == "" { + username = SystemUsername + } + ctx = ctxutil.ContextWithSender(ctx, sender.New(username, SystemUserPath, "")) + return ctx +} + +func IsNotFound(err error) bool { + if strings.Contains(err.Error(), "not exist") { + return true + } + if strings.Contains(err.Error(), "not found") { + return true + } + return false +} + +func IsDeleted(err error) bool { + if strings.Contains(err.Error(), "is [deleted]") { + return true + } + return false +} diff --git a/pkg/simple/client/openpitrix/openpitrixclient.go b/pkg/simple/client/openpitrix/openpitrixclient.go deleted file mode 100644 index 0e8f4a32c..000000000 --- a/pkg/simple/client/openpitrix/openpitrixclient.go +++ /dev/null @@ -1,243 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 openpitrix - -import ( - "context" - "fmt" - "k8s.io/klog" - "openpitrix.io/openpitrix/pkg/manager" - "openpitrix.io/openpitrix/pkg/pb" - "openpitrix.io/openpitrix/pkg/sender" - "openpitrix.io/openpitrix/pkg/util/ctxutil" - "strconv" - "strings" -) - -const ( - KubernetesProvider = "kubernetes" - Unknown = "-" - DeploySuffix = "-Deployment" - DaemonSuffix = "-DaemonSet" - StateSuffix = "-StatefulSet" - SystemUsername = "system" - SystemUserPath = ":system" -) - -type OpenPitrixClient struct { - runtime pb.RuntimeManagerClient - cluster pb.ClusterManagerClient - app pb.AppManagerClient - repo pb.RepoManagerClient - category pb.CategoryManagerClient - attachment pb.AttachmentManagerClient - repoIndexer pb.RepoIndexerClient -} - -func parseToHostPort(endpoint string) (string, int, error) { - args := strings.Split(endpoint, ":") - if len(args) != 2 { - return "", 0, fmt.Errorf("invalid server host: %s", endpoint) - } - host := args[0] - port, err := strconv.Atoi(args[1]) - if err != nil { - return "", 0, err - } - return host, port, nil -} - -func newRuntimeManagerClient(endpoint string) (pb.RuntimeManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewRuntimeManagerClient(conn), nil -} -func newClusterManagerClient(endpoint string) (pb.ClusterManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewClusterManagerClient(conn), nil -} -func newCategoryManagerClient(endpoint string) (pb.CategoryManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewCategoryManagerClient(conn), nil -} - -func newAttachmentManagerClient(endpoint string) (pb.AttachmentManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewAttachmentManagerClient(conn), nil -} - -func newRepoManagerClient(endpoint string) (pb.RepoManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewRepoManagerClient(conn), nil -} - -func newRepoIndexer(endpoint string) (pb.RepoIndexerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewRepoIndexerClient(conn), nil -} - -func newAppManagerClient(endpoint string) (pb.AppManagerClient, error) { - host, port, err := parseToHostPort(endpoint) - conn, err := manager.NewClient(host, port) - if err != nil { - return nil, err - } - return pb.NewAppManagerClient(conn), nil -} - -func NewOpenPitrixClient(options *OpenPitrixOptions) (*OpenPitrixClient, error) { - - runtimeMangerClient, err := newRuntimeManagerClient(options.RuntimeManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - clusterManagerClient, err := newClusterManagerClient(options.ClusterManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - repoManagerClient, err := newRepoManagerClient(options.RepoManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - repoIndexerClient, err := newRepoIndexer(options.RepoIndexerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - appManagerClient, err := newAppManagerClient(options.AppManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - categoryManagerClient, err := newCategoryManagerClient(options.CategoryManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - attachmentManagerClient, err := newAttachmentManagerClient(options.AttachmentManagerEndpoint) - - if err != nil { - klog.Error(err) - return nil, err - } - - client := OpenPitrixClient{ - runtime: runtimeMangerClient, - cluster: clusterManagerClient, - repo: repoManagerClient, - app: appManagerClient, - category: categoryManagerClient, - attachment: attachmentManagerClient, - repoIndexer: repoIndexerClient, - } - - return &client, nil -} -func (c *OpenPitrixClient) Runtime() pb.RuntimeManagerClient { - return c.runtime -} -func (c *OpenPitrixClient) App() pb.AppManagerClient { - return c.app -} -func (c *OpenPitrixClient) Cluster() pb.ClusterManagerClient { - return c.cluster -} -func (c *OpenPitrixClient) Category() pb.CategoryManagerClient { - return c.category -} - -func (c *OpenPitrixClient) Repo() pb.RepoManagerClient { - return c.repo -} - -func (c *OpenPitrixClient) RepoIndexer() pb.RepoIndexerClient { - return c.repoIndexer -} - -func (c *OpenPitrixClient) Attachment() pb.AttachmentManagerClient { - return c.attachment -} - -func SystemContext() context.Context { - ctx := context.Background() - ctx = ctxutil.ContextWithSender(ctx, sender.New(SystemUsername, SystemUserPath, "")) - return ctx -} -func ContextWithUsername(username string) context.Context { - ctx := context.Background() - if username == "" { - username = SystemUsername - } - ctx = ctxutil.ContextWithSender(ctx, sender.New(username, SystemUserPath, "")) - return ctx -} - -func IsNotFound(err error) bool { - if strings.Contains(err.Error(), "not exist") { - return true - } - if strings.Contains(err.Error(), "not found") { - return true - } - return false -} - -func IsDeleted(err error) bool { - if strings.Contains(err.Error(), "is [deleted]") { - return true - } - return false -} diff --git a/pkg/simple/client/openpitrix/options.go b/pkg/simple/client/openpitrix/options.go index d577bd5f4..62a5e7869 100644 --- a/pkg/simple/client/openpitrix/options.go +++ b/pkg/simple/client/openpitrix/options.go @@ -6,7 +6,7 @@ import ( "kubesphere.io/kubesphere/pkg/utils/reflectutils" ) -type OpenPitrixOptions struct { +type Options struct { RuntimeManagerEndpoint string `json:"runtimeManagerEndpoint,omitempty" yaml:"runtimeManagerEndpoint,omitempty"` ClusterManagerEndpoint string `json:"clusterManagerEndpoint,omitempty" yaml:"clusterManagerEndpoint,omitempty"` RepoManagerEndpoint string `json:"repoManagerEndpoint,omitempty" yaml:"repoManagerEndpoint,omitempty"` @@ -16,11 +16,11 @@ type OpenPitrixOptions struct { RepoIndexerEndpoint string `json:"repoIndexerEndpoint,omitempty" yaml:"repoIndexerEndpoint,omitempty"` } -func NewOpenPitrixOptions() *OpenPitrixOptions { - return &OpenPitrixOptions{} +func NewOptions() *Options { + return &Options{} } -func (s *OpenPitrixOptions) ApplyTo(options *OpenPitrixOptions) { +func (s *Options) ApplyTo(options *Options) { if options == nil { options = s return @@ -30,7 +30,7 @@ func (s *OpenPitrixOptions) ApplyTo(options *OpenPitrixOptions) { } } -func (s *OpenPitrixOptions) IsEmpty() bool { +func (s *Options) IsEmpty() bool { return s.RuntimeManagerEndpoint == "" && s.ClusterManagerEndpoint == "" && s.RepoManagerEndpoint == "" && @@ -40,7 +40,7 @@ func (s *OpenPitrixOptions) IsEmpty() bool { s.RepoIndexerEndpoint == "" } -func (s *OpenPitrixOptions) Validate() []error { +func (s *Options) Validate() []error { var errs []error if s.RuntimeManagerEndpoint != "" { @@ -89,25 +89,25 @@ func (s *OpenPitrixOptions) Validate() []error { return errs } -func (s *OpenPitrixOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.RuntimeManagerEndpoint, "openpitrix-runtime-manager-endpoint", s.RuntimeManagerEndpoint, ""+ +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.RuntimeManagerEndpoint, "openpitrix-runtime-manager-endpoint", c.RuntimeManagerEndpoint, ""+ "OpenPitrix runtime manager endpoint") - fs.StringVar(&s.AppManagerEndpoint, "openpitrix-app-manager-endpoint", s.AppManagerEndpoint, ""+ + fs.StringVar(&s.AppManagerEndpoint, "openpitrix-app-manager-endpoint", c.AppManagerEndpoint, ""+ "OpenPitrix app manager endpoint") - fs.StringVar(&s.ClusterManagerEndpoint, "openpitrix-cluster-manager-endpoint", s.ClusterManagerEndpoint, ""+ + fs.StringVar(&s.ClusterManagerEndpoint, "openpitrix-cluster-manager-endpoint", c.ClusterManagerEndpoint, ""+ "OpenPitrix cluster manager endpoint") - fs.StringVar(&s.CategoryManagerEndpoint, "openpitrix-category-manager-endpoint", s.CategoryManagerEndpoint, ""+ + fs.StringVar(&s.CategoryManagerEndpoint, "openpitrix-category-manager-endpoint", c.CategoryManagerEndpoint, ""+ "OpenPitrix category manager endpoint") - fs.StringVar(&s.RepoManagerEndpoint, "openpitrix-repo-manager-endpoint", s.RepoManagerEndpoint, ""+ + fs.StringVar(&s.RepoManagerEndpoint, "openpitrix-repo-manager-endpoint", c.RepoManagerEndpoint, ""+ "OpenPitrix repo manager endpoint") - fs.StringVar(&s.RepoIndexerEndpoint, "openpitrix-repo-indexer-endpoint", s.RepoIndexerEndpoint, ""+ + fs.StringVar(&s.RepoIndexerEndpoint, "openpitrix-repo-indexer-endpoint", c.RepoIndexerEndpoint, ""+ "OpenPitrix repo indexer endpoint") - fs.StringVar(&s.AttachmentManagerEndpoint, "openpitrix-attachment-manager-endpoint", s.AttachmentManagerEndpoint, ""+ + fs.StringVar(&s.AttachmentManagerEndpoint, "openpitrix-attachment-manager-endpoint", c.AttachmentManagerEndpoint, ""+ "OpenPitrix attachment manager endpoint") } diff --git a/pkg/simple/client/prometheus/options.go b/pkg/simple/client/prometheus/options.go deleted file mode 100644 index 6ade8c720..000000000 --- a/pkg/simple/client/prometheus/options.go +++ /dev/null @@ -1,42 +0,0 @@ -package prometheus - -import ( - "github.com/spf13/pflag" -) - -type PrometheusOptions struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` - SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"` -} - -func NewPrometheusOptions() *PrometheusOptions { - return &PrometheusOptions{ - Endpoint: "", - SecondaryEndpoint: "", - } -} - -func (s *PrometheusOptions) Validate() []error { - errs := []error{} - - return errs -} - -func (s *PrometheusOptions) ApplyTo(options *PrometheusOptions) { - if s.Endpoint != "" { - options.Endpoint = s.Endpoint - } - - if s.SecondaryEndpoint != "" { - options.SecondaryEndpoint = s.SecondaryEndpoint - } -} - -func (s *PrometheusOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Endpoint, "prometheus-endpoint", s.Endpoint, ""+ - "Prometheus service endpoint which stores KubeSphere monitoring data, if left "+ - "blank, will use builtin metrics-server as data source.") - - fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", s.SecondaryEndpoint, ""+ - "Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.") -} diff --git a/pkg/simple/client/prometheus/prometheus.go b/pkg/simple/client/prometheus/prometheus.go deleted file mode 100644 index 5309caa30..000000000 --- a/pkg/simple/client/prometheus/prometheus.go +++ /dev/null @@ -1,82 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 prometheus - -import ( - "fmt" - jsoniter "github.com/json-iterator/go" - "io/ioutil" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/api/monitoring/v1alpha2" - "net/http" - "time" -) - -type PrometheusClient struct { - client *http.Client - endpoint string - secondaryEndpoint string -} - -func NewPrometheusClient(options *PrometheusOptions) (*PrometheusClient, error) { - return &PrometheusClient{ - client: &http.Client{ - Timeout: 10 * time.Second, - }, - endpoint: options.Endpoint, - secondaryEndpoint: options.SecondaryEndpoint, - }, nil -} - -func (c *PrometheusClient) QueryToK8SPrometheus(queryType string, params string) (apiResponse v1alpha2.APIResponse) { - return c.query(c.endpoint, queryType, params) -} - -func (c *PrometheusClient) QueryToK8SSystemPrometheus(queryType string, params string) (apiResponse v1alpha2.APIResponse) { - return c.query(c.secondaryEndpoint, queryType, params) -} - -var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary - -func (c *PrometheusClient) query(endpoint string, queryType string, params string) (apiResponse v1alpha2.APIResponse) { - url := fmt.Sprintf("%s/api/v1/%s?%s", endpoint, queryType, params) - - response, err := c.client.Get(url) - if err != nil { - klog.Error(err) - apiResponse.Status = "error" - return apiResponse - } - defer response.Body.Close() - - body, err := ioutil.ReadAll(response.Body) - if err != nil { - klog.Error(err) - apiResponse.Status = "error" - return apiResponse - } - - err = jsonIter.Unmarshal(body, &apiResponse) - if err != nil { - klog.Errorf("fail to unmarshal prometheus query result: %s", err.Error()) - apiResponse.Status = "error" - return apiResponse - } - - return apiResponse -} diff --git a/pkg/simple/client/redis/options.go b/pkg/simple/client/redis/options.go deleted file mode 100644 index a3e9d25de..000000000 --- a/pkg/simple/client/redis/options.go +++ /dev/null @@ -1,46 +0,0 @@ -package redis - -import ( - "github.com/go-redis/redis" - "github.com/spf13/pflag" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" -) - -type RedisOptions struct { - RedisURL string -} - -// NewRedisOptions returns options points to nowhere, -// because redis is not required for some components -func NewRedisOptions() *RedisOptions { - return &RedisOptions{ - RedisURL: "", - } -} - -// Validate check options -func (r *RedisOptions) Validate() []error { - errors := make([]error, 0) - - _, err := redis.ParseURL(r.RedisURL) - - if err != nil { - errors = append(errors, err) - } - - return errors -} - -// ApplyTo apply to another options if it's a enabled option(non empty host) -func (r *RedisOptions) ApplyTo(options *RedisOptions) { - if r.RedisURL != "" { - reflectutils.Override(options, r) - } -} - -// AddFlags add option flags to command line flags, -// if redis-host left empty, the following options will be ignored. -func (r *RedisOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&r.RedisURL, "redis-url", "", "Redis connection URL. If left blank, means redis is unnecessary, "+ - "redis will be disabled. e.g. redis://:password@host:port/db") -} diff --git a/pkg/simple/client/redis/redis.go b/pkg/simple/client/redis/redis.go deleted file mode 100644 index 4a1fb83b3..000000000 --- a/pkg/simple/client/redis/redis.go +++ /dev/null @@ -1,70 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 redis - -import ( - "github.com/go-redis/redis" - "k8s.io/klog" -) - -type RedisClient struct { - client *redis.Client -} - -func NewRedisClientOrDie(options *RedisOptions, stopCh <-chan struct{}) *RedisClient { - client, err := NewRedisClient(options, stopCh) - if err != nil { - panic(err) - } - - return client -} - -func NewRedisClient(option *RedisOptions, stopCh <-chan struct{}) (*RedisClient, error) { - var r RedisClient - - options, err := redis.ParseURL(option.RedisURL) - - if err != nil { - klog.Error(err) - return nil, err - } - - r.client = redis.NewClient(options) - - if err := r.client.Ping().Err(); err != nil { - klog.Error("unable to reach redis host", err) - r.client.Close() - return nil, err - } - - if stopCh != nil { - go func() { - <-stopCh - if err := r.client.Close(); err != nil { - klog.Error(err) - } - }() - } - - return &r, nil -} - -func (r *RedisClient) Redis() *redis.Client { - return r.client -} diff --git a/pkg/simple/client/s2is3/options.go b/pkg/simple/client/s2is3/options.go deleted file mode 100644 index 32b676bc4..000000000 --- a/pkg/simple/client/s2is3/options.go +++ /dev/null @@ -1,69 +0,0 @@ -package s2is3 - -import ( - "github.com/spf13/pflag" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" -) - -// S3Options contains configuration to access a s3 service -type S3Options struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` - Region string `json:"region,omitempty" yaml:"region"` - DisableSSL bool `json:"disableSSL" yaml:"disableSSL"` - ForcePathStyle bool `json:"forcePathStyle" yaml:"forcePathStyle"` - AccessKeyID string `json:"accessKeyID,omitempty" yaml:"accessKeyID"` - SecretAccessKey string `json:"secretAccessKey,omitempty" yaml:"secretAccessKey"` - SessionToken string `json:"sessionToken,omitempty" yaml:"sessionToken"` - Bucket string `json:"bucket,omitempty" yaml:"bucket"` -} - -// NewS3Options creates a default disabled S3Options(empty endpoint) -func NewS3Options() *S3Options { - return &S3Options{ - Endpoint: "", - Region: "us-east-1", - DisableSSL: true, - ForcePathStyle: true, - AccessKeyID: "AKIAIOSFODNN7EXAMPLE", - SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", - SessionToken: "", - Bucket: "s2i-binaries", - } -} - -// Validate check options values -func (s *S3Options) Validate() []error { - errors := []error{} - - return errors -} - -// ApplyTo overrides options if it's valid, which endpoint is not empty -func (s *S3Options) ApplyTo(options *S3Options) { - if s.Endpoint != "" { - reflectutils.Override(options, s) - } -} - -// AddFlags add options flags to command line flags, -// if s3-endpoint if left empty, following options will be ignored -func (s *S3Options) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Endpoint, "s3-endpoint", s.Endpoint, ""+ - "Endpoint to access to s3 object storage service, if left blank, the following options "+ - "will be ignored.") - - fs.StringVar(&s.Region, "s3-region", s.Region, ""+ - "Region of s3 that will access to, like us-east-1.") - - fs.StringVar(&s.AccessKeyID, "s3-access-key-id", s.AccessKeyID, "access key of s2i s3") - - fs.StringVar(&s.SecretAccessKey, "s3-secret-access-key", s.SecretAccessKey, "secret access key of s2i s3") - - fs.StringVar(&s.SessionToken, "s3-session-token", s.SessionToken, "session token of s2i s3") - - fs.StringVar(&s.Bucket, "s3-bucket", s.Bucket, "bucket name of s2i s3") - - fs.BoolVar(&s.DisableSSL, "s3-disable-SSL", s.DisableSSL, "disable ssl") - - fs.BoolVar(&s.ForcePathStyle, "s3-force-path-style", s.ForcePathStyle, "force path style") -} diff --git a/pkg/simple/client/s2is3/s3.go b/pkg/simple/client/s2is3/s3.go deleted file mode 100644 index e9f7ec09f..000000000 --- a/pkg/simple/client/s2is3/s3.go +++ /dev/null @@ -1,79 +0,0 @@ -package s2is3 - -import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" - "k8s.io/klog" -) - -type S3Client struct { - s3Client *s3.S3 - s3Session *session.Session - bucket string -} - -func NewS3Client(options *S3Options) (*S3Client, error) { - cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken) - - config := aws.Config{ - Region: aws.String(options.Region), - Endpoint: aws.String(options.Endpoint), - DisableSSL: aws.Bool(options.DisableSSL), - S3ForcePathStyle: aws.Bool(options.ForcePathStyle), - Credentials: cred, - } - - s, err := session.NewSession(&config) - if err != nil { - klog.Error(err) - return nil, err - } - - var c S3Client - - c.s3Client = s3.New(s) - c.s3Session = s - c.bucket = options.Bucket - - return &c, nil -} - -// NewS3ClientOrDie creates S3Client and panics if there is an error -func NewS3ClientOrDie(options *S3Options) *S3Client { - cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken) - - config := aws.Config{ - Region: aws.String(options.Region), - Endpoint: aws.String(options.Endpoint), - DisableSSL: aws.Bool(options.DisableSSL), - S3ForcePathStyle: aws.Bool(options.ForcePathStyle), - Credentials: cred, - } - - s, err := session.NewSession(&config) - if err != nil { - panic(err) - } - - client := s3.New(s) - - return &S3Client{ - s3Client: client, - s3Session: s, - bucket: options.Bucket, - } -} - -func (s *S3Client) Client() *s3.S3 { - - return s.s3Client -} -func (s *S3Client) Session() *session.Session { - return s.s3Session -} - -func (s *S3Client) Bucket() *string { - return aws.String(s.bucket) -} diff --git a/pkg/simple/client/s3/fake/fakes3.go b/pkg/simple/client/s3/fake/fakes3.go new file mode 100644 index 000000000..6a3b5c247 --- /dev/null +++ b/pkg/simple/client/s3/fake/fakes3.go @@ -0,0 +1,47 @@ +package fake + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/s3" + "io" +) + +type FakeS3 struct { + Storage map[string]*Object +} + +func NewFakeS3(objects ...*Object) *FakeS3 { + s3 := &FakeS3{Storage: map[string]*Object{}} + for _, object := range objects { + s3.Storage[object.Key] = object + } + return s3 +} + +type Object struct { + Key string + FileName string + Body io.Reader +} + +func (s *FakeS3) Upload(key, fileName string, body io.Reader) error { + s.Storage[key] = &Object{ + Key: key, + FileName: fileName, + Body: body, + } + return nil +} + +func (s *FakeS3) GetDownloadURL(key string, fileName string) (string, error) { + if o, ok := s.Storage[key]; ok { + return fmt.Sprintf("http://%s/%s", o.Key, fileName), nil + } + return "", awserr.New(s3.ErrCodeNoSuchKey, "no such object", nil) +} + +func (s *FakeS3) Delete(key string) error { + delete(s.Storage, key) + return nil +} diff --git a/pkg/simple/client/s3/fake/fakes3_test.go b/pkg/simple/client/s3/fake/fakes3_test.go new file mode 100644 index 000000000..7a103e1bf --- /dev/null +++ b/pkg/simple/client/s3/fake/fakes3_test.go @@ -0,0 +1,52 @@ +package fake + +import ( + "fmt" + "testing" +) + +func TestFakeS3(t *testing.T) { + s3 := NewFakeS3() + key := "hello" + fileName := "world" + err := s3.Upload(key, fileName, nil) + if err != nil { + t.Fatal(err) + } + o, ok := s3.Storage["hello"] + if !ok { + t.Fatal("should have hello object") + } + if o.Key != key || o.FileName != fileName { + t.Fatalf("Key should be %s, FileName should be %s", key, fileName) + } + + url, err := s3.GetDownloadURL(key, fileName+"1") + if err != nil { + t.Fatal(err) + } + if url != fmt.Sprintf("http://%s/%s", key, fileName+"1") { + t.Fatalf("url should be %s", fmt.Sprintf("http://%s/%s", key, fileName+"1")) + } + + url, err = s3.GetDownloadURL(key, fileName+"2") + if err != nil { + t.Fatal(err) + } + if url != fmt.Sprintf("http://%s/%s", key, fileName+"2") { + t.Fatalf("url should be %s", fmt.Sprintf("http://%s/%s", key, fileName+"2")) + } + + err = s3.Delete(key) + if err != nil { + t.Fatal(err) + } + _, ok = s3.Storage["hello"] + if ok { + t.Fatal("should not have hello object") + } + err = s3.Delete(key) + if err != nil { + t.Fatal(err) + } +} diff --git a/pkg/simple/client/s3/interface.go b/pkg/simple/client/s3/interface.go new file mode 100644 index 000000000..96672ca0f --- /dev/null +++ b/pkg/simple/client/s3/interface.go @@ -0,0 +1,15 @@ +package s3 + +import ( + "io" +) + +type Interface interface { + // Upload uploads a object to storage and returns object location if succeeded + Upload(key, fileName string, body io.Reader) error + + GetDownloadURL(key string, fileName string) (string, error) + + // Delete deletes an object by its key + Delete(key string) error +} diff --git a/pkg/simple/client/s3/options.go b/pkg/simple/client/s3/options.go new file mode 100644 index 000000000..68c37ad09 --- /dev/null +++ b/pkg/simple/client/s3/options.go @@ -0,0 +1,69 @@ +package s3 + +import ( + "github.com/spf13/pflag" + "kubesphere.io/kubesphere/pkg/utils/reflectutils" +) + +// Options contains configuration to access a s3 service +type Options struct { + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` + Region string `json:"region,omitempty" yaml:"region"` + DisableSSL bool `json:"disableSSL" yaml:"disableSSL"` + ForcePathStyle bool `json:"forcePathStyle" yaml:"forcePathStyle"` + AccessKeyID string `json:"accessKeyID,omitempty" yaml:"accessKeyID"` + SecretAccessKey string `json:"secretAccessKey,omitempty" yaml:"secretAccessKey"` + SessionToken string `json:"sessionToken,omitempty" yaml:"sessionToken"` + Bucket string `json:"bucket,omitempty" yaml:"bucket"` +} + +// NewS3Options creates a default disabled Options(empty endpoint) +func NewS3Options() *Options { + return &Options{ + Endpoint: "", + Region: "us-east-1", + DisableSSL: true, + ForcePathStyle: true, + AccessKeyID: "AKIAIOSFODNN7EXAMPLE", + SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + SessionToken: "", + Bucket: "s2i-binaries", + } +} + +// Validate check options values +func (s *Options) Validate() []error { + var errors []error + + return errors +} + +// ApplyTo overrides options if it's valid, which endpoint is not empty +func (s *Options) ApplyTo(options *Options) { + if s.Endpoint != "" { + reflectutils.Override(options, s) + } +} + +// AddFlags add options flags to command line flags, +// if s3-endpoint if left empty, following options will be ignored +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.Endpoint, "s3-endpoint", c.Endpoint, ""+ + "Endpoint to access to s3 object storage service, if left blank, the following options "+ + "will be ignored.") + + fs.StringVar(&s.Region, "s3-region", c.Region, ""+ + "Region of s3 that will access to, like us-east-1.") + + fs.StringVar(&s.AccessKeyID, "s3-access-key-id", c.AccessKeyID, "access key of s2i s3") + + fs.StringVar(&s.SecretAccessKey, "s3-secret-access-key", c.SecretAccessKey, "secret access key of s2i s3") + + fs.StringVar(&s.SessionToken, "s3-session-token", c.SessionToken, "session token of s2i s3") + + fs.StringVar(&s.Bucket, "s3-bucket", c.Bucket, "bucket name of s2i s3") + + fs.BoolVar(&s.DisableSSL, "s3-disable-SSL", c.DisableSSL, "disable ssl") + + fs.BoolVar(&s.ForcePathStyle, "s3-force-path-style", c.ForcePathStyle, "force path style") +} diff --git a/pkg/simple/client/s3/s3.go b/pkg/simple/client/s3/s3.go new file mode 100644 index 000000000..244f78c32 --- /dev/null +++ b/pkg/simple/client/s3/s3.go @@ -0,0 +1,91 @@ +package s3 + +import ( + "code.cloudfoundry.org/bytefmt" + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "io" + "k8s.io/klog" + "time" +) + +type Client struct { + s3Client *s3.S3 + s3Session *session.Session + bucket string +} + +func (s *Client) Upload(key, fileName string, body io.Reader) error { + uploader := s3manager.NewUploader(s.s3Session, func(uploader *s3manager.Uploader) { + uploader.PartSize = 5 * bytefmt.MEGABYTE + uploader.LeavePartsOnError = true + }) + _, err := uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + Body: body, + ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)), + }) + return err +} + +func (s *Client) GetDownloadURL(key string, fileName string) (string, error) { + req, _ := s.s3Client.GetObjectRequest(&s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)), + }) + return req.Presign(5 * time.Minute) +} + +func (s *Client) Delete(key string) error { + _, err := s.s3Client.DeleteObject( + &s3.DeleteObjectInput{Bucket: aws.String(s.bucket), + Key: aws.String(key), + }) + if err != nil { + return err + } + return nil +} + +func NewS3Client(options *Options) (Interface, error) { + cred := credentials.NewStaticCredentials(options.AccessKeyID, options.SecretAccessKey, options.SessionToken) + + config := aws.Config{ + Region: aws.String(options.Region), + Endpoint: aws.String(options.Endpoint), + DisableSSL: aws.Bool(options.DisableSSL), + S3ForcePathStyle: aws.Bool(options.ForcePathStyle), + Credentials: cred, + } + + s, err := session.NewSession(&config) + if err != nil { + klog.Error(err) + return nil, err + } + + var c Client + + c.s3Client = s3.New(s) + c.s3Session = s + c.bucket = options.Bucket + + return &c, nil +} + +func (s *Client) Client() *s3.S3 { + return s.s3Client +} +func (s *Client) Session() *session.Session { + return s.s3Session +} + +func (s *Client) Bucket() *string { + return aws.String(s.bucket) +} diff --git a/pkg/simple/client/servicemesh/options.go b/pkg/simple/client/servicemesh/options.go index 7056228e8..fb3bb4d76 100644 --- a/pkg/simple/client/servicemesh/options.go +++ b/pkg/simple/client/servicemesh/options.go @@ -2,7 +2,7 @@ package servicemesh import "github.com/spf13/pflag" -type ServiceMeshOptions struct { +type Options struct { // istio pilot discovery service url IstioPilotHost string `json:"istioPilotHost,omitempty" yaml:"istioPilotHost"` @@ -15,21 +15,21 @@ type ServiceMeshOptions struct { } // NewServiceMeshOptions returns a `zero` instance -func NewServiceMeshOptions() *ServiceMeshOptions { - return &ServiceMeshOptions{ +func NewServiceMeshOptions() *Options { + return &Options{ IstioPilotHost: "", JaegerQueryHost: "", ServicemeshPrometheusHost: "", } } -func (s *ServiceMeshOptions) Validate() []error { +func (s *Options) Validate() []error { errors := []error{} return errors } -func (s *ServiceMeshOptions) ApplyTo(options *ServiceMeshOptions) { +func (s *Options) ApplyTo(options *Options) { if s.ServicemeshPrometheusHost != "" { options.ServicemeshPrometheusHost = s.ServicemeshPrometheusHost } @@ -43,13 +43,13 @@ func (s *ServiceMeshOptions) ApplyTo(options *ServiceMeshOptions) { } } -func (s *ServiceMeshOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.IstioPilotHost, "istio-pilot-host", s.IstioPilotHost, ""+ +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.IstioPilotHost, "istio-pilot-host", c.IstioPilotHost, ""+ "istio pilot discovery service url") - fs.StringVar(&s.JaegerQueryHost, "jaeger-query-host", s.JaegerQueryHost, ""+ + fs.StringVar(&s.JaegerQueryHost, "jaeger-query-host", c.JaegerQueryHost, ""+ "jaeger query service url") - fs.StringVar(&s.ServicemeshPrometheusHost, "servicemesh-prometheus-host", s.ServicemeshPrometheusHost, ""+ + fs.StringVar(&s.ServicemeshPrometheusHost, "servicemesh-prometheus-host", c.ServicemeshPrometheusHost, ""+ "prometheus service for servicemesh") } diff --git a/pkg/simple/client/sonarqube/interface.go b/pkg/simple/client/sonarqube/interface.go new file mode 100644 index 000000000..de1b78774 --- /dev/null +++ b/pkg/simple/client/sonarqube/interface.go @@ -0,0 +1,72 @@ +package sonarqube + +import ( + sonargo "github.com/kubesphere/sonargo/sonar" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" +) + +type SonarInterface interface { + GetSonarResultsByTaskIds(taskId ...string) ([]*SonarStatus, error) +} + +type sonarQube struct { + client *sonargo.Client +} + +func NewSonar(client *sonargo.Client) *sonarQube { + return &sonarQube{client: client} +} + +const ( + SonarAnalysisActionClass = "hudson.plugins.sonar.action.SonarAnalysisAction" + SonarMetricKeys = "alert_status,quality_gate_details,bugs,new_bugs,reliability_rating,new_reliability_rating,vulnerabilities,new_vulnerabilities,security_rating,new_security_rating,code_smells,new_code_smells,sqale_rating,new_maintainability_rating,sqale_index,new_technical_debt,coverage,new_coverage,new_lines_to_cover,tests,duplicated_lines_density,new_duplicated_lines_density,duplicated_blocks,ncloc,ncloc_language_distribution,projects,new_lines" + SonarAdditionalFields = "metrics,periods" +) + +type SonarStatus struct { + Measures *sonargo.MeasuresComponentObject `json:"measures,omitempty"` + Issues *sonargo.IssuesSearchObject `json:"issues,omitempty"` + GeneralAction *devops.GeneralAction `json:"generalAction,omitempty"` + Task *sonargo.CeTaskObject `json:"task,omitempty"` +} + +func (s *sonarQube) GetSonarResultsByTaskIds(taskIds ...string) ([]*SonarStatus, error) { + sonarStatuses := make([]*SonarStatus, 0) + for _, taskId := range taskIds { + sonarStatus := &SonarStatus{} + taskOptions := &sonargo.CeTaskOption{ + Id: taskId, + } + ceTask, _, err := s.client.Ce.Task(taskOptions) + if err != nil { + klog.Errorf("get sonar task error [%+v]", err) + continue + } + sonarStatus.Task = ceTask + measuresComponentOption := &sonargo.MeasuresComponentOption{ + Component: ceTask.Task.ComponentKey, + AdditionalFields: SonarAdditionalFields, + MetricKeys: SonarMetricKeys, + } + measures, _, err := s.client.Measures.Component(measuresComponentOption) + if err != nil { + klog.Errorf("get sonar task error [%+v]", err) + continue + } + sonarStatus.Measures = measures + + issuesSearchOption := &sonargo.IssuesSearchOption{ + AdditionalFields: "_all", + ComponentKeys: ceTask.Task.ComponentKey, + Resolved: "false", + Ps: "10", + S: "FILE_LINE", + Facets: "severities,types", + } + issuesSearch, _, err := s.client.Issues.Search(issuesSearchOption) + sonarStatus.Issues = issuesSearch + sonarStatuses = append(sonarStatuses, sonarStatus) + } + return sonarStatuses, nil +} diff --git a/pkg/simple/client/sonarqube/options.go b/pkg/simple/client/sonarqube/options.go index 92343c7a1..821d02f56 100644 --- a/pkg/simple/client/sonarqube/options.go +++ b/pkg/simple/client/sonarqube/options.go @@ -4,39 +4,35 @@ import ( "github.com/spf13/pflag" ) -type SonarQubeOptions struct { +type Options struct { Host string `json:",omitempty" yaml:"host" description:"SonarQube service host address"` Token string `json:",omitempty" yaml:"token" description:"SonarQube service token"` } -func NewSonarQubeOptions() *SonarQubeOptions { - return &SonarQubeOptions{ +func NewSonarQubeOptions() *Options { + return &Options{ Host: "", Token: "", } } -func NewDefaultSonarQubeOptions() *SonarQubeOptions { - return NewSonarQubeOptions() -} - -func (s *SonarQubeOptions) Validate() []error { - errors := []error{} +func (s *Options) Validate() []error { + var errors []error return errors } -func (s *SonarQubeOptions) ApplyTo(options *SonarQubeOptions) { +func (s *Options) ApplyTo(options *Options) { if s.Host != "" { options.Host = s.Host options.Token = s.Token } } -func (s *SonarQubeOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Host, "sonarqube-host", s.Host, ""+ +func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { + fs.StringVar(&s.Host, "sonarqube-host", c.Host, ""+ "Sonarqube service address, if left empty, following sonarqube options will be ignored.") - fs.StringVar(&s.Token, "sonarqube-token", s.Token, ""+ + fs.StringVar(&s.Token, "sonarqube-token", c.Token, ""+ "Sonarqube service access token.") } diff --git a/pkg/simple/client/sonarqube/sonarqube.go b/pkg/simple/client/sonarqube/sonarqube.go index a6453fe8a..a622d9607 100644 --- a/pkg/simple/client/sonarqube/sonarqube.go +++ b/pkg/simple/client/sonarqube/sonarqube.go @@ -7,11 +7,11 @@ import ( "strings" ) -type SonarQubeClient struct { +type Client struct { client *sonargo.Client } -func NewSonarQubeClient(options *SonarQubeOptions) (*SonarQubeClient, error) { +func NewSonarQubeClient(options *Options) (*Client, error) { var endpoint string if strings.HasSuffix(options.Host, "/") { @@ -26,10 +26,10 @@ func NewSonarQubeClient(options *SonarQubeOptions) (*SonarQubeClient, error) { return nil, err } - return &SonarQubeClient{client: sonar}, err + return &Client{client: sonar}, err } -func NewSonarQubeClientOrDie(options *SonarQubeOptions) *SonarQubeClient { +func NewSonarQubeClientOrDie(options *Options) *Client { var endpoint string if strings.HasSuffix(options.Host, "/") { @@ -44,11 +44,11 @@ func NewSonarQubeClientOrDie(options *SonarQubeOptions) *SonarQubeClient { panic(err) } - return &SonarQubeClient{client: sonar} + return &Client{client: sonar} } // return sonarqube client // Also we can wrap some methods to avoid direct use sonar client -func (s *SonarQubeClient) SonarQube() *sonargo.Client { +func (s *Client) SonarQube() *sonargo.Client { return s.client } diff --git a/pkg/utils/jwtutil/jwt.go b/pkg/utils/jwtutil/jwt.go deleted file mode 100644 index fe95334b0..000000000 --- a/pkg/utils/jwtutil/jwt.go +++ /dev/null @@ -1,63 +0,0 @@ -/* - - Copyright 2019 The KubeSphere 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 jwtutil - -import ( - "fmt" - "github.com/dgrijalva/jwt-go" -) - -const secretEnv = "JWT_SECRET" - -var secret []byte - -func Setup(key string) { - secret = []byte(key) -} - -func MustSigned(claims jwt.MapClaims) string { - uToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - token, err := uToken.SignedString(secret) - if err != nil { - panic(err) - } - return token -} - -func provideKey(token *jwt.Token) (interface{}, error) { - if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { - return secret, nil - } else { - return nil, fmt.Errorf("expect token signed with HMAC but got %v", token.Header["alg"]) - } -} - -func ValidateToken(uToken string) (*jwt.Token, error) { - - if len(uToken) == 0 { - return nil, fmt.Errorf("token length is zero") - } - - token, err := jwt.Parse(uToken, provideKey) - - if err != nil { - return nil, err - } - - return token, nil -} diff --git a/pkg/utils/k8sutil/k8sutil.go b/pkg/utils/k8sutil/k8sutil.go index 7d01fa424..bff210a54 100644 --- a/pkg/utils/k8sutil/k8sutil.go +++ b/pkg/utils/k8sutil/k8sutil.go @@ -18,9 +18,7 @@ package k8sutil import ( - "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "kubesphere.io/kubesphere/pkg/models" ) func IsControlledBy(reference []metav1.OwnerReference, kind string, name string) bool { @@ -40,34 +38,3 @@ func GetControlledWorkspace(reference []metav1.OwnerReference) string { } return "" } - -func ContainsUser(subjects interface{}, username string) bool { - switch subjects.(type) { - case []*v1.Subject: - for _, subject := range subjects.([]*v1.Subject) { - if subject.Kind == v1.UserKind && subject.Name == username { - return true - } - } - case []v1.Subject: - for _, subject := range subjects.([]v1.Subject) { - if subject.Kind == v1.UserKind && subject.Name == username { - return true - } - } - case []models.User: - for _, u := range subjects.([]models.User) { - if u.Username == username { - return true - } - } - - case []*models.User: - for _, u := range subjects.([]*models.User) { - if u.Username == username { - return true - } - } - } - return false -} diff --git a/pkg/version/version.go b/pkg/version/version.go index 27a2d5882..6df6f0acf 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -17,3 +17,5 @@ */ package version + +var Version = "v0.0.0" diff --git a/tools/cmd/crd-doc-gen/main.go b/tools/cmd/crd-doc-gen/main.go index f69a2449e..2edefe5d7 100644 --- a/tools/cmd/crd-doc-gen/main.go +++ b/tools/cmd/crd-doc-gen/main.go @@ -4,24 +4,25 @@ import ( "flag" "io/ioutil" "k8s.io/apimachinery/pkg/api/meta" - servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" + urlruntime "k8s.io/apimachinery/pkg/util/runtime" + clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1" + devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" "kubesphere.io/kubesphere/tools/lib" "log" "os" "path/filepath" "github.com/go-openapi/spec" - s2iv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" - urlruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/kube-openapi/pkg/common" devopsinstall "kubesphere.io/kubesphere/pkg/apis/devops/crdinstall" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" networkinstall "kubesphere.io/kubesphere/pkg/apis/network/crdinstall" networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1" servicemeshinstall "kubesphere.io/kubesphere/pkg/apis/servicemesh/crdinstall" + servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2" tenantinstall "kubesphere.io/kubesphere/pkg/apis/tenant/crdinstall" tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" ) @@ -38,17 +39,15 @@ func main() { Scheme = runtime.NewScheme() Codecs = serializer.NewCodecFactory(Scheme) ) - // tmp install s2i api group because - // cannot use Scheme (type *"kubesphere.io/kubesphere/vendor/k8s.io/apimachinery/pkg/runtime".Scheme) as - // type *"github.com/kubesphere/s2ioperator/vendor/k8s.io/apimachinery/pkg/runtime".Scheme in argument to "github.com/kubesphere/s2ioperator/pkg/apis/devops/install".Install - urlruntime.Must(s2iv1alpha1.AddToScheme(Scheme)) - urlruntime.Must(Scheme.SetVersionPriority(s2iv1alpha1.SchemeGroupVersion)) servicemeshinstall.Install(Scheme) tenantinstall.Install(Scheme) networkinstall.Install(Scheme) devopsinstall.Install(Scheme) + urlruntime.Must(clusterv1alpha1.AddToScheme(Scheme)) + urlruntime.Must(Scheme.SetVersionPriority(clusterv1alpha1.SchemeGroupVersion)) + mapper := meta.NewDefaultRESTMapper(nil) mapper.AddSpecific(servicemeshv1alpha2.SchemeGroupVersion.WithKind(servicemeshv1alpha2.ResourceKindServicePolicy), @@ -63,24 +62,38 @@ func main() { tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace), tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourceSingularWorkspace), meta.RESTScopeRoot) - mapper.AddSpecific(s2iv1alpha1.SchemeGroupVersion.WithKind(s2iv1alpha1.ResourceKindS2iBuilder), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iBuilder), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourceSingularS2iBuilder), meta.RESTScopeRoot) + mapper.AddSpecific(devopsv1alpha1.SchemeGroupVersion.WithKind(devopsv1alpha1.ResourceKindS2iBuilder), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilder), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularS2iBuilder), meta.RESTScopeRoot) - mapper.AddSpecific(s2iv1alpha1.SchemeGroupVersion.WithKind(s2iv1alpha1.ResourceKindS2iBuilderTemplate), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iBuilderTemplate), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourceSingularS2iBuilderTemplate), meta.RESTScopeRoot) + mapper.AddSpecific(devopsv1alpha1.SchemeGroupVersion.WithKind(devopsv1alpha1.ResourceKindS2iBuilderTemplate), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilderTemplate), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularS2iBuilderTemplate), meta.RESTScopeRoot) - mapper.AddSpecific(s2iv1alpha1.SchemeGroupVersion.WithKind(s2iv1alpha1.ResourceKindS2iRun), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iRun), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourceSingularS2iRun), meta.RESTScopeRoot) + mapper.AddSpecific(devopsv1alpha1.SchemeGroupVersion.WithKind(devopsv1alpha1.ResourceKindS2iRun), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iRun), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularS2iRun), meta.RESTScopeRoot) mapper.AddSpecific(devopsv1alpha1.SchemeGroupVersion.WithKind(devopsv1alpha1.ResourceKindS2iBinary), - devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularServicePolicy), - devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralServicePolicy), meta.RESTScopeRoot) + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceSingularS2iBinary), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBinary), meta.RESTScopeRoot) - mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindWorkspaceNetworkPolicy), - networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralWorkspaceNetworkPolicy), - networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularWorkspaceNetworkPolicy), meta.RESTScopeRoot) + mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindNamespaceNetworkPolicy), + networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy), + networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularNamespaceNetworkPolicy), meta.RESTScopeRoot) + mapper.AddSpecific(devopsv1alpha3.SchemeGroupVersion.WithKind(devopsv1alpha3.ResourceKindDevOpsProject), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourceSingularDevOpsProject), meta.RESTScopeRoot) + mapper.AddSpecific(devopsv1alpha3.SchemeGroupVersion.WithKind(devopsv1alpha3.ResourceKindPipeline), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralPipeline), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourceSingularPipeline), meta.RESTScopeRoot) + + mapper.AddSpecific(clusterv1alpha1.SchemeGroupVersion.WithKind(clusterv1alpha1.ResourceKindCluster), + clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralCluster), + clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesSingularCluster), meta.RESTScopeRoot) + + mapper.AddSpecific(clusterv1alpha1.SchemeGroupVersion.WithKind(clusterv1alpha1.ResourceKindCluster), + clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralCluster), + clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesSingularCluster), meta.RESTScopeRoot) spec, err := lib.RenderOpenAPISpec(lib.Config{ Scheme: Scheme, @@ -101,9 +114,10 @@ func main() { OpenAPIDefinitions: []common.GetOpenAPIDefinitions{ servicemeshv1alpha2.GetOpenAPIDefinitions, tenantv1alpha1.GetOpenAPIDefinitions, - s2iv1alpha1.GetOpenAPIDefinitions, networkv1alpha1.GetOpenAPIDefinitions, devopsv1alpha1.GetOpenAPIDefinitions, + devopsv1alpha3.GetOpenAPIDefinitions, + clusterv1alpha1.GetOpenAPIDefinitions, }, Resources: []schema.GroupVersionResource{ //TODO(runzexia) At present, the document generation requires the openapi structure of the go language, @@ -112,11 +126,14 @@ func main() { //servicemeshv1alpha2.SchemeGroupVersion.WithResource(servicemeshv1alpha2.ResourcePluralStrategy), //servicemeshv1alpha2.SchemeGroupVersion.WithResource(servicemeshv1alpha2.ResourcePluralServicePolicy), tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iRun), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iBuilderTemplate), - s2iv1alpha1.SchemeGroupVersion.WithResource(s2iv1alpha1.ResourcePluralS2iBuilder), - networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralWorkspaceNetworkPolicy), - devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourceKindS2iBinary), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBinary), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iRun), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilderTemplate), + devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilder), + networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject), + devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralPipeline), + clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralCluster), }, Mapper: mapper, }) diff --git a/tools/cmd/doc-gen/main.go b/tools/cmd/doc-gen/main.go index b4f23cca9..448eb203e 100644 --- a/tools/cmd/doc-gen/main.go +++ b/tools/cmd/doc-gen/main.go @@ -24,22 +24,37 @@ import ( "fmt" "github.com/emicklei/go-restful" "github.com/emicklei/go-restful-openapi" + "github.com/go-openapi/loads" "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + "github.com/pkg/errors" "io/ioutil" + urlruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/klog" + authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" - _ "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/install" + "kubesphere.io/kubesphere/pkg/informers" + devopsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/devops/v1alpha2" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/iam/v1alpha2" + loggingv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/logging/v1alpha2" + monitoringv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/monitoring/v1alpha3" + networkv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/network/v1alpha2" + openpitrixv1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v1" + operationsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/operations/v1alpha2" + resourcesv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha2" + resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/kapis/resources/v1alpha3" + metricsv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/servicemesh/metrics/v1alpha2" + tenantv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/tenant/v1alpha2" + terminalv1alpha2 "kubesphere.io/kubesphere/pkg/kapis/terminal/v1alpha2" + "kubesphere.io/kubesphere/pkg/models/iam/am" + "kubesphere.io/kubesphere/pkg/models/iam/im" + "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "kubesphere.io/kubesphere/pkg/simple/client/k8s" + fakes3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake" + "kubesphere.io/kubesphere/pkg/version" "log" - // Install apis - _ "kubesphere.io/kubesphere/pkg/kapis/devops/install" - _ "kubesphere.io/kubesphere/pkg/kapis/iam/install" - _ "kubesphere.io/kubesphere/pkg/kapis/logging/install" - _ "kubesphere.io/kubesphere/pkg/kapis/monitoring/install" - _ "kubesphere.io/kubesphere/pkg/kapis/openpitrix/install" - _ "kubesphere.io/kubesphere/pkg/kapis/operations/install" - _ "kubesphere.io/kubesphere/pkg/kapis/resources/install" - _ "kubesphere.io/kubesphere/pkg/kapis/tenant/install" - _ "kubesphere.io/kubesphere/pkg/kapis/terminal/install" ) var output string @@ -50,14 +65,65 @@ func init() { func main() { flag.Parse() - generateSwaggerJson() + swaggerSpec := generateSwaggerJson() + + err := validateSpec(swaggerSpec) + if err != nil { + klog.Warningf("Swagger specification has errors") + } } -func generateSwaggerJson() { +func validateSpec(apiSpec []byte) error { + + swaggerDoc, err := loads.Analyzed(apiSpec, "") + if err != nil { + return err + } + + // Attempts to report about all errors + validate.SetContinueOnErrors(true) + + v := validate.NewSpecValidator(swaggerDoc.Schema(), strfmt.Default) + result, _ := v.Validate(swaggerDoc) + + if result.HasWarnings() { + log.Printf("See warnings below:\n") + for _, desc := range result.Warnings { + log.Printf("- WARNING: %s\n", desc.Error()) + } + + } + if result.HasErrors() { + str := fmt.Sprintf("The swagger spec is invalid against swagger specification %s.\nSee errors below:\n", swaggerDoc.Version()) + for _, desc := range result.Errors { + str += fmt.Sprintf("- %s\n", desc.Error()) + } + log.Println(str) + return errors.New(str) + } + + return nil +} + +func generateSwaggerJson() []byte { container := runtime.Container + clientsets := k8s.NewNullClient() - apiTree(container) + informerFactory := informers.NewNullInformerFactory() + + urlruntime.Must(devopsv1alpha2.AddToContainer(container, informerFactory.KubeSphereSharedInformerFactory(), &fake.Devops{}, nil, clientsets.KubeSphere(), fakes3.NewFakeS3())) + urlruntime.Must(iamv1alpha2.AddToContainer(container, im.NewOperator(clientsets.KubeSphere(), informerFactory.KubeSphereSharedInformerFactory()), am.NewAMOperator(clientsets.KubeSphere(), informerFactory.KubeSphereSharedInformerFactory()), authoptions.NewAuthenticateOptions())) + urlruntime.Must(loggingv1alpha2.AddToContainer(container, clientsets, nil)) + urlruntime.Must(monitoringv1alpha3.AddToContainer(container, clientsets.Kubernetes(), nil)) + urlruntime.Must(openpitrixv1.AddToContainer(container, informerFactory, nil)) + urlruntime.Must(operationsv1alpha2.AddToContainer(container, clientsets.Kubernetes())) + urlruntime.Must(resourcesv1alpha2.AddToContainer(container, clientsets.Kubernetes(), informerFactory)) + urlruntime.Must(resourcesv1alpha3.AddToContainer(container, informerFactory)) + urlruntime.Must(tenantv1alpha2.AddToContainer(container, clientsets, informerFactory)) + urlruntime.Must(terminalv1alpha2.AddToContainer(container, clientsets.Kubernetes(), nil)) + urlruntime.Must(metricsv1alpha2.AddToContainer(container)) + urlruntime.Must(networkv1alpha2.AddToContainer(container, "")) config := restfulspec.Config{ WebServices: container.RegisteredWebServices(), @@ -103,7 +169,7 @@ func generateSwaggerJson() { }, { Name: "Logging", - Tags: []string{constants.LogQueryTag, constants.FluentBitSetting}, + Tags: []string{constants.LogQueryTag}, }, }) @@ -113,6 +179,8 @@ func generateSwaggerJson() { log.Fatal(err) } log.Printf("successfully written to %s", output) + + return data } func enrichSwaggerObject(swo *spec.Swagger) { @@ -129,7 +197,7 @@ func enrichSwaggerObject(swo *spec.Swagger) { Name: "Apache", URL: "http://www.apache.org/licenses/", }, - Version: "2.0.2", + Version: version.Version, }, } diff --git a/tools/lib/render.go b/tools/lib/render.go index aa1b3c482..0d99600db 100644 --- a/tools/lib/render.go +++ b/tools/lib/render.go @@ -86,15 +86,15 @@ func RenderOpenAPISpec(cfg Config) (string, error) { { // api router map - table := map[schema.GroupVersion]map[string]ResourceInfo{} + table := map[string]map[string]ResourceInfo{} for _, gvr := range cfg.Resources { var resmap map[string]ResourceInfo // init ResourceInfo map - if m, found := table[gvr.GroupVersion()]; found { + if m, found := table[gvr.Group]; found { resmap = m } else { resmap = map[string]ResourceInfo{} - table[gvr.GroupVersion()] = resmap + table[gvr.Group] = resmap } gvk, err := cfg.Mapper.KindFor(gvr) @@ -119,13 +119,15 @@ func RenderOpenAPISpec(cfg Config) (string, error) { } } - for gv, resmap := range table { - apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(gv.Group, cfg.Scheme, metav1.ParameterCodec, cfg.Codecs) - apiGroupInfo.MetaGroupVersion = &gv - storage := map[string]rest.Storage{} + for g, resmap := range table { + apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(g, cfg.Scheme, metav1.ParameterCodec, cfg.Codecs) + storage := map[string]map[string]rest.Storage{} for r, stuff := range resmap { - storage[r] = NewREST(stuff) - storage[r+"/status"] = NewStatusREST( + if storage[stuff.gvk.Version] == nil { + storage[stuff.gvk.Version] = map[string]rest.Storage{} + } + storage[stuff.gvk.Version][r] = NewREST(stuff) + storage[stuff.gvk.Version][r+"/status"] = NewStatusREST( StatusResourceInfo{ gvk: struct { Group string @@ -135,8 +137,9 @@ func RenderOpenAPISpec(cfg Config) (string, error) { obj: stuff.obj, }) } - - apiGroupInfo.VersionedResourcesStorageMap[gv.Version] = storage + for version, s := range storage { + apiGroupInfo.VersionedResourcesStorageMap[version] = s + } if err := genericServer.InstallAPIGroup(&apiGroupInfo); err != nil { log.Fatal(err) diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go deleted file mode 100644 index 125b7033c..000000000 --- a/vendor/cloud.google.com/go/compute/metadata/metadata.go +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright 2014 Google LLC -// -// 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 metadata provides access to Google Compute Engine (GCE) -// metadata and API service accounts. -// -// This package is a wrapper around the GCE metadata service, -// as documented at https://developers.google.com/compute/docs/metadata. -package metadata // import "cloud.google.com/go/compute/metadata" - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/url" - "os" - "runtime" - "strings" - "sync" - "time" -) - -const ( - // metadataIP is the documented metadata server IP address. - metadataIP = "169.254.169.254" - - // metadataHostEnv is the environment variable specifying the - // GCE metadata hostname. If empty, the default value of - // metadataIP ("169.254.169.254") is used instead. - // This is variable name is not defined by any spec, as far as - // I know; it was made up for the Go package. - metadataHostEnv = "GCE_METADATA_HOST" - - userAgent = "gcloud-golang/0.1" -) - -type cachedValue struct { - k string - trim bool - mu sync.Mutex - v string -} - -var ( - projID = &cachedValue{k: "project/project-id", trim: true} - projNum = &cachedValue{k: "project/numeric-project-id", trim: true} - instID = &cachedValue{k: "instance/id", trim: true} -) - -var ( - defaultClient = &Client{hc: &http.Client{ - Transport: &http.Transport{ - Dial: (&net.Dialer{ - Timeout: 2 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - ResponseHeaderTimeout: 2 * time.Second, - }, - }} - subscribeClient = &Client{hc: &http.Client{ - Transport: &http.Transport{ - Dial: (&net.Dialer{ - Timeout: 2 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - }, - }} -) - -// NotDefinedError is returned when requested metadata is not defined. -// -// The underlying string is the suffix after "/computeMetadata/v1/". -// -// This error is not returned if the value is defined to be the empty -// string. -type NotDefinedError string - -func (suffix NotDefinedError) Error() string { - return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) -} - -func (c *cachedValue) get(cl *Client) (v string, err error) { - defer c.mu.Unlock() - c.mu.Lock() - if c.v != "" { - return c.v, nil - } - if c.trim { - v, err = cl.getTrimmed(c.k) - } else { - v, err = cl.Get(c.k) - } - if err == nil { - c.v = v - } - return -} - -var ( - onGCEOnce sync.Once - onGCE bool -) - -// OnGCE reports whether this process is running on Google Compute Engine. -func OnGCE() bool { - onGCEOnce.Do(initOnGCE) - return onGCE -} - -func initOnGCE() { - onGCE = testOnGCE() -} - -func testOnGCE() bool { - // The user explicitly said they're on GCE, so trust them. - if os.Getenv(metadataHostEnv) != "" { - return true - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - resc := make(chan bool, 2) - - // Try two strategies in parallel. - // See https://github.com/googleapis/google-cloud-go/issues/194 - go func() { - req, _ := http.NewRequest("GET", "http://"+metadataIP, nil) - req.Header.Set("User-Agent", userAgent) - res, err := defaultClient.hc.Do(req.WithContext(ctx)) - if err != nil { - resc <- false - return - } - defer res.Body.Close() - resc <- res.Header.Get("Metadata-Flavor") == "Google" - }() - - go func() { - addrs, err := net.LookupHost("metadata.google.internal") - if err != nil || len(addrs) == 0 { - resc <- false - return - } - resc <- strsContains(addrs, metadataIP) - }() - - tryHarder := systemInfoSuggestsGCE() - if tryHarder { - res := <-resc - if res { - // The first strategy succeeded, so let's use it. - return true - } - // Wait for either the DNS or metadata server probe to - // contradict the other one and say we are running on - // GCE. Give it a lot of time to do so, since the system - // info already suggests we're running on a GCE BIOS. - timer := time.NewTimer(5 * time.Second) - defer timer.Stop() - select { - case res = <-resc: - return res - case <-timer.C: - // Too slow. Who knows what this system is. - return false - } - } - - // There's no hint from the system info that we're running on - // GCE, so use the first probe's result as truth, whether it's - // true or false. The goal here is to optimize for speed for - // users who are NOT running on GCE. We can't assume that - // either a DNS lookup or an HTTP request to a blackholed IP - // address is fast. Worst case this should return when the - // metaClient's Transport.ResponseHeaderTimeout or - // Transport.Dial.Timeout fires (in two seconds). - return <-resc -} - -// systemInfoSuggestsGCE reports whether the local system (without -// doing network requests) suggests that we're running on GCE. If this -// returns true, testOnGCE tries a bit harder to reach its metadata -// server. -func systemInfoSuggestsGCE() bool { - if runtime.GOOS != "linux" { - // We don't have any non-Linux clues available, at least yet. - return false - } - slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name") - name := strings.TrimSpace(string(slurp)) - return name == "Google" || name == "Google Compute Engine" -} - -// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no -// ResponseHeaderTimeout). -func Subscribe(suffix string, fn func(v string, ok bool) error) error { - return subscribeClient.Subscribe(suffix, fn) -} - -// Get calls Client.Get on the default client. -func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } - -// ProjectID returns the current instance's project ID string. -func ProjectID() (string, error) { return defaultClient.ProjectID() } - -// NumericProjectID returns the current instance's numeric project ID. -func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } - -// InternalIP returns the instance's primary internal IP address. -func InternalIP() (string, error) { return defaultClient.InternalIP() } - -// ExternalIP returns the instance's primary external (public) IP address. -func ExternalIP() (string, error) { return defaultClient.ExternalIP() } - -// Hostname returns the instance's hostname. This will be of the form -// ".c..internal". -func Hostname() (string, error) { return defaultClient.Hostname() } - -// InstanceTags returns the list of user-defined instance tags, -// assigned when initially creating a GCE instance. -func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } - -// InstanceID returns the current VM's numeric instance ID. -func InstanceID() (string, error) { return defaultClient.InstanceID() } - -// InstanceName returns the current VM's instance ID string. -func InstanceName() (string, error) { return defaultClient.InstanceName() } - -// Zone returns the current VM's zone, such as "us-central1-b". -func Zone() (string, error) { return defaultClient.Zone() } - -// InstanceAttributes calls Client.InstanceAttributes on the default client. -func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } - -// ProjectAttributes calls Client.ProjectAttributes on the default client. -func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } - -// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. -func InstanceAttributeValue(attr string) (string, error) { - return defaultClient.InstanceAttributeValue(attr) -} - -// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. -func ProjectAttributeValue(attr string) (string, error) { - return defaultClient.ProjectAttributeValue(attr) -} - -// Scopes calls Client.Scopes on the default client. -func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } - -func strsContains(ss []string, s string) bool { - for _, v := range ss { - if v == s { - return true - } - } - return false -} - -// A Client provides metadata. -type Client struct { - hc *http.Client -} - -// NewClient returns a Client that can be used to fetch metadata. All HTTP requests -// will use the given http.Client instead of the default client. -func NewClient(c *http.Client) *Client { - return &Client{hc: c} -} - -// getETag returns a value from the metadata service as well as the associated ETag. -// This func is otherwise equivalent to Get. -func (c *Client) getETag(suffix string) (value, etag string, err error) { - // Using a fixed IP makes it very difficult to spoof the metadata service in - // a container, which is an important use-case for local testing of cloud - // deployments. To enable spoofing of the metadata service, the environment - // variable GCE_METADATA_HOST is first inspected to decide where metadata - // requests shall go. - host := os.Getenv(metadataHostEnv) - if host == "" { - // Using 169.254.169.254 instead of "metadata" here because Go - // binaries built with the "netgo" tag and without cgo won't - // know the search suffix for "metadata" is - // ".google.internal", and this IP address is documented as - // being stable anyway. - host = metadataIP - } - u := "http://" + host + "/computeMetadata/v1/" + suffix - req, _ := http.NewRequest("GET", u, nil) - req.Header.Set("Metadata-Flavor", "Google") - req.Header.Set("User-Agent", userAgent) - res, err := c.hc.Do(req) - if err != nil { - return "", "", err - } - defer res.Body.Close() - if res.StatusCode == http.StatusNotFound { - return "", "", NotDefinedError(suffix) - } - all, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", "", err - } - if res.StatusCode != 200 { - return "", "", &Error{Code: res.StatusCode, Message: string(all)} - } - return string(all), res.Header.Get("Etag"), nil -} - -// Get returns a value from the metadata service. -// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". -// -// If the GCE_METADATA_HOST environment variable is not defined, a default of -// 169.254.169.254 will be used instead. -// -// If the requested metadata is not defined, the returned error will -// be of type NotDefinedError. -func (c *Client) Get(suffix string) (string, error) { - val, _, err := c.getETag(suffix) - return val, err -} - -func (c *Client) getTrimmed(suffix string) (s string, err error) { - s, err = c.Get(suffix) - s = strings.TrimSpace(s) - return -} - -func (c *Client) lines(suffix string) ([]string, error) { - j, err := c.Get(suffix) - if err != nil { - return nil, err - } - s := strings.Split(strings.TrimSpace(j), "\n") - for i := range s { - s[i] = strings.TrimSpace(s[i]) - } - return s, nil -} - -// ProjectID returns the current instance's project ID string. -func (c *Client) ProjectID() (string, error) { return projID.get(c) } - -// NumericProjectID returns the current instance's numeric project ID. -func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } - -// InstanceID returns the current VM's numeric instance ID. -func (c *Client) InstanceID() (string, error) { return instID.get(c) } - -// InternalIP returns the instance's primary internal IP address. -func (c *Client) InternalIP() (string, error) { - return c.getTrimmed("instance/network-interfaces/0/ip") -} - -// ExternalIP returns the instance's primary external (public) IP address. -func (c *Client) ExternalIP() (string, error) { - return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") -} - -// Hostname returns the instance's hostname. This will be of the form -// ".c..internal". -func (c *Client) Hostname() (string, error) { - return c.getTrimmed("instance/hostname") -} - -// InstanceTags returns the list of user-defined instance tags, -// assigned when initially creating a GCE instance. -func (c *Client) InstanceTags() ([]string, error) { - var s []string - j, err := c.Get("instance/tags") - if err != nil { - return nil, err - } - if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil { - return nil, err - } - return s, nil -} - -// InstanceName returns the current VM's instance ID string. -func (c *Client) InstanceName() (string, error) { - host, err := c.Hostname() - if err != nil { - return "", err - } - return strings.Split(host, ".")[0], nil -} - -// Zone returns the current VM's zone, such as "us-central1-b". -func (c *Client) Zone() (string, error) { - zone, err := c.getTrimmed("instance/zone") - // zone is of the form "projects//zones/". - if err != nil { - return "", err - } - return zone[strings.LastIndex(zone, "/")+1:], nil -} - -// InstanceAttributes returns the list of user-defined attributes, -// assigned when initially creating a GCE VM instance. The value of an -// attribute can be obtained with InstanceAttributeValue. -func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } - -// ProjectAttributes returns the list of user-defined attributes -// applying to the project as a whole, not just this VM. The value of -// an attribute can be obtained with ProjectAttributeValue. -func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } - -// InstanceAttributeValue returns the value of the provided VM -// instance attribute. -// -// If the requested attribute is not defined, the returned error will -// be of type NotDefinedError. -// -// InstanceAttributeValue may return ("", nil) if the attribute was -// defined to be the empty string. -func (c *Client) InstanceAttributeValue(attr string) (string, error) { - return c.Get("instance/attributes/" + attr) -} - -// ProjectAttributeValue returns the value of the provided -// project attribute. -// -// If the requested attribute is not defined, the returned error will -// be of type NotDefinedError. -// -// ProjectAttributeValue may return ("", nil) if the attribute was -// defined to be the empty string. -func (c *Client) ProjectAttributeValue(attr string) (string, error) { - return c.Get("project/attributes/" + attr) -} - -// Scopes returns the service account scopes for the given account. -// The account may be empty or the string "default" to use the instance's -// main account. -func (c *Client) Scopes(serviceAccount string) ([]string, error) { - if serviceAccount == "" { - serviceAccount = "default" - } - return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") -} - -// Subscribe subscribes to a value from the metadata service. -// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". -// The suffix may contain query parameters. -// -// Subscribe calls fn with the latest metadata value indicated by the provided -// suffix. If the metadata value is deleted, fn is called with the empty string -// and ok false. Subscribe blocks until fn returns a non-nil error or the value -// is deleted. Subscribe returns the error value returned from the last call to -// fn, which may be nil when ok == false. -func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { - const failedSubscribeSleep = time.Second * 5 - - // First check to see if the metadata value exists at all. - val, lastETag, err := c.getETag(suffix) - if err != nil { - return err - } - - if err := fn(val, true); err != nil { - return err - } - - ok := true - if strings.ContainsRune(suffix, '?') { - suffix += "&wait_for_change=true&last_etag=" - } else { - suffix += "?wait_for_change=true&last_etag=" - } - for { - val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) - if err != nil { - if _, deleted := err.(NotDefinedError); !deleted { - time.Sleep(failedSubscribeSleep) - continue // Retry on other errors. - } - ok = false - } - lastETag = etag - - if err := fn(val, ok); err != nil || !ok { - return err - } - } -} - -// Error contains an error response from the server. -type Error struct { - // Code is the HTTP response status code. - Code int - // Message is the server response message. - Message string -} - -func (e *Error) Error() string { - return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message) -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2015 Microsoft Corporation - - 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/github.com/Azure/go-autorest/autorest/adal/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/adal/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2015 Microsoft Corporation - - 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/github.com/Azure/go-autorest/autorest/adal/README.md b/vendor/github.com/Azure/go-autorest/autorest/adal/README.md deleted file mode 100644 index fec416a9c..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/README.md +++ /dev/null @@ -1,292 +0,0 @@ -# Azure Active Directory authentication for Go - -This is a standalone package for authenticating with Azure Active -Directory from other Go libraries and applications, in particular the [Azure SDK -for Go](https://github.com/Azure/azure-sdk-for-go). - -Note: Despite the package's name it is not related to other "ADAL" libraries -maintained in the [github.com/AzureAD](https://github.com/AzureAD) org. Issues -should be opened in [this repo's](https://github.com/Azure/go-autorest/issues) -or [the SDK's](https://github.com/Azure/azure-sdk-for-go/issues) issue -trackers. - -## Install - -```bash -go get -u github.com/Azure/go-autorest/autorest/adal -``` - -## Usage - -An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) by following these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli). - -### Register an Azure AD Application with secret - - -1. Register a new application with a `secret` credential - - ``` - az ad app create \ - --display-name example-app \ - --homepage https://example-app/home \ - --identifier-uris https://example-app/app \ - --password secret - ``` - -2. Create a service principal using the `Application ID` from previous step - - ``` - az ad sp create --id "Application ID" - ``` - - * Replace `Application ID` with `appId` from step 1. - -### Register an Azure AD Application with certificate - -1. Create a private key - - ``` - openssl genrsa -out "example-app.key" 2048 - ``` - -2. Create the certificate - - ``` - openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr" - openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000 - ``` - -3. Create the PKCS12 version of the certificate containing also the private key - - ``` - openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass: - - ``` - -4. Register a new application with the certificate content form `example-app.crt` - - ``` - certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)" - - az ad app create \ - --display-name example-app \ - --homepage https://example-app/home \ - --identifier-uris https://example-app/app \ - --key-usage Verify --end-date 2018-01-01 \ - --key-value "${certificateContents}" - ``` - -5. Create a service principal using the `Application ID` from previous step - - ``` - az ad sp create --id "APPLICATION_ID" - ``` - - * Replace `APPLICATION_ID` with `appId` from step 4. - - -### Grant the necessary permissions - -Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained -level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles) -which can be assigned to a service principal of an Azure AD application depending of your needs. - -``` -az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME" -``` - -* Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step. -* Replace the `ROLE_NAME` with a role name of your choice. - -It is also possible to define custom role definitions. - -``` -az role definition create --role-definition role-definition.json -``` - -* Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file. - - -### Acquire Access Token - -The common configuration used by all flows: - -```Go -const activeDirectoryEndpoint = "https://login.microsoftonline.com/" -tenantID := "TENANT_ID" -oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID) - -applicationID := "APPLICATION_ID" - -callback := func(token adal.Token) error { - // This is called after the token is acquired -} - -// The resource for which the token is acquired -resource := "https://management.core.windows.net/" -``` - -* Replace the `TENANT_ID` with your tenant ID. -* Replace the `APPLICATION_ID` with the value from previous section. - -#### Client Credentials - -```Go -applicationSecret := "APPLICATION_SECRET" - -spt, err := adal.NewServicePrincipalToken( - *oauthConfig, - appliationID, - applicationSecret, - resource, - callbacks...) -if err != nil { - return nil, err -} - -// Acquire a new access token -err = spt.Refresh() -if (err == nil) { - token := spt.Token -} -``` - -* Replace the `APPLICATION_SECRET` with the `password` value from previous section. - -#### Client Certificate - -```Go -certificatePath := "./example-app.pfx" - -certData, err := ioutil.ReadFile(certificatePath) -if err != nil { - return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err) -} - -// Get the certificate and private key from pfx file -certificate, rsaPrivateKey, err := decodePkcs12(certData, "") -if err != nil { - return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) -} - -spt, err := adal.NewServicePrincipalTokenFromCertificate( - *oauthConfig, - applicationID, - certificate, - rsaPrivateKey, - resource, - callbacks...) - -// Acquire a new access token -err = spt.Refresh() -if (err == nil) { - token := spt.Token -} -``` - -* Update the certificate path to point to the example-app.pfx file which was created in previous section. - - -#### Device Code - -```Go -oauthClient := &http.Client{} - -// Acquire the device code -deviceCode, err := adal.InitiateDeviceAuth( - oauthClient, - *oauthConfig, - applicationID, - resource) -if err != nil { - return nil, fmt.Errorf("Failed to start device auth flow: %s", err) -} - -// Display the authentication message -fmt.Println(*deviceCode.Message) - -// Wait here until the user is authenticated -token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) -if err != nil { - return nil, fmt.Errorf("Failed to finish device auth flow: %s", err) -} - -spt, err := adal.NewServicePrincipalTokenFromManualToken( - *oauthConfig, - applicationID, - resource, - *token, - callbacks...) - -if (err == nil) { - token := spt.Token -} -``` - -#### Username password authenticate - -```Go -spt, err := adal.NewServicePrincipalTokenFromUsernamePassword( - *oauthConfig, - applicationID, - username, - password, - resource, - callbacks...) - -if (err == nil) { - token := spt.Token -} -``` - -#### Authorization code authenticate - -``` Go -spt, err := adal.NewServicePrincipalTokenFromAuthorizationCode( - *oauthConfig, - applicationID, - clientSecret, - authorizationCode, - redirectURI, - resource, - callbacks...) - -err = spt.Refresh() -if (err == nil) { - token := spt.Token -} -``` - -### Command Line Tool - -A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above. - -``` -adal -h - -Usage of ./adal: - -applicationId string - application id - -certificatePath string - path to pk12/PFC application certificate - -mode string - authentication mode (device, secret, cert, refresh) (default "device") - -resource string - resource for which the token is requested - -secret string - application secret - -tenantId string - tenant id - -tokenCachePath string - location of oath token cache (default "/home/cgc/.adal/accessToken.json") -``` - -Example acquire a token for `https://management.core.windows.net/` using device code flow: - -``` -adal -mode device \ - -applicationId "APPLICATION_ID" \ - -tenantId "TENANT_ID" \ - -resource https://management.core.windows.net/ - -``` diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go b/vendor/github.com/Azure/go-autorest/autorest/adal/config.go deleted file mode 100644 index fa5964742..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/config.go +++ /dev/null @@ -1,151 +0,0 @@ -package adal - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "errors" - "fmt" - "net/url" -) - -const ( - activeDirectoryEndpointTemplate = "%s/oauth2/%s%s" -) - -// OAuthConfig represents the endpoints needed -// in OAuth operations -type OAuthConfig struct { - AuthorityEndpoint url.URL `json:"authorityEndpoint"` - AuthorizeEndpoint url.URL `json:"authorizeEndpoint"` - TokenEndpoint url.URL `json:"tokenEndpoint"` - DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"` -} - -// IsZero returns true if the OAuthConfig object is zero-initialized. -func (oac OAuthConfig) IsZero() bool { - return oac == OAuthConfig{} -} - -func validateStringParam(param, name string) error { - if len(param) == 0 { - return fmt.Errorf("parameter '" + name + "' cannot be empty") - } - return nil -} - -// NewOAuthConfig returns an OAuthConfig with tenant specific urls -func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) { - apiVer := "1.0" - return NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID, &apiVer) -} - -// NewOAuthConfigWithAPIVersion returns an OAuthConfig with tenant specific urls. -// If apiVersion is not nil the "api-version" query parameter will be appended to the endpoint URLs with the specified value. -func NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID string, apiVersion *string) (*OAuthConfig, error) { - if err := validateStringParam(activeDirectoryEndpoint, "activeDirectoryEndpoint"); err != nil { - return nil, err - } - api := "" - // it's legal for tenantID to be empty so don't validate it - if apiVersion != nil { - if err := validateStringParam(*apiVersion, "apiVersion"); err != nil { - return nil, err - } - api = fmt.Sprintf("?api-version=%s", *apiVersion) - } - u, err := url.Parse(activeDirectoryEndpoint) - if err != nil { - return nil, err - } - authorityURL, err := u.Parse(tenantID) - if err != nil { - return nil, err - } - authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", api)) - if err != nil { - return nil, err - } - tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", api)) - if err != nil { - return nil, err - } - deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", api)) - if err != nil { - return nil, err - } - - return &OAuthConfig{ - AuthorityEndpoint: *authorityURL, - AuthorizeEndpoint: *authorizeURL, - TokenEndpoint: *tokenURL, - DeviceCodeEndpoint: *deviceCodeURL, - }, nil -} - -// MultiTenantOAuthConfig provides endpoints for primary and aulixiary tenant IDs. -type MultiTenantOAuthConfig interface { - PrimaryTenant() *OAuthConfig - AuxiliaryTenants() []*OAuthConfig -} - -// OAuthOptions contains optional OAuthConfig creation arguments. -type OAuthOptions struct { - APIVersion string -} - -func (c OAuthOptions) apiVersion() string { - if c.APIVersion != "" { - return fmt.Sprintf("?api-version=%s", c.APIVersion) - } - return "1.0" -} - -// NewMultiTenantOAuthConfig creates an object that support multitenant OAuth configuration. -// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/authenticate-multi-tenant for more information. -func NewMultiTenantOAuthConfig(activeDirectoryEndpoint, primaryTenantID string, auxiliaryTenantIDs []string, options OAuthOptions) (MultiTenantOAuthConfig, error) { - if len(auxiliaryTenantIDs) == 0 || len(auxiliaryTenantIDs) > 3 { - return nil, errors.New("must specify one to three auxiliary tenants") - } - mtCfg := multiTenantOAuthConfig{ - cfgs: make([]*OAuthConfig, len(auxiliaryTenantIDs)+1), - } - apiVer := options.apiVersion() - pri, err := NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, primaryTenantID, &apiVer) - if err != nil { - return nil, fmt.Errorf("failed to create OAuthConfig for primary tenant: %v", err) - } - mtCfg.cfgs[0] = pri - for i := range auxiliaryTenantIDs { - aux, err := NewOAuthConfig(activeDirectoryEndpoint, auxiliaryTenantIDs[i]) - if err != nil { - return nil, fmt.Errorf("failed to create OAuthConfig for tenant '%s': %v", auxiliaryTenantIDs[i], err) - } - mtCfg.cfgs[i+1] = aux - } - return mtCfg, nil -} - -type multiTenantOAuthConfig struct { - // first config in the slice is the primary tenant - cfgs []*OAuthConfig -} - -func (m multiTenantOAuthConfig) PrimaryTenant() *OAuthConfig { - return m.cfgs[0] -} - -func (m multiTenantOAuthConfig) AuxiliaryTenants() []*OAuthConfig { - return m.cfgs[1:] -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go b/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go deleted file mode 100644 index b38f4c245..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/devicetoken.go +++ /dev/null @@ -1,242 +0,0 @@ -package adal - -// Copyright 2017 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* - This file is largely based on rjw57/oauth2device's code, with the follow differences: - * scope -> resource, and only allow a single one - * receive "Message" in the DeviceCode struct and show it to users as the prompt - * azure-xplat-cli has the following behavior that this emulates: - - does not send client_secret during the token exchange - - sends resource again in the token exchange request -*/ - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" -) - -const ( - logPrefix = "autorest/adal/devicetoken:" -) - -var ( - // ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow - ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix) - - // ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow - ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix) - - // ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow - ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix) - - // ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow - ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix) - - // ErrDeviceSlowDown represents the service telling us we're polling too often during device flow - ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix) - - // ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow - ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix) - - // ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow - ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix) - - errCodeSendingFails = "Error occurred while sending request for Device Authorization Code" - errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint" - errTokenSendingFails = "Error occurred while sending request with device code for a token" - errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)" - errStatusNotOK = "Error HTTP status != 200" -) - -// DeviceCode is the object returned by the device auth endpoint -// It contains information to instruct the user to complete the auth flow -type DeviceCode struct { - DeviceCode *string `json:"device_code,omitempty"` - UserCode *string `json:"user_code,omitempty"` - VerificationURL *string `json:"verification_url,omitempty"` - ExpiresIn *int64 `json:"expires_in,string,omitempty"` - Interval *int64 `json:"interval,string,omitempty"` - - Message *string `json:"message"` // Azure specific - Resource string // store the following, stored when initiating, used when exchanging - OAuthConfig OAuthConfig - ClientID string -} - -// TokenError is the object returned by the token exchange endpoint -// when something is amiss -type TokenError struct { - Error *string `json:"error,omitempty"` - ErrorCodes []int `json:"error_codes,omitempty"` - ErrorDescription *string `json:"error_description,omitempty"` - Timestamp *string `json:"timestamp,omitempty"` - TraceID *string `json:"trace_id,omitempty"` -} - -// DeviceToken is the object return by the token exchange endpoint -// It can either look like a Token or an ErrorToken, so put both here -// and check for presence of "Error" to know if we are in error state -type deviceToken struct { - Token - TokenError -} - -// InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode -// that can be used with CheckForUserCompletion or WaitForUserCompletion. -func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) { - v := url.Values{ - "client_id": []string{clientID}, - "resource": []string{resource}, - } - - s := v.Encode() - body := ioutil.NopCloser(strings.NewReader(s)) - - req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error()) - } - - req.ContentLength = int64(len(s)) - req.Header.Set(contentType, mimeTypeFormPost) - resp, err := sender.Do(req) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error()) - } - defer resp.Body.Close() - - rb, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error()) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK) - } - - if len(strings.Trim(string(rb), " ")) == 0 { - return nil, ErrDeviceCodeEmpty - } - - var code DeviceCode - err = json.Unmarshal(rb, &code) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error()) - } - - code.ClientID = clientID - code.Resource = resource - code.OAuthConfig = oauthConfig - - return &code, nil -} - -// CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint -// to see if the device flow has: been completed, timed out, or otherwise failed -func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) { - v := url.Values{ - "client_id": []string{code.ClientID}, - "code": []string{*code.DeviceCode}, - "grant_type": []string{OAuthGrantTypeDeviceCode}, - "resource": []string{code.Resource}, - } - - s := v.Encode() - body := ioutil.NopCloser(strings.NewReader(s)) - - req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error()) - } - - req.ContentLength = int64(len(s)) - req.Header.Set(contentType, mimeTypeFormPost) - resp, err := sender.Do(req) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error()) - } - defer resp.Body.Close() - - rb, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error()) - } - - if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK) - } - if len(strings.Trim(string(rb), " ")) == 0 { - return nil, ErrOAuthTokenEmpty - } - - var token deviceToken - err = json.Unmarshal(rb, &token) - if err != nil { - return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error()) - } - - if token.Error == nil { - return &token.Token, nil - } - - switch *token.Error { - case "authorization_pending": - return nil, ErrDeviceAuthorizationPending - case "slow_down": - return nil, ErrDeviceSlowDown - case "access_denied": - return nil, ErrDeviceAccessDenied - case "code_expired": - return nil, ErrDeviceCodeExpired - default: - return nil, ErrDeviceGeneric - } -} - -// WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs. -// This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'. -func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) { - intervalDuration := time.Duration(*code.Interval) * time.Second - waitDuration := intervalDuration - - for { - token, err := CheckForUserCompletion(sender, code) - - if err == nil { - return token, nil - } - - switch err { - case ErrDeviceSlowDown: - waitDuration += waitDuration - case ErrDeviceAuthorizationPending: - // noop - default: // everything else is "fatal" to us - return nil, err - } - - if waitDuration > (intervalDuration * 3) { - return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix) - } - - time.Sleep(waitDuration) - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/go.mod b/vendor/github.com/Azure/go-autorest/autorest/adal/go.mod deleted file mode 100644 index 66db8b9e2..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/Azure/go-autorest/autorest/adal - -go 1.12 - -require ( - github.com/Azure/go-autorest/autorest/date v0.1.0 - github.com/Azure/go-autorest/autorest/mocks v0.1.0 - github.com/Azure/go-autorest/tracing v0.5.0 - github.com/dgrijalva/jwt-go v3.2.0+incompatible - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 -) diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/go.sum b/vendor/github.com/Azure/go-autorest/autorest/adal/go.sum deleted file mode 100644 index 9525ff736..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go b/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go deleted file mode 100644 index 9e15f2751..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/persist.go +++ /dev/null @@ -1,73 +0,0 @@ -package adal - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" -) - -// LoadToken restores a Token object from a file located at 'path'. -func LoadToken(path string) (*Token, error) { - file, err := os.Open(path) - if err != nil { - return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) - } - defer file.Close() - - var token Token - - dec := json.NewDecoder(file) - if err = dec.Decode(&token); err != nil { - return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err) - } - return &token, nil -} - -// SaveToken persists an oauth token at the given location on disk. -// It moves the new file into place so it can safely be used to replace an existing file -// that maybe accessed by multiple processes. -func SaveToken(path string, mode os.FileMode, token Token) error { - dir := filepath.Dir(path) - err := os.MkdirAll(dir, os.ModePerm) - if err != nil { - return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err) - } - - newFile, err := ioutil.TempFile(dir, "token") - if err != nil { - return fmt.Errorf("failed to create the temp file to write the token: %v", err) - } - tempPath := newFile.Name() - - if err := json.NewEncoder(newFile).Encode(token); err != nil { - return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err) - } - if err := newFile.Close(); err != nil { - return fmt.Errorf("failed to close temp file %s: %v", tempPath, err) - } - - // Atomic replace to avoid multi-writer file corruptions - if err := os.Rename(tempPath, path); err != nil { - return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err) - } - if err := os.Chmod(path, mode); err != nil { - return fmt.Errorf("failed to chmod the token file %s: %v", path, err) - } - return nil -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go b/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go deleted file mode 100644 index d7e4372bb..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/sender.go +++ /dev/null @@ -1,95 +0,0 @@ -package adal - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "crypto/tls" - "net/http" - "net/http/cookiejar" - "sync" - - "github.com/Azure/go-autorest/tracing" -) - -const ( - contentType = "Content-Type" - mimeTypeFormPost = "application/x-www-form-urlencoded" -) - -var defaultSender Sender -var defaultSenderInit = &sync.Once{} - -// Sender is the interface that wraps the Do method to send HTTP requests. -// -// The standard http.Client conforms to this interface. -type Sender interface { - Do(*http.Request) (*http.Response, error) -} - -// SenderFunc is a method that implements the Sender interface. -type SenderFunc func(*http.Request) (*http.Response, error) - -// Do implements the Sender interface on SenderFunc. -func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { - return sf(r) -} - -// SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the -// http.Request and pass it along or, first, pass the http.Request along then react to the -// http.Response result. -type SendDecorator func(Sender) Sender - -// CreateSender creates, decorates, and returns, as a Sender, the default http.Client. -func CreateSender(decorators ...SendDecorator) Sender { - return DecorateSender(sender(), decorators...) -} - -// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to -// the Sender. Decorators are applied in the order received, but their affect upon the request -// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a -// post-decorator (pass the http.Request along and react to the results in http.Response). -func DecorateSender(s Sender, decorators ...SendDecorator) Sender { - for _, decorate := range decorators { - s = decorate(s) - } - return s -} - -func sender() Sender { - // note that we can't init defaultSender in init() since it will - // execute before calling code has had a chance to enable tracing - defaultSenderInit.Do(func() { - // Use behaviour compatible with DefaultTransport, but require TLS minimum version. - defaultTransport := http.DefaultTransport.(*http.Transport) - transport := &http.Transport{ - Proxy: defaultTransport.Proxy, - DialContext: defaultTransport.DialContext, - MaxIdleConns: defaultTransport.MaxIdleConns, - IdleConnTimeout: defaultTransport.IdleConnTimeout, - TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, - ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, - TLSClientConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - }, - } - var roundTripper http.RoundTripper = transport - if tracing.IsEnabled() { - roundTripper = tracing.NewTransport(transport) - } - j, _ := cookiejar.New(nil) - defaultSender = &http.Client{Jar: j, Transport: roundTripper} - }) - return defaultSender -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go b/vendor/github.com/Azure/go-autorest/autorest/adal/token.go deleted file mode 100644 index b72753498..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/token.go +++ /dev/null @@ -1,1080 +0,0 @@ -package adal - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "math" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" - - "github.com/Azure/go-autorest/autorest/date" - "github.com/dgrijalva/jwt-go" -) - -const ( - defaultRefresh = 5 * time.Minute - - // OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow - OAuthGrantTypeDeviceCode = "device_code" - - // OAuthGrantTypeClientCredentials is the "grant_type" identifier used in credential flows - OAuthGrantTypeClientCredentials = "client_credentials" - - // OAuthGrantTypeUserPass is the "grant_type" identifier used in username and password auth flows - OAuthGrantTypeUserPass = "password" - - // OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows - OAuthGrantTypeRefreshToken = "refresh_token" - - // OAuthGrantTypeAuthorizationCode is the "grant_type" identifier used in authorization code flows - OAuthGrantTypeAuthorizationCode = "authorization_code" - - // metadataHeader is the header required by MSI extension - metadataHeader = "Metadata" - - // msiEndpoint is the well known endpoint for getting MSI authentications tokens - msiEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" - - // the default number of attempts to refresh an MSI authentication token - defaultMaxMSIRefreshAttempts = 5 -) - -// OAuthTokenProvider is an interface which should be implemented by an access token retriever -type OAuthTokenProvider interface { - OAuthToken() string -} - -// MultitenantOAuthTokenProvider provides tokens used for multi-tenant authorization. -type MultitenantOAuthTokenProvider interface { - PrimaryOAuthToken() string - AuxiliaryOAuthTokens() []string -} - -// TokenRefreshError is an interface used by errors returned during token refresh. -type TokenRefreshError interface { - error - Response() *http.Response -} - -// Refresher is an interface for token refresh functionality -type Refresher interface { - Refresh() error - RefreshExchange(resource string) error - EnsureFresh() error -} - -// RefresherWithContext is an interface for token refresh functionality -type RefresherWithContext interface { - RefreshWithContext(ctx context.Context) error - RefreshExchangeWithContext(ctx context.Context, resource string) error - EnsureFreshWithContext(ctx context.Context) error -} - -// TokenRefreshCallback is the type representing callbacks that will be called after -// a successful token refresh -type TokenRefreshCallback func(Token) error - -// Token encapsulates the access token used to authorize Azure requests. -// https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow#service-to-service-access-token-response -type Token struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - - ExpiresIn json.Number `json:"expires_in"` - ExpiresOn json.Number `json:"expires_on"` - NotBefore json.Number `json:"not_before"` - - Resource string `json:"resource"` - Type string `json:"token_type"` -} - -func newToken() Token { - return Token{ - ExpiresIn: "0", - ExpiresOn: "0", - NotBefore: "0", - } -} - -// IsZero returns true if the token object is zero-initialized. -func (t Token) IsZero() bool { - return t == Token{} -} - -// Expires returns the time.Time when the Token expires. -func (t Token) Expires() time.Time { - s, err := t.ExpiresOn.Float64() - if err != nil { - s = -3600 - } - - expiration := date.NewUnixTimeFromSeconds(s) - - return time.Time(expiration).UTC() -} - -// IsExpired returns true if the Token is expired, false otherwise. -func (t Token) IsExpired() bool { - return t.WillExpireIn(0) -} - -// WillExpireIn returns true if the Token will expire after the passed time.Duration interval -// from now, false otherwise. -func (t Token) WillExpireIn(d time.Duration) bool { - return !t.Expires().After(time.Now().Add(d)) -} - -//OAuthToken return the current access token -func (t *Token) OAuthToken() string { - return t.AccessToken -} - -// ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form -// that is submitted when acquiring an oAuth token. -type ServicePrincipalSecret interface { - SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error -} - -// ServicePrincipalNoSecret represents a secret type that contains no secret -// meaning it is not valid for fetching a fresh token. This is used by Manual -type ServicePrincipalNoSecret struct { -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret -// It only returns an error for the ServicePrincipalNoSecret type -func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token") -} - -// MarshalJSON implements the json.Marshaler interface. -func (noSecret ServicePrincipalNoSecret) MarshalJSON() ([]byte, error) { - type tokenType struct { - Type string `json:"type"` - } - return json.Marshal(tokenType{ - Type: "ServicePrincipalNoSecret", - }) -} - -// ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization. -type ServicePrincipalTokenSecret struct { - ClientSecret string `json:"value"` -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. -// It will populate the form submitted during oAuth Token Acquisition using the client_secret. -func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - v.Set("client_secret", tokenSecret.ClientSecret) - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (tokenSecret ServicePrincipalTokenSecret) MarshalJSON() ([]byte, error) { - type tokenType struct { - Type string `json:"type"` - Value string `json:"value"` - } - return json.Marshal(tokenType{ - Type: "ServicePrincipalTokenSecret", - Value: tokenSecret.ClientSecret, - }) -} - -// ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs. -type ServicePrincipalCertificateSecret struct { - Certificate *x509.Certificate - PrivateKey *rsa.PrivateKey -} - -// SignJwt returns the JWT signed with the certificate's private key. -func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) { - hasher := sha1.New() - _, err := hasher.Write(secret.Certificate.Raw) - if err != nil { - return "", err - } - - thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) - - // The jti (JWT ID) claim provides a unique identifier for the JWT. - jti := make([]byte, 20) - _, err = rand.Read(jti) - if err != nil { - return "", err - } - - token := jwt.New(jwt.SigningMethodRS256) - token.Header["x5t"] = thumbprint - x5c := []string{base64.StdEncoding.EncodeToString(secret.Certificate.Raw)} - token.Header["x5c"] = x5c - token.Claims = jwt.MapClaims{ - "aud": spt.inner.OauthConfig.TokenEndpoint.String(), - "iss": spt.inner.ClientID, - "sub": spt.inner.ClientID, - "jti": base64.URLEncoding.EncodeToString(jti), - "nbf": time.Now().Unix(), - "exp": time.Now().Add(time.Hour * 24).Unix(), - } - - signedString, err := token.SignedString(secret.PrivateKey) - return signedString, err -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. -// It will populate the form submitted during oAuth Token Acquisition using a JWT signed with a certificate. -func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - jwt, err := secret.SignJwt(spt) - if err != nil { - return err - } - - v.Set("client_assertion", jwt) - v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (secret ServicePrincipalCertificateSecret) MarshalJSON() ([]byte, error) { - return nil, errors.New("marshalling ServicePrincipalCertificateSecret is not supported") -} - -// ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension. -type ServicePrincipalMSISecret struct { -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. -func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (msiSecret ServicePrincipalMSISecret) MarshalJSON() ([]byte, error) { - return nil, errors.New("marshalling ServicePrincipalMSISecret is not supported") -} - -// ServicePrincipalUsernamePasswordSecret implements ServicePrincipalSecret for username and password auth. -type ServicePrincipalUsernamePasswordSecret struct { - Username string `json:"username"` - Password string `json:"password"` -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. -func (secret *ServicePrincipalUsernamePasswordSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - v.Set("username", secret.Username) - v.Set("password", secret.Password) - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (secret ServicePrincipalUsernamePasswordSecret) MarshalJSON() ([]byte, error) { - type tokenType struct { - Type string `json:"type"` - Username string `json:"username"` - Password string `json:"password"` - } - return json.Marshal(tokenType{ - Type: "ServicePrincipalUsernamePasswordSecret", - Username: secret.Username, - Password: secret.Password, - }) -} - -// ServicePrincipalAuthorizationCodeSecret implements ServicePrincipalSecret for authorization code auth. -type ServicePrincipalAuthorizationCodeSecret struct { - ClientSecret string `json:"value"` - AuthorizationCode string `json:"authCode"` - RedirectURI string `json:"redirect"` -} - -// SetAuthenticationValues is a method of the interface ServicePrincipalSecret. -func (secret *ServicePrincipalAuthorizationCodeSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { - v.Set("code", secret.AuthorizationCode) - v.Set("client_secret", secret.ClientSecret) - v.Set("redirect_uri", secret.RedirectURI) - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (secret ServicePrincipalAuthorizationCodeSecret) MarshalJSON() ([]byte, error) { - type tokenType struct { - Type string `json:"type"` - Value string `json:"value"` - AuthCode string `json:"authCode"` - Redirect string `json:"redirect"` - } - return json.Marshal(tokenType{ - Type: "ServicePrincipalAuthorizationCodeSecret", - Value: secret.ClientSecret, - AuthCode: secret.AuthorizationCode, - Redirect: secret.RedirectURI, - }) -} - -// ServicePrincipalToken encapsulates a Token created for a Service Principal. -type ServicePrincipalToken struct { - inner servicePrincipalToken - refreshLock *sync.RWMutex - sender Sender - refreshCallbacks []TokenRefreshCallback - // MaxMSIRefreshAttempts is the maximum number of attempts to refresh an MSI token. - MaxMSIRefreshAttempts int -} - -// MarshalTokenJSON returns the marshalled inner token. -func (spt ServicePrincipalToken) MarshalTokenJSON() ([]byte, error) { - return json.Marshal(spt.inner.Token) -} - -// SetRefreshCallbacks replaces any existing refresh callbacks with the specified callbacks. -func (spt *ServicePrincipalToken) SetRefreshCallbacks(callbacks []TokenRefreshCallback) { - spt.refreshCallbacks = callbacks -} - -// MarshalJSON implements the json.Marshaler interface. -func (spt ServicePrincipalToken) MarshalJSON() ([]byte, error) { - return json.Marshal(spt.inner) -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (spt *ServicePrincipalToken) UnmarshalJSON(data []byte) error { - // need to determine the token type - raw := map[string]interface{}{} - err := json.Unmarshal(data, &raw) - if err != nil { - return err - } - secret := raw["secret"].(map[string]interface{}) - switch secret["type"] { - case "ServicePrincipalNoSecret": - spt.inner.Secret = &ServicePrincipalNoSecret{} - case "ServicePrincipalTokenSecret": - spt.inner.Secret = &ServicePrincipalTokenSecret{} - case "ServicePrincipalCertificateSecret": - return errors.New("unmarshalling ServicePrincipalCertificateSecret is not supported") - case "ServicePrincipalMSISecret": - return errors.New("unmarshalling ServicePrincipalMSISecret is not supported") - case "ServicePrincipalUsernamePasswordSecret": - spt.inner.Secret = &ServicePrincipalUsernamePasswordSecret{} - case "ServicePrincipalAuthorizationCodeSecret": - spt.inner.Secret = &ServicePrincipalAuthorizationCodeSecret{} - default: - return fmt.Errorf("unrecognized token type '%s'", secret["type"]) - } - err = json.Unmarshal(data, &spt.inner) - if err != nil { - return err - } - // Don't override the refreshLock or the sender if those have been already set. - if spt.refreshLock == nil { - spt.refreshLock = &sync.RWMutex{} - } - if spt.sender == nil { - spt.sender = sender() - } - return nil -} - -// internal type used for marshalling/unmarshalling -type servicePrincipalToken struct { - Token Token `json:"token"` - Secret ServicePrincipalSecret `json:"secret"` - OauthConfig OAuthConfig `json:"oauth"` - ClientID string `json:"clientID"` - Resource string `json:"resource"` - AutoRefresh bool `json:"autoRefresh"` - RefreshWithin time.Duration `json:"refreshWithin"` -} - -func validateOAuthConfig(oac OAuthConfig) error { - if oac.IsZero() { - return fmt.Errorf("parameter 'oauthConfig' cannot be zero-initialized") - } - return nil -} - -// NewServicePrincipalTokenWithSecret create a ServicePrincipalToken using the supplied ServicePrincipalSecret implementation. -func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, resource string, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(id, "id"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - if secret == nil { - return nil, fmt.Errorf("parameter 'secret' cannot be nil") - } - spt := &ServicePrincipalToken{ - inner: servicePrincipalToken{ - Token: newToken(), - OauthConfig: oauthConfig, - Secret: secret, - ClientID: id, - Resource: resource, - AutoRefresh: true, - RefreshWithin: defaultRefresh, - }, - refreshLock: &sync.RWMutex{}, - sender: sender(), - refreshCallbacks: callbacks, - } - return spt, nil -} - -// NewServicePrincipalTokenFromManualToken creates a ServicePrincipalToken using the supplied token -func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID string, resource string, token Token, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - if token.IsZero() { - return nil, fmt.Errorf("parameter 'token' cannot be zero-initialized") - } - spt, err := NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - &ServicePrincipalNoSecret{}, - callbacks...) - if err != nil { - return nil, err - } - - spt.inner.Token = token - - return spt, nil -} - -// NewServicePrincipalTokenFromManualTokenSecret creates a ServicePrincipalToken using the supplied token and secret -func NewServicePrincipalTokenFromManualTokenSecret(oauthConfig OAuthConfig, clientID string, resource string, token Token, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - if secret == nil { - return nil, fmt.Errorf("parameter 'secret' cannot be nil") - } - if token.IsZero() { - return nil, fmt.Errorf("parameter 'token' cannot be zero-initialized") - } - spt, err := NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - secret, - callbacks...) - if err != nil { - return nil, err - } - - spt.inner.Token = token - - return spt, nil -} - -// NewServicePrincipalToken creates a ServicePrincipalToken from the supplied Service Principal -// credentials scoped to the named resource. -func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(secret, "secret"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - return NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - &ServicePrincipalTokenSecret{ - ClientSecret: secret, - }, - callbacks..., - ) -} - -// NewServicePrincipalTokenFromCertificate creates a ServicePrincipalToken from the supplied pkcs12 bytes. -func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - if certificate == nil { - return nil, fmt.Errorf("parameter 'certificate' cannot be nil") - } - if privateKey == nil { - return nil, fmt.Errorf("parameter 'privateKey' cannot be nil") - } - return NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - &ServicePrincipalCertificateSecret{ - PrivateKey: privateKey, - Certificate: certificate, - }, - callbacks..., - ) -} - -// NewServicePrincipalTokenFromUsernamePassword creates a ServicePrincipalToken from the username and password. -func NewServicePrincipalTokenFromUsernamePassword(oauthConfig OAuthConfig, clientID string, username string, password string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(username, "username"); err != nil { - return nil, err - } - if err := validateStringParam(password, "password"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - return NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - &ServicePrincipalUsernamePasswordSecret{ - Username: username, - Password: password, - }, - callbacks..., - ) -} - -// NewServicePrincipalTokenFromAuthorizationCode creates a ServicePrincipalToken from the -func NewServicePrincipalTokenFromAuthorizationCode(oauthConfig OAuthConfig, clientID string, clientSecret string, authorizationCode string, redirectURI string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - - if err := validateOAuthConfig(oauthConfig); err != nil { - return nil, err - } - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(clientSecret, "clientSecret"); err != nil { - return nil, err - } - if err := validateStringParam(authorizationCode, "authorizationCode"); err != nil { - return nil, err - } - if err := validateStringParam(redirectURI, "redirectURI"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - - return NewServicePrincipalTokenWithSecret( - oauthConfig, - clientID, - resource, - &ServicePrincipalAuthorizationCodeSecret{ - ClientSecret: clientSecret, - AuthorizationCode: authorizationCode, - RedirectURI: redirectURI, - }, - callbacks..., - ) -} - -// GetMSIVMEndpoint gets the MSI endpoint on Virtual Machines. -func GetMSIVMEndpoint() (string, error) { - return msiEndpoint, nil -} - -// NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension. -// It will use the system assigned identity when creating the token. -func NewServicePrincipalTokenFromMSI(msiEndpoint, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - return newServicePrincipalTokenFromMSI(msiEndpoint, resource, nil, callbacks...) -} - -// NewServicePrincipalTokenFromMSIWithUserAssignedID creates a ServicePrincipalToken via the MSI VM Extension. -// It will use the specified user assigned identity when creating the token. -func NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource string, userAssignedID string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - return newServicePrincipalTokenFromMSI(msiEndpoint, resource, &userAssignedID, callbacks...) -} - -func newServicePrincipalTokenFromMSI(msiEndpoint, resource string, userAssignedID *string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { - if err := validateStringParam(msiEndpoint, "msiEndpoint"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - if userAssignedID != nil { - if err := validateStringParam(*userAssignedID, "userAssignedID"); err != nil { - return nil, err - } - } - // We set the oauth config token endpoint to be MSI's endpoint - msiEndpointURL, err := url.Parse(msiEndpoint) - if err != nil { - return nil, err - } - - v := url.Values{} - v.Set("resource", resource) - v.Set("api-version", "2018-02-01") - if userAssignedID != nil { - v.Set("client_id", *userAssignedID) - } - msiEndpointURL.RawQuery = v.Encode() - - spt := &ServicePrincipalToken{ - inner: servicePrincipalToken{ - Token: newToken(), - OauthConfig: OAuthConfig{ - TokenEndpoint: *msiEndpointURL, - }, - Secret: &ServicePrincipalMSISecret{}, - Resource: resource, - AutoRefresh: true, - RefreshWithin: defaultRefresh, - }, - refreshLock: &sync.RWMutex{}, - sender: sender(), - refreshCallbacks: callbacks, - MaxMSIRefreshAttempts: defaultMaxMSIRefreshAttempts, - } - - if userAssignedID != nil { - spt.inner.ClientID = *userAssignedID - } - - return spt, nil -} - -// internal type that implements TokenRefreshError -type tokenRefreshError struct { - message string - resp *http.Response -} - -// Error implements the error interface which is part of the TokenRefreshError interface. -func (tre tokenRefreshError) Error() string { - return tre.message -} - -// Response implements the TokenRefreshError interface, it returns the raw HTTP response from the refresh operation. -func (tre tokenRefreshError) Response() *http.Response { - return tre.resp -} - -func newTokenRefreshError(message string, resp *http.Response) TokenRefreshError { - return tokenRefreshError{message: message, resp: resp} -} - -// EnsureFresh will refresh the token if it will expire within the refresh window (as set by -// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use. -func (spt *ServicePrincipalToken) EnsureFresh() error { - return spt.EnsureFreshWithContext(context.Background()) -} - -// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by -// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use. -func (spt *ServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error { - if spt.inner.AutoRefresh && spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) { - // take the write lock then check to see if the token was already refreshed - spt.refreshLock.Lock() - defer spt.refreshLock.Unlock() - if spt.inner.Token.WillExpireIn(spt.inner.RefreshWithin) { - return spt.refreshInternal(ctx, spt.inner.Resource) - } - } - return nil -} - -// InvokeRefreshCallbacks calls any TokenRefreshCallbacks that were added to the SPT during initialization -func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error { - if spt.refreshCallbacks != nil { - for _, callback := range spt.refreshCallbacks { - err := callback(spt.inner.Token) - if err != nil { - return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err) - } - } - } - return nil -} - -// Refresh obtains a fresh token for the Service Principal. -// This method is not safe for concurrent use and should be syncrhonized. -func (spt *ServicePrincipalToken) Refresh() error { - return spt.RefreshWithContext(context.Background()) -} - -// RefreshWithContext obtains a fresh token for the Service Principal. -// This method is not safe for concurrent use and should be syncrhonized. -func (spt *ServicePrincipalToken) RefreshWithContext(ctx context.Context) error { - spt.refreshLock.Lock() - defer spt.refreshLock.Unlock() - return spt.refreshInternal(ctx, spt.inner.Resource) -} - -// RefreshExchange refreshes the token, but for a different resource. -// This method is not safe for concurrent use and should be syncrhonized. -func (spt *ServicePrincipalToken) RefreshExchange(resource string) error { - return spt.RefreshExchangeWithContext(context.Background(), resource) -} - -// RefreshExchangeWithContext refreshes the token, but for a different resource. -// This method is not safe for concurrent use and should be syncrhonized. -func (spt *ServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context, resource string) error { - spt.refreshLock.Lock() - defer spt.refreshLock.Unlock() - return spt.refreshInternal(ctx, resource) -} - -func (spt *ServicePrincipalToken) getGrantType() string { - switch spt.inner.Secret.(type) { - case *ServicePrincipalUsernamePasswordSecret: - return OAuthGrantTypeUserPass - case *ServicePrincipalAuthorizationCodeSecret: - return OAuthGrantTypeAuthorizationCode - default: - return OAuthGrantTypeClientCredentials - } -} - -func isIMDS(u url.URL) bool { - imds, err := url.Parse(msiEndpoint) - if err != nil { - return false - } - return u.Host == imds.Host && u.Path == imds.Path -} - -func (spt *ServicePrincipalToken) refreshInternal(ctx context.Context, resource string) error { - req, err := http.NewRequest(http.MethodPost, spt.inner.OauthConfig.TokenEndpoint.String(), nil) - if err != nil { - return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err) - } - req.Header.Add("User-Agent", UserAgent()) - req = req.WithContext(ctx) - if !isIMDS(spt.inner.OauthConfig.TokenEndpoint) { - v := url.Values{} - v.Set("client_id", spt.inner.ClientID) - v.Set("resource", resource) - - if spt.inner.Token.RefreshToken != "" { - v.Set("grant_type", OAuthGrantTypeRefreshToken) - v.Set("refresh_token", spt.inner.Token.RefreshToken) - // web apps must specify client_secret when refreshing tokens - // see https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code#refreshing-the-access-tokens - if spt.getGrantType() == OAuthGrantTypeAuthorizationCode { - err := spt.inner.Secret.SetAuthenticationValues(spt, &v) - if err != nil { - return err - } - } - } else { - v.Set("grant_type", spt.getGrantType()) - err := spt.inner.Secret.SetAuthenticationValues(spt, &v) - if err != nil { - return err - } - } - - s := v.Encode() - body := ioutil.NopCloser(strings.NewReader(s)) - req.ContentLength = int64(len(s)) - req.Header.Set(contentType, mimeTypeFormPost) - req.Body = body - } - - if _, ok := spt.inner.Secret.(*ServicePrincipalMSISecret); ok { - req.Method = http.MethodGet - req.Header.Set(metadataHeader, "true") - } - - var resp *http.Response - if isIMDS(spt.inner.OauthConfig.TokenEndpoint) { - resp, err = retryForIMDS(spt.sender, req, spt.MaxMSIRefreshAttempts) - } else { - resp, err = spt.sender.Do(req) - } - if err != nil { - return newTokenRefreshError(fmt.Sprintf("adal: Failed to execute the refresh request. Error = '%v'", err), nil) - } - - defer resp.Body.Close() - rb, err := ioutil.ReadAll(resp.Body) - - if resp.StatusCode != http.StatusOK { - if err != nil { - return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Failed reading response body: %v", resp.StatusCode, err), resp) - } - return newTokenRefreshError(fmt.Sprintf("adal: Refresh request failed. Status Code = '%d'. Response body: %s", resp.StatusCode, string(rb)), resp) - } - - // for the following error cases don't return a TokenRefreshError. the operation succeeded - // but some transient failure happened during deserialization. by returning a generic error - // the retry logic will kick in (we don't retry on TokenRefreshError). - - if err != nil { - return fmt.Errorf("adal: Failed to read a new service principal token during refresh. Error = '%v'", err) - } - if len(strings.Trim(string(rb), " ")) == 0 { - return fmt.Errorf("adal: Empty service principal token received during refresh") - } - var token Token - err = json.Unmarshal(rb, &token) - if err != nil { - return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb)) - } - - spt.inner.Token = token - - return spt.InvokeRefreshCallbacks(token) -} - -// retry logic specific to retrieving a token from the IMDS endpoint -func retryForIMDS(sender Sender, req *http.Request, maxAttempts int) (resp *http.Response, err error) { - // copied from client.go due to circular dependency - retries := []int{ - http.StatusRequestTimeout, // 408 - http.StatusTooManyRequests, // 429 - http.StatusInternalServerError, // 500 - http.StatusBadGateway, // 502 - http.StatusServiceUnavailable, // 503 - http.StatusGatewayTimeout, // 504 - } - // extra retry status codes specific to IMDS - retries = append(retries, - http.StatusNotFound, - http.StatusGone, - // all remaining 5xx - http.StatusNotImplemented, - http.StatusHTTPVersionNotSupported, - http.StatusVariantAlsoNegotiates, - http.StatusInsufficientStorage, - http.StatusLoopDetected, - http.StatusNotExtended, - http.StatusNetworkAuthenticationRequired) - - // see https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/how-to-use-vm-token#retry-guidance - - const maxDelay time.Duration = 60 * time.Second - - attempt := 0 - delay := time.Duration(0) - - for attempt < maxAttempts { - resp, err = sender.Do(req) - // retry on temporary network errors, e.g. transient network failures. - // if we don't receive a response then assume we can't connect to the - // endpoint so we're likely not running on an Azure VM so don't retry. - if (err != nil && !isTemporaryNetworkError(err)) || resp == nil || resp.StatusCode == http.StatusOK || !containsInt(retries, resp.StatusCode) { - return - } - - // perform exponential backoff with a cap. - // must increment attempt before calculating delay. - attempt++ - // the base value of 2 is the "delta backoff" as specified in the guidance doc - delay += (time.Duration(math.Pow(2, float64(attempt))) * time.Second) - if delay > maxDelay { - delay = maxDelay - } - - select { - case <-time.After(delay): - // intentionally left blank - case <-req.Context().Done(): - err = req.Context().Err() - return - } - } - return -} - -// returns true if the specified error is a temporary network error or false if it's not. -// if the error doesn't implement the net.Error interface the return value is true. -func isTemporaryNetworkError(err error) bool { - if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) { - return true - } - return false -} - -// returns true if slice ints contains the value n -func containsInt(ints []int, n int) bool { - for _, i := range ints { - if i == n { - return true - } - } - return false -} - -// SetAutoRefresh enables or disables automatic refreshing of stale tokens. -func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) { - spt.inner.AutoRefresh = autoRefresh -} - -// SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will -// refresh the token. -func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) { - spt.inner.RefreshWithin = d - return -} - -// SetSender sets the http.Client used when obtaining the Service Principal token. An -// undecorated http.Client is used by default. -func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s } - -// OAuthToken implements the OAuthTokenProvider interface. It returns the current access token. -func (spt *ServicePrincipalToken) OAuthToken() string { - spt.refreshLock.RLock() - defer spt.refreshLock.RUnlock() - return spt.inner.Token.OAuthToken() -} - -// Token returns a copy of the current token. -func (spt *ServicePrincipalToken) Token() Token { - spt.refreshLock.RLock() - defer spt.refreshLock.RUnlock() - return spt.inner.Token -} - -// MultiTenantServicePrincipalToken contains tokens for multi-tenant authorization. -type MultiTenantServicePrincipalToken struct { - PrimaryToken *ServicePrincipalToken - AuxiliaryTokens []*ServicePrincipalToken -} - -// PrimaryOAuthToken returns the primary authorization token. -func (mt *MultiTenantServicePrincipalToken) PrimaryOAuthToken() string { - return mt.PrimaryToken.OAuthToken() -} - -// AuxiliaryOAuthTokens returns one to three auxiliary authorization tokens. -func (mt *MultiTenantServicePrincipalToken) AuxiliaryOAuthTokens() []string { - tokens := make([]string, len(mt.AuxiliaryTokens)) - for i := range mt.AuxiliaryTokens { - tokens[i] = mt.AuxiliaryTokens[i].OAuthToken() - } - return tokens -} - -// EnsureFreshWithContext will refresh the token if it will expire within the refresh window (as set by -// RefreshWithin) and autoRefresh flag is on. This method is safe for concurrent use. -func (mt *MultiTenantServicePrincipalToken) EnsureFreshWithContext(ctx context.Context) error { - if err := mt.PrimaryToken.EnsureFreshWithContext(ctx); err != nil { - return fmt.Errorf("failed to refresh primary token: %v", err) - } - for _, aux := range mt.AuxiliaryTokens { - if err := aux.EnsureFreshWithContext(ctx); err != nil { - return fmt.Errorf("failed to refresh auxiliary token: %v", err) - } - } - return nil -} - -// RefreshWithContext obtains a fresh token for the Service Principal. -func (mt *MultiTenantServicePrincipalToken) RefreshWithContext(ctx context.Context) error { - if err := mt.PrimaryToken.RefreshWithContext(ctx); err != nil { - return fmt.Errorf("failed to refresh primary token: %v", err) - } - for _, aux := range mt.AuxiliaryTokens { - if err := aux.RefreshWithContext(ctx); err != nil { - return fmt.Errorf("failed to refresh auxiliary token: %v", err) - } - } - return nil -} - -// RefreshExchangeWithContext refreshes the token, but for a different resource. -func (mt *MultiTenantServicePrincipalToken) RefreshExchangeWithContext(ctx context.Context, resource string) error { - if err := mt.PrimaryToken.RefreshExchangeWithContext(ctx, resource); err != nil { - return fmt.Errorf("failed to refresh primary token: %v", err) - } - for _, aux := range mt.AuxiliaryTokens { - if err := aux.RefreshExchangeWithContext(ctx, resource); err != nil { - return fmt.Errorf("failed to refresh auxiliary token: %v", err) - } - } - return nil -} - -// NewMultiTenantServicePrincipalToken creates a new MultiTenantServicePrincipalToken with the specified credentials and resource. -func NewMultiTenantServicePrincipalToken(multiTenantCfg MultiTenantOAuthConfig, clientID string, secret string, resource string) (*MultiTenantServicePrincipalToken, error) { - if err := validateStringParam(clientID, "clientID"); err != nil { - return nil, err - } - if err := validateStringParam(secret, "secret"); err != nil { - return nil, err - } - if err := validateStringParam(resource, "resource"); err != nil { - return nil, err - } - auxTenants := multiTenantCfg.AuxiliaryTenants() - m := MultiTenantServicePrincipalToken{ - AuxiliaryTokens: make([]*ServicePrincipalToken, len(auxTenants)), - } - primary, err := NewServicePrincipalToken(*multiTenantCfg.PrimaryTenant(), clientID, secret, resource) - if err != nil { - return nil, fmt.Errorf("failed to create SPT for primary tenant: %v", err) - } - m.PrimaryToken = primary - for i := range auxTenants { - aux, err := NewServicePrincipalToken(*auxTenants[i], clientID, secret, resource) - if err != nil { - return nil, fmt.Errorf("failed to create SPT for auxiliary tenant: %v", err) - } - m.AuxiliaryTokens[i] = aux - } - return &m, nil -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/adal/version.go b/vendor/github.com/Azure/go-autorest/autorest/adal/version.go deleted file mode 100644 index c867b3484..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/adal/version.go +++ /dev/null @@ -1,45 +0,0 @@ -package adal - -import ( - "fmt" - "runtime" -) - -// Copyright 2017 Microsoft Corporation -// -// 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. - -const number = "v1.0.0" - -var ( - ua = fmt.Sprintf("Go/%s (%s-%s) go-autorest/adal/%s", - runtime.Version(), - runtime.GOARCH, - runtime.GOOS, - number, - ) -) - -// UserAgent returns a string containing the Go version, system architecture and OS, and the adal version. -func UserAgent() string { - return ua -} - -// AddToUserAgent adds an extension to the current user agent -func AddToUserAgent(extension string) error { - if extension != "" { - ua = fmt.Sprintf("%s %s", ua, extension) - return nil - } - return fmt.Errorf("Extension was empty, User Agent remained as '%s'", ua) -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/authorization.go b/vendor/github.com/Azure/go-autorest/autorest/authorization.go deleted file mode 100644 index 54e87b5b6..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/authorization.go +++ /dev/null @@ -1,336 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "crypto/tls" - "encoding/base64" - "fmt" - "net/http" - "net/url" - "strings" - - "github.com/Azure/go-autorest/autorest/adal" -) - -const ( - bearerChallengeHeader = "Www-Authenticate" - bearer = "Bearer" - tenantID = "tenantID" - apiKeyAuthorizerHeader = "Ocp-Apim-Subscription-Key" - bingAPISdkHeader = "X-BingApis-SDK-Client" - golangBingAPISdkHeaderValue = "Go-SDK" - authorization = "Authorization" - basic = "Basic" -) - -// Authorizer is the interface that provides a PrepareDecorator used to supply request -// authorization. Most often, the Authorizer decorator runs last so it has access to the full -// state of the formed HTTP request. -type Authorizer interface { - WithAuthorization() PrepareDecorator -} - -// NullAuthorizer implements a default, "do nothing" Authorizer. -type NullAuthorizer struct{} - -// WithAuthorization returns a PrepareDecorator that does nothing. -func (na NullAuthorizer) WithAuthorization() PrepareDecorator { - return WithNothing() -} - -// APIKeyAuthorizer implements API Key authorization. -type APIKeyAuthorizer struct { - headers map[string]interface{} - queryParameters map[string]interface{} -} - -// NewAPIKeyAuthorizerWithHeaders creates an ApiKeyAuthorizer with headers. -func NewAPIKeyAuthorizerWithHeaders(headers map[string]interface{}) *APIKeyAuthorizer { - return NewAPIKeyAuthorizer(headers, nil) -} - -// NewAPIKeyAuthorizerWithQueryParameters creates an ApiKeyAuthorizer with query parameters. -func NewAPIKeyAuthorizerWithQueryParameters(queryParameters map[string]interface{}) *APIKeyAuthorizer { - return NewAPIKeyAuthorizer(nil, queryParameters) -} - -// NewAPIKeyAuthorizer creates an ApiKeyAuthorizer with headers. -func NewAPIKeyAuthorizer(headers map[string]interface{}, queryParameters map[string]interface{}) *APIKeyAuthorizer { - return &APIKeyAuthorizer{headers: headers, queryParameters: queryParameters} -} - -// WithAuthorization returns a PrepareDecorator that adds an HTTP headers and Query Parameters. -func (aka *APIKeyAuthorizer) WithAuthorization() PrepareDecorator { - return func(p Preparer) Preparer { - return DecoratePreparer(p, WithHeaders(aka.headers), WithQueryParameters(aka.queryParameters)) - } -} - -// CognitiveServicesAuthorizer implements authorization for Cognitive Services. -type CognitiveServicesAuthorizer struct { - subscriptionKey string -} - -// NewCognitiveServicesAuthorizer is -func NewCognitiveServicesAuthorizer(subscriptionKey string) *CognitiveServicesAuthorizer { - return &CognitiveServicesAuthorizer{subscriptionKey: subscriptionKey} -} - -// WithAuthorization is -func (csa *CognitiveServicesAuthorizer) WithAuthorization() PrepareDecorator { - headers := make(map[string]interface{}) - headers[apiKeyAuthorizerHeader] = csa.subscriptionKey - headers[bingAPISdkHeader] = golangBingAPISdkHeaderValue - - return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() -} - -// BearerAuthorizer implements the bearer authorization -type BearerAuthorizer struct { - tokenProvider adal.OAuthTokenProvider -} - -// NewBearerAuthorizer crates a BearerAuthorizer using the given token provider -func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer { - return &BearerAuthorizer{tokenProvider: tp} -} - -// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose -// value is "Bearer " followed by the token. -// -// By default, the token will be automatically refreshed through the Refresher interface. -func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - // the ordering is important here, prefer RefresherWithContext if available - if refresher, ok := ba.tokenProvider.(adal.RefresherWithContext); ok { - err = refresher.EnsureFreshWithContext(r.Context()) - } else if refresher, ok := ba.tokenProvider.(adal.Refresher); ok { - err = refresher.EnsureFresh() - } - if err != nil { - var resp *http.Response - if tokError, ok := err.(adal.TokenRefreshError); ok { - resp = tokError.Response() - } - return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", resp, - "Failed to refresh the Token for request to %s", r.URL) - } - return Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken()))) - } - return r, err - }) - } -} - -// BearerAuthorizerCallbackFunc is the authentication callback signature. -type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error) - -// BearerAuthorizerCallback implements bearer authorization via a callback. -type BearerAuthorizerCallback struct { - sender Sender - callback BearerAuthorizerCallbackFunc -} - -// NewBearerAuthorizerCallback creates a bearer authorization callback. The callback -// is invoked when the HTTP request is submitted. -func NewBearerAuthorizerCallback(s Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback { - if s == nil { - s = sender(tls.RenegotiateNever) - } - return &BearerAuthorizerCallback{sender: s, callback: callback} -} - -// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value -// is "Bearer " followed by the token. The BearerAuthorizer is obtained via a user-supplied callback. -// -// By default, the token will be automatically refreshed through the Refresher interface. -func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - // make a copy of the request and remove the body as it's not - // required and avoids us having to create a copy of it. - rCopy := *r - removeRequestBody(&rCopy) - - resp, err := bacb.sender.Do(&rCopy) - if err == nil && resp.StatusCode == 401 { - defer resp.Body.Close() - if hasBearerChallenge(resp) { - bc, err := newBearerChallenge(resp) - if err != nil { - return r, err - } - if bacb.callback != nil { - ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"]) - if err != nil { - return r, err - } - return Prepare(r, ba.WithAuthorization()) - } - } - } - } - return r, err - }) - } -} - -// returns true if the HTTP response contains a bearer challenge -func hasBearerChallenge(resp *http.Response) bool { - authHeader := resp.Header.Get(bearerChallengeHeader) - if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 { - return false - } - return true -} - -type bearerChallenge struct { - values map[string]string -} - -func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) { - challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader)) - trimmedChallenge := challenge[len(bearer)+1:] - - // challenge is a set of key=value pairs that are comma delimited - pairs := strings.Split(trimmedChallenge, ",") - if len(pairs) < 1 { - err = fmt.Errorf("challenge '%s' contains no pairs", challenge) - return bc, err - } - - bc.values = make(map[string]string) - for i := range pairs { - trimmedPair := strings.TrimSpace(pairs[i]) - pair := strings.Split(trimmedPair, "=") - if len(pair) == 2 { - // remove the enclosing quotes - key := strings.Trim(pair[0], "\"") - value := strings.Trim(pair[1], "\"") - - switch key { - case "authorization", "authorization_uri": - // strip the tenant ID from the authorization URL - asURL, err := url.Parse(value) - if err != nil { - return bc, err - } - bc.values[tenantID] = asURL.Path[1:] - default: - bc.values[key] = value - } - } - } - - return bc, err -} - -// EventGridKeyAuthorizer implements authorization for event grid using key authentication. -type EventGridKeyAuthorizer struct { - topicKey string -} - -// NewEventGridKeyAuthorizer creates a new EventGridKeyAuthorizer -// with the specified topic key. -func NewEventGridKeyAuthorizer(topicKey string) EventGridKeyAuthorizer { - return EventGridKeyAuthorizer{topicKey: topicKey} -} - -// WithAuthorization returns a PrepareDecorator that adds the aeg-sas-key authentication header. -func (egta EventGridKeyAuthorizer) WithAuthorization() PrepareDecorator { - headers := map[string]interface{}{ - "aeg-sas-key": egta.topicKey, - } - return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() -} - -// BasicAuthorizer implements basic HTTP authorization by adding the Authorization HTTP header -// with the value "Basic " where is a base64-encoded username:password tuple. -type BasicAuthorizer struct { - userName string - password string -} - -// NewBasicAuthorizer creates a new BasicAuthorizer with the specified username and password. -func NewBasicAuthorizer(userName, password string) *BasicAuthorizer { - return &BasicAuthorizer{ - userName: userName, - password: password, - } -} - -// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose -// value is "Basic " followed by the base64-encoded username:password tuple. -func (ba *BasicAuthorizer) WithAuthorization() PrepareDecorator { - headers := make(map[string]interface{}) - headers[authorization] = basic + " " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", ba.userName, ba.password))) - - return NewAPIKeyAuthorizerWithHeaders(headers).WithAuthorization() -} - -// MultiTenantServicePrincipalTokenAuthorizer provides authentication across tenants. -type MultiTenantServicePrincipalTokenAuthorizer interface { - WithAuthorization() PrepareDecorator -} - -// NewMultiTenantServicePrincipalTokenAuthorizer crates a BearerAuthorizer using the given token provider -func NewMultiTenantServicePrincipalTokenAuthorizer(tp adal.MultitenantOAuthTokenProvider) MultiTenantServicePrincipalTokenAuthorizer { - return &multiTenantSPTAuthorizer{tp: tp} -} - -type multiTenantSPTAuthorizer struct { - tp adal.MultitenantOAuthTokenProvider -} - -// WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header using the -// primary token along with the auxiliary authorization header using the auxiliary tokens. -// -// By default, the token will be automatically refreshed through the Refresher interface. -func (mt multiTenantSPTAuthorizer) WithAuthorization() PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err != nil { - return r, err - } - if refresher, ok := mt.tp.(adal.RefresherWithContext); ok { - err = refresher.EnsureFreshWithContext(r.Context()) - if err != nil { - var resp *http.Response - if tokError, ok := err.(adal.TokenRefreshError); ok { - resp = tokError.Response() - } - return r, NewErrorWithError(err, "azure.multiTenantSPTAuthorizer", "WithAuthorization", resp, - "Failed to refresh one or more Tokens for request to %s", r.URL) - } - } - r, err = Prepare(r, WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", mt.tp.PrimaryOAuthToken()))) - if err != nil { - return r, err - } - auxTokens := mt.tp.AuxiliaryOAuthTokens() - for i := range auxTokens { - auxTokens[i] = fmt.Sprintf("Bearer %s", auxTokens[i]) - } - return Prepare(r, WithHeader(headerAuxAuthorization, strings.Join(auxTokens, "; "))) - }) - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/autorest.go b/vendor/github.com/Azure/go-autorest/autorest/autorest.go deleted file mode 100644 index aafdf021f..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/autorest.go +++ /dev/null @@ -1,150 +0,0 @@ -/* -Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines -and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/) -generated Go code. - -The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending, -and Responding. A typical pattern is: - - req, err := Prepare(&http.Request{}, - token.WithAuthorization()) - - resp, err := Send(req, - WithLogging(logger), - DoErrorIfStatusCode(http.StatusInternalServerError), - DoCloseIfError(), - DoRetryForAttempts(5, time.Second)) - - err = Respond(resp, - ByDiscardingBody(), - ByClosing()) - -Each phase relies on decorators to modify and / or manage processing. Decorators may first modify -and then pass the data along, pass the data first and then modify the result, or wrap themselves -around passing the data (such as a logger might do). Decorators run in the order provided. For -example, the following: - - req, err := Prepare(&http.Request{}, - WithBaseURL("https://microsoft.com/"), - WithPath("a"), - WithPath("b"), - WithPath("c")) - -will set the URL to: - - https://microsoft.com/a/b/c - -Preparers and Responders may be shared and re-used (assuming the underlying decorators support -sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders -shared among multiple go-routines, and a single Sender shared among multiple sending go-routines, -all bound together by means of input / output channels. - -Decorators hold their passed state within a closure (such as the path components in the example -above). Be careful to share Preparers and Responders only in a context where such held state -applies. For example, it may not make sense to share a Preparer that applies a query string from a -fixed set of values. Similarly, sharing a Responder that reads the response body into a passed -struct (e.g., ByUnmarshallingJson) is likely incorrect. - -Lastly, the Swagger specification (https://swagger.io) that drives AutoRest -(https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The -github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure -correct parsing and formatting. - -Errors raised by autorest objects and methods will conform to the autorest.Error interface. - -See the included examples for more detail. For details on the suggested use of this package by -generated clients, see the Client described below. -*/ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "context" - "net/http" - "time" -) - -const ( - // HeaderLocation specifies the HTTP Location header. - HeaderLocation = "Location" - - // HeaderRetryAfter specifies the HTTP Retry-After header. - HeaderRetryAfter = "Retry-After" -) - -// ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set -// and false otherwise. -func ResponseHasStatusCode(resp *http.Response, codes ...int) bool { - if resp == nil { - return false - } - return containsInt(codes, resp.StatusCode) -} - -// GetLocation retrieves the URL from the Location header of the passed response. -func GetLocation(resp *http.Response) string { - return resp.Header.Get(HeaderLocation) -} - -// GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If -// the header is absent or is malformed, it will return the supplied default delay time.Duration. -func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration { - retry := resp.Header.Get(HeaderRetryAfter) - if retry == "" { - return defaultDelay - } - - d, err := time.ParseDuration(retry + "s") - if err != nil { - return defaultDelay - } - - return d -} - -// NewPollingRequest allocates and returns a new http.Request to poll for the passed response. -func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) { - location := GetLocation(resp) - if location == "" { - return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling") - } - - req, err := Prepare(&http.Request{Cancel: cancel}, - AsGet(), - WithBaseURL(location)) - if err != nil { - return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location) - } - - return req, nil -} - -// NewPollingRequestWithContext allocates and returns a new http.Request with the specified context to poll for the passed response. -func NewPollingRequestWithContext(ctx context.Context, resp *http.Response) (*http.Request, error) { - location := GetLocation(resp) - if location == "" { - return nil, NewErrorWithResponse("autorest", "NewPollingRequestWithContext", resp, "Location header missing from response that requires polling") - } - - req, err := Prepare((&http.Request{}).WithContext(ctx), - AsGet(), - WithBaseURL(location)) - if err != nil { - return nil, NewErrorWithError(err, "autorest", "NewPollingRequestWithContext", nil, "Failure creating poll request to %s", location) - } - - return req, nil -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go deleted file mode 100644 index 1cb41cbeb..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go +++ /dev/null @@ -1,924 +0,0 @@ -package azure - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "strings" - "time" - - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/tracing" -) - -const ( - headerAsyncOperation = "Azure-AsyncOperation" -) - -const ( - operationInProgress string = "InProgress" - operationCanceled string = "Canceled" - operationFailed string = "Failed" - operationSucceeded string = "Succeeded" -) - -var pollingCodes = [...]int{http.StatusNoContent, http.StatusAccepted, http.StatusCreated, http.StatusOK} - -// Future provides a mechanism to access the status and results of an asynchronous request. -// Since futures are stateful they should be passed by value to avoid race conditions. -type Future struct { - pt pollingTracker -} - -// NewFutureFromResponse returns a new Future object initialized -// with the initial response from an asynchronous operation. -func NewFutureFromResponse(resp *http.Response) (Future, error) { - pt, err := createPollingTracker(resp) - return Future{pt: pt}, err -} - -// Response returns the last HTTP response. -func (f Future) Response() *http.Response { - if f.pt == nil { - return nil - } - return f.pt.latestResponse() -} - -// Status returns the last status message of the operation. -func (f Future) Status() string { - if f.pt == nil { - return "" - } - return f.pt.pollingStatus() -} - -// PollingMethod returns the method used to monitor the status of the asynchronous operation. -func (f Future) PollingMethod() PollingMethodType { - if f.pt == nil { - return PollingUnknown - } - return f.pt.pollingMethod() -} - -// DoneWithContext queries the service to see if the operation has completed. -func (f *Future) DoneWithContext(ctx context.Context, sender autorest.Sender) (done bool, err error) { - ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.DoneWithContext") - defer func() { - sc := -1 - resp := f.Response() - if resp != nil { - sc = resp.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - - if f.pt == nil { - return false, autorest.NewError("Future", "Done", "future is not initialized") - } - if f.pt.hasTerminated() { - return true, f.pt.pollingError() - } - if err := f.pt.pollForStatus(ctx, sender); err != nil { - return false, err - } - if err := f.pt.checkForErrors(); err != nil { - return f.pt.hasTerminated(), err - } - if err := f.pt.updatePollingState(f.pt.provisioningStateApplicable()); err != nil { - return false, err - } - if err := f.pt.initPollingMethod(); err != nil { - return false, err - } - if err := f.pt.updatePollingMethod(); err != nil { - return false, err - } - return f.pt.hasTerminated(), f.pt.pollingError() -} - -// GetPollingDelay returns a duration the application should wait before checking -// the status of the asynchronous request and true; this value is returned from -// the service via the Retry-After response header. If the header wasn't returned -// then the function returns the zero-value time.Duration and false. -func (f Future) GetPollingDelay() (time.Duration, bool) { - if f.pt == nil { - return 0, false - } - resp := f.pt.latestResponse() - if resp == nil { - return 0, false - } - - retry := resp.Header.Get(autorest.HeaderRetryAfter) - if retry == "" { - return 0, false - } - - d, err := time.ParseDuration(retry + "s") - if err != nil { - panic(err) - } - - return d, true -} - -// WaitForCompletionRef will return when one of the following conditions is met: the long -// running operation has completed, the provided context is cancelled, or the client's -// polling duration has been exceeded. It will retry failed polling attempts based on -// the retry value defined in the client up to the maximum retry attempts. -// If no deadline is specified in the context then the client.PollingDuration will be -// used to determine if a default deadline should be used. -// If PollingDuration is greater than zero the value will be used as the context's timeout. -// If PollingDuration is zero then no default deadline will be used. -func (f *Future) WaitForCompletionRef(ctx context.Context, client autorest.Client) (err error) { - ctx = tracing.StartSpan(ctx, "github.com/Azure/go-autorest/autorest/azure/async.WaitForCompletionRef") - defer func() { - sc := -1 - resp := f.Response() - if resp != nil { - sc = resp.StatusCode - } - tracing.EndSpan(ctx, sc, err) - }() - cancelCtx := ctx - // if the provided context already has a deadline don't override it - _, hasDeadline := ctx.Deadline() - if d := client.PollingDuration; !hasDeadline && d != 0 { - var cancel context.CancelFunc - cancelCtx, cancel = context.WithTimeout(ctx, d) - defer cancel() - } - - done, err := f.DoneWithContext(ctx, client) - for attempts := 0; !done; done, err = f.DoneWithContext(ctx, client) { - if attempts >= client.RetryAttempts { - return autorest.NewErrorWithError(err, "Future", "WaitForCompletion", f.pt.latestResponse(), "the number of retries has been exceeded") - } - // we want delayAttempt to be zero in the non-error case so - // that DelayForBackoff doesn't perform exponential back-off - var delayAttempt int - var delay time.Duration - if err == nil { - // check for Retry-After delay, if not present use the client's polling delay - var ok bool - delay, ok = f.GetPollingDelay() - if !ok { - delay = client.PollingDelay - } - } else { - // there was an error polling for status so perform exponential - // back-off based on the number of attempts using the client's retry - // duration. update attempts after delayAttempt to avoid off-by-one. - delayAttempt = attempts - delay = client.RetryDuration - attempts++ - } - // wait until the delay elapses or the context is cancelled - delayElapsed := autorest.DelayForBackoff(delay, delayAttempt, cancelCtx.Done()) - if !delayElapsed { - return autorest.NewErrorWithError(cancelCtx.Err(), "Future", "WaitForCompletion", f.pt.latestResponse(), "context has been cancelled") - } - } - return -} - -// MarshalJSON implements the json.Marshaler interface. -func (f Future) MarshalJSON() ([]byte, error) { - return json.Marshal(f.pt) -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (f *Future) UnmarshalJSON(data []byte) error { - // unmarshal into JSON object to determine the tracker type - obj := map[string]interface{}{} - err := json.Unmarshal(data, &obj) - if err != nil { - return err - } - if obj["method"] == nil { - return autorest.NewError("Future", "UnmarshalJSON", "missing 'method' property") - } - method := obj["method"].(string) - switch strings.ToUpper(method) { - case http.MethodDelete: - f.pt = &pollingTrackerDelete{} - case http.MethodPatch: - f.pt = &pollingTrackerPatch{} - case http.MethodPost: - f.pt = &pollingTrackerPost{} - case http.MethodPut: - f.pt = &pollingTrackerPut{} - default: - return autorest.NewError("Future", "UnmarshalJSON", "unsupoorted method '%s'", method) - } - // now unmarshal into the tracker - return json.Unmarshal(data, &f.pt) -} - -// PollingURL returns the URL used for retrieving the status of the long-running operation. -func (f Future) PollingURL() string { - if f.pt == nil { - return "" - } - return f.pt.pollingURL() -} - -// GetResult should be called once polling has completed successfully. -// It makes the final GET call to retrieve the resultant payload. -func (f Future) GetResult(sender autorest.Sender) (*http.Response, error) { - if f.pt.finalGetURL() == "" { - // we can end up in this situation if the async operation returns a 200 - // with no polling URLs. in that case return the response which should - // contain the JSON payload (only do this for successful terminal cases). - if lr := f.pt.latestResponse(); lr != nil && f.pt.hasSucceeded() { - return lr, nil - } - return nil, autorest.NewError("Future", "GetResult", "missing URL for retrieving result") - } - req, err := http.NewRequest(http.MethodGet, f.pt.finalGetURL(), nil) - if err != nil { - return nil, err - } - return sender.Do(req) -} - -type pollingTracker interface { - // these methods can differ per tracker - - // checks the response headers and status code to determine the polling mechanism - updatePollingMethod() error - - // checks the response for tracker-specific error conditions - checkForErrors() error - - // returns true if provisioning state should be checked - provisioningStateApplicable() bool - - // methods common to all trackers - - // initializes a tracker's polling URL and method, called for each iteration. - // these values can be overridden by each polling tracker as required. - initPollingMethod() error - - // initializes the tracker's internal state, call this when the tracker is created - initializeState() error - - // makes an HTTP request to check the status of the LRO - pollForStatus(ctx context.Context, sender autorest.Sender) error - - // updates internal tracker state, call this after each call to pollForStatus - updatePollingState(provStateApl bool) error - - // returns the error response from the service, can be nil - pollingError() error - - // returns the polling method being used - pollingMethod() PollingMethodType - - // returns the state of the LRO as returned from the service - pollingStatus() string - - // returns the URL used for polling status - pollingURL() string - - // returns the URL used for the final GET to retrieve the resource - finalGetURL() string - - // returns true if the LRO is in a terminal state - hasTerminated() bool - - // returns true if the LRO is in a failed terminal state - hasFailed() bool - - // returns true if the LRO is in a successful terminal state - hasSucceeded() bool - - // returns the cached HTTP response after a call to pollForStatus(), can be nil - latestResponse() *http.Response -} - -type pollingTrackerBase struct { - // resp is the last response, either from the submission of the LRO or from polling - resp *http.Response - - // method is the HTTP verb, this is needed for deserialization - Method string `json:"method"` - - // rawBody is the raw JSON response body - rawBody map[string]interface{} - - // denotes if polling is using async-operation or location header - Pm PollingMethodType `json:"pollingMethod"` - - // the URL to poll for status - URI string `json:"pollingURI"` - - // the state of the LRO as returned from the service - State string `json:"lroState"` - - // the URL to GET for the final result - FinalGetURI string `json:"resultURI"` - - // used to hold an error object returned from the service - Err *ServiceError `json:"error,omitempty"` -} - -func (pt *pollingTrackerBase) initializeState() error { - // determine the initial polling state based on response body and/or HTTP status - // code. this is applicable to the initial LRO response, not polling responses! - pt.Method = pt.resp.Request.Method - if err := pt.updateRawBody(); err != nil { - return err - } - switch pt.resp.StatusCode { - case http.StatusOK: - if ps := pt.getProvisioningState(); ps != nil { - pt.State = *ps - if pt.hasFailed() { - pt.updateErrorFromResponse() - return pt.pollingError() - } - } else { - pt.State = operationSucceeded - } - case http.StatusCreated: - if ps := pt.getProvisioningState(); ps != nil { - pt.State = *ps - } else { - pt.State = operationInProgress - } - case http.StatusAccepted: - pt.State = operationInProgress - case http.StatusNoContent: - pt.State = operationSucceeded - default: - pt.State = operationFailed - pt.updateErrorFromResponse() - return pt.pollingError() - } - return pt.initPollingMethod() -} - -func (pt pollingTrackerBase) getProvisioningState() *string { - if pt.rawBody != nil && pt.rawBody["properties"] != nil { - p := pt.rawBody["properties"].(map[string]interface{}) - if ps := p["provisioningState"]; ps != nil { - s := ps.(string) - return &s - } - } - return nil -} - -func (pt *pollingTrackerBase) updateRawBody() error { - pt.rawBody = map[string]interface{}{} - if pt.resp.ContentLength != 0 { - defer pt.resp.Body.Close() - b, err := ioutil.ReadAll(pt.resp.Body) - if err != nil { - return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to read response body") - } - // observed in 204 responses over HTTP/2.0; the content length is -1 but body is empty - if len(b) == 0 { - return nil - } - // put the body back so it's available to other callers - pt.resp.Body = ioutil.NopCloser(bytes.NewReader(b)) - if err = json.Unmarshal(b, &pt.rawBody); err != nil { - return autorest.NewErrorWithError(err, "pollingTrackerBase", "updateRawBody", nil, "failed to unmarshal response body") - } - } - return nil -} - -func (pt *pollingTrackerBase) pollForStatus(ctx context.Context, sender autorest.Sender) error { - req, err := http.NewRequest(http.MethodGet, pt.URI, nil) - if err != nil { - return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to create HTTP request") - } - - req = req.WithContext(ctx) - preparer := autorest.CreatePreparer(autorest.GetPrepareDecorators(ctx)...) - req, err = preparer.Prepare(req) - if err != nil { - return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed preparing HTTP request") - } - pt.resp, err = sender.Do(req) - if err != nil { - return autorest.NewErrorWithError(err, "pollingTrackerBase", "pollForStatus", nil, "failed to send HTTP request") - } - if autorest.ResponseHasStatusCode(pt.resp, pollingCodes[:]...) { - // reset the service error on success case - pt.Err = nil - err = pt.updateRawBody() - } else { - // check response body for error content - pt.updateErrorFromResponse() - err = pt.pollingError() - } - return err -} - -// attempts to unmarshal a ServiceError type from the response body. -// if that fails then make a best attempt at creating something meaningful. -// NOTE: this assumes that the async operation has failed. -func (pt *pollingTrackerBase) updateErrorFromResponse() { - var err error - if pt.resp.ContentLength != 0 { - type respErr struct { - ServiceError *ServiceError `json:"error"` - } - re := respErr{} - defer pt.resp.Body.Close() - var b []byte - if b, err = ioutil.ReadAll(pt.resp.Body); err != nil || len(b) == 0 { - goto Default - } - if err = json.Unmarshal(b, &re); err != nil { - goto Default - } - // unmarshalling the error didn't yield anything, try unwrapped error - if re.ServiceError == nil { - err = json.Unmarshal(b, &re.ServiceError) - if err != nil { - goto Default - } - } - // the unmarshaller will ensure re.ServiceError is non-nil - // even if there was no content unmarshalled so check the code. - if re.ServiceError.Code != "" { - pt.Err = re.ServiceError - return - } - } -Default: - se := &ServiceError{ - Code: pt.pollingStatus(), - Message: "The async operation failed.", - } - if err != nil { - se.InnerError = make(map[string]interface{}) - se.InnerError["unmarshalError"] = err.Error() - } - // stick the response body into the error object in hopes - // it contains something useful to help diagnose the failure. - if len(pt.rawBody) > 0 { - se.AdditionalInfo = []map[string]interface{}{ - pt.rawBody, - } - } - pt.Err = se -} - -func (pt *pollingTrackerBase) updatePollingState(provStateApl bool) error { - if pt.Pm == PollingAsyncOperation && pt.rawBody["status"] != nil { - pt.State = pt.rawBody["status"].(string) - } else { - if pt.resp.StatusCode == http.StatusAccepted { - pt.State = operationInProgress - } else if provStateApl { - if ps := pt.getProvisioningState(); ps != nil { - pt.State = *ps - } else { - pt.State = operationSucceeded - } - } else { - return autorest.NewError("pollingTrackerBase", "updatePollingState", "the response from the async operation has an invalid status code") - } - } - // if the operation has failed update the error state - if pt.hasFailed() { - pt.updateErrorFromResponse() - } - return nil -} - -func (pt pollingTrackerBase) pollingError() error { - if pt.Err == nil { - return nil - } - return pt.Err -} - -func (pt pollingTrackerBase) pollingMethod() PollingMethodType { - return pt.Pm -} - -func (pt pollingTrackerBase) pollingStatus() string { - return pt.State -} - -func (pt pollingTrackerBase) pollingURL() string { - return pt.URI -} - -func (pt pollingTrackerBase) finalGetURL() string { - return pt.FinalGetURI -} - -func (pt pollingTrackerBase) hasTerminated() bool { - return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed) || strings.EqualFold(pt.State, operationSucceeded) -} - -func (pt pollingTrackerBase) hasFailed() bool { - return strings.EqualFold(pt.State, operationCanceled) || strings.EqualFold(pt.State, operationFailed) -} - -func (pt pollingTrackerBase) hasSucceeded() bool { - return strings.EqualFold(pt.State, operationSucceeded) -} - -func (pt pollingTrackerBase) latestResponse() *http.Response { - return pt.resp -} - -// error checking common to all trackers -func (pt pollingTrackerBase) baseCheckForErrors() error { - // for Azure-AsyncOperations the response body cannot be nil or empty - if pt.Pm == PollingAsyncOperation { - if pt.resp.Body == nil || pt.resp.ContentLength == 0 { - return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "for Azure-AsyncOperation response body cannot be nil") - } - if pt.rawBody["status"] == nil { - return autorest.NewError("pollingTrackerBase", "baseCheckForErrors", "missing status property in Azure-AsyncOperation response body") - } - } - return nil -} - -// default initialization of polling URL/method. each verb tracker will update this as required. -func (pt *pollingTrackerBase) initPollingMethod() error { - if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - return nil - } - if lh, err := getURLFromLocationHeader(pt.resp); err != nil { - return err - } else if lh != "" { - pt.URI = lh - pt.Pm = PollingLocation - return nil - } - // it's ok if we didn't find a polling header, this will be handled elsewhere - return nil -} - -// DELETE - -type pollingTrackerDelete struct { - pollingTrackerBase -} - -func (pt *pollingTrackerDelete) updatePollingMethod() error { - // for 201 the Location header is required - if pt.resp.StatusCode == http.StatusCreated { - if lh, err := getURLFromLocationHeader(pt.resp); err != nil { - return err - } else if lh == "" { - return autorest.NewError("pollingTrackerDelete", "updateHeaders", "missing Location header in 201 response") - } else { - pt.URI = lh - } - pt.Pm = PollingLocation - pt.FinalGetURI = pt.URI - } - // for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary - if pt.resp.StatusCode == http.StatusAccepted { - ao, err := getURLFromAsyncOpHeader(pt.resp) - if err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - // if the Location header is invalid and we already have a polling URL - // then we don't care if the Location header URL is malformed. - if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" { - return err - } else if lh != "" { - if ao == "" { - pt.URI = lh - pt.Pm = PollingLocation - } - // when both headers are returned we use the value in the Location header for the final GET - pt.FinalGetURI = lh - } - // make sure a polling URL was found - if pt.URI == "" { - return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response") - } - } - return nil -} - -func (pt pollingTrackerDelete) checkForErrors() error { - return pt.baseCheckForErrors() -} - -func (pt pollingTrackerDelete) provisioningStateApplicable() bool { - return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent -} - -// PATCH - -type pollingTrackerPatch struct { - pollingTrackerBase -} - -func (pt *pollingTrackerPatch) updatePollingMethod() error { - // by default we can use the original URL for polling and final GET - if pt.URI == "" { - pt.URI = pt.resp.Request.URL.String() - } - if pt.FinalGetURI == "" { - pt.FinalGetURI = pt.resp.Request.URL.String() - } - if pt.Pm == PollingUnknown { - pt.Pm = PollingRequestURI - } - // for 201 it's permissible for no headers to be returned - if pt.resp.StatusCode == http.StatusCreated { - if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - } - // for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary - // note the absence of the "final GET" mechanism for PATCH - if pt.resp.StatusCode == http.StatusAccepted { - ao, err := getURLFromAsyncOpHeader(pt.resp) - if err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - if ao == "" { - if lh, err := getURLFromLocationHeader(pt.resp); err != nil { - return err - } else if lh == "" { - return autorest.NewError("pollingTrackerPatch", "updateHeaders", "didn't get any suitable polling URLs in 202 response") - } else { - pt.URI = lh - pt.Pm = PollingLocation - } - } - } - return nil -} - -func (pt pollingTrackerPatch) checkForErrors() error { - return pt.baseCheckForErrors() -} - -func (pt pollingTrackerPatch) provisioningStateApplicable() bool { - return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated -} - -// POST - -type pollingTrackerPost struct { - pollingTrackerBase -} - -func (pt *pollingTrackerPost) updatePollingMethod() error { - // 201 requires Location header - if pt.resp.StatusCode == http.StatusCreated { - if lh, err := getURLFromLocationHeader(pt.resp); err != nil { - return err - } else if lh == "" { - return autorest.NewError("pollingTrackerPost", "updateHeaders", "missing Location header in 201 response") - } else { - pt.URI = lh - pt.FinalGetURI = lh - pt.Pm = PollingLocation - } - } - // for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary - if pt.resp.StatusCode == http.StatusAccepted { - ao, err := getURLFromAsyncOpHeader(pt.resp) - if err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - // if the Location header is invalid and we already have a polling URL - // then we don't care if the Location header URL is malformed. - if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" { - return err - } else if lh != "" { - if ao == "" { - pt.URI = lh - pt.Pm = PollingLocation - } - // when both headers are returned we use the value in the Location header for the final GET - pt.FinalGetURI = lh - } - // make sure a polling URL was found - if pt.URI == "" { - return autorest.NewError("pollingTrackerPost", "updateHeaders", "didn't get any suitable polling URLs in 202 response") - } - } - return nil -} - -func (pt pollingTrackerPost) checkForErrors() error { - return pt.baseCheckForErrors() -} - -func (pt pollingTrackerPost) provisioningStateApplicable() bool { - return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusNoContent -} - -// PUT - -type pollingTrackerPut struct { - pollingTrackerBase -} - -func (pt *pollingTrackerPut) updatePollingMethod() error { - // by default we can use the original URL for polling and final GET - if pt.URI == "" { - pt.URI = pt.resp.Request.URL.String() - } - if pt.FinalGetURI == "" { - pt.FinalGetURI = pt.resp.Request.URL.String() - } - if pt.Pm == PollingUnknown { - pt.Pm = PollingRequestURI - } - // for 201 it's permissible for no headers to be returned - if pt.resp.StatusCode == http.StatusCreated { - if ao, err := getURLFromAsyncOpHeader(pt.resp); err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - } - // for 202 prefer the Azure-AsyncOperation header but fall back to Location if necessary - if pt.resp.StatusCode == http.StatusAccepted { - ao, err := getURLFromAsyncOpHeader(pt.resp) - if err != nil { - return err - } else if ao != "" { - pt.URI = ao - pt.Pm = PollingAsyncOperation - } - // if the Location header is invalid and we already have a polling URL - // then we don't care if the Location header URL is malformed. - if lh, err := getURLFromLocationHeader(pt.resp); err != nil && pt.URI == "" { - return err - } else if lh != "" { - if ao == "" { - pt.URI = lh - pt.Pm = PollingLocation - } - } - // make sure a polling URL was found - if pt.URI == "" { - return autorest.NewError("pollingTrackerPut", "updateHeaders", "didn't get any suitable polling URLs in 202 response") - } - } - return nil -} - -func (pt pollingTrackerPut) checkForErrors() error { - err := pt.baseCheckForErrors() - if err != nil { - return err - } - // if there are no LRO headers then the body cannot be empty - ao, err := getURLFromAsyncOpHeader(pt.resp) - if err != nil { - return err - } - lh, err := getURLFromLocationHeader(pt.resp) - if err != nil { - return err - } - if ao == "" && lh == "" && len(pt.rawBody) == 0 { - return autorest.NewError("pollingTrackerPut", "checkForErrors", "the response did not contain a body") - } - return nil -} - -func (pt pollingTrackerPut) provisioningStateApplicable() bool { - return pt.resp.StatusCode == http.StatusOK || pt.resp.StatusCode == http.StatusCreated -} - -// creates a polling tracker based on the verb of the original request -func createPollingTracker(resp *http.Response) (pollingTracker, error) { - var pt pollingTracker - switch strings.ToUpper(resp.Request.Method) { - case http.MethodDelete: - pt = &pollingTrackerDelete{pollingTrackerBase: pollingTrackerBase{resp: resp}} - case http.MethodPatch: - pt = &pollingTrackerPatch{pollingTrackerBase: pollingTrackerBase{resp: resp}} - case http.MethodPost: - pt = &pollingTrackerPost{pollingTrackerBase: pollingTrackerBase{resp: resp}} - case http.MethodPut: - pt = &pollingTrackerPut{pollingTrackerBase: pollingTrackerBase{resp: resp}} - default: - return nil, autorest.NewError("azure", "createPollingTracker", "unsupported HTTP method %s", resp.Request.Method) - } - if err := pt.initializeState(); err != nil { - return pt, err - } - // this initializes the polling header values, we do this during creation in case the - // initial response send us invalid values; this way the API call will return a non-nil - // error (not doing this means the error shows up in Future.Done) - return pt, pt.updatePollingMethod() -} - -// gets the polling URL from the Azure-AsyncOperation header. -// ensures the URL is well-formed and absolute. -func getURLFromAsyncOpHeader(resp *http.Response) (string, error) { - s := resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation)) - if s == "" { - return "", nil - } - if !isValidURL(s) { - return "", autorest.NewError("azure", "getURLFromAsyncOpHeader", "invalid polling URL '%s'", s) - } - return s, nil -} - -// gets the polling URL from the Location header. -// ensures the URL is well-formed and absolute. -func getURLFromLocationHeader(resp *http.Response) (string, error) { - s := resp.Header.Get(http.CanonicalHeaderKey(autorest.HeaderLocation)) - if s == "" { - return "", nil - } - if !isValidURL(s) { - return "", autorest.NewError("azure", "getURLFromLocationHeader", "invalid polling URL '%s'", s) - } - return s, nil -} - -// verify that the URL is valid and absolute -func isValidURL(s string) bool { - u, err := url.Parse(s) - return err == nil && u.IsAbs() -} - -// PollingMethodType defines a type used for enumerating polling mechanisms. -type PollingMethodType string - -const ( - // PollingAsyncOperation indicates the polling method uses the Azure-AsyncOperation header. - PollingAsyncOperation PollingMethodType = "AsyncOperation" - - // PollingLocation indicates the polling method uses the Location header. - PollingLocation PollingMethodType = "Location" - - // PollingRequestURI indicates the polling method uses the original request URI. - PollingRequestURI PollingMethodType = "RequestURI" - - // PollingUnknown indicates an unknown polling method and is the default value. - PollingUnknown PollingMethodType = "" -) - -// AsyncOpIncompleteError is the type that's returned from a future that has not completed. -type AsyncOpIncompleteError struct { - // FutureType is the name of the type composed of a azure.Future. - FutureType string -} - -// Error returns an error message including the originating type name of the error. -func (e AsyncOpIncompleteError) Error() string { - return fmt.Sprintf("%s: asynchronous operation has not completed", e.FutureType) -} - -// NewAsyncOpIncompleteError creates a new AsyncOpIncompleteError with the specified parameters. -func NewAsyncOpIncompleteError(futureType string) AsyncOpIncompleteError { - return AsyncOpIncompleteError{ - FutureType: futureType, - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go b/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go deleted file mode 100644 index 3a0a439ff..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/azure.go +++ /dev/null @@ -1,326 +0,0 @@ -// Package azure provides Azure-specific implementations used with AutoRest. -// See the included examples for more detail. -package azure - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "regexp" - "strconv" - "strings" - - "github.com/Azure/go-autorest/autorest" -) - -const ( - // HeaderClientID is the Azure extension header to set a user-specified request ID. - HeaderClientID = "x-ms-client-request-id" - - // HeaderReturnClientID is the Azure extension header to set if the user-specified request ID - // should be included in the response. - HeaderReturnClientID = "x-ms-return-client-request-id" - - // HeaderRequestID is the Azure extension header of the service generated request ID returned - // in the response. - HeaderRequestID = "x-ms-request-id" -) - -// ServiceError encapsulates the error response from an Azure service. -// It adhears to the OData v4 specification for error responses. -type ServiceError struct { - Code string `json:"code"` - Message string `json:"message"` - Target *string `json:"target"` - Details []map[string]interface{} `json:"details"` - InnerError map[string]interface{} `json:"innererror"` - AdditionalInfo []map[string]interface{} `json:"additionalInfo"` -} - -func (se ServiceError) Error() string { - result := fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message) - - if se.Target != nil { - result += fmt.Sprintf(" Target=%q", *se.Target) - } - - if se.Details != nil { - d, err := json.Marshal(se.Details) - if err != nil { - result += fmt.Sprintf(" Details=%v", se.Details) - } - result += fmt.Sprintf(" Details=%v", string(d)) - } - - if se.InnerError != nil { - d, err := json.Marshal(se.InnerError) - if err != nil { - result += fmt.Sprintf(" InnerError=%v", se.InnerError) - } - result += fmt.Sprintf(" InnerError=%v", string(d)) - } - - if se.AdditionalInfo != nil { - d, err := json.Marshal(se.AdditionalInfo) - if err != nil { - result += fmt.Sprintf(" AdditionalInfo=%v", se.AdditionalInfo) - } - result += fmt.Sprintf(" AdditionalInfo=%v", string(d)) - } - - return result -} - -// UnmarshalJSON implements the json.Unmarshaler interface for the ServiceError type. -func (se *ServiceError) UnmarshalJSON(b []byte) error { - // per the OData v4 spec the details field must be an array of JSON objects. - // unfortunately not all services adhear to the spec and just return a single - // object instead of an array with one object. so we have to perform some - // shenanigans to accommodate both cases. - // http://docs.oasis-open.org/odata/odata-json-format/v4.0/os/odata-json-format-v4.0-os.html#_Toc372793091 - - type serviceError1 struct { - Code string `json:"code"` - Message string `json:"message"` - Target *string `json:"target"` - Details []map[string]interface{} `json:"details"` - InnerError map[string]interface{} `json:"innererror"` - AdditionalInfo []map[string]interface{} `json:"additionalInfo"` - } - - type serviceError2 struct { - Code string `json:"code"` - Message string `json:"message"` - Target *string `json:"target"` - Details map[string]interface{} `json:"details"` - InnerError map[string]interface{} `json:"innererror"` - AdditionalInfo []map[string]interface{} `json:"additionalInfo"` - } - - se1 := serviceError1{} - err := json.Unmarshal(b, &se1) - if err == nil { - se.populate(se1.Code, se1.Message, se1.Target, se1.Details, se1.InnerError, se1.AdditionalInfo) - return nil - } - - se2 := serviceError2{} - err = json.Unmarshal(b, &se2) - if err == nil { - se.populate(se2.Code, se2.Message, se2.Target, nil, se2.InnerError, se2.AdditionalInfo) - se.Details = append(se.Details, se2.Details) - return nil - } - return err -} - -func (se *ServiceError) populate(code, message string, target *string, details []map[string]interface{}, inner map[string]interface{}, additional []map[string]interface{}) { - se.Code = code - se.Message = message - se.Target = target - se.Details = details - se.InnerError = inner - se.AdditionalInfo = additional -} - -// RequestError describes an error response returned by Azure service. -type RequestError struct { - autorest.DetailedError - - // The error returned by the Azure service. - ServiceError *ServiceError `json:"error"` - - // The request id (from the x-ms-request-id-header) of the request. - RequestID string -} - -// Error returns a human-friendly error message from service error. -func (e RequestError) Error() string { - return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v", - e.StatusCode, e.ServiceError) -} - -// IsAzureError returns true if the passed error is an Azure Service error; false otherwise. -func IsAzureError(e error) bool { - _, ok := e.(*RequestError) - return ok -} - -// Resource contains details about an Azure resource. -type Resource struct { - SubscriptionID string - ResourceGroup string - Provider string - ResourceType string - ResourceName string -} - -// ParseResourceID parses a resource ID into a ResourceDetails struct. -// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-resource#return-value-4. -func ParseResourceID(resourceID string) (Resource, error) { - - const resourceIDPatternText = `(?i)subscriptions/(.+)/resourceGroups/(.+)/providers/(.+?)/(.+?)/(.+)` - resourceIDPattern := regexp.MustCompile(resourceIDPatternText) - match := resourceIDPattern.FindStringSubmatch(resourceID) - - if len(match) == 0 { - return Resource{}, fmt.Errorf("parsing failed for %s. Invalid resource Id format", resourceID) - } - - v := strings.Split(match[5], "/") - resourceName := v[len(v)-1] - - result := Resource{ - SubscriptionID: match[1], - ResourceGroup: match[2], - Provider: match[3], - ResourceType: match[4], - ResourceName: resourceName, - } - - return result, nil -} - -// NewErrorWithError creates a new Error conforming object from the -// passed packageType, method, statusCode of the given resp (UndefinedStatusCode -// if resp is nil), message, and original error. message is treated as a format -// string to which the optional args apply. -func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError { - if v, ok := original.(*RequestError); ok { - return *v - } - - statusCode := autorest.UndefinedStatusCode - if resp != nil { - statusCode = resp.StatusCode - } - return RequestError{ - DetailedError: autorest.DetailedError{ - Original: original, - PackageType: packageType, - Method: method, - StatusCode: statusCode, - Message: fmt.Sprintf(message, args...), - }, - } -} - -// WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of -// x-ms-client-request-id whose value is the passed, undecorated UUID (e.g., -// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id -// header to true such that UUID accompanies the http.Response. -func WithReturningClientID(uuid string) autorest.PrepareDecorator { - preparer := autorest.CreatePreparer( - WithClientID(uuid), - WithReturnClientID(true)) - - return func(p autorest.Preparer) autorest.Preparer { - return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err != nil { - return r, err - } - return preparer.Prepare(r) - }) - } -} - -// WithClientID returns a PrepareDecorator that adds an HTTP extension header of -// x-ms-client-request-id whose value is passed, undecorated UUID (e.g., -// "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). -func WithClientID(uuid string) autorest.PrepareDecorator { - return autorest.WithHeader(HeaderClientID, uuid) -} - -// WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of -// x-ms-return-client-request-id whose boolean value indicates if the value of the -// x-ms-client-request-id header should be included in the http.Response. -func WithReturnClientID(b bool) autorest.PrepareDecorator { - return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b)) -} - -// ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the -// http.Request sent to the service (and returned in the http.Response) -func ExtractClientID(resp *http.Response) string { - return autorest.ExtractHeaderValue(HeaderClientID, resp) -} - -// ExtractRequestID extracts the Azure server generated request identifier from the -// x-ms-request-id header. -func ExtractRequestID(resp *http.Response) string { - return autorest.ExtractHeaderValue(HeaderRequestID, resp) -} - -// WithErrorUnlessStatusCode returns a RespondDecorator that emits an -// azure.RequestError by reading the response body unless the response HTTP status code -// is among the set passed. -// -// If there is a chance service may return responses other than the Azure error -// format and the response cannot be parsed into an error, a decoding error will -// be returned containing the response body. In any case, the Responder will -// return an error if the status code is not satisfied. -// -// If this Responder returns an error, the response body will be replaced with -// an in-memory reader, which needs no further closing. -func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator { - return func(r autorest.Responder) autorest.Responder { - return autorest.ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) { - var e RequestError - defer resp.Body.Close() - - // Copy and replace the Body in case it does not contain an error object. - // This will leave the Body available to the caller. - b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e) - resp.Body = ioutil.NopCloser(&b) - if decodeErr != nil { - return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr) - } - if e.ServiceError == nil { - // Check if error is unwrapped ServiceError - if err := json.Unmarshal(b.Bytes(), &e.ServiceError); err != nil { - return err - } - } - if e.ServiceError.Message == "" { - // if we're here it means the returned error wasn't OData v4 compliant. - // try to unmarshal the body as raw JSON in hopes of getting something. - rawBody := map[string]interface{}{} - if err := json.Unmarshal(b.Bytes(), &rawBody); err != nil { - return err - } - e.ServiceError = &ServiceError{ - Code: "Unknown", - Message: "Unknown service error", - } - if len(rawBody) > 0 { - e.ServiceError.Details = []map[string]interface{}{rawBody} - } - } - e.Response = resp - e.RequestID = ExtractRequestID(resp) - if e.StatusCode == nil { - e.StatusCode = resp.StatusCode - } - err = &e - } - return err - }) - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go b/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go deleted file mode 100644 index 6c20b8179..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/environments.go +++ /dev/null @@ -1,244 +0,0 @@ -package azure - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" -) - -const ( - // EnvironmentFilepathName captures the name of the environment variable containing the path to the file - // to be used while populating the Azure Environment. - EnvironmentFilepathName = "AZURE_ENVIRONMENT_FILEPATH" - - // NotAvailable is used for endpoints and resource IDs that are not available for a given cloud. - NotAvailable = "N/A" -) - -var environments = map[string]Environment{ - "AZURECHINACLOUD": ChinaCloud, - "AZUREGERMANCLOUD": GermanCloud, - "AZUREPUBLICCLOUD": PublicCloud, - "AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, -} - -// ResourceIdentifier contains a set of Azure resource IDs. -type ResourceIdentifier struct { - Graph string `json:"graph"` - KeyVault string `json:"keyVault"` - Datalake string `json:"datalake"` - Batch string `json:"batch"` - OperationalInsights string `json:"operationalInsights"` - Storage string `json:"storage"` -} - -// Environment represents a set of endpoints for each of Azure's Clouds. -type Environment struct { - Name string `json:"name"` - ManagementPortalURL string `json:"managementPortalURL"` - PublishSettingsURL string `json:"publishSettingsURL"` - ServiceManagementEndpoint string `json:"serviceManagementEndpoint"` - ResourceManagerEndpoint string `json:"resourceManagerEndpoint"` - ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"` - GalleryEndpoint string `json:"galleryEndpoint"` - KeyVaultEndpoint string `json:"keyVaultEndpoint"` - GraphEndpoint string `json:"graphEndpoint"` - ServiceBusEndpoint string `json:"serviceBusEndpoint"` - BatchManagementEndpoint string `json:"batchManagementEndpoint"` - StorageEndpointSuffix string `json:"storageEndpointSuffix"` - SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"` - TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"` - KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"` - ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"` - ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"` - ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"` - ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"` - CosmosDBDNSSuffix string `json:"cosmosDBDNSSuffix"` - TokenAudience string `json:"tokenAudience"` - ResourceIdentifiers ResourceIdentifier `json:"resourceIdentifiers"` -} - -var ( - // PublicCloud is the default public Azure cloud environment - PublicCloud = Environment{ - Name: "AzurePublicCloud", - ManagementPortalURL: "https://manage.windowsazure.com/", - PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index", - ServiceManagementEndpoint: "https://management.core.windows.net/", - ResourceManagerEndpoint: "https://management.azure.com/", - ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", - GalleryEndpoint: "https://gallery.azure.com/", - KeyVaultEndpoint: "https://vault.azure.net/", - GraphEndpoint: "https://graph.windows.net/", - ServiceBusEndpoint: "https://servicebus.windows.net/", - BatchManagementEndpoint: "https://batch.core.windows.net/", - StorageEndpointSuffix: "core.windows.net", - SQLDatabaseDNSSuffix: "database.windows.net", - TrafficManagerDNSSuffix: "trafficmanager.net", - KeyVaultDNSSuffix: "vault.azure.net", - ServiceBusEndpointSuffix: "servicebus.windows.net", - ServiceManagementVMDNSSuffix: "cloudapp.net", - ResourceManagerVMDNSSuffix: "cloudapp.azure.com", - ContainerRegistryDNSSuffix: "azurecr.io", - CosmosDBDNSSuffix: "documents.azure.com", - TokenAudience: "https://management.azure.com/", - ResourceIdentifiers: ResourceIdentifier{ - Graph: "https://graph.windows.net/", - KeyVault: "https://vault.azure.net", - Datalake: "https://datalake.azure.net/", - Batch: "https://batch.core.windows.net/", - OperationalInsights: "https://api.loganalytics.io", - Storage: "https://storage.azure.com/", - }, - } - - // USGovernmentCloud is the cloud environment for the US Government - USGovernmentCloud = Environment{ - Name: "AzureUSGovernmentCloud", - ManagementPortalURL: "https://manage.windowsazure.us/", - PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index", - ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/", - ResourceManagerEndpoint: "https://management.usgovcloudapi.net/", - ActiveDirectoryEndpoint: "https://login.microsoftonline.us/", - GalleryEndpoint: "https://gallery.usgovcloudapi.net/", - KeyVaultEndpoint: "https://vault.usgovcloudapi.net/", - GraphEndpoint: "https://graph.windows.net/", - ServiceBusEndpoint: "https://servicebus.usgovcloudapi.net/", - BatchManagementEndpoint: "https://batch.core.usgovcloudapi.net/", - StorageEndpointSuffix: "core.usgovcloudapi.net", - SQLDatabaseDNSSuffix: "database.usgovcloudapi.net", - TrafficManagerDNSSuffix: "usgovtrafficmanager.net", - KeyVaultDNSSuffix: "vault.usgovcloudapi.net", - ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", - ServiceManagementVMDNSSuffix: "usgovcloudapp.net", - ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us", - ContainerRegistryDNSSuffix: "azurecr.us", - CosmosDBDNSSuffix: "documents.azure.us", - TokenAudience: "https://management.usgovcloudapi.net/", - ResourceIdentifiers: ResourceIdentifier{ - Graph: "https://graph.windows.net/", - KeyVault: "https://vault.usgovcloudapi.net", - Datalake: NotAvailable, - Batch: "https://batch.core.usgovcloudapi.net/", - OperationalInsights: "https://api.loganalytics.us", - Storage: "https://storage.azure.com/", - }, - } - - // ChinaCloud is the cloud environment operated in China - ChinaCloud = Environment{ - Name: "AzureChinaCloud", - ManagementPortalURL: "https://manage.chinacloudapi.com/", - PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index", - ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/", - ResourceManagerEndpoint: "https://management.chinacloudapi.cn/", - ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/", - GalleryEndpoint: "https://gallery.chinacloudapi.cn/", - KeyVaultEndpoint: "https://vault.azure.cn/", - GraphEndpoint: "https://graph.chinacloudapi.cn/", - ServiceBusEndpoint: "https://servicebus.chinacloudapi.cn/", - BatchManagementEndpoint: "https://batch.chinacloudapi.cn/", - StorageEndpointSuffix: "core.chinacloudapi.cn", - SQLDatabaseDNSSuffix: "database.chinacloudapi.cn", - TrafficManagerDNSSuffix: "trafficmanager.cn", - KeyVaultDNSSuffix: "vault.azure.cn", - ServiceBusEndpointSuffix: "servicebus.chinacloudapi.cn", - ServiceManagementVMDNSSuffix: "chinacloudapp.cn", - ResourceManagerVMDNSSuffix: "cloudapp.azure.cn", - ContainerRegistryDNSSuffix: "azurecr.cn", - CosmosDBDNSSuffix: "documents.azure.cn", - TokenAudience: "https://management.chinacloudapi.cn/", - ResourceIdentifiers: ResourceIdentifier{ - Graph: "https://graph.chinacloudapi.cn/", - KeyVault: "https://vault.azure.cn", - Datalake: NotAvailable, - Batch: "https://batch.chinacloudapi.cn/", - OperationalInsights: NotAvailable, - Storage: "https://storage.azure.com/", - }, - } - - // GermanCloud is the cloud environment operated in Germany - GermanCloud = Environment{ - Name: "AzureGermanCloud", - ManagementPortalURL: "http://portal.microsoftazure.de/", - PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index", - ServiceManagementEndpoint: "https://management.core.cloudapi.de/", - ResourceManagerEndpoint: "https://management.microsoftazure.de/", - ActiveDirectoryEndpoint: "https://login.microsoftonline.de/", - GalleryEndpoint: "https://gallery.cloudapi.de/", - KeyVaultEndpoint: "https://vault.microsoftazure.de/", - GraphEndpoint: "https://graph.cloudapi.de/", - ServiceBusEndpoint: "https://servicebus.cloudapi.de/", - BatchManagementEndpoint: "https://batch.cloudapi.de/", - StorageEndpointSuffix: "core.cloudapi.de", - SQLDatabaseDNSSuffix: "database.cloudapi.de", - TrafficManagerDNSSuffix: "azuretrafficmanager.de", - KeyVaultDNSSuffix: "vault.microsoftazure.de", - ServiceBusEndpointSuffix: "servicebus.cloudapi.de", - ServiceManagementVMDNSSuffix: "azurecloudapp.de", - ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de", - ContainerRegistryDNSSuffix: NotAvailable, - CosmosDBDNSSuffix: "documents.microsoftazure.de", - TokenAudience: "https://management.microsoftazure.de/", - ResourceIdentifiers: ResourceIdentifier{ - Graph: "https://graph.cloudapi.de/", - KeyVault: "https://vault.microsoftazure.de", - Datalake: NotAvailable, - Batch: "https://batch.cloudapi.de/", - OperationalInsights: NotAvailable, - Storage: "https://storage.azure.com/", - }, - } -) - -// EnvironmentFromName returns an Environment based on the common name specified. -func EnvironmentFromName(name string) (Environment, error) { - // IMPORTANT - // As per @radhikagupta5: - // This is technical debt, fundamentally here because Kubernetes is not currently accepting - // contributions to the providers. Once that is an option, the provider should be updated to - // directly call `EnvironmentFromFile`. Until then, we rely on dispatching Azure Stack environment creation - // from this method based on the name that is provided to us. - if strings.EqualFold(name, "AZURESTACKCLOUD") { - return EnvironmentFromFile(os.Getenv(EnvironmentFilepathName)) - } - - name = strings.ToUpper(name) - env, ok := environments[name] - if !ok { - return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name) - } - - return env, nil -} - -// EnvironmentFromFile loads an Environment from a configuration file available on disk. -// This function is particularly useful in the Hybrid Cloud model, where one must define their own -// endpoints. -func EnvironmentFromFile(location string) (unmarshaled Environment, err error) { - fileContents, err := ioutil.ReadFile(location) - if err != nil { - return - } - - err = json.Unmarshal(fileContents, &unmarshaled) - - return -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/metadata_environment.go b/vendor/github.com/Azure/go-autorest/autorest/azure/metadata_environment.go deleted file mode 100644 index 507f9e95c..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/metadata_environment.go +++ /dev/null @@ -1,245 +0,0 @@ -package azure - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" - - "github.com/Azure/go-autorest/autorest" -) - -// Copyright 2017 Microsoft Corporation -// -// 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. - -type audience []string - -type authentication struct { - LoginEndpoint string `json:"loginEndpoint"` - Audiences audience `json:"audiences"` -} - -type environmentMetadataInfo struct { - GalleryEndpoint string `json:"galleryEndpoint"` - GraphEndpoint string `json:"graphEndpoint"` - PortalEndpoint string `json:"portalEndpoint"` - Authentication authentication `json:"authentication"` -} - -// EnvironmentProperty represent property names that clients can override -type EnvironmentProperty string - -const ( - // EnvironmentName ... - EnvironmentName EnvironmentProperty = "name" - // EnvironmentManagementPortalURL .. - EnvironmentManagementPortalURL EnvironmentProperty = "managementPortalURL" - // EnvironmentPublishSettingsURL ... - EnvironmentPublishSettingsURL EnvironmentProperty = "publishSettingsURL" - // EnvironmentServiceManagementEndpoint ... - EnvironmentServiceManagementEndpoint EnvironmentProperty = "serviceManagementEndpoint" - // EnvironmentResourceManagerEndpoint ... - EnvironmentResourceManagerEndpoint EnvironmentProperty = "resourceManagerEndpoint" - // EnvironmentActiveDirectoryEndpoint ... - EnvironmentActiveDirectoryEndpoint EnvironmentProperty = "activeDirectoryEndpoint" - // EnvironmentGalleryEndpoint ... - EnvironmentGalleryEndpoint EnvironmentProperty = "galleryEndpoint" - // EnvironmentKeyVaultEndpoint ... - EnvironmentKeyVaultEndpoint EnvironmentProperty = "keyVaultEndpoint" - // EnvironmentGraphEndpoint ... - EnvironmentGraphEndpoint EnvironmentProperty = "graphEndpoint" - // EnvironmentServiceBusEndpoint ... - EnvironmentServiceBusEndpoint EnvironmentProperty = "serviceBusEndpoint" - // EnvironmentBatchManagementEndpoint ... - EnvironmentBatchManagementEndpoint EnvironmentProperty = "batchManagementEndpoint" - // EnvironmentStorageEndpointSuffix ... - EnvironmentStorageEndpointSuffix EnvironmentProperty = "storageEndpointSuffix" - // EnvironmentSQLDatabaseDNSSuffix ... - EnvironmentSQLDatabaseDNSSuffix EnvironmentProperty = "sqlDatabaseDNSSuffix" - // EnvironmentTrafficManagerDNSSuffix ... - EnvironmentTrafficManagerDNSSuffix EnvironmentProperty = "trafficManagerDNSSuffix" - // EnvironmentKeyVaultDNSSuffix ... - EnvironmentKeyVaultDNSSuffix EnvironmentProperty = "keyVaultDNSSuffix" - // EnvironmentServiceBusEndpointSuffix ... - EnvironmentServiceBusEndpointSuffix EnvironmentProperty = "serviceBusEndpointSuffix" - // EnvironmentServiceManagementVMDNSSuffix ... - EnvironmentServiceManagementVMDNSSuffix EnvironmentProperty = "serviceManagementVMDNSSuffix" - // EnvironmentResourceManagerVMDNSSuffix ... - EnvironmentResourceManagerVMDNSSuffix EnvironmentProperty = "resourceManagerVMDNSSuffix" - // EnvironmentContainerRegistryDNSSuffix ... - EnvironmentContainerRegistryDNSSuffix EnvironmentProperty = "containerRegistryDNSSuffix" - // EnvironmentTokenAudience ... - EnvironmentTokenAudience EnvironmentProperty = "tokenAudience" -) - -// OverrideProperty represents property name and value that clients can override -type OverrideProperty struct { - Key EnvironmentProperty - Value string -} - -// EnvironmentFromURL loads an Environment from a URL -// This function is particularly useful in the Hybrid Cloud model, where one may define their own -// endpoints. -func EnvironmentFromURL(resourceManagerEndpoint string, properties ...OverrideProperty) (environment Environment, err error) { - var metadataEnvProperties environmentMetadataInfo - - if resourceManagerEndpoint == "" { - return environment, fmt.Errorf("Metadata resource manager endpoint is empty") - } - - if metadataEnvProperties, err = retrieveMetadataEnvironment(resourceManagerEndpoint); err != nil { - return environment, err - } - - // Give priority to user's override values - overrideProperties(&environment, properties) - - if environment.Name == "" { - environment.Name = "HybridEnvironment" - } - stampDNSSuffix := environment.StorageEndpointSuffix - if stampDNSSuffix == "" { - stampDNSSuffix = strings.TrimSuffix(strings.TrimPrefix(strings.Replace(resourceManagerEndpoint, strings.Split(resourceManagerEndpoint, ".")[0], "", 1), "."), "/") - environment.StorageEndpointSuffix = stampDNSSuffix - } - if environment.KeyVaultDNSSuffix == "" { - environment.KeyVaultDNSSuffix = fmt.Sprintf("%s.%s", "vault", stampDNSSuffix) - } - if environment.KeyVaultEndpoint == "" { - environment.KeyVaultEndpoint = fmt.Sprintf("%s%s", "https://", environment.KeyVaultDNSSuffix) - } - if environment.TokenAudience == "" { - environment.TokenAudience = metadataEnvProperties.Authentication.Audiences[0] - } - if environment.ActiveDirectoryEndpoint == "" { - environment.ActiveDirectoryEndpoint = metadataEnvProperties.Authentication.LoginEndpoint - } - if environment.ResourceManagerEndpoint == "" { - environment.ResourceManagerEndpoint = resourceManagerEndpoint - } - if environment.GalleryEndpoint == "" { - environment.GalleryEndpoint = metadataEnvProperties.GalleryEndpoint - } - if environment.GraphEndpoint == "" { - environment.GraphEndpoint = metadataEnvProperties.GraphEndpoint - } - - return environment, nil -} - -func overrideProperties(environment *Environment, properties []OverrideProperty) { - for _, property := range properties { - switch property.Key { - case EnvironmentName: - { - environment.Name = property.Value - } - case EnvironmentManagementPortalURL: - { - environment.ManagementPortalURL = property.Value - } - case EnvironmentPublishSettingsURL: - { - environment.PublishSettingsURL = property.Value - } - case EnvironmentServiceManagementEndpoint: - { - environment.ServiceManagementEndpoint = property.Value - } - case EnvironmentResourceManagerEndpoint: - { - environment.ResourceManagerEndpoint = property.Value - } - case EnvironmentActiveDirectoryEndpoint: - { - environment.ActiveDirectoryEndpoint = property.Value - } - case EnvironmentGalleryEndpoint: - { - environment.GalleryEndpoint = property.Value - } - case EnvironmentKeyVaultEndpoint: - { - environment.KeyVaultEndpoint = property.Value - } - case EnvironmentGraphEndpoint: - { - environment.GraphEndpoint = property.Value - } - case EnvironmentServiceBusEndpoint: - { - environment.ServiceBusEndpoint = property.Value - } - case EnvironmentBatchManagementEndpoint: - { - environment.BatchManagementEndpoint = property.Value - } - case EnvironmentStorageEndpointSuffix: - { - environment.StorageEndpointSuffix = property.Value - } - case EnvironmentSQLDatabaseDNSSuffix: - { - environment.SQLDatabaseDNSSuffix = property.Value - } - case EnvironmentTrafficManagerDNSSuffix: - { - environment.TrafficManagerDNSSuffix = property.Value - } - case EnvironmentKeyVaultDNSSuffix: - { - environment.KeyVaultDNSSuffix = property.Value - } - case EnvironmentServiceBusEndpointSuffix: - { - environment.ServiceBusEndpointSuffix = property.Value - } - case EnvironmentServiceManagementVMDNSSuffix: - { - environment.ServiceManagementVMDNSSuffix = property.Value - } - case EnvironmentResourceManagerVMDNSSuffix: - { - environment.ResourceManagerVMDNSSuffix = property.Value - } - case EnvironmentContainerRegistryDNSSuffix: - { - environment.ContainerRegistryDNSSuffix = property.Value - } - case EnvironmentTokenAudience: - { - environment.TokenAudience = property.Value - } - } - } -} - -func retrieveMetadataEnvironment(endpoint string) (environment environmentMetadataInfo, err error) { - client := autorest.NewClientWithUserAgent("") - managementEndpoint := fmt.Sprintf("%s%s", strings.TrimSuffix(endpoint, "/"), "/metadata/endpoints?api-version=1.0") - req, _ := http.NewRequest("GET", managementEndpoint, nil) - response, err := client.Do(req) - if err != nil { - return environment, err - } - defer response.Body.Close() - jsonResponse, err := ioutil.ReadAll(response.Body) - if err != nil { - return environment, err - } - err = json.Unmarshal(jsonResponse, &environment) - return environment, err -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go b/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go deleted file mode 100644 index 86ce9f2b5..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/rp.go +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2017 Microsoft Corporation -// -// 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 azure - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "strings" - "time" - - "github.com/Azure/go-autorest/autorest" -) - -// DoRetryWithRegistration tries to register the resource provider in case it is unregistered. -// It also handles request retries -func DoRetryWithRegistration(client autorest.Client) autorest.SendDecorator { - return func(s autorest.Sender) autorest.Sender { - return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) { - rr := autorest.NewRetriableRequest(r) - for currentAttempt := 0; currentAttempt < client.RetryAttempts; currentAttempt++ { - err = rr.Prepare() - if err != nil { - return resp, err - } - - resp, err = autorest.SendWithSender(s, rr.Request(), - autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), - ) - if err != nil { - return resp, err - } - - if resp.StatusCode != http.StatusConflict || client.SkipResourceProviderRegistration { - return resp, err - } - var re RequestError - err = autorest.Respond( - resp, - autorest.ByUnmarshallingJSON(&re), - ) - if err != nil { - return resp, err - } - err = re - - if re.ServiceError != nil && re.ServiceError.Code == "MissingSubscriptionRegistration" { - regErr := register(client, r, re) - if regErr != nil { - return resp, fmt.Errorf("failed auto registering Resource Provider: %s. Original error: %s", regErr, err) - } - } - } - return resp, err - }) - } -} - -func getProvider(re RequestError) (string, error) { - if re.ServiceError != nil && len(re.ServiceError.Details) > 0 { - return re.ServiceError.Details[0]["target"].(string), nil - } - return "", errors.New("provider was not found in the response") -} - -func register(client autorest.Client, originalReq *http.Request, re RequestError) error { - subID := getSubscription(originalReq.URL.Path) - if subID == "" { - return errors.New("missing parameter subscriptionID to register resource provider") - } - providerName, err := getProvider(re) - if err != nil { - return fmt.Errorf("missing parameter provider to register resource provider: %s", err) - } - newURL := url.URL{ - Scheme: originalReq.URL.Scheme, - Host: originalReq.URL.Host, - } - - // taken from the resources SDK - // with almost identical code, this sections are easier to mantain - // It is also not a good idea to import the SDK here - // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L252 - pathParameters := map[string]interface{}{ - "resourceProviderNamespace": autorest.Encode("path", providerName), - "subscriptionId": autorest.Encode("path", subID), - } - - const APIVersion = "2016-09-01" - queryParameters := map[string]interface{}{ - "api-version": APIVersion, - } - - preparer := autorest.CreatePreparer( - autorest.AsPost(), - autorest.WithBaseURL(newURL.String()), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}/register", pathParameters), - autorest.WithQueryParameters(queryParameters), - ) - - req, err := preparer.Prepare(&http.Request{}) - if err != nil { - return err - } - req = req.WithContext(originalReq.Context()) - - resp, err := autorest.SendWithSender(client, req, - autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), - ) - if err != nil { - return err - } - - type Provider struct { - RegistrationState *string `json:"registrationState,omitempty"` - } - var provider Provider - - err = autorest.Respond( - resp, - WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&provider), - autorest.ByClosing(), - ) - if err != nil { - return err - } - - // poll for registered provisioning state - registrationStartTime := time.Now() - for err == nil && (client.PollingDuration == 0 || (client.PollingDuration != 0 && time.Since(registrationStartTime) < client.PollingDuration)) { - // taken from the resources SDK - // https://github.com/Azure/azure-sdk-for-go/blob/9f366792afa3e0ddaecdc860e793ba9d75e76c27/arm/resources/resources/providers.go#L45 - preparer := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(newURL.String()), - autorest.WithPathParameters("/subscriptions/{subscriptionId}/providers/{resourceProviderNamespace}", pathParameters), - autorest.WithQueryParameters(queryParameters), - ) - req, err = preparer.Prepare(&http.Request{}) - if err != nil { - return err - } - req = req.WithContext(originalReq.Context()) - - resp, err := autorest.SendWithSender(client, req, - autorest.DoRetryForStatusCodes(client.RetryAttempts, client.RetryDuration, autorest.StatusCodesForRetry...), - ) - if err != nil { - return err - } - - err = autorest.Respond( - resp, - WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&provider), - autorest.ByClosing(), - ) - if err != nil { - return err - } - - if provider.RegistrationState != nil && - *provider.RegistrationState == "Registered" { - break - } - - delayed := autorest.DelayWithRetryAfter(resp, originalReq.Context().Done()) - if !delayed && !autorest.DelayForBackoff(client.PollingDelay, 0, originalReq.Context().Done()) { - return originalReq.Context().Err() - } - } - if client.PollingDuration != 0 && !(time.Since(registrationStartTime) < client.PollingDuration) { - return errors.New("polling for resource provider registration has exceeded the polling duration") - } - return err -} - -func getSubscription(path string) string { - parts := strings.Split(path, "/") - for i, v := range parts { - if v == "subscriptions" && (i+1) < len(parts) { - return parts[i+1] - } - } - return "" -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/client.go b/vendor/github.com/Azure/go-autorest/autorest/client.go deleted file mode 100644 index 1c6a0617a..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/client.go +++ /dev/null @@ -1,300 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "crypto/tls" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "strings" - "time" - - "github.com/Azure/go-autorest/logger" -) - -const ( - // DefaultPollingDelay is a reasonable delay between polling requests. - DefaultPollingDelay = 60 * time.Second - - // DefaultPollingDuration is a reasonable total polling duration. - DefaultPollingDuration = 15 * time.Minute - - // DefaultRetryAttempts is number of attempts for retry status codes (5xx). - DefaultRetryAttempts = 3 - - // DefaultRetryDuration is the duration to wait between retries. - DefaultRetryDuration = 30 * time.Second -) - -var ( - // StatusCodesForRetry are a defined group of status code for which the client will retry - StatusCodesForRetry = []int{ - http.StatusRequestTimeout, // 408 - http.StatusTooManyRequests, // 429 - http.StatusInternalServerError, // 500 - http.StatusBadGateway, // 502 - http.StatusServiceUnavailable, // 503 - http.StatusGatewayTimeout, // 504 - } -) - -const ( - requestFormat = `HTTP Request Begin =================================================== -%s -===================================================== HTTP Request End -` - responseFormat = `HTTP Response Begin =================================================== -%s -===================================================== HTTP Response End -` -) - -// Response serves as the base for all responses from generated clients. It provides access to the -// last http.Response. -type Response struct { - *http.Response `json:"-"` -} - -// IsHTTPStatus returns true if the returned HTTP status code matches the provided status code. -// If there was no response (i.e. the underlying http.Response is nil) the return value is false. -func (r Response) IsHTTPStatus(statusCode int) bool { - if r.Response == nil { - return false - } - return r.Response.StatusCode == statusCode -} - -// HasHTTPStatus returns true if the returned HTTP status code matches one of the provided status codes. -// If there was no response (i.e. the underlying http.Response is nil) or not status codes are provided -// the return value is false. -func (r Response) HasHTTPStatus(statusCodes ...int) bool { - return ResponseHasStatusCode(r.Response, statusCodes...) -} - -// LoggingInspector implements request and response inspectors that log the full request and -// response to a supplied log. -type LoggingInspector struct { - Logger *log.Logger -} - -// WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The -// body is restored after being emitted. -// -// Note: Since it reads the entire Body, this decorator should not be used where body streaming is -// important. It is best used to trace JSON or similar body values. -func (li LoggingInspector) WithInspection() PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - var body, b bytes.Buffer - - defer r.Body.Close() - - r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body)) - if err := r.Write(&b); err != nil { - return nil, fmt.Errorf("Failed to write response: %v", err) - } - - li.Logger.Printf(requestFormat, b.String()) - - r.Body = ioutil.NopCloser(&body) - return p.Prepare(r) - }) - } -} - -// ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The -// body is restored after being emitted. -// -// Note: Since it reads the entire Body, this decorator should not be used where body streaming is -// important. It is best used to trace JSON or similar body values. -func (li LoggingInspector) ByInspecting() RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - var body, b bytes.Buffer - defer resp.Body.Close() - resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body)) - if err := resp.Write(&b); err != nil { - return fmt.Errorf("Failed to write response: %v", err) - } - - li.Logger.Printf(responseFormat, b.String()) - - resp.Body = ioutil.NopCloser(&body) - return r.Respond(resp) - }) - } -} - -// Client is the base for autorest generated clients. It provides default, "do nothing" -// implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the -// standard, undecorated http.Client as a default Sender. -// -// Generated clients should also use Error (see NewError and NewErrorWithError) for errors and -// return responses that compose with Response. -// -// Most customization of generated clients is best achieved by supplying a custom Authorizer, custom -// RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit -// breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence -// sending the request by providing a decorated Sender. -type Client struct { - Authorizer Authorizer - Sender Sender - RequestInspector PrepareDecorator - ResponseInspector RespondDecorator - - // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header - PollingDelay time.Duration - - // PollingDuration sets the maximum polling time after which an error is returned. - // Setting this to zero will use the provided context to control the duration. - PollingDuration time.Duration - - // RetryAttempts sets the default number of retry attempts for client. - RetryAttempts int - - // RetryDuration sets the delay duration for retries. - RetryDuration time.Duration - - // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent - // through the Do method. - UserAgent string - - Jar http.CookieJar - - // Set to true to skip attempted registration of resource providers (false by default). - SkipResourceProviderRegistration bool -} - -// NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed -// string. -func NewClientWithUserAgent(ua string) Client { - return newClient(ua, tls.RenegotiateNever) -} - -// ClientOptions contains various Client configuration options. -type ClientOptions struct { - // UserAgent is an optional user-agent string to append to the default user agent. - UserAgent string - - // Renegotiation is an optional setting to control client-side TLS renegotiation. - Renegotiation tls.RenegotiationSupport -} - -// NewClientWithOptions returns an instance of a Client with the specified values. -func NewClientWithOptions(options ClientOptions) Client { - return newClient(options.UserAgent, options.Renegotiation) -} - -func newClient(ua string, renegotiation tls.RenegotiationSupport) Client { - c := Client{ - PollingDelay: DefaultPollingDelay, - PollingDuration: DefaultPollingDuration, - RetryAttempts: DefaultRetryAttempts, - RetryDuration: DefaultRetryDuration, - UserAgent: UserAgent(), - } - c.Sender = c.sender(renegotiation) - c.AddToUserAgent(ua) - return c -} - -// AddToUserAgent adds an extension to the current user agent -func (c *Client) AddToUserAgent(extension string) error { - if extension != "" { - c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension) - return nil - } - return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent) -} - -// Do implements the Sender interface by invoking the active Sender after applying authorization. -// If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent -// is set, apply set the User-Agent header. -func (c Client) Do(r *http.Request) (*http.Response, error) { - if r.UserAgent() == "" { - r, _ = Prepare(r, - WithUserAgent(c.UserAgent)) - } - // NOTE: c.WithInspection() must be last in the list so that it can inspect all preceding operations - r, err := Prepare(r, - c.WithAuthorization(), - c.WithInspection()) - if err != nil { - var resp *http.Response - if detErr, ok := err.(DetailedError); ok { - // if the authorization failed (e.g. invalid credentials) there will - // be a response associated with the error, be sure to return it. - resp = detErr.Response - } - return resp, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") - } - logger.Instance.WriteRequest(r, logger.Filter{ - Header: func(k string, v []string) (bool, []string) { - // remove the auth token from the log - if strings.EqualFold(k, "Authorization") || strings.EqualFold(k, "Ocp-Apim-Subscription-Key") { - v = []string{"**REDACTED**"} - } - return true, v - }, - }) - resp, err := SendWithSender(c.sender(tls.RenegotiateNever), r) - logger.Instance.WriteResponse(resp, logger.Filter{}) - Respond(resp, c.ByInspecting()) - return resp, err -} - -// sender returns the Sender to which to send requests. -func (c Client) sender(renengotiation tls.RenegotiationSupport) Sender { - if c.Sender == nil { - return sender(renengotiation) - } - return c.Sender -} - -// WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator -// from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer. -func (c Client) WithAuthorization() PrepareDecorator { - return c.authorizer().WithAuthorization() -} - -// authorizer returns the Authorizer to use. -func (c Client) authorizer() Authorizer { - if c.Authorizer == nil { - return NullAuthorizer{} - } - return c.Authorizer -} - -// WithInspection is a convenience method that passes the request to the supplied RequestInspector, -// if present, or returns the WithNothing PrepareDecorator otherwise. -func (c Client) WithInspection() PrepareDecorator { - if c.RequestInspector == nil { - return WithNothing() - } - return c.RequestInspector -} - -// ByInspecting is a convenience method that passes the response to the supplied ResponseInspector, -// if present, or returns the ByIgnoring RespondDecorator otherwise. -func (c Client) ByInspecting() RespondDecorator { - if c.ResponseInspector == nil { - return ByIgnoring() - } - return c.ResponseInspector -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/LICENSE b/vendor/github.com/Azure/go-autorest/autorest/date/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2015 Microsoft Corporation - - 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/github.com/Azure/go-autorest/autorest/date/date.go b/vendor/github.com/Azure/go-autorest/autorest/date/date.go deleted file mode 100644 index c45710656..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/date.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Package date provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/) -defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of -time.Time types. And both convert to time.Time through a ToTime method. -*/ -package date - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "fmt" - "time" -) - -const ( - fullDate = "2006-01-02" - fullDateJSON = `"2006-01-02"` - dateFormat = "%04d-%02d-%02d" - jsonFormat = `"%04d-%02d-%02d"` -) - -// Date defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e., -// 2006-01-02). -type Date struct { - time.Time -} - -// ParseDate create a new Date from the passed string. -func ParseDate(date string) (d Date, err error) { - return parseDate(date, fullDate) -} - -func parseDate(date string, format string) (Date, error) { - d, err := time.Parse(format, date) - return Date{Time: d}, err -} - -// MarshalBinary preserves the Date as a byte array conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d Date) MarshalBinary() ([]byte, error) { - return d.MarshalText() -} - -// UnmarshalBinary reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d *Date) UnmarshalBinary(data []byte) error { - return d.UnmarshalText(data) -} - -// MarshalJSON preserves the Date as a JSON string conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d Date) MarshalJSON() (json []byte, err error) { - return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil -} - -// UnmarshalJSON reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d *Date) UnmarshalJSON(data []byte) (err error) { - d.Time, err = time.Parse(fullDateJSON, string(data)) - return err -} - -// MarshalText preserves the Date as a byte array conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d Date) MarshalText() (text []byte, err error) { - return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil -} - -// UnmarshalText reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., -// 2006-01-02). -func (d *Date) UnmarshalText(data []byte) (err error) { - d.Time, err = time.Parse(fullDate, string(data)) - return err -} - -// String returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02). -func (d Date) String() string { - return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day()) -} - -// ToTime returns a Date as a time.Time -func (d Date) ToTime() time.Time { - return d.Time -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/go.mod b/vendor/github.com/Azure/go-autorest/autorest/date/go.mod deleted file mode 100644 index 13a1e9803..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/Azure/go-autorest/autorest/date - -go 1.12 diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/time.go b/vendor/github.com/Azure/go-autorest/autorest/date/time.go deleted file mode 100644 index b453fad04..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/time.go +++ /dev/null @@ -1,103 +0,0 @@ -package date - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "regexp" - "time" -) - -// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. -const ( - azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"` - azureUtcFormat = "2006-01-02T15:04:05.999999999" - rfc3339JSON = `"` + time.RFC3339Nano + `"` - rfc3339 = time.RFC3339Nano - tzOffsetRegex = `(Z|z|\+|-)(\d+:\d+)*"*$` -) - -// Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e., -// 2006-01-02T15:04:05Z). -type Time struct { - time.Time -} - -// MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e., -// 2006-01-02T15:04:05Z). -func (t Time) MarshalBinary() ([]byte, error) { - return t.Time.MarshalText() -} - -// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time -// (i.e., 2006-01-02T15:04:05Z). -func (t *Time) UnmarshalBinary(data []byte) error { - return t.UnmarshalText(data) -} - -// MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e., -// 2006-01-02T15:04:05Z). -func (t Time) MarshalJSON() (json []byte, err error) { - return t.Time.MarshalJSON() -} - -// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time -// (i.e., 2006-01-02T15:04:05Z). -func (t *Time) UnmarshalJSON(data []byte) (err error) { - timeFormat := azureUtcFormatJSON - match, err := regexp.Match(tzOffsetRegex, data) - if err != nil { - return err - } else if match { - timeFormat = rfc3339JSON - } - t.Time, err = ParseTime(timeFormat, string(data)) - return err -} - -// MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e., -// 2006-01-02T15:04:05Z). -func (t Time) MarshalText() (text []byte, err error) { - return t.Time.MarshalText() -} - -// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time -// (i.e., 2006-01-02T15:04:05Z). -func (t *Time) UnmarshalText(data []byte) (err error) { - timeFormat := azureUtcFormat - match, err := regexp.Match(tzOffsetRegex, data) - if err != nil { - return err - } else if match { - timeFormat = rfc3339 - } - t.Time, err = ParseTime(timeFormat, string(data)) - return err -} - -// String returns the Time formatted as an RFC3339 date-time string (i.e., -// 2006-01-02T15:04:05Z). -func (t Time) String() string { - // Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does. - b, err := t.MarshalText() - if err != nil { - return "" - } - return string(b) -} - -// ToTime returns a Time as a time.Time -func (t Time) ToTime() time.Time { - return t.Time -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go b/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go deleted file mode 100644 index 48fb39ba9..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/timerfc1123.go +++ /dev/null @@ -1,100 +0,0 @@ -package date - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "errors" - "time" -) - -const ( - rfc1123JSON = `"` + time.RFC1123 + `"` - rfc1123 = time.RFC1123 -) - -// TimeRFC1123 defines a type similar to time.Time but assumes a layout of RFC1123 date-time (i.e., -// Mon, 02 Jan 2006 15:04:05 MST). -type TimeRFC1123 struct { - time.Time -} - -// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC1123 date-time -// (i.e., Mon, 02 Jan 2006 15:04:05 MST). -func (t *TimeRFC1123) UnmarshalJSON(data []byte) (err error) { - t.Time, err = ParseTime(rfc1123JSON, string(data)) - if err != nil { - return err - } - return nil -} - -// MarshalJSON preserves the Time as a JSON string conforming to RFC1123 date-time (i.e., -// Mon, 02 Jan 2006 15:04:05 MST). -func (t TimeRFC1123) MarshalJSON() ([]byte, error) { - if y := t.Year(); y < 0 || y >= 10000 { - return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") - } - b := []byte(t.Format(rfc1123JSON)) - return b, nil -} - -// MarshalText preserves the Time as a byte array conforming to RFC1123 date-time (i.e., -// Mon, 02 Jan 2006 15:04:05 MST). -func (t TimeRFC1123) MarshalText() ([]byte, error) { - if y := t.Year(); y < 0 || y >= 10000 { - return nil, errors.New("Time.MarshalText: year outside of range [0,9999]") - } - - b := []byte(t.Format(rfc1123)) - return b, nil -} - -// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC1123 date-time -// (i.e., Mon, 02 Jan 2006 15:04:05 MST). -func (t *TimeRFC1123) UnmarshalText(data []byte) (err error) { - t.Time, err = ParseTime(rfc1123, string(data)) - if err != nil { - return err - } - return nil -} - -// MarshalBinary preserves the Time as a byte array conforming to RFC1123 date-time (i.e., -// Mon, 02 Jan 2006 15:04:05 MST). -func (t TimeRFC1123) MarshalBinary() ([]byte, error) { - return t.MarshalText() -} - -// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC1123 date-time -// (i.e., Mon, 02 Jan 2006 15:04:05 MST). -func (t *TimeRFC1123) UnmarshalBinary(data []byte) error { - return t.UnmarshalText(data) -} - -// ToTime returns a Time as a time.Time -func (t TimeRFC1123) ToTime() time.Time { - return t.Time -} - -// String returns the Time formatted as an RFC1123 date-time string (i.e., -// Mon, 02 Jan 2006 15:04:05 MST). -func (t TimeRFC1123) String() string { - // Note: time.Time.String does not return an RFC1123 compliant string, time.Time.MarshalText does. - b, err := t.MarshalText() - if err != nil { - return "" - } - return string(b) -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go b/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go deleted file mode 100644 index 7073959b2..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/unixtime.go +++ /dev/null @@ -1,123 +0,0 @@ -package date - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "time" -) - -// unixEpoch is the moment in time that should be treated as timestamp 0. -var unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) - -// UnixTime marshals and unmarshals a time that is represented as the number -// of seconds (ignoring skip-seconds) since the Unix Epoch. -type UnixTime time.Time - -// Duration returns the time as a Duration since the UnixEpoch. -func (t UnixTime) Duration() time.Duration { - return time.Time(t).Sub(unixEpoch) -} - -// NewUnixTimeFromSeconds creates a UnixTime as a number of seconds from the UnixEpoch. -func NewUnixTimeFromSeconds(seconds float64) UnixTime { - return NewUnixTimeFromDuration(time.Duration(seconds * float64(time.Second))) -} - -// NewUnixTimeFromNanoseconds creates a UnixTime as a number of nanoseconds from the UnixEpoch. -func NewUnixTimeFromNanoseconds(nanoseconds int64) UnixTime { - return NewUnixTimeFromDuration(time.Duration(nanoseconds)) -} - -// NewUnixTimeFromDuration creates a UnixTime as a duration of time since the UnixEpoch. -func NewUnixTimeFromDuration(dur time.Duration) UnixTime { - return UnixTime(unixEpoch.Add(dur)) -} - -// UnixEpoch retreives the moment considered the Unix Epoch. I.e. The time represented by '0' -func UnixEpoch() time.Time { - return unixEpoch -} - -// MarshalJSON preserves the UnixTime as a JSON number conforming to Unix Timestamp requirements. -// (i.e. the number of seconds since midnight January 1st, 1970 not considering leap seconds.) -func (t UnixTime) MarshalJSON() ([]byte, error) { - buffer := &bytes.Buffer{} - enc := json.NewEncoder(buffer) - err := enc.Encode(float64(time.Time(t).UnixNano()) / 1e9) - if err != nil { - return nil, err - } - return buffer.Bytes(), nil -} - -// UnmarshalJSON reconstitures a UnixTime saved as a JSON number of the number of seconds since -// midnight January 1st, 1970. -func (t *UnixTime) UnmarshalJSON(text []byte) error { - dec := json.NewDecoder(bytes.NewReader(text)) - - var secondsSinceEpoch float64 - if err := dec.Decode(&secondsSinceEpoch); err != nil { - return err - } - - *t = NewUnixTimeFromSeconds(secondsSinceEpoch) - - return nil -} - -// MarshalText stores the number of seconds since the Unix Epoch as a textual floating point number. -func (t UnixTime) MarshalText() ([]byte, error) { - cast := time.Time(t) - return cast.MarshalText() -} - -// UnmarshalText populates a UnixTime with a value stored textually as a floating point number of seconds since the Unix Epoch. -func (t *UnixTime) UnmarshalText(raw []byte) error { - var unmarshaled time.Time - - if err := unmarshaled.UnmarshalText(raw); err != nil { - return err - } - - *t = UnixTime(unmarshaled) - return nil -} - -// MarshalBinary converts a UnixTime into a binary.LittleEndian float64 of nanoseconds since the epoch. -func (t UnixTime) MarshalBinary() ([]byte, error) { - buf := &bytes.Buffer{} - - payload := int64(t.Duration()) - - if err := binary.Write(buf, binary.LittleEndian, &payload); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// UnmarshalBinary converts a from a binary.LittleEndian float64 of nanoseconds since the epoch into a UnixTime. -func (t *UnixTime) UnmarshalBinary(raw []byte) error { - var nanosecondsSinceEpoch int64 - - if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &nanosecondsSinceEpoch); err != nil { - return err - } - *t = NewUnixTimeFromNanoseconds(nanosecondsSinceEpoch) - return nil -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/date/utility.go b/vendor/github.com/Azure/go-autorest/autorest/date/utility.go deleted file mode 100644 index 12addf0eb..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/date/utility.go +++ /dev/null @@ -1,25 +0,0 @@ -package date - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "strings" - "time" -) - -// ParseTime to parse Time string to specified format. -func ParseTime(format string, t string) (d time.Time, err error) { - return time.Parse(format, strings.ToUpper(t)) -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/error.go b/vendor/github.com/Azure/go-autorest/autorest/error.go deleted file mode 100644 index f724f3332..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/error.go +++ /dev/null @@ -1,98 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "fmt" - "net/http" -) - -const ( - // UndefinedStatusCode is used when HTTP status code is not available for an error. - UndefinedStatusCode = 0 -) - -// DetailedError encloses a error with details of the package, method, and associated HTTP -// status code (if any). -type DetailedError struct { - Original error - - // PackageType is the package type of the object emitting the error. For types, the value - // matches that produced the the '%T' format specifier of the fmt package. For other elements, - // such as functions, it is just the package name (e.g., "autorest"). - PackageType string - - // Method is the name of the method raising the error. - Method string - - // StatusCode is the HTTP Response StatusCode (if non-zero) that led to the error. - StatusCode interface{} - - // Message is the error message. - Message string - - // Service Error is the response body of failed API in bytes - ServiceError []byte - - // Response is the response object that was returned during failure if applicable. - Response *http.Response -} - -// NewError creates a new Error conforming object from the passed packageType, method, and -// message. message is treated as a format string to which the optional args apply. -func NewError(packageType string, method string, message string, args ...interface{}) DetailedError { - return NewErrorWithError(nil, packageType, method, nil, message, args...) -} - -// NewErrorWithResponse creates a new Error conforming object from the passed -// packageType, method, statusCode of the given resp (UndefinedStatusCode if -// resp is nil), and message. message is treated as a format string to which the -// optional args apply. -func NewErrorWithResponse(packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { - return NewErrorWithError(nil, packageType, method, resp, message, args...) -} - -// NewErrorWithError creates a new Error conforming object from the -// passed packageType, method, statusCode of the given resp (UndefinedStatusCode -// if resp is nil), message, and original error. message is treated as a format -// string to which the optional args apply. -func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { - if v, ok := original.(DetailedError); ok { - return v - } - - statusCode := UndefinedStatusCode - if resp != nil { - statusCode = resp.StatusCode - } - - return DetailedError{ - Original: original, - PackageType: packageType, - Method: method, - StatusCode: statusCode, - Message: fmt.Sprintf(message, args...), - Response: resp, - } -} - -// Error returns a formatted containing all available details (i.e., PackageType, Method, -// StatusCode, Message, and original error (if any)). -func (e DetailedError) Error() string { - if e.Original == nil { - return fmt.Sprintf("%s#%s: %s: StatusCode=%d", e.PackageType, e.Method, e.Message, e.StatusCode) - } - return fmt.Sprintf("%s#%s: %s: StatusCode=%d -- Original Error: %v", e.PackageType, e.Method, e.Message, e.StatusCode, e.Original) -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/go.mod b/vendor/github.com/Azure/go-autorest/autorest/go.mod deleted file mode 100644 index ab2ae66ac..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/Azure/go-autorest/autorest - -go 1.12 - -require ( - github.com/Azure/go-autorest/autorest/adal v0.5.0 - github.com/Azure/go-autorest/autorest/mocks v0.2.0 - github.com/Azure/go-autorest/logger v0.1.0 - github.com/Azure/go-autorest/tracing v0.5.0 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 -) diff --git a/vendor/github.com/Azure/go-autorest/autorest/go.sum b/vendor/github.com/Azure/go-autorest/autorest/go.sum deleted file mode 100644 index 729b99cd0..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/go.sum +++ /dev/null @@ -1,18 +0,0 @@ -github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/mocks v0.1.0 h1:Kx+AUU2Te+A3JIyYn6Dfs+cFgx5XorQKuIXrZGoq/SI= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/Azure/go-autorest/autorest/preparer.go b/vendor/github.com/Azure/go-autorest/autorest/preparer.go deleted file mode 100644 index 9f864ab1a..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/preparer.go +++ /dev/null @@ -1,548 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "context" - "encoding/json" - "encoding/xml" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net/http" - "net/url" - "strings" -) - -const ( - mimeTypeJSON = "application/json" - mimeTypeOctetStream = "application/octet-stream" - mimeTypeFormPost = "application/x-www-form-urlencoded" - - headerAuthorization = "Authorization" - headerAuxAuthorization = "x-ms-authorization-auxiliary" - headerContentType = "Content-Type" - headerUserAgent = "User-Agent" -) - -// used as a key type in context.WithValue() -type ctxPrepareDecorators struct{} - -// WithPrepareDecorators adds the specified PrepareDecorators to the provided context. -// If no PrepareDecorators are provided the context is unchanged. -func WithPrepareDecorators(ctx context.Context, prepareDecorator []PrepareDecorator) context.Context { - if len(prepareDecorator) == 0 { - return ctx - } - return context.WithValue(ctx, ctxPrepareDecorators{}, prepareDecorator) -} - -// GetPrepareDecorators returns the PrepareDecorators in the provided context or the provided default PrepareDecorators. -func GetPrepareDecorators(ctx context.Context, defaultPrepareDecorators ...PrepareDecorator) []PrepareDecorator { - inCtx := ctx.Value(ctxPrepareDecorators{}) - if pd, ok := inCtx.([]PrepareDecorator); ok { - return pd - } - return defaultPrepareDecorators -} - -// Preparer is the interface that wraps the Prepare method. -// -// Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations -// must ensure to not share or hold per-invocation state since Preparers may be shared and re-used. -type Preparer interface { - Prepare(*http.Request) (*http.Request, error) -} - -// PreparerFunc is a method that implements the Preparer interface. -type PreparerFunc func(*http.Request) (*http.Request, error) - -// Prepare implements the Preparer interface on PreparerFunc. -func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) { - return pf(r) -} - -// PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the -// http.Request and pass it along or, first, pass the http.Request along then affect the result. -type PrepareDecorator func(Preparer) Preparer - -// CreatePreparer creates, decorates, and returns a Preparer. -// Without decorators, the returned Preparer returns the passed http.Request unmodified. -// Preparers are safe to share and re-use. -func CreatePreparer(decorators ...PrepareDecorator) Preparer { - return DecoratePreparer( - Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })), - decorators...) -} - -// DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it -// applies to the Preparer. Decorators are applied in the order received, but their affect upon the -// request depends on whether they are a pre-decorator (change the http.Request and then pass it -// along) or a post-decorator (pass the http.Request along and alter it on return). -func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer { - for _, decorate := range decorators { - p = decorate(p) - } - return p -} - -// Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators. -// It creates a Preparer from the decorators which it then applies to the passed http.Request. -func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) { - if r == nil { - return nil, NewError("autorest", "Prepare", "Invoked without an http.Request") - } - return CreatePreparer(decorators...).Prepare(r) -} - -// WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed -// http.Request. -func WithNothing() PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - return p.Prepare(r) - }) - } -} - -// WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to -// the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before -// adding the header. -func WithHeader(header string, value string) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.Header == nil { - r.Header = make(http.Header) - } - r.Header.Set(http.CanonicalHeaderKey(header), value) - } - return r, err - }) - } -} - -// WithHeaders returns a PrepareDecorator that sets the specified HTTP headers of the http.Request to -// the passed value. It canonicalizes the passed headers name (via http.CanonicalHeaderKey) before -// adding them. -func WithHeaders(headers map[string]interface{}) PrepareDecorator { - h := ensureValueStrings(headers) - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.Header == nil { - r.Header = make(http.Header) - } - - for name, value := range h { - r.Header.Set(http.CanonicalHeaderKey(name), value) - } - } - return r, err - }) - } -} - -// WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose -// value is "Bearer " followed by the supplied token. -func WithBearerAuthorization(token string) PrepareDecorator { - return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token)) -} - -// AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value -// is the passed contentType. -func AsContentType(contentType string) PrepareDecorator { - return WithHeader(headerContentType, contentType) -} - -// WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the -// passed string. -func WithUserAgent(ua string) PrepareDecorator { - return WithHeader(headerUserAgent, ua) -} - -// AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is -// "application/x-www-form-urlencoded". -func AsFormURLEncoded() PrepareDecorator { - return AsContentType(mimeTypeFormPost) -} - -// AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is -// "application/json". -func AsJSON() PrepareDecorator { - return AsContentType(mimeTypeJSON) -} - -// AsOctetStream returns a PrepareDecorator that adds the "application/octet-stream" Content-Type header. -func AsOctetStream() PrepareDecorator { - return AsContentType(mimeTypeOctetStream) -} - -// WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The -// decorator does not validate that the passed method string is a known HTTP method. -func WithMethod(method string) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r.Method = method - return p.Prepare(r) - }) - } -} - -// AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE. -func AsDelete() PrepareDecorator { return WithMethod("DELETE") } - -// AsGet returns a PrepareDecorator that sets the HTTP method to GET. -func AsGet() PrepareDecorator { return WithMethod("GET") } - -// AsHead returns a PrepareDecorator that sets the HTTP method to HEAD. -func AsHead() PrepareDecorator { return WithMethod("HEAD") } - -// AsMerge returns a PrepareDecorator that sets the HTTP method to MERGE. -func AsMerge() PrepareDecorator { return WithMethod("MERGE") } - -// AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS. -func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") } - -// AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH. -func AsPatch() PrepareDecorator { return WithMethod("PATCH") } - -// AsPost returns a PrepareDecorator that sets the HTTP method to POST. -func AsPost() PrepareDecorator { return WithMethod("POST") } - -// AsPut returns a PrepareDecorator that sets the HTTP method to PUT. -func AsPut() PrepareDecorator { return WithMethod("PUT") } - -// WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed -// from the supplied baseUrl. -func WithBaseURL(baseURL string) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - var u *url.URL - if u, err = url.Parse(baseURL); err != nil { - return r, err - } - if u.Scheme == "" { - err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL) - } - if err == nil { - r.URL = u - } - } - return r, err - }) - } -} - -// WithBytes returns a PrepareDecorator that takes a list of bytes -// which passes the bytes directly to the body -func WithBytes(input *[]byte) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if input == nil { - return r, fmt.Errorf("Input Bytes was nil") - } - - r.ContentLength = int64(len(*input)) - r.Body = ioutil.NopCloser(bytes.NewReader(*input)) - } - return r, err - }) - } -} - -// WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the -// request base URL (i.e., http.Request.URL) with the corresponding values from the passed map. -func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator { - parameters := ensureValueStrings(urlParameters) - for key, value := range parameters { - baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1) - } - return WithBaseURL(baseURL) -} - -// WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the -// http.Request body. -func WithFormData(v url.Values) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - s := v.Encode() - - if r.Header == nil { - r.Header = make(http.Header) - } - r.Header.Set(http.CanonicalHeaderKey(headerContentType), mimeTypeFormPost) - r.ContentLength = int64(len(s)) - r.Body = ioutil.NopCloser(strings.NewReader(s)) - } - return r, err - }) - } -} - -// WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters -// into the http.Request body. -func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - var body bytes.Buffer - writer := multipart.NewWriter(&body) - for key, value := range formDataParameters { - if rc, ok := value.(io.ReadCloser); ok { - var fd io.Writer - if fd, err = writer.CreateFormFile(key, key); err != nil { - return r, err - } - if _, err = io.Copy(fd, rc); err != nil { - return r, err - } - } else { - if err = writer.WriteField(key, ensureValueString(value)); err != nil { - return r, err - } - } - } - if err = writer.Close(); err != nil { - return r, err - } - if r.Header == nil { - r.Header = make(http.Header) - } - r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType()) - r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) - r.ContentLength = int64(body.Len()) - return r, err - } - return r, err - }) - } -} - -// WithFile returns a PrepareDecorator that sends file in request body. -func WithFile(f io.ReadCloser) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - b, err := ioutil.ReadAll(f) - if err != nil { - return r, err - } - r.Body = ioutil.NopCloser(bytes.NewReader(b)) - r.ContentLength = int64(len(b)) - } - return r, err - }) - } -} - -// WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request -// and sets the Content-Length header. -func WithBool(v bool) PrepareDecorator { - return WithString(fmt.Sprintf("%v", v)) -} - -// WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the -// request and sets the Content-Length header. -func WithFloat32(v float32) PrepareDecorator { - return WithString(fmt.Sprintf("%v", v)) -} - -// WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the -// request and sets the Content-Length header. -func WithFloat64(v float64) PrepareDecorator { - return WithString(fmt.Sprintf("%v", v)) -} - -// WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request -// and sets the Content-Length header. -func WithInt32(v int32) PrepareDecorator { - return WithString(fmt.Sprintf("%v", v)) -} - -// WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request -// and sets the Content-Length header. -func WithInt64(v int64) PrepareDecorator { - return WithString(fmt.Sprintf("%v", v)) -} - -// WithString returns a PrepareDecorator that encodes the passed string into the body of the request -// and sets the Content-Length header. -func WithString(v string) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - r.ContentLength = int64(len(v)) - r.Body = ioutil.NopCloser(strings.NewReader(v)) - } - return r, err - }) - } -} - -// WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the -// request and sets the Content-Length header. -func WithJSON(v interface{}) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - b, err := json.Marshal(v) - if err == nil { - r.ContentLength = int64(len(b)) - r.Body = ioutil.NopCloser(bytes.NewReader(b)) - } - } - return r, err - }) - } -} - -// WithXML returns a PrepareDecorator that encodes the data passed as XML into the body of the -// request and sets the Content-Length header. -func WithXML(v interface{}) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - b, err := xml.Marshal(v) - if err == nil { - // we have to tack on an XML header - withHeader := xml.Header + string(b) - bytesWithHeader := []byte(withHeader) - - r.ContentLength = int64(len(bytesWithHeader)) - r.Body = ioutil.NopCloser(bytes.NewReader(bytesWithHeader)) - } - } - return r, err - }) - } -} - -// WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path -// is absolute (that is, it begins with a "/"), it replaces the existing path. -func WithPath(path string) PrepareDecorator { - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.URL == nil { - return r, NewError("autorest", "WithPath", "Invoked with a nil URL") - } - if r.URL, err = parseURL(r.URL, path); err != nil { - return r, err - } - } - return r, err - }) - } -} - -// WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the -// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The -// values will be escaped (aka URL encoded) before insertion into the path. -func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { - parameters := escapeValueStrings(ensureValueStrings(pathParameters)) - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.URL == nil { - return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL") - } - for key, value := range parameters { - path = strings.Replace(path, "{"+key+"}", value, -1) - } - if r.URL, err = parseURL(r.URL, path); err != nil { - return r, err - } - } - return r, err - }) - } -} - -// WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the -// request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. -func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { - parameters := ensureValueStrings(pathParameters) - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.URL == nil { - return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL") - } - for key, value := range parameters { - path = strings.Replace(path, "{"+key+"}", value, -1) - } - - if r.URL, err = parseURL(r.URL, path); err != nil { - return r, err - } - } - return r, err - }) - } -} - -func parseURL(u *url.URL, path string) (*url.URL, error) { - p := strings.TrimRight(u.String(), "/") - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - return url.Parse(p + path) -} - -// WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters -// given in the supplied map (i.e., key=value). -func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator { - parameters := ensureValueStrings(queryParameters) - return func(p Preparer) Preparer { - return PreparerFunc(func(r *http.Request) (*http.Request, error) { - r, err := p.Prepare(r) - if err == nil { - if r.URL == nil { - return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL") - } - - v := r.URL.Query() - for key, value := range parameters { - d, err := url.QueryUnescape(value) - if err != nil { - return r, err - } - v.Add(key, d) - } - r.URL.RawQuery = v.Encode() - } - return r, err - }) - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/responder.go b/vendor/github.com/Azure/go-autorest/autorest/responder.go deleted file mode 100644 index 349e1963a..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/responder.go +++ /dev/null @@ -1,269 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "encoding/json" - "encoding/xml" - "fmt" - "io" - "io/ioutil" - "net/http" - "strings" -) - -// Responder is the interface that wraps the Respond method. -// -// Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold -// state since Responders may be shared and re-used. -type Responder interface { - Respond(*http.Response) error -} - -// ResponderFunc is a method that implements the Responder interface. -type ResponderFunc func(*http.Response) error - -// Respond implements the Responder interface on ResponderFunc. -func (rf ResponderFunc) Respond(r *http.Response) error { - return rf(r) -} - -// RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to -// the http.Response and pass it along or, first, pass the http.Response along then react. -type RespondDecorator func(Responder) Responder - -// CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned -// Responder returns the passed http.Response unmodified. Responders may or may not be safe to share -// and re-used: It depends on the applied decorators. For example, a standard decorator that closes -// the response body is fine to share whereas a decorator that reads the body into a passed struct -// is not. -// -// To prevent memory leaks, ensure that at least one Responder closes the response body. -func CreateResponder(decorators ...RespondDecorator) Responder { - return DecorateResponder( - Responder(ResponderFunc(func(r *http.Response) error { return nil })), - decorators...) -} - -// DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it -// applies to the Responder. Decorators are applied in the order received, but their affect upon the -// request depends on whether they are a pre-decorator (react to the http.Response and then pass it -// along) or a post-decorator (pass the http.Response along and then react). -func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder { - for _, decorate := range decorators { - r = decorate(r) - } - return r -} - -// Respond accepts an http.Response and a, possibly empty, set of RespondDecorators. -// It creates a Responder from the decorators it then applies to the passed http.Response. -func Respond(r *http.Response, decorators ...RespondDecorator) error { - if r == nil { - return nil - } - return CreateResponder(decorators...).Respond(r) -} - -// ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined -// to the next RespondDecorator. -func ByIgnoring() RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - return r.Respond(resp) - }) - } -} - -// ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as -// the Body is read. -func ByCopying(b *bytes.Buffer) RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil && resp != nil && resp.Body != nil { - resp.Body = TeeReadCloser(resp.Body, b) - } - return err - }) - } -} - -// ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which -// it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed -// Responder is invoked prior to discarding the response body, the decorator may occur anywhere -// within the set. -func ByDiscardingBody() RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil && resp != nil && resp.Body != nil { - if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { - return fmt.Errorf("Error discarding the response body: %v", err) - } - } - return err - }) - } -} - -// ByClosing returns a RespondDecorator that first invokes the passed Responder after which it -// closes the response body. Since the passed Responder is invoked prior to closing the response -// body, the decorator may occur anywhere within the set. -func ByClosing() RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if resp != nil && resp.Body != nil { - if err := resp.Body.Close(); err != nil { - return fmt.Errorf("Error closing the response body: %v", err) - } - } - return err - }) - } -} - -// ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which -// it closes the response if the passed Responder returns an error and the response body exists. -func ByClosingIfError() RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err != nil && resp != nil && resp.Body != nil { - if err := resp.Body.Close(); err != nil { - return fmt.Errorf("Error closing the response body: %v", err) - } - } - return err - }) - } -} - -// ByUnmarshallingBytes returns a RespondDecorator that copies the Bytes returned in the -// response Body into the value pointed to by v. -func ByUnmarshallingBytes(v *[]byte) RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil { - bytes, errInner := ioutil.ReadAll(resp.Body) - if errInner != nil { - err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) - } else { - *v = bytes - } - } - return err - }) - } -} - -// ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the -// response Body into the value pointed to by v. -func ByUnmarshallingJSON(v interface{}) RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil { - b, errInner := ioutil.ReadAll(resp.Body) - // Some responses might include a BOM, remove for successful unmarshalling - b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) - if errInner != nil { - err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) - } else if len(strings.Trim(string(b), " ")) > 0 { - errInner = json.Unmarshal(b, v) - if errInner != nil { - err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b)) - } - } - } - return err - }) - } -} - -// ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the -// response Body into the value pointed to by v. -func ByUnmarshallingXML(v interface{}) RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil { - b, errInner := ioutil.ReadAll(resp.Body) - if errInner != nil { - err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) - } else { - errInner = xml.Unmarshal(b, v) - if errInner != nil { - err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b)) - } - } - } - return err - }) - } -} - -// WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response -// StatusCode is among the set passed. On error, response body is fully read into a buffer and -// presented in the returned error, as well as in the response body. -func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { - return func(r Responder) Responder { - return ResponderFunc(func(resp *http.Response) error { - err := r.Respond(resp) - if err == nil && !ResponseHasStatusCode(resp, codes...) { - derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s", - resp.Request.Method, - resp.Request.URL, - resp.Status) - if resp.Body != nil { - defer resp.Body.Close() - b, _ := ioutil.ReadAll(resp.Body) - derr.ServiceError = b - resp.Body = ioutil.NopCloser(bytes.NewReader(b)) - } - err = derr - } - return err - }) - } -} - -// WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is -// anything other than HTTP 200. -func WithErrorUnlessOK() RespondDecorator { - return WithErrorUnlessStatusCode(http.StatusOK) -} - -// ExtractHeader extracts all values of the specified header from the http.Response. It returns an -// empty string slice if the passed http.Response is nil or the header does not exist. -func ExtractHeader(header string, resp *http.Response) []string { - if resp != nil && resp.Header != nil { - return resp.Header[http.CanonicalHeaderKey(header)] - } - return nil -} - -// ExtractHeaderValue extracts the first value of the specified header from the http.Response. It -// returns an empty string if the passed http.Response is nil or the header does not exist. -func ExtractHeaderValue(header string, resp *http.Response) string { - h := ExtractHeader(header, resp) - if len(h) > 0 { - return h[0] - } - return "" -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go deleted file mode 100644 index fa11dbed7..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest.go +++ /dev/null @@ -1,52 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" -) - -// NewRetriableRequest returns a wrapper around an HTTP request that support retry logic. -func NewRetriableRequest(req *http.Request) *RetriableRequest { - return &RetriableRequest{req: req} -} - -// Request returns the wrapped HTTP request. -func (rr *RetriableRequest) Request() *http.Request { - return rr.req -} - -func (rr *RetriableRequest) prepareFromByteReader() (err error) { - // fall back to making a copy (only do this once) - b := []byte{} - if rr.req.ContentLength > 0 { - b = make([]byte, rr.req.ContentLength) - _, err = io.ReadFull(rr.req.Body, b) - if err != nil { - return err - } - } else { - b, err = ioutil.ReadAll(rr.req.Body) - if err != nil { - return err - } - } - rr.br = bytes.NewReader(b) - rr.req.Body = ioutil.NopCloser(rr.br) - return err -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go deleted file mode 100644 index 7143cc61b..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.7.go +++ /dev/null @@ -1,54 +0,0 @@ -// +build !go1.8 - -// Copyright 2017 Microsoft Corporation -// -// 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 autorest - -import ( - "bytes" - "io/ioutil" - "net/http" -) - -// RetriableRequest provides facilities for retrying an HTTP request. -type RetriableRequest struct { - req *http.Request - br *bytes.Reader -} - -// Prepare signals that the request is about to be sent. -func (rr *RetriableRequest) Prepare() (err error) { - // preserve the request body; this is to support retry logic as - // the underlying transport will always close the reqeust body - if rr.req.Body != nil { - if rr.br != nil { - _, err = rr.br.Seek(0, 0 /*io.SeekStart*/) - rr.req.Body = ioutil.NopCloser(rr.br) - } - if err != nil { - return err - } - if rr.br == nil { - // fall back to making a copy (only do this once) - err = rr.prepareFromByteReader() - } - } - return err -} - -func removeRequestBody(req *http.Request) { - req.Body = nil - req.ContentLength = 0 -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go b/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go deleted file mode 100644 index ae15c6bf9..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/retriablerequest_1.8.go +++ /dev/null @@ -1,66 +0,0 @@ -// +build go1.8 - -// Copyright 2017 Microsoft Corporation -// -// 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 autorest - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" -) - -// RetriableRequest provides facilities for retrying an HTTP request. -type RetriableRequest struct { - req *http.Request - rc io.ReadCloser - br *bytes.Reader -} - -// Prepare signals that the request is about to be sent. -func (rr *RetriableRequest) Prepare() (err error) { - // preserve the request body; this is to support retry logic as - // the underlying transport will always close the reqeust body - if rr.req.Body != nil { - if rr.rc != nil { - rr.req.Body = rr.rc - } else if rr.br != nil { - _, err = rr.br.Seek(0, io.SeekStart) - rr.req.Body = ioutil.NopCloser(rr.br) - } - if err != nil { - return err - } - if rr.req.GetBody != nil { - // this will allow us to preserve the body without having to - // make a copy. note we need to do this on each iteration - rr.rc, err = rr.req.GetBody() - if err != nil { - return err - } - } else if rr.br == nil { - // fall back to making a copy (only do this once) - err = rr.prepareFromByteReader() - } - } - return err -} - -func removeRequestBody(req *http.Request) { - req.Body = nil - req.GetBody = nil - req.ContentLength = 0 -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/sender.go b/vendor/github.com/Azure/go-autorest/autorest/sender.go deleted file mode 100644 index e582489b3..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/sender.go +++ /dev/null @@ -1,411 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "context" - "crypto/tls" - "fmt" - "log" - "math" - "net/http" - "net/http/cookiejar" - "strconv" - "time" - - "github.com/Azure/go-autorest/tracing" -) - -// used as a key type in context.WithValue() -type ctxSendDecorators struct{} - -// WithSendDecorators adds the specified SendDecorators to the provided context. -// If no SendDecorators are provided the context is unchanged. -func WithSendDecorators(ctx context.Context, sendDecorator []SendDecorator) context.Context { - if len(sendDecorator) == 0 { - return ctx - } - return context.WithValue(ctx, ctxSendDecorators{}, sendDecorator) -} - -// GetSendDecorators returns the SendDecorators in the provided context or the provided default SendDecorators. -func GetSendDecorators(ctx context.Context, defaultSendDecorators ...SendDecorator) []SendDecorator { - inCtx := ctx.Value(ctxSendDecorators{}) - if sd, ok := inCtx.([]SendDecorator); ok { - return sd - } - return defaultSendDecorators -} - -// Sender is the interface that wraps the Do method to send HTTP requests. -// -// The standard http.Client conforms to this interface. -type Sender interface { - Do(*http.Request) (*http.Response, error) -} - -// SenderFunc is a method that implements the Sender interface. -type SenderFunc func(*http.Request) (*http.Response, error) - -// Do implements the Sender interface on SenderFunc. -func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { - return sf(r) -} - -// SendDecorator takes and possibly decorates, by wrapping, a Sender. Decorators may affect the -// http.Request and pass it along or, first, pass the http.Request along then react to the -// http.Response result. -type SendDecorator func(Sender) Sender - -// CreateSender creates, decorates, and returns, as a Sender, the default http.Client. -func CreateSender(decorators ...SendDecorator) Sender { - return DecorateSender(sender(tls.RenegotiateNever), decorators...) -} - -// DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to -// the Sender. Decorators are applied in the order received, but their affect upon the request -// depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a -// post-decorator (pass the http.Request along and react to the results in http.Response). -func DecorateSender(s Sender, decorators ...SendDecorator) Sender { - for _, decorate := range decorators { - s = decorate(s) - } - return s -} - -// Send sends, by means of the default http.Client, the passed http.Request, returning the -// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which -// it will apply the http.Client before invoking the Do method. -// -// Send is a convenience method and not recommended for production. Advanced users should use -// SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client). -// -// Send will not poll or retry requests. -func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) { - return SendWithSender(sender(tls.RenegotiateNever), r, decorators...) -} - -// SendWithSender sends the passed http.Request, through the provided Sender, returning the -// http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which -// it will apply the http.Client before invoking the Do method. -// -// SendWithSender will not poll or retry requests. -func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) { - return DecorateSender(s, decorators...).Do(r) -} - -func sender(renengotiation tls.RenegotiationSupport) Sender { - // Use behaviour compatible with DefaultTransport, but require TLS minimum version. - defaultTransport := http.DefaultTransport.(*http.Transport) - transport := &http.Transport{ - Proxy: defaultTransport.Proxy, - DialContext: defaultTransport.DialContext, - MaxIdleConns: defaultTransport.MaxIdleConns, - IdleConnTimeout: defaultTransport.IdleConnTimeout, - TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, - ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, - TLSClientConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - Renegotiation: renengotiation, - }, - } - var roundTripper http.RoundTripper = transport - if tracing.IsEnabled() { - roundTripper = tracing.NewTransport(transport) - } - j, _ := cookiejar.New(nil) - return &http.Client{Jar: j, Transport: roundTripper} -} - -// AfterDelay returns a SendDecorator that delays for the passed time.Duration before -// invoking the Sender. The delay may be terminated by closing the optional channel on the -// http.Request. If canceled, no further Senders are invoked. -func AfterDelay(d time.Duration) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - if !DelayForBackoff(d, 0, r.Context().Done()) { - return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay") - } - return s.Do(r) - }) - } -} - -// AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request. -func AsIs() SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - return s.Do(r) - }) - } -} - -// DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which -// it closes the response if the passed Sender returns an error and the response body exists. -func DoCloseIfError() SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - resp, err := s.Do(r) - if err != nil { - Respond(resp, ByDiscardingBody(), ByClosing()) - } - return resp, err - }) - } -} - -// DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is -// among the set passed. Since these are artificial errors, the response body may still require -// closing. -func DoErrorIfStatusCode(codes ...int) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - resp, err := s.Do(r) - if err == nil && ResponseHasStatusCode(resp, codes...) { - err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s", - resp.Request.Method, - resp.Request.URL, - resp.Status) - } - return resp, err - }) - } -} - -// DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response -// StatusCode is among the set passed. Since these are artificial errors, the response body -// may still require closing. -func DoErrorUnlessStatusCode(codes ...int) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - resp, err := s.Do(r) - if err == nil && !ResponseHasStatusCode(resp, codes...) { - err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s", - resp.Request.Method, - resp.Request.URL, - resp.Status) - } - return resp, err - }) - } -} - -// DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the -// passed status codes. It expects the http.Response to contain a Location header providing the -// URL at which to poll (using GET) and will poll until the time passed is equal to or greater than -// the supplied duration. It will delay between requests for the duration specified in the -// RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by -// closing the optional channel on the http.Request. -func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { - resp, err = s.Do(r) - - if err == nil && ResponseHasStatusCode(resp, codes...) { - r, err = NewPollingRequestWithContext(r.Context(), resp) - - for err == nil && ResponseHasStatusCode(resp, codes...) { - Respond(resp, - ByDiscardingBody(), - ByClosing()) - resp, err = SendWithSender(s, r, - AfterDelay(GetRetryAfter(resp, delay))) - } - } - - return resp, err - }) - } -} - -// DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified -// number of attempts, exponentially backing off between requests using the supplied backoff -// time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on -// the http.Request. -func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { - rr := NewRetriableRequest(r) - for attempt := 0; attempt < attempts; attempt++ { - err = rr.Prepare() - if err != nil { - return resp, err - } - resp, err = s.Do(rr.Request()) - if err == nil { - return resp, err - } - if !DelayForBackoff(backoff, attempt, r.Context().Done()) { - return nil, r.Context().Err() - } - } - return resp, err - }) - } -} - -// DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified -// number of attempts, exponentially backing off between requests using the supplied backoff -// time.Duration (which may be zero). Retrying may be canceled by cancelling the context on the http.Request. -// NOTE: Code http.StatusTooManyRequests (429) will *not* be counted against the number of attempts. -func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - return doRetryForStatusCodesImpl(s, r, false, attempts, backoff, 0, codes...) - }) - } -} - -// DoRetryForStatusCodesWithCap returns a SendDecorator that retries for specified statusCodes for up to the -// specified number of attempts, exponentially backing off between requests using the supplied backoff -// time.Duration (which may be zero). To cap the maximum possible delay between iterations specify a value greater -// than zero for cap. Retrying may be canceled by cancelling the context on the http.Request. -func DoRetryForStatusCodesWithCap(attempts int, backoff, cap time.Duration, codes ...int) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - return doRetryForStatusCodesImpl(s, r, true, attempts, backoff, cap, codes...) - }) - } -} - -func doRetryForStatusCodesImpl(s Sender, r *http.Request, count429 bool, attempts int, backoff, cap time.Duration, codes ...int) (resp *http.Response, err error) { - rr := NewRetriableRequest(r) - // Increment to add the first call (attempts denotes number of retries) - for attempt := 0; attempt < attempts+1; { - err = rr.Prepare() - if err != nil { - return - } - resp, err = s.Do(rr.Request()) - // if the error isn't temporary don't bother retrying - if err != nil && !IsTemporaryNetworkError(err) { - return - } - // we want to retry if err is not nil (e.g. transient network failure). note that for failed authentication - // resp and err will both have a value, so in this case we don't want to retry as it will never succeed. - if err == nil && !ResponseHasStatusCode(resp, codes...) || IsTokenRefreshError(err) { - return resp, err - } - delayed := DelayWithRetryAfter(resp, r.Context().Done()) - if !delayed && !DelayForBackoffWithCap(backoff, cap, attempt, r.Context().Done()) { - return resp, r.Context().Err() - } - // when count429 == false don't count a 429 against the number - // of attempts so that we continue to retry until it succeeds - if count429 || (resp == nil || resp.StatusCode != http.StatusTooManyRequests) { - attempt++ - } - } - return resp, err -} - -// DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header. -// The value of Retry-After can be either the number of seconds or a date in RFC1123 format. -// The function returns true after successfully waiting for the specified duration. If there is -// no Retry-After header or the wait is cancelled the return value is false. -func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool { - if resp == nil { - return false - } - var dur time.Duration - ra := resp.Header.Get("Retry-After") - if retryAfter, _ := strconv.Atoi(ra); retryAfter > 0 { - dur = time.Duration(retryAfter) * time.Second - } else if t, err := time.Parse(time.RFC1123, ra); err == nil { - dur = t.Sub(time.Now()) - } - if dur > 0 { - select { - case <-time.After(dur): - return true - case <-cancel: - return false - } - } - return false -} - -// DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal -// to or greater than the specified duration, exponentially backing off between requests using the -// supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the -// optional channel on the http.Request. -func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { - rr := NewRetriableRequest(r) - end := time.Now().Add(d) - for attempt := 0; time.Now().Before(end); attempt++ { - err = rr.Prepare() - if err != nil { - return resp, err - } - resp, err = s.Do(rr.Request()) - if err == nil { - return resp, err - } - if !DelayForBackoff(backoff, attempt, r.Context().Done()) { - return nil, r.Context().Err() - } - } - return resp, err - }) - } -} - -// WithLogging returns a SendDecorator that implements simple before and after logging of the -// request. -func WithLogging(logger *log.Logger) SendDecorator { - return func(s Sender) Sender { - return SenderFunc(func(r *http.Request) (*http.Response, error) { - logger.Printf("Sending %s %s", r.Method, r.URL) - resp, err := s.Do(r) - if err != nil { - logger.Printf("%s %s received error '%v'", r.Method, r.URL, err) - } else { - logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status) - } - return resp, err - }) - } -} - -// DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of -// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set -// to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early, -// returns false. -// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt -// count. -func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool { - return DelayForBackoffWithCap(backoff, 0, attempt, cancel) -} - -// DelayForBackoffWithCap invokes time.After for the supplied backoff duration raised to the power of -// passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set -// to zero for no delay. To cap the maximum possible delay specify a value greater than zero for cap. -// The delay may be canceled by closing the passed channel. If terminated early, returns false. -// Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt -// count. -func DelayForBackoffWithCap(backoff, cap time.Duration, attempt int, cancel <-chan struct{}) bool { - d := time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second - if cap > 0 && d > cap { - d = cap - } - select { - case <-time.After(d): - return true - case <-cancel: - return false - } -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/utility.go b/vendor/github.com/Azure/go-autorest/autorest/utility.go deleted file mode 100644 index 08cf11c11..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/utility.go +++ /dev/null @@ -1,228 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "encoding/json" - "encoding/xml" - "fmt" - "io" - "net" - "net/http" - "net/url" - "reflect" - "strings" - - "github.com/Azure/go-autorest/autorest/adal" -) - -// EncodedAs is a series of constants specifying various data encodings -type EncodedAs string - -const ( - // EncodedAsJSON states that data is encoded as JSON - EncodedAsJSON EncodedAs = "JSON" - - // EncodedAsXML states that data is encoded as Xml - EncodedAsXML EncodedAs = "XML" -) - -// Decoder defines the decoding method json.Decoder and xml.Decoder share -type Decoder interface { - Decode(v interface{}) error -} - -// NewDecoder creates a new decoder appropriate to the passed encoding. -// encodedAs specifies the type of encoding and r supplies the io.Reader containing the -// encoded data. -func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder { - if encodedAs == EncodedAsJSON { - return json.NewDecoder(r) - } else if encodedAs == EncodedAsXML { - return xml.NewDecoder(r) - } - return nil -} - -// CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy -// is especially useful if there is a chance the data will fail to decode. -// encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v -// is the decoding destination. -func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (bytes.Buffer, error) { - b := bytes.Buffer{} - return b, NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v) -} - -// TeeReadCloser returns a ReadCloser that writes to w what it reads from rc. -// It utilizes io.TeeReader to copy the data read and has the same behavior when reading. -// Further, when it is closed, it ensures that rc is closed as well. -func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser { - return &teeReadCloser{rc, io.TeeReader(rc, w)} -} - -type teeReadCloser struct { - rc io.ReadCloser - r io.Reader -} - -func (t *teeReadCloser) Read(p []byte) (int, error) { - return t.r.Read(p) -} - -func (t *teeReadCloser) Close() error { - return t.rc.Close() -} - -func containsInt(ints []int, n int) bool { - for _, i := range ints { - if i == n { - return true - } - } - return false -} - -func escapeValueStrings(m map[string]string) map[string]string { - for key, value := range m { - m[key] = url.QueryEscape(value) - } - return m -} - -func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string { - mapOfStrings := make(map[string]string) - for key, value := range mapOfInterface { - mapOfStrings[key] = ensureValueString(value) - } - return mapOfStrings -} - -func ensureValueString(value interface{}) string { - if value == nil { - return "" - } - switch v := value.(type) { - case string: - return v - case []byte: - return string(v) - default: - return fmt.Sprintf("%v", v) - } -} - -// MapToValues method converts map[string]interface{} to url.Values. -func MapToValues(m map[string]interface{}) url.Values { - v := url.Values{} - for key, value := range m { - x := reflect.ValueOf(value) - if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { - for i := 0; i < x.Len(); i++ { - v.Add(key, ensureValueString(x.Index(i))) - } - } else { - v.Add(key, ensureValueString(value)) - } - } - return v -} - -// AsStringSlice method converts interface{} to []string. This expects a -//that the parameter passed to be a slice or array of a type that has the underlying -//type a string. -func AsStringSlice(s interface{}) ([]string, error) { - v := reflect.ValueOf(s) - if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { - return nil, NewError("autorest", "AsStringSlice", "the value's type is not an array.") - } - stringSlice := make([]string, 0, v.Len()) - - for i := 0; i < v.Len(); i++ { - stringSlice = append(stringSlice, v.Index(i).String()) - } - return stringSlice, nil -} - -// String method converts interface v to string. If interface is a list, it -// joins list elements using the separator. Note that only sep[0] will be used for -// joining if any separator is specified. -func String(v interface{}, sep ...string) string { - if len(sep) == 0 { - return ensureValueString(v) - } - stringSlice, ok := v.([]string) - if ok == false { - var err error - stringSlice, err = AsStringSlice(v) - if err != nil { - panic(fmt.Sprintf("autorest: Couldn't convert value to a string %s.", err)) - } - } - return ensureValueString(strings.Join(stringSlice, sep[0])) -} - -// Encode method encodes url path and query parameters. -func Encode(location string, v interface{}, sep ...string) string { - s := String(v, sep...) - switch strings.ToLower(location) { - case "path": - return pathEscape(s) - case "query": - return queryEscape(s) - default: - return s - } -} - -func pathEscape(s string) string { - return strings.Replace(url.QueryEscape(s), "+", "%20", -1) -} - -func queryEscape(s string) string { - return url.QueryEscape(s) -} - -// ChangeToGet turns the specified http.Request into a GET (it assumes it wasn't). -// This is mainly useful for long-running operations that use the Azure-AsyncOperation -// header, so we change the initial PUT into a GET to retrieve the final result. -func ChangeToGet(req *http.Request) *http.Request { - req.Method = "GET" - req.Body = nil - req.ContentLength = 0 - req.Header.Del("Content-Length") - return req -} - -// IsTokenRefreshError returns true if the specified error implements the TokenRefreshError -// interface. If err is a DetailedError it will walk the chain of Original errors. -func IsTokenRefreshError(err error) bool { - if _, ok := err.(adal.TokenRefreshError); ok { - return true - } - if de, ok := err.(DetailedError); ok { - return IsTokenRefreshError(de.Original) - } - return false -} - -// IsTemporaryNetworkError returns true if the specified error is a temporary network error or false -// if it's not. If the error doesn't implement the net.Error interface the return value is true. -func IsTemporaryNetworkError(err error) bool { - if netErr, ok := err.(net.Error); !ok || (ok && netErr.Temporary()) { - return true - } - return false -} diff --git a/vendor/github.com/Azure/go-autorest/autorest/version.go b/vendor/github.com/Azure/go-autorest/autorest/version.go deleted file mode 100644 index cb851937a..000000000 --- a/vendor/github.com/Azure/go-autorest/autorest/version.go +++ /dev/null @@ -1,41 +0,0 @@ -package autorest - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "fmt" - "runtime" -) - -const number = "v13.0.0" - -var ( - userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", - runtime.Version(), - runtime.GOARCH, - runtime.GOOS, - number, - ) -) - -// UserAgent returns a string containing the Go version, system architecture and OS, and the go-autorest version. -func UserAgent() string { - return userAgent -} - -// Version returns the semantic version (see http://semver.org). -func Version() string { - return number -} diff --git a/vendor/github.com/Azure/go-autorest/logger/LICENSE b/vendor/github.com/Azure/go-autorest/logger/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/vendor/github.com/Azure/go-autorest/logger/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2015 Microsoft Corporation - - 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/github.com/Azure/go-autorest/logger/go.mod b/vendor/github.com/Azure/go-autorest/logger/go.mod deleted file mode 100644 index f22ed56bc..000000000 --- a/vendor/github.com/Azure/go-autorest/logger/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/Azure/go-autorest/logger - -go 1.12 diff --git a/vendor/github.com/Azure/go-autorest/logger/logger.go b/vendor/github.com/Azure/go-autorest/logger/logger.go deleted file mode 100644 index da09f394c..000000000 --- a/vendor/github.com/Azure/go-autorest/logger/logger.go +++ /dev/null @@ -1,328 +0,0 @@ -package logger - -// Copyright 2017 Microsoft Corporation -// -// 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. - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" -) - -// LevelType tells a logger the minimum level to log. When code reports a log entry, -// the LogLevel indicates the level of the log entry. The logger only records entries -// whose level is at least the level it was told to log. See the Log* constants. -// For example, if a logger is configured with LogError, then LogError, LogPanic, -// and LogFatal entries will be logged; lower level entries are ignored. -type LevelType uint32 - -const ( - // LogNone tells a logger not to log any entries passed to it. - LogNone LevelType = iota - - // LogFatal tells a logger to log all LogFatal entries passed to it. - LogFatal - - // LogPanic tells a logger to log all LogPanic and LogFatal entries passed to it. - LogPanic - - // LogError tells a logger to log all LogError, LogPanic and LogFatal entries passed to it. - LogError - - // LogWarning tells a logger to log all LogWarning, LogError, LogPanic and LogFatal entries passed to it. - LogWarning - - // LogInfo tells a logger to log all LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it. - LogInfo - - // LogDebug tells a logger to log all LogDebug, LogInfo, LogWarning, LogError, LogPanic and LogFatal entries passed to it. - LogDebug -) - -const ( - logNone = "NONE" - logFatal = "FATAL" - logPanic = "PANIC" - logError = "ERROR" - logWarning = "WARNING" - logInfo = "INFO" - logDebug = "DEBUG" - logUnknown = "UNKNOWN" -) - -// ParseLevel converts the specified string into the corresponding LevelType. -func ParseLevel(s string) (lt LevelType, err error) { - switch strings.ToUpper(s) { - case logFatal: - lt = LogFatal - case logPanic: - lt = LogPanic - case logError: - lt = LogError - case logWarning: - lt = LogWarning - case logInfo: - lt = LogInfo - case logDebug: - lt = LogDebug - default: - err = fmt.Errorf("bad log level '%s'", s) - } - return -} - -// String implements the stringer interface for LevelType. -func (lt LevelType) String() string { - switch lt { - case LogNone: - return logNone - case LogFatal: - return logFatal - case LogPanic: - return logPanic - case LogError: - return logError - case LogWarning: - return logWarning - case LogInfo: - return logInfo - case LogDebug: - return logDebug - default: - return logUnknown - } -} - -// Filter defines functions for filtering HTTP request/response content. -type Filter struct { - // URL returns a potentially modified string representation of a request URL. - URL func(u *url.URL) string - - // Header returns a potentially modified set of values for the specified key. - // To completely exclude the header key/values return false. - Header func(key string, val []string) (bool, []string) - - // Body returns a potentially modified request/response body. - Body func(b []byte) []byte -} - -func (f Filter) processURL(u *url.URL) string { - if f.URL == nil { - return u.String() - } - return f.URL(u) -} - -func (f Filter) processHeader(k string, val []string) (bool, []string) { - if f.Header == nil { - return true, val - } - return f.Header(k, val) -} - -func (f Filter) processBody(b []byte) []byte { - if f.Body == nil { - return b - } - return f.Body(b) -} - -// Writer defines methods for writing to a logging facility. -type Writer interface { - // Writeln writes the specified message with the standard log entry header and new-line character. - Writeln(level LevelType, message string) - - // Writef writes the specified format specifier with the standard log entry header and no new-line character. - Writef(level LevelType, format string, a ...interface{}) - - // WriteRequest writes the specified HTTP request to the logger if the log level is greater than - // or equal to LogInfo. The request body, if set, is logged at level LogDebug or higher. - // Custom filters can be specified to exclude URL, header, and/or body content from the log. - // By default no request content is excluded. - WriteRequest(req *http.Request, filter Filter) - - // WriteResponse writes the specified HTTP response to the logger if the log level is greater than - // or equal to LogInfo. The response body, if set, is logged at level LogDebug or higher. - // Custom filters can be specified to exclude URL, header, and/or body content from the log. - // By default no response content is excluded. - WriteResponse(resp *http.Response, filter Filter) -} - -// Instance is the default log writer initialized during package init. -// This can be replaced with a custom implementation as required. -var Instance Writer - -// default log level -var logLevel = LogNone - -// Level returns the value specified in AZURE_GO_AUTOREST_LOG_LEVEL. -// If no value was specified the default value is LogNone. -// Custom loggers can call this to retrieve the configured log level. -func Level() LevelType { - return logLevel -} - -func init() { - // separated for testing purposes - initDefaultLogger() -} - -func initDefaultLogger() { - // init with nilLogger so callers don't have to do a nil check on Default - Instance = nilLogger{} - llStr := strings.ToLower(os.Getenv("AZURE_GO_SDK_LOG_LEVEL")) - if llStr == "" { - return - } - var err error - logLevel, err = ParseLevel(llStr) - if err != nil { - fmt.Fprintf(os.Stderr, "go-autorest: failed to parse log level: %s\n", err.Error()) - return - } - if logLevel == LogNone { - return - } - // default to stderr - dest := os.Stderr - lfStr := os.Getenv("AZURE_GO_SDK_LOG_FILE") - if strings.EqualFold(lfStr, "stdout") { - dest = os.Stdout - } else if lfStr != "" { - lf, err := os.Create(lfStr) - if err == nil { - dest = lf - } else { - fmt.Fprintf(os.Stderr, "go-autorest: failed to create log file, using stderr: %s\n", err.Error()) - } - } - Instance = fileLogger{ - logLevel: logLevel, - mu: &sync.Mutex{}, - logFile: dest, - } -} - -// the nil logger does nothing -type nilLogger struct{} - -func (nilLogger) Writeln(LevelType, string) {} - -func (nilLogger) Writef(LevelType, string, ...interface{}) {} - -func (nilLogger) WriteRequest(*http.Request, Filter) {} - -func (nilLogger) WriteResponse(*http.Response, Filter) {} - -// A File is used instead of a Logger so the stream can be flushed after every write. -type fileLogger struct { - logLevel LevelType - mu *sync.Mutex // for synchronizing writes to logFile - logFile *os.File -} - -func (fl fileLogger) Writeln(level LevelType, message string) { - fl.Writef(level, "%s\n", message) -} - -func (fl fileLogger) Writef(level LevelType, format string, a ...interface{}) { - if fl.logLevel >= level { - fl.mu.Lock() - defer fl.mu.Unlock() - fmt.Fprintf(fl.logFile, "%s %s", entryHeader(level), fmt.Sprintf(format, a...)) - fl.logFile.Sync() - } -} - -func (fl fileLogger) WriteRequest(req *http.Request, filter Filter) { - if req == nil || fl.logLevel < LogInfo { - return - } - b := &bytes.Buffer{} - fmt.Fprintf(b, "%s REQUEST: %s %s\n", entryHeader(LogInfo), req.Method, filter.processURL(req.URL)) - // dump headers - for k, v := range req.Header { - if ok, mv := filter.processHeader(k, v); ok { - fmt.Fprintf(b, "%s: %s\n", k, strings.Join(mv, ",")) - } - } - if fl.shouldLogBody(req.Header, req.Body) { - // dump body - body, err := ioutil.ReadAll(req.Body) - if err == nil { - fmt.Fprintln(b, string(filter.processBody(body))) - if nc, ok := req.Body.(io.Seeker); ok { - // rewind to the beginning - nc.Seek(0, io.SeekStart) - } else { - // recreate the body - req.Body = ioutil.NopCloser(bytes.NewReader(body)) - } - } else { - fmt.Fprintf(b, "failed to read body: %v\n", err) - } - } - fl.mu.Lock() - defer fl.mu.Unlock() - fmt.Fprint(fl.logFile, b.String()) - fl.logFile.Sync() -} - -func (fl fileLogger) WriteResponse(resp *http.Response, filter Filter) { - if resp == nil || fl.logLevel < LogInfo { - return - } - b := &bytes.Buffer{} - fmt.Fprintf(b, "%s RESPONSE: %d %s\n", entryHeader(LogInfo), resp.StatusCode, filter.processURL(resp.Request.URL)) - // dump headers - for k, v := range resp.Header { - if ok, mv := filter.processHeader(k, v); ok { - fmt.Fprintf(b, "%s: %s\n", k, strings.Join(mv, ",")) - } - } - if fl.shouldLogBody(resp.Header, resp.Body) { - // dump body - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err == nil { - fmt.Fprintln(b, string(filter.processBody(body))) - resp.Body = ioutil.NopCloser(bytes.NewReader(body)) - } else { - fmt.Fprintf(b, "failed to read body: %v\n", err) - } - } - fl.mu.Lock() - defer fl.mu.Unlock() - fmt.Fprint(fl.logFile, b.String()) - fl.logFile.Sync() -} - -// returns true if the provided body should be included in the log -func (fl fileLogger) shouldLogBody(header http.Header, body io.ReadCloser) bool { - ct := header.Get("Content-Type") - return fl.logLevel >= LogDebug && body != nil && !strings.Contains(ct, "application/octet-stream") -} - -// creates standard header for log entries, it contains a timestamp and the log level -func entryHeader(level LevelType) string { - // this format provides a fixed number of digits so the size of the timestamp is constant - return fmt.Sprintf("(%s) %s:", time.Now().Format("2006-01-02T15:04:05.0000000Z07:00"), level.String()) -} diff --git a/vendor/github.com/Azure/go-autorest/tracing/LICENSE b/vendor/github.com/Azure/go-autorest/tracing/LICENSE deleted file mode 100644 index b9d6a27ea..000000000 --- a/vendor/github.com/Azure/go-autorest/tracing/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ - - 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 - - Copyright 2015 Microsoft Corporation - - 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/github.com/Azure/go-autorest/tracing/go.mod b/vendor/github.com/Azure/go-autorest/tracing/go.mod deleted file mode 100644 index 25c34c108..000000000 --- a/vendor/github.com/Azure/go-autorest/tracing/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/Azure/go-autorest/tracing - -go 1.12 diff --git a/vendor/github.com/Azure/go-autorest/tracing/tracing.go b/vendor/github.com/Azure/go-autorest/tracing/tracing.go deleted file mode 100644 index 0e7a6e962..000000000 --- a/vendor/github.com/Azure/go-autorest/tracing/tracing.go +++ /dev/null @@ -1,67 +0,0 @@ -package tracing - -// Copyright 2018 Microsoft Corporation -// -// 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. - -import ( - "context" - "net/http" -) - -// Tracer represents an HTTP tracing facility. -type Tracer interface { - NewTransport(base *http.Transport) http.RoundTripper - StartSpan(ctx context.Context, name string) context.Context - EndSpan(ctx context.Context, httpStatusCode int, err error) -} - -var ( - tracer Tracer -) - -// Register will register the provided Tracer. Pass nil to unregister a Tracer. -func Register(t Tracer) { - tracer = t -} - -// IsEnabled returns true if a Tracer has been registered. -func IsEnabled() bool { - return tracer != nil -} - -// NewTransport creates a new instrumenting http.RoundTripper for the -// registered Tracer. If no Tracer has been registered it returns nil. -func NewTransport(base *http.Transport) http.RoundTripper { - if tracer != nil { - return tracer.NewTransport(base) - } - return nil -} - -// StartSpan starts a trace span with the specified name, associating it with the -// provided context. Has no effect if a Tracer has not been registered. -func StartSpan(ctx context.Context, name string) context.Context { - if tracer != nil { - return tracer.StartSpan(ctx, name) - } - return ctx -} - -// EndSpan ends a previously started span stored in the context. -// Has no effect if a Tracer has not been registered. -func EndSpan(ctx context.Context, httpStatusCode int, err error) { - if tracer != nil { - tracer.EndSpan(ctx, httpStatusCode, err) - } -} diff --git a/vendor/github.com/OneOfOne/xxhash/.gitignore b/vendor/github.com/OneOfOne/xxhash/.gitignore new file mode 100644 index 000000000..f4faa7f8f --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/.gitignore @@ -0,0 +1,4 @@ +*.txt +*.pprof +cmap2/ +cache/ diff --git a/vendor/github.com/OneOfOne/xxhash/.travis.yml b/vendor/github.com/OneOfOne/xxhash/.travis.yml new file mode 100644 index 000000000..9b5bb481e --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/.travis.yml @@ -0,0 +1,12 @@ +language: go +sudo: false + +go: + - 1.8 + - 1.9 + - "1.10" + - master + +script: + - go test -tags safe ./... + - go test ./... diff --git a/vendor/github.com/OneOfOne/xxhash/LICENSE b/vendor/github.com/OneOfOne/xxhash/LICENSE new file mode 100644 index 000000000..9e30b4f34 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/LICENSE @@ -0,0 +1,187 @@ + 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. diff --git a/vendor/github.com/OneOfOne/xxhash/README.md b/vendor/github.com/OneOfOne/xxhash/README.md new file mode 100644 index 000000000..23174eb56 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/README.md @@ -0,0 +1,75 @@ +# xxhash [![GoDoc](https://godoc.org/github.com/OneOfOne/xxhash?status.svg)](https://godoc.org/github.com/OneOfOne/xxhash) [![Build Status](https://travis-ci.org/OneOfOne/xxhash.svg?branch=master)](https://travis-ci.org/OneOfOne/xxhash) [![Coverage](https://gocover.io/_badge/github.com/OneOfOne/xxhash)](https://gocover.io/github.com/OneOfOne/xxhash) + +This is a native Go implementation of the excellent [xxhash](https://github.com/Cyan4973/xxHash)* algorithm, an extremely fast non-cryptographic Hash algorithm, working at speeds close to RAM limits. + +* The C implementation is ([Copyright](https://github.com/Cyan4973/xxHash/blob/master/LICENSE) (c) 2012-2014, Yann Collet) + +## Install + + go get github.com/OneOfOne/xxhash + +## Features + +* On Go 1.7+ the pure go version is faster than CGO for all inputs. +* Supports ChecksumString{32,64} xxhash{32,64}.WriteString, which uses no copies when it can, falls back to copy on appengine. +* The native version falls back to a less optimized version on appengine due to the lack of unsafe. +* Almost as fast as the mostly pure assembly version written by the brilliant [cespare](https://github.com/cespare/xxhash), while also supporting seeds. +* To manually toggle the appengine version build with `-tags safe`. + +## Benchmark + +### Core i7-4790 @ 3.60GHz, Linux 4.12.6-1-ARCH (64bit), Go tip (+ff90f4af66 2017-08-19) + +```bash +➤ go test -bench '64' -count 5 -tags cespare | benchstat /dev/stdin +name time/op + +# https://github.com/cespare/xxhash +XXSum64Cespare/Func-8 160ns ± 2% +XXSum64Cespare/Struct-8 173ns ± 1% +XXSum64ShortCespare/Func-8 6.78ns ± 1% +XXSum64ShortCespare/Struct-8 19.6ns ± 2% + +# this package (default mode, using unsafe) +XXSum64/Func-8 170ns ± 1% +XXSum64/Struct-8 182ns ± 1% +XXSum64Short/Func-8 13.5ns ± 3% +XXSum64Short/Struct-8 20.4ns ± 0% + +# this package (appengine, *not* using unsafe) +XXSum64/Func-8 241ns ± 5% +XXSum64/Struct-8 243ns ± 6% +XXSum64Short/Func-8 15.2ns ± 2% +XXSum64Short/Struct-8 23.7ns ± 5% + +CRC64ISO-8 1.23µs ± 1% +CRC64ISOString-8 2.71µs ± 4% +CRC64ISOShort-8 22.2ns ± 3% + +Fnv64-8 2.34µs ± 1% +Fnv64Short-8 74.7ns ± 8% +# +``` + +## Usage + +```go + h := xxhash.New64() + // r, err := os.Open("......") + // defer f.Close() + r := strings.NewReader(F) + io.Copy(h, r) + fmt.Println("xxhash.Backend:", xxhash.Backend) + fmt.Println("File checksum:", h.Sum64()) +``` + +[playground](http://play.golang.org/p/rhRN3RdQyd) + +## TODO + +* Rewrite the 32bit version to be more optimized. +* General cleanup as the Go inliner gets smarter. + +## License + +This project is released under the Apache v2. licence. See [LICENCE](LICENCE) for more details. diff --git a/vendor/github.com/OneOfOne/xxhash/go.mod b/vendor/github.com/OneOfOne/xxhash/go.mod new file mode 100644 index 000000000..2f3334267 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/go.mod @@ -0,0 +1 @@ +module github.com/OneOfOne/xxhash diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash.go b/vendor/github.com/OneOfOne/xxhash/xxhash.go new file mode 100644 index 000000000..2387d6593 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/xxhash.go @@ -0,0 +1,189 @@ +package xxhash + +const ( + prime32x1 uint32 = 2654435761 + prime32x2 uint32 = 2246822519 + prime32x3 uint32 = 3266489917 + prime32x4 uint32 = 668265263 + prime32x5 uint32 = 374761393 + + prime64x1 uint64 = 11400714785074694791 + prime64x2 uint64 = 14029467366897019727 + prime64x3 uint64 = 1609587929392839161 + prime64x4 uint64 = 9650029242287828579 + prime64x5 uint64 = 2870177450012600261 + + maxInt32 int32 = (1<<31 - 1) + + // precomputed zero Vs for seed 0 + zero64x1 = 0x60ea27eeadc0b5d6 + zero64x2 = 0xc2b2ae3d27d4eb4f + zero64x3 = 0x0 + zero64x4 = 0x61c8864e7a143579 +) + +// Checksum32 returns the checksum of the input data with the seed set to 0. +func Checksum32(in []byte) uint32 { + return Checksum32S(in, 0) +} + +// ChecksumString32 returns the checksum of the input data, without creating a copy, with the seed set to 0. +func ChecksumString32(s string) uint32 { + return ChecksumString32S(s, 0) +} + +type XXHash32 struct { + mem [16]byte + ln, memIdx int32 + v1, v2, v3, v4 uint32 + seed uint32 +} + +// Size returns the number of bytes Sum will return. +func (xx *XXHash32) Size() int { + return 4 +} + +// BlockSize returns the hash's underlying block size. +// The Write method must be able to accept any amount +// of data, but it may operate more efficiently if all writes +// are a multiple of the block size. +func (xx *XXHash32) BlockSize() int { + return 16 +} + +// NewS32 creates a new hash.Hash32 computing the 32bit xxHash checksum starting with the specific seed. +func NewS32(seed uint32) (xx *XXHash32) { + xx = &XXHash32{ + seed: seed, + } + xx.Reset() + return +} + +// New32 creates a new hash.Hash32 computing the 32bit xxHash checksum starting with the seed set to 0. +func New32() *XXHash32 { + return NewS32(0) +} + +func (xx *XXHash32) Reset() { + xx.v1 = xx.seed + prime32x1 + prime32x2 + xx.v2 = xx.seed + prime32x2 + xx.v3 = xx.seed + xx.v4 = xx.seed - prime32x1 + xx.ln, xx.memIdx = 0, 0 +} + +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (xx *XXHash32) Sum(in []byte) []byte { + s := xx.Sum32() + return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) +} + +// Checksum64 an alias for Checksum64S(in, 0) +func Checksum64(in []byte) uint64 { + return Checksum64S(in, 0) +} + +// ChecksumString64 returns the checksum of the input data, without creating a copy, with the seed set to 0. +func ChecksumString64(s string) uint64 { + return ChecksumString64S(s, 0) +} + +type XXHash64 struct { + v1, v2, v3, v4 uint64 + seed uint64 + ln uint64 + mem [32]byte + memIdx int8 +} + +// Size returns the number of bytes Sum will return. +func (xx *XXHash64) Size() int { + return 8 +} + +// BlockSize returns the hash's underlying block size. +// The Write method must be able to accept any amount +// of data, but it may operate more efficiently if all writes +// are a multiple of the block size. +func (xx *XXHash64) BlockSize() int { + return 32 +} + +// NewS64 creates a new hash.Hash64 computing the 64bit xxHash checksum starting with the specific seed. +func NewS64(seed uint64) (xx *XXHash64) { + xx = &XXHash64{ + seed: seed, + } + xx.Reset() + return +} + +// New64 creates a new hash.Hash64 computing the 64bit xxHash checksum starting with the seed set to 0x0. +func New64() *XXHash64 { + return NewS64(0) +} + +func (xx *XXHash64) Reset() { + xx.ln, xx.memIdx = 0, 0 + xx.v1, xx.v2, xx.v3, xx.v4 = resetVs64(xx.seed) +} + +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (xx *XXHash64) Sum(in []byte) []byte { + s := xx.Sum64() + return append(in, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s)) +} + +// force the compiler to use ROTL instructions + +func rotl32_1(x uint32) uint32 { return (x << 1) | (x >> (32 - 1)) } +func rotl32_7(x uint32) uint32 { return (x << 7) | (x >> (32 - 7)) } +func rotl32_11(x uint32) uint32 { return (x << 11) | (x >> (32 - 11)) } +func rotl32_12(x uint32) uint32 { return (x << 12) | (x >> (32 - 12)) } +func rotl32_13(x uint32) uint32 { return (x << 13) | (x >> (32 - 13)) } +func rotl32_17(x uint32) uint32 { return (x << 17) | (x >> (32 - 17)) } +func rotl32_18(x uint32) uint32 { return (x << 18) | (x >> (32 - 18)) } + +func rotl64_1(x uint64) uint64 { return (x << 1) | (x >> (64 - 1)) } +func rotl64_7(x uint64) uint64 { return (x << 7) | (x >> (64 - 7)) } +func rotl64_11(x uint64) uint64 { return (x << 11) | (x >> (64 - 11)) } +func rotl64_12(x uint64) uint64 { return (x << 12) | (x >> (64 - 12)) } +func rotl64_18(x uint64) uint64 { return (x << 18) | (x >> (64 - 18)) } +func rotl64_23(x uint64) uint64 { return (x << 23) | (x >> (64 - 23)) } +func rotl64_27(x uint64) uint64 { return (x << 27) | (x >> (64 - 27)) } +func rotl64_31(x uint64) uint64 { return (x << 31) | (x >> (64 - 31)) } + +func mix64(h uint64) uint64 { + h ^= h >> 33 + h *= prime64x2 + h ^= h >> 29 + h *= prime64x3 + h ^= h >> 32 + return h +} + +func resetVs64(seed uint64) (v1, v2, v3, v4 uint64) { + if seed == 0 { + return zero64x1, zero64x2, zero64x3, zero64x4 + } + return (seed + prime64x1 + prime64x2), (seed + prime64x2), (seed), (seed - prime64x1) +} + +// borrowed from cespare +func round64(h, v uint64) uint64 { + h += v * prime64x2 + h = rotl64_31(h) + h *= prime64x1 + return h +} + +func mergeRound64(h, v uint64) uint64 { + v = round64(0, v) + h ^= v + h = h*prime64x1 + prime64x4 + return h +} diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go b/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go new file mode 100644 index 000000000..ae48e0c5c --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/xxhash_go17.go @@ -0,0 +1,161 @@ +package xxhash + +func u32(in []byte) uint32 { + return uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24 +} + +func u64(in []byte) uint64 { + return uint64(in[0]) | uint64(in[1])<<8 | uint64(in[2])<<16 | uint64(in[3])<<24 | uint64(in[4])<<32 | uint64(in[5])<<40 | uint64(in[6])<<48 | uint64(in[7])<<56 +} + +// Checksum32S returns the checksum of the input bytes with the specific seed. +func Checksum32S(in []byte, seed uint32) (h uint32) { + var i int + + if len(in) > 15 { + var ( + v1 = seed + prime32x1 + prime32x2 + v2 = seed + prime32x2 + v3 = seed + 0 + v4 = seed - prime32x1 + ) + for ; i < len(in)-15; i += 16 { + in := in[i : i+16 : len(in)] + v1 += u32(in[0:4:len(in)]) * prime32x2 + v1 = rotl32_13(v1) * prime32x1 + + v2 += u32(in[4:8:len(in)]) * prime32x2 + v2 = rotl32_13(v2) * prime32x1 + + v3 += u32(in[8:12:len(in)]) * prime32x2 + v3 = rotl32_13(v3) * prime32x1 + + v4 += u32(in[12:16:len(in)]) * prime32x2 + v4 = rotl32_13(v4) * prime32x1 + } + + h = rotl32_1(v1) + rotl32_7(v2) + rotl32_12(v3) + rotl32_18(v4) + + } else { + h = seed + prime32x5 + } + + h += uint32(len(in)) + for ; i <= len(in)-4; i += 4 { + in := in[i : i+4 : len(in)] + h += u32(in[0:4:len(in)]) * prime32x3 + h = rotl32_17(h) * prime32x4 + } + + for ; i < len(in); i++ { + h += uint32(in[i]) * prime32x5 + h = rotl32_11(h) * prime32x1 + } + + h ^= h >> 15 + h *= prime32x2 + h ^= h >> 13 + h *= prime32x3 + h ^= h >> 16 + + return +} + +func (xx *XXHash32) Write(in []byte) (n int, err error) { + i, ml := 0, int(xx.memIdx) + n = len(in) + xx.ln += int32(n) + + if d := 16 - ml; ml > 0 && ml+len(in) > 16 { + xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in[:d])) + ml, in = 16, in[d:len(in):len(in)] + } else if ml+len(in) < 16 { + xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in)) + return + } + + if ml > 0 { + i += 16 - ml + xx.memIdx += int32(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in)) + in := xx.mem[:16:len(xx.mem)] + + xx.v1 += u32(in[0:4:len(in)]) * prime32x2 + xx.v1 = rotl32_13(xx.v1) * prime32x1 + + xx.v2 += u32(in[4:8:len(in)]) * prime32x2 + xx.v2 = rotl32_13(xx.v2) * prime32x1 + + xx.v3 += u32(in[8:12:len(in)]) * prime32x2 + xx.v3 = rotl32_13(xx.v3) * prime32x1 + + xx.v4 += u32(in[12:16:len(in)]) * prime32x2 + xx.v4 = rotl32_13(xx.v4) * prime32x1 + + xx.memIdx = 0 + } + + for ; i <= len(in)-16; i += 16 { + in := in[i : i+16 : len(in)] + xx.v1 += u32(in[0:4:len(in)]) * prime32x2 + xx.v1 = rotl32_13(xx.v1) * prime32x1 + + xx.v2 += u32(in[4:8:len(in)]) * prime32x2 + xx.v2 = rotl32_13(xx.v2) * prime32x1 + + xx.v3 += u32(in[8:12:len(in)]) * prime32x2 + xx.v3 = rotl32_13(xx.v3) * prime32x1 + + xx.v4 += u32(in[12:16:len(in)]) * prime32x2 + xx.v4 = rotl32_13(xx.v4) * prime32x1 + } + + if len(in)-i != 0 { + xx.memIdx += int32(copy(xx.mem[xx.memIdx:], in[i:len(in):len(in)])) + } + + return +} + +func (xx *XXHash32) Sum32() (h uint32) { + var i int32 + if xx.ln > 15 { + h = rotl32_1(xx.v1) + rotl32_7(xx.v2) + rotl32_12(xx.v3) + rotl32_18(xx.v4) + } else { + h = xx.seed + prime32x5 + } + + h += uint32(xx.ln) + + if xx.memIdx > 0 { + for ; i < xx.memIdx-3; i += 4 { + in := xx.mem[i : i+4 : len(xx.mem)] + h += u32(in[0:4:len(in)]) * prime32x3 + h = rotl32_17(h) * prime32x4 + } + + for ; i < xx.memIdx; i++ { + h += uint32(xx.mem[i]) * prime32x5 + h = rotl32_11(h) * prime32x1 + } + } + h ^= h >> 15 + h *= prime32x2 + h ^= h >> 13 + h *= prime32x3 + h ^= h >> 16 + + return +} + +// Checksum64S returns the 64bit xxhash checksum for a single input +func Checksum64S(in []byte, seed uint64) uint64 { + if len(in) == 0 && seed == 0 { + return 0xef46db3751d8e999 + } + + if len(in) > 31 { + return checksum64(in, seed) + } + + return checksum64Short(in, seed) +} diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go b/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go new file mode 100644 index 000000000..7532c2d31 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/xxhash_safe.go @@ -0,0 +1,183 @@ +// +build appengine safe ppc64le ppc64be mipsle mipsbe + +package xxhash + +// Backend returns the current version of xxhash being used. +const Backend = "GoSafe" + +func ChecksumString32S(s string, seed uint32) uint32 { + return Checksum32S([]byte(s), seed) +} + +func (xx *XXHash32) WriteString(s string) (int, error) { + if len(s) == 0 { + return 0, nil + } + return xx.Write([]byte(s)) +} + +func ChecksumString64S(s string, seed uint64) uint64 { + return Checksum64S([]byte(s), seed) +} + +func (xx *XXHash64) WriteString(s string) (int, error) { + if len(s) == 0 { + return 0, nil + } + return xx.Write([]byte(s)) +} + +func checksum64(in []byte, seed uint64) (h uint64) { + var ( + v1, v2, v3, v4 = resetVs64(seed) + + i int + ) + + for ; i < len(in)-31; i += 32 { + in := in[i : i+32 : len(in)] + v1 = round64(v1, u64(in[0:8:len(in)])) + v2 = round64(v2, u64(in[8:16:len(in)])) + v3 = round64(v3, u64(in[16:24:len(in)])) + v4 = round64(v4, u64(in[24:32:len(in)])) + } + + h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4) + + h = mergeRound64(h, v1) + h = mergeRound64(h, v2) + h = mergeRound64(h, v3) + h = mergeRound64(h, v4) + + h += uint64(len(in)) + + for ; i < len(in)-7; i += 8 { + h ^= round64(0, u64(in[i:len(in):len(in)])) + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + for ; i < len(in)-3; i += 4 { + h ^= uint64(u32(in[i:len(in):len(in)])) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + } + + for ; i < len(in); i++ { + h ^= uint64(in[i]) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + + return mix64(h) +} + +func checksum64Short(in []byte, seed uint64) uint64 { + var ( + h = seed + prime64x5 + uint64(len(in)) + i int + ) + + for ; i < len(in)-7; i += 8 { + k := u64(in[i : i+8 : len(in)]) + h ^= round64(0, k) + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + for ; i < len(in)-3; i += 4 { + h ^= uint64(u32(in[i:i+4:len(in)])) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + } + + for ; i < len(in); i++ { + h ^= uint64(in[i]) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + + return mix64(h) +} + +func (xx *XXHash64) Write(in []byte) (n int, err error) { + var ( + ml = int(xx.memIdx) + d = 32 - ml + ) + + n = len(in) + xx.ln += uint64(n) + + if ml+len(in) < 32 { + xx.memIdx += int8(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in)) + return + } + + i, v1, v2, v3, v4 := 0, xx.v1, xx.v2, xx.v3, xx.v4 + if ml > 0 && ml+len(in) > 32 { + xx.memIdx += int8(copy(xx.mem[xx.memIdx:len(xx.mem):len(xx.mem)], in[:d:len(in)])) + in = in[d:len(in):len(in)] + + in := xx.mem[0:32:len(xx.mem)] + + v1 = round64(v1, u64(in[0:8:len(in)])) + v2 = round64(v2, u64(in[8:16:len(in)])) + v3 = round64(v3, u64(in[16:24:len(in)])) + v4 = round64(v4, u64(in[24:32:len(in)])) + + xx.memIdx = 0 + } + + for ; i < len(in)-31; i += 32 { + in := in[i : i+32 : len(in)] + v1 = round64(v1, u64(in[0:8:len(in)])) + v2 = round64(v2, u64(in[8:16:len(in)])) + v3 = round64(v3, u64(in[16:24:len(in)])) + v4 = round64(v4, u64(in[24:32:len(in)])) + } + + if len(in)-i != 0 { + xx.memIdx += int8(copy(xx.mem[xx.memIdx:], in[i:len(in):len(in)])) + } + + xx.v1, xx.v2, xx.v3, xx.v4 = v1, v2, v3, v4 + + return +} + +func (xx *XXHash64) Sum64() (h uint64) { + var i int + if xx.ln > 31 { + v1, v2, v3, v4 := xx.v1, xx.v2, xx.v3, xx.v4 + h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4) + + h = mergeRound64(h, v1) + h = mergeRound64(h, v2) + h = mergeRound64(h, v3) + h = mergeRound64(h, v4) + } else { + h = xx.seed + prime64x5 + } + + h += uint64(xx.ln) + if xx.memIdx > 0 { + in := xx.mem[:xx.memIdx] + for ; i < int(xx.memIdx)-7; i += 8 { + in := in[i : i+8 : len(in)] + k := u64(in[0:8:len(in)]) + k *= prime64x2 + k = rotl64_31(k) + k *= prime64x1 + h ^= k + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + for ; i < int(xx.memIdx)-3; i += 4 { + in := in[i : i+4 : len(in)] + h ^= uint64(u32(in[0:4:len(in)])) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + } + + for ; i < int(xx.memIdx); i++ { + h ^= uint64(in[i]) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + } + + return mix64(h) +} diff --git a/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go b/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go new file mode 100644 index 000000000..caacdc8b5 --- /dev/null +++ b/vendor/github.com/OneOfOne/xxhash/xxhash_unsafe.go @@ -0,0 +1,238 @@ +// +build !safe +// +build !appengine +// +build !ppc64le +// +build !mipsle +// +build !ppc64be +// +build !mipsbe + +package xxhash + +import ( + "reflect" + "unsafe" +) + +// Backend returns the current version of xxhash being used. +const Backend = "GoUnsafe" + +// ChecksumString32S returns the checksum of the input data, without creating a copy, with the specific seed. +func ChecksumString32S(s string, seed uint32) uint32 { + if len(s) == 0 { + return Checksum32S(nil, seed) + } + ss := (*reflect.StringHeader)(unsafe.Pointer(&s)) + return Checksum32S((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)], seed) +} + +func (xx *XXHash32) WriteString(s string) (int, error) { + if len(s) == 0 { + return 0, nil + } + + ss := (*reflect.StringHeader)(unsafe.Pointer(&s)) + return xx.Write((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)]) +} + +// ChecksumString64S returns the checksum of the input data, without creating a copy, with the specific seed. +func ChecksumString64S(s string, seed uint64) uint64 { + if len(s) == 0 { + return Checksum64S(nil, seed) + } + + ss := (*reflect.StringHeader)(unsafe.Pointer(&s)) + return Checksum64S((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s):len(s)], seed) +} + +func (xx *XXHash64) WriteString(s string) (int, error) { + if len(s) == 0 { + return 0, nil + } + ss := (*reflect.StringHeader)(unsafe.Pointer(&s)) + return xx.Write((*[maxInt32]byte)(unsafe.Pointer(ss.Data))[:len(s)]) +} + +func checksum64(in []byte, seed uint64) uint64 { + var ( + wordsLen = len(in) >> 3 + words = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen] + + h uint64 = prime64x5 + + v1, v2, v3, v4 = resetVs64(seed) + + i int + ) + + for ; i < len(words)-3; i += 4 { + words := (*[4]uint64)(unsafe.Pointer(&words[i])) + + v1 = round64(v1, words[0]) + v2 = round64(v2, words[1]) + v3 = round64(v3, words[2]) + v4 = round64(v4, words[3]) + } + + h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4) + + h = mergeRound64(h, v1) + h = mergeRound64(h, v2) + h = mergeRound64(h, v3) + h = mergeRound64(h, v4) + + h += uint64(len(in)) + + for _, k := range words[i:] { + h ^= round64(0, k) + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + if in = in[wordsLen<<3 : len(in) : len(in)]; len(in) > 3 { + words := (*[1]uint32)(unsafe.Pointer(&in[0])) + h ^= uint64(words[0]) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + + in = in[4:len(in):len(in)] + } + + for _, b := range in { + h ^= uint64(b) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + + return mix64(h) +} + +func checksum64Short(in []byte, seed uint64) uint64 { + var ( + h = seed + prime64x5 + uint64(len(in)) + i int + ) + + if len(in) > 7 { + var ( + wordsLen = len(in) >> 3 + words = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen] + ) + + for i := range words { + h ^= round64(0, words[i]) + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + i = wordsLen << 3 + } + + if in = in[i:len(in):len(in)]; len(in) > 3 { + words := (*[1]uint32)(unsafe.Pointer(&in[0])) + h ^= uint64(words[0]) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + + in = in[4:len(in):len(in)] + } + + for _, b := range in { + h ^= uint64(b) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + + return mix64(h) +} + +func (xx *XXHash64) Write(in []byte) (n int, err error) { + mem, idx := xx.mem[:], int(xx.memIdx) + + xx.ln, n = xx.ln+uint64(len(in)), len(in) + + if idx+len(in) < 32 { + xx.memIdx += int8(copy(mem[idx:len(mem):len(mem)], in)) + return + } + + var ( + v1, v2, v3, v4 = xx.v1, xx.v2, xx.v3, xx.v4 + + i int + ) + + if d := 32 - int(idx); d > 0 && int(idx)+len(in) > 31 { + copy(mem[idx:len(mem):len(mem)], in[:len(in):len(in)]) + + words := (*[4]uint64)(unsafe.Pointer(&mem[0])) + + v1 = round64(v1, words[0]) + v2 = round64(v2, words[1]) + v3 = round64(v3, words[2]) + v4 = round64(v4, words[3]) + + if in, xx.memIdx = in[d:len(in):len(in)], 0; len(in) == 0 { + goto RET + } + } + + for ; i < len(in)-31; i += 32 { + words := (*[4]uint64)(unsafe.Pointer(&in[i])) + + v1 = round64(v1, words[0]) + v2 = round64(v2, words[1]) + v3 = round64(v3, words[2]) + v4 = round64(v4, words[3]) + } + + if len(in)-i != 0 { + xx.memIdx += int8(copy(mem[xx.memIdx:len(mem):len(mem)], in[i:len(in):len(in)])) + } + +RET: + xx.v1, xx.v2, xx.v3, xx.v4 = v1, v2, v3, v4 + + return +} + +func (xx *XXHash64) Sum64() (h uint64) { + if seed := xx.seed; xx.ln > 31 { + v1, v2, v3, v4 := xx.v1, xx.v2, xx.v3, xx.v4 + h = rotl64_1(v1) + rotl64_7(v2) + rotl64_12(v3) + rotl64_18(v4) + + h = mergeRound64(h, v1) + h = mergeRound64(h, v2) + h = mergeRound64(h, v3) + h = mergeRound64(h, v4) + } else if seed == 0 { + h = prime64x5 + } else { + h = seed + prime64x5 + } + + h += uint64(xx.ln) + + if xx.memIdx == 0 { + return mix64(h) + } + + var ( + in = xx.mem[:xx.memIdx:xx.memIdx] + wordsLen = len(in) >> 3 + words = ((*[maxInt32 / 8]uint64)(unsafe.Pointer(&in[0])))[:wordsLen:wordsLen] + ) + + for _, k := range words { + h ^= round64(0, k) + h = rotl64_27(h)*prime64x1 + prime64x4 + } + + if in = in[wordsLen<<3 : len(in) : len(in)]; len(in) > 3 { + words := (*[1]uint32)(unsafe.Pointer(&in[0])) + + h ^= uint64(words[0]) * prime64x1 + h = rotl64_23(h)*prime64x2 + prime64x3 + + in = in[4:len(in):len(in)] + } + + for _, b := range in { + h ^= uint64(b) * prime64x5 + h = rotl64_11(h) * prime64x1 + } + + return mix64(h) +} diff --git a/vendor/github.com/cenkalti/backoff/.gitignore b/vendor/github.com/cenkalti/backoff/.gitignore deleted file mode 100644 index 00268614f..000000000 --- a/vendor/github.com/cenkalti/backoff/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/vendor/github.com/cenkalti/backoff/.travis.yml b/vendor/github.com/cenkalti/backoff/.travis.yml deleted file mode 100644 index 47a6a46ec..000000000 --- a/vendor/github.com/cenkalti/backoff/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go -go: - - 1.7 - - 1.x - - tip -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover -script: - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/cenkalti/backoff/LICENSE b/vendor/github.com/cenkalti/backoff/LICENSE deleted file mode 100644 index 89b817996..000000000 --- a/vendor/github.com/cenkalti/backoff/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Cenk Altı - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cenkalti/backoff/README.md b/vendor/github.com/cenkalti/backoff/README.md deleted file mode 100644 index 55ebc98fc..000000000 --- a/vendor/github.com/cenkalti/backoff/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls] - -This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client]. - -[Exponential backoff][exponential backoff wiki] -is an algorithm that uses feedback to multiplicatively decrease the rate of some process, -in order to gradually find an acceptable rate. -The retries exponentially increase and stop increasing when a certain threshold is met. - -## Usage - -See https://godoc.org/github.com/cenkalti/backoff#pkg-examples - -## Contributing - -* I would like to keep this library as small as possible. -* Please don't send a PR without opening an issue and discussing it first. -* If proposed change is not a common use case, I will probably not accept it. - -[godoc]: https://godoc.org/github.com/cenkalti/backoff -[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png -[travis]: https://travis-ci.org/cenkalti/backoff -[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master -[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master -[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master - -[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java -[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff - -[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_ diff --git a/vendor/github.com/cenkalti/backoff/backoff.go b/vendor/github.com/cenkalti/backoff/backoff.go deleted file mode 100644 index 3676ee405..000000000 --- a/vendor/github.com/cenkalti/backoff/backoff.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package backoff implements backoff algorithms for retrying operations. -// -// Use Retry function for retrying operations that may fail. -// If Retry does not meet your needs, -// copy/paste the function into your project and modify as you wish. -// -// There is also Ticker type similar to time.Ticker. -// You can use it if you need to work with channels. -// -// See Examples section below for usage examples. -package backoff - -import "time" - -// BackOff is a backoff policy for retrying an operation. -type BackOff interface { - // NextBackOff returns the duration to wait before retrying the operation, - // or backoff. Stop to indicate that no more retries should be made. - // - // Example usage: - // - // duration := backoff.NextBackOff(); - // if (duration == backoff.Stop) { - // // Do not retry operation. - // } else { - // // Sleep for duration and retry operation. - // } - // - NextBackOff() time.Duration - - // Reset to initial state. - Reset() -} - -// Stop indicates that no more retries should be made for use in NextBackOff(). -const Stop time.Duration = -1 - -// ZeroBackOff is a fixed backoff policy whose backoff time is always zero, -// meaning that the operation is retried immediately without waiting, indefinitely. -type ZeroBackOff struct{} - -func (b *ZeroBackOff) Reset() {} - -func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 } - -// StopBackOff is a fixed backoff policy that always returns backoff.Stop for -// NextBackOff(), meaning that the operation should never be retried. -type StopBackOff struct{} - -func (b *StopBackOff) Reset() {} - -func (b *StopBackOff) NextBackOff() time.Duration { return Stop } - -// ConstantBackOff is a backoff policy that always returns the same backoff delay. -// This is in contrast to an exponential backoff policy, -// which returns a delay that grows longer as you call NextBackOff() over and over again. -type ConstantBackOff struct { - Interval time.Duration -} - -func (b *ConstantBackOff) Reset() {} -func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval } - -func NewConstantBackOff(d time.Duration) *ConstantBackOff { - return &ConstantBackOff{Interval: d} -} diff --git a/vendor/github.com/cenkalti/backoff/context.go b/vendor/github.com/cenkalti/backoff/context.go deleted file mode 100644 index 7706faa2b..000000000 --- a/vendor/github.com/cenkalti/backoff/context.go +++ /dev/null @@ -1,63 +0,0 @@ -package backoff - -import ( - "context" - "time" -) - -// BackOffContext is a backoff policy that stops retrying after the context -// is canceled. -type BackOffContext interface { - BackOff - Context() context.Context -} - -type backOffContext struct { - BackOff - ctx context.Context -} - -// WithContext returns a BackOffContext with context ctx -// -// ctx must not be nil -func WithContext(b BackOff, ctx context.Context) BackOffContext { - if ctx == nil { - panic("nil context") - } - - if b, ok := b.(*backOffContext); ok { - return &backOffContext{ - BackOff: b.BackOff, - ctx: ctx, - } - } - - return &backOffContext{ - BackOff: b, - ctx: ctx, - } -} - -func ensureContext(b BackOff) BackOffContext { - if cb, ok := b.(BackOffContext); ok { - return cb - } - return WithContext(b, context.Background()) -} - -func (b *backOffContext) Context() context.Context { - return b.ctx -} - -func (b *backOffContext) NextBackOff() time.Duration { - select { - case <-b.ctx.Done(): - return Stop - default: - } - next := b.BackOff.NextBackOff() - if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next { - return Stop - } - return next -} diff --git a/vendor/github.com/cenkalti/backoff/exponential.go b/vendor/github.com/cenkalti/backoff/exponential.go deleted file mode 100644 index a031a6597..000000000 --- a/vendor/github.com/cenkalti/backoff/exponential.go +++ /dev/null @@ -1,153 +0,0 @@ -package backoff - -import ( - "math/rand" - "time" -) - -/* -ExponentialBackOff is a backoff implementation that increases the backoff -period for each retry attempt using a randomization function that grows exponentially. - -NextBackOff() is calculated using the following formula: - - randomized interval = - RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor]) - -In other words NextBackOff() will range between the randomization factor -percentage below and above the retry interval. - -For example, given the following parameters: - - RetryInterval = 2 - RandomizationFactor = 0.5 - Multiplier = 2 - -the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, -multiplied by the exponential, that is, between 2 and 6 seconds. - -Note: MaxInterval caps the RetryInterval and not the randomized interval. - -If the time elapsed since an ExponentialBackOff instance is created goes past the -MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop. - -The elapsed time can be reset by calling Reset(). - -Example: Given the following default arguments, for 10 tries the sequence will be, -and assuming we go over the MaxElapsedTime on the 10th try: - - Request # RetryInterval (seconds) Randomized Interval (seconds) - - 1 0.5 [0.25, 0.75] - 2 0.75 [0.375, 1.125] - 3 1.125 [0.562, 1.687] - 4 1.687 [0.8435, 2.53] - 5 2.53 [1.265, 3.795] - 6 3.795 [1.897, 5.692] - 7 5.692 [2.846, 8.538] - 8 8.538 [4.269, 12.807] - 9 12.807 [6.403, 19.210] - 10 19.210 backoff.Stop - -Note: Implementation is not thread-safe. -*/ -type ExponentialBackOff struct { - InitialInterval time.Duration - RandomizationFactor float64 - Multiplier float64 - MaxInterval time.Duration - // After MaxElapsedTime the ExponentialBackOff stops. - // It never stops if MaxElapsedTime == 0. - MaxElapsedTime time.Duration - Clock Clock - - currentInterval time.Duration - startTime time.Time -} - -// Clock is an interface that returns current time for BackOff. -type Clock interface { - Now() time.Time -} - -// Default values for ExponentialBackOff. -const ( - DefaultInitialInterval = 500 * time.Millisecond - DefaultRandomizationFactor = 0.5 - DefaultMultiplier = 1.5 - DefaultMaxInterval = 60 * time.Second - DefaultMaxElapsedTime = 15 * time.Minute -) - -// NewExponentialBackOff creates an instance of ExponentialBackOff using default values. -func NewExponentialBackOff() *ExponentialBackOff { - b := &ExponentialBackOff{ - InitialInterval: DefaultInitialInterval, - RandomizationFactor: DefaultRandomizationFactor, - Multiplier: DefaultMultiplier, - MaxInterval: DefaultMaxInterval, - MaxElapsedTime: DefaultMaxElapsedTime, - Clock: SystemClock, - } - b.Reset() - return b -} - -type systemClock struct{} - -func (t systemClock) Now() time.Time { - return time.Now() -} - -// SystemClock implements Clock interface that uses time.Now(). -var SystemClock = systemClock{} - -// Reset the interval back to the initial retry interval and restarts the timer. -func (b *ExponentialBackOff) Reset() { - b.currentInterval = b.InitialInterval - b.startTime = b.Clock.Now() -} - -// NextBackOff calculates the next backoff interval using the formula: -// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval) -func (b *ExponentialBackOff) NextBackOff() time.Duration { - // Make sure we have not gone over the maximum elapsed time. - if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime { - return Stop - } - defer b.incrementCurrentInterval() - return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval) -} - -// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance -// is created and is reset when Reset() is called. -// -// The elapsed time is computed using time.Now().UnixNano(). It is -// safe to call even while the backoff policy is used by a running -// ticker. -func (b *ExponentialBackOff) GetElapsedTime() time.Duration { - return b.Clock.Now().Sub(b.startTime) -} - -// Increments the current interval by multiplying it with the multiplier. -func (b *ExponentialBackOff) incrementCurrentInterval() { - // Check for overflow, if overflow is detected set the current interval to the max interval. - if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier { - b.currentInterval = b.MaxInterval - } else { - b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier) - } -} - -// Returns a random value from the following interval: -// [randomizationFactor * currentInterval, randomizationFactor * currentInterval]. -func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { - var delta = randomizationFactor * float64(currentInterval) - var minInterval = float64(currentInterval) - delta - var maxInterval = float64(currentInterval) + delta - - // Get a random value from the range [minInterval, maxInterval]. - // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then - // we want a 33% chance for selecting either 1, 2 or 3. - return time.Duration(minInterval + (random * (maxInterval - minInterval + 1))) -} diff --git a/vendor/github.com/cenkalti/backoff/retry.go b/vendor/github.com/cenkalti/backoff/retry.go deleted file mode 100644 index e936a506f..000000000 --- a/vendor/github.com/cenkalti/backoff/retry.go +++ /dev/null @@ -1,82 +0,0 @@ -package backoff - -import "time" - -// An Operation is executing by Retry() or RetryNotify(). -// The operation will be retried using a backoff policy if it returns an error. -type Operation func() error - -// Notify is a notify-on-error function. It receives an operation error and -// backoff delay if the operation failed (with an error). -// -// NOTE that if the backoff policy stated to stop retrying, -// the notify function isn't called. -type Notify func(error, time.Duration) - -// Retry the operation o until it does not return error or BackOff stops. -// o is guaranteed to be run at least once. -// -// If o returns a *PermanentError, the operation is not retried, and the -// wrapped error is returned. -// -// Retry sleeps the goroutine for the duration returned by BackOff after a -// failed operation returns. -func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) } - -// RetryNotify calls notify function with the error and wait duration -// for each failed attempt before sleep. -func RetryNotify(operation Operation, b BackOff, notify Notify) error { - var err error - var next time.Duration - var t *time.Timer - - cb := ensureContext(b) - - b.Reset() - for { - if err = operation(); err == nil { - return nil - } - - if permanent, ok := err.(*PermanentError); ok { - return permanent.Err - } - - if next = cb.NextBackOff(); next == Stop { - return err - } - - if notify != nil { - notify(err, next) - } - - if t == nil { - t = time.NewTimer(next) - defer t.Stop() - } else { - t.Reset(next) - } - - select { - case <-cb.Context().Done(): - return err - case <-t.C: - } - } -} - -// PermanentError signals that the operation should not be retried. -type PermanentError struct { - Err error -} - -func (e *PermanentError) Error() string { - return e.Err.Error() -} - -// Permanent wraps the given err in a *PermanentError. -func Permanent(err error) *PermanentError { - return &PermanentError{ - Err: err, - } -} diff --git a/vendor/github.com/cenkalti/backoff/ticker.go b/vendor/github.com/cenkalti/backoff/ticker.go deleted file mode 100644 index e41084b0e..000000000 --- a/vendor/github.com/cenkalti/backoff/ticker.go +++ /dev/null @@ -1,82 +0,0 @@ -package backoff - -import ( - "sync" - "time" -) - -// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. -// -// Ticks will continue to arrive when the previous operation is still running, -// so operations that take a while to fail could run in quick succession. -type Ticker struct { - C <-chan time.Time - c chan time.Time - b BackOffContext - stop chan struct{} - stopOnce sync.Once -} - -// NewTicker returns a new Ticker containing a channel that will send -// the time at times specified by the BackOff argument. Ticker is -// guaranteed to tick at least once. The channel is closed when Stop -// method is called or BackOff stops. It is not safe to manipulate the -// provided backoff policy (notably calling NextBackOff or Reset) -// while the ticker is running. -func NewTicker(b BackOff) *Ticker { - c := make(chan time.Time) - t := &Ticker{ - C: c, - c: c, - b: ensureContext(b), - stop: make(chan struct{}), - } - t.b.Reset() - go t.run() - return t -} - -// Stop turns off a ticker. After Stop, no more ticks will be sent. -func (t *Ticker) Stop() { - t.stopOnce.Do(func() { close(t.stop) }) -} - -func (t *Ticker) run() { - c := t.c - defer close(c) - - // Ticker is guaranteed to tick at least once. - afterC := t.send(time.Now()) - - for { - if afterC == nil { - return - } - - select { - case tick := <-afterC: - afterC = t.send(tick) - case <-t.stop: - t.c = nil // Prevent future ticks from being sent to the channel. - return - case <-t.b.Context().Done(): - return - } - } -} - -func (t *Ticker) send(tick time.Time) <-chan time.Time { - select { - case t.c <- tick: - case <-t.stop: - return nil - } - - next := t.b.NextBackOff() - if next == Stop { - t.Stop() - return nil - } - - return time.After(next) -} diff --git a/vendor/github.com/cenkalti/backoff/tries.go b/vendor/github.com/cenkalti/backoff/tries.go deleted file mode 100644 index cfeefd9b7..000000000 --- a/vendor/github.com/cenkalti/backoff/tries.go +++ /dev/null @@ -1,35 +0,0 @@ -package backoff - -import "time" - -/* -WithMaxRetries creates a wrapper around another BackOff, which will -return Stop if NextBackOff() has been called too many times since -the last time Reset() was called - -Note: Implementation is not thread-safe. -*/ -func WithMaxRetries(b BackOff, max uint64) BackOff { - return &backOffTries{delegate: b, maxTries: max} -} - -type backOffTries struct { - delegate BackOff - maxTries uint64 - numTries uint64 -} - -func (b *backOffTries) NextBackOff() time.Duration { - if b.maxTries > 0 { - if b.maxTries <= b.numTries { - return Stop - } - b.numTries++ - } - return b.delegate.NextBackOff() -} - -func (b *backOffTries) Reset() { - b.numTries = 0 - b.delegate.Reset() -} diff --git a/vendor/github.com/cheekybits/genny/LICENSE b/vendor/github.com/cheekybits/genny/LICENSE deleted file mode 100644 index 519d7f227..000000000 --- a/vendor/github.com/cheekybits/genny/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 cheekybits - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/vendor/github.com/cheekybits/genny/generic/doc.go b/vendor/github.com/cheekybits/genny/generic/doc.go deleted file mode 100644 index 3bd6c869c..000000000 --- a/vendor/github.com/cheekybits/genny/generic/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package generic contains the generic marker types. -package generic diff --git a/vendor/github.com/cheekybits/genny/generic/generic.go b/vendor/github.com/cheekybits/genny/generic/generic.go deleted file mode 100644 index 04a2306cb..000000000 --- a/vendor/github.com/cheekybits/genny/generic/generic.go +++ /dev/null @@ -1,13 +0,0 @@ -package generic - -// Type is the placeholder type that indicates a generic value. -// When genny is executed, variables of this type will be replaced with -// references to the specific types. -// var GenericType generic.Type -type Type interface{} - -// Number is the placehoder type that indiccates a generic numerical value. -// When genny is executed, variables of this type will be replaced with -// references to the specific types. -// var GenericType generic.Number -type Number float64 diff --git a/vendor/github.com/coreos/etcd/pkg/srv/srv.go b/vendor/github.com/coreos/etcd/pkg/srv/srv.go deleted file mode 100644 index 600061ce8..000000000 --- a/vendor/github.com/coreos/etcd/pkg/srv/srv.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2015 The etcd 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 srv looks up DNS SRV records. -package srv - -import ( - "fmt" - "net" - "net/url" - "strings" - - "github.com/coreos/etcd/pkg/types" -) - -var ( - // indirection for testing - lookupSRV = net.LookupSRV // net.DefaultResolver.LookupSRV when ctxs don't conflict - resolveTCPAddr = net.ResolveTCPAddr -) - -// GetCluster gets the cluster information via DNS discovery. -// Also sees each entry as a separate instance. -func GetCluster(service, name, dns string, apurls types.URLs) ([]string, error) { - tempName := int(0) - tcp2ap := make(map[string]url.URL) - - // First, resolve the apurls - for _, url := range apurls { - tcpAddr, err := resolveTCPAddr("tcp", url.Host) - if err != nil { - return nil, err - } - tcp2ap[tcpAddr.String()] = url - } - - stringParts := []string{} - updateNodeMap := func(service, scheme string) error { - _, addrs, err := lookupSRV(service, "tcp", dns) - if err != nil { - return err - } - for _, srv := range addrs { - port := fmt.Sprintf("%d", srv.Port) - host := net.JoinHostPort(srv.Target, port) - tcpAddr, terr := resolveTCPAddr("tcp", host) - if terr != nil { - err = terr - continue - } - n := "" - url, ok := tcp2ap[tcpAddr.String()] - if ok { - n = name - } - if n == "" { - n = fmt.Sprintf("%d", tempName) - tempName++ - } - // SRV records have a trailing dot but URL shouldn't. - shortHost := strings.TrimSuffix(srv.Target, ".") - urlHost := net.JoinHostPort(shortHost, port) - if ok && url.Scheme != scheme { - err = fmt.Errorf("bootstrap at %s from DNS for %s has scheme mismatch with expected peer %s", scheme+"://"+urlHost, service, url.String()) - } else { - stringParts = append(stringParts, fmt.Sprintf("%s=%s://%s", n, scheme, urlHost)) - } - } - if len(stringParts) == 0 { - return err - } - return nil - } - - failCount := 0 - err := updateNodeMap(service+"-ssl", "https") - srvErr := make([]string, 2) - if err != nil { - srvErr[0] = fmt.Sprintf("error querying DNS SRV records for _%s-ssl %s", service, err) - failCount++ - } - err = updateNodeMap(service, "http") - if err != nil { - srvErr[1] = fmt.Sprintf("error querying DNS SRV records for _%s %s", service, err) - failCount++ - } - if failCount == 2 { - return nil, fmt.Errorf("srv: too many errors querying DNS SRV records (%q, %q)", srvErr[0], srvErr[1]) - } - return stringParts, nil -} - -type SRVClients struct { - Endpoints []string - SRVs []*net.SRV -} - -// GetClient looks up the client endpoints for a service and domain. -func GetClient(service, domain string) (*SRVClients, error) { - var urls []*url.URL - var srvs []*net.SRV - - updateURLs := func(service, scheme string) error { - _, addrs, err := lookupSRV(service, "tcp", domain) - if err != nil { - return err - } - for _, srv := range addrs { - urls = append(urls, &url.URL{ - Scheme: scheme, - Host: net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)), - }) - } - srvs = append(srvs, addrs...) - return nil - } - - errHTTPS := updateURLs(service+"-ssl", "https") - errHTTP := updateURLs(service, "http") - - if errHTTPS != nil && errHTTP != nil { - return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP) - } - - endpoints := make([]string, len(urls)) - for i := range urls { - endpoints[i] = urls[i].String() - } - return &SRVClients{Endpoints: endpoints, SRVs: srvs}, nil -} diff --git a/vendor/github.com/dustin/go-humanize/.travis.yml b/vendor/github.com/dustin/go-humanize/.travis.yml deleted file mode 100644 index ba95cdd15..000000000 --- a/vendor/github.com/dustin/go-humanize/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -sudo: false -language: go -go: - - 1.3.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - master -matrix: - allow_failures: - - go: master - fast_finish: true -install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . - - go test -v -race ./... diff --git a/vendor/github.com/dustin/go-humanize/LICENSE b/vendor/github.com/dustin/go-humanize/LICENSE deleted file mode 100644 index 8d9a94a90..000000000 --- a/vendor/github.com/dustin/go-humanize/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2005-2008 Dustin Sallings - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - diff --git a/vendor/github.com/dustin/go-humanize/README.markdown b/vendor/github.com/dustin/go-humanize/README.markdown deleted file mode 100644 index 91b4ae564..000000000 --- a/vendor/github.com/dustin/go-humanize/README.markdown +++ /dev/null @@ -1,124 +0,0 @@ -# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) - -Just a few functions for helping humanize times and sizes. - -`go get` it as `github.com/dustin/go-humanize`, import it as -`"github.com/dustin/go-humanize"`, use it as `humanize`. - -See [godoc](https://godoc.org/github.com/dustin/go-humanize) for -complete documentation. - -## Sizes - -This lets you take numbers like `82854982` and convert them to useful -strings like, `83 MB` or `79 MiB` (whichever you prefer). - -Example: - -```go -fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. -``` - -## Times - -This lets you take a `time.Time` and spit it out in relative terms. -For example, `12 seconds ago` or `3 days from now`. - -Example: - -```go -fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. -``` - -Thanks to Kyle Lemons for the time implementation from an IRC -conversation one day. It's pretty neat. - -## Ordinals - -From a [mailing list discussion][odisc] where a user wanted to be able -to label ordinals. - - 0 -> 0th - 1 -> 1st - 2 -> 2nd - 3 -> 3rd - 4 -> 4th - [...] - -Example: - -```go -fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. -``` - -## Commas - -Want to shove commas into numbers? Be my guest. - - 0 -> 0 - 100 -> 100 - 1000 -> 1,000 - 1000000000 -> 1,000,000,000 - -100000 -> -100,000 - -Example: - -```go -fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. -``` - -## Ftoa - -Nicer float64 formatter that removes trailing zeros. - -```go -fmt.Printf("%f", 2.24) // 2.240000 -fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 -fmt.Printf("%f", 2.0) // 2.000000 -fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 -``` - -## SI notation - -Format numbers with [SI notation][sinotation]. - -Example: - -```go -humanize.SI(0.00000000223, "M") // 2.23 nM -``` - -## English-specific functions - -The following functions are in the `humanize/english` subpackage. - -### Plurals - -Simple English pluralization - -```go -english.PluralWord(1, "object", "") // object -english.PluralWord(42, "object", "") // objects -english.PluralWord(2, "bus", "") // buses -english.PluralWord(99, "locus", "loci") // loci - -english.Plural(1, "object", "") // 1 object -english.Plural(42, "object", "") // 42 objects -english.Plural(2, "bus", "") // 2 buses -english.Plural(99, "locus", "loci") // 99 loci -``` - -### Word series - -Format comma-separated words lists with conjuctions: - -```go -english.WordSeries([]string{"foo"}, "and") // foo -english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar -english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz - -english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz -``` - -[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion -[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix diff --git a/vendor/github.com/dustin/go-humanize/big.go b/vendor/github.com/dustin/go-humanize/big.go deleted file mode 100644 index f49dc337d..000000000 --- a/vendor/github.com/dustin/go-humanize/big.go +++ /dev/null @@ -1,31 +0,0 @@ -package humanize - -import ( - "math/big" -) - -// order of magnitude (to a max order) -func oomm(n, b *big.Int, maxmag int) (float64, int) { - mag := 0 - m := &big.Int{} - for n.Cmp(b) >= 0 { - n.DivMod(n, b, m) - mag++ - if mag == maxmag && maxmag >= 0 { - break - } - } - return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag -} - -// total order of magnitude -// (same as above, but with no upper limit) -func oom(n, b *big.Int) (float64, int) { - mag := 0 - m := &big.Int{} - for n.Cmp(b) >= 0 { - n.DivMod(n, b, m) - mag++ - } - return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag -} diff --git a/vendor/github.com/dustin/go-humanize/bigbytes.go b/vendor/github.com/dustin/go-humanize/bigbytes.go deleted file mode 100644 index 1a2bf6172..000000000 --- a/vendor/github.com/dustin/go-humanize/bigbytes.go +++ /dev/null @@ -1,173 +0,0 @@ -package humanize - -import ( - "fmt" - "math/big" - "strings" - "unicode" -) - -var ( - bigIECExp = big.NewInt(1024) - - // BigByte is one byte in bit.Ints - BigByte = big.NewInt(1) - // BigKiByte is 1,024 bytes in bit.Ints - BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) - // BigMiByte is 1,024 k bytes in bit.Ints - BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) - // BigGiByte is 1,024 m bytes in bit.Ints - BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) - // BigTiByte is 1,024 g bytes in bit.Ints - BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) - // BigPiByte is 1,024 t bytes in bit.Ints - BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) - // BigEiByte is 1,024 p bytes in bit.Ints - BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) - // BigZiByte is 1,024 e bytes in bit.Ints - BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) - // BigYiByte is 1,024 z bytes in bit.Ints - BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) -) - -var ( - bigSIExp = big.NewInt(1000) - - // BigSIByte is one SI byte in big.Ints - BigSIByte = big.NewInt(1) - // BigKByte is 1,000 SI bytes in big.Ints - BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) - // BigMByte is 1,000 SI k bytes in big.Ints - BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) - // BigGByte is 1,000 SI m bytes in big.Ints - BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) - // BigTByte is 1,000 SI g bytes in big.Ints - BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) - // BigPByte is 1,000 SI t bytes in big.Ints - BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) - // BigEByte is 1,000 SI p bytes in big.Ints - BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) - // BigZByte is 1,000 SI e bytes in big.Ints - BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) - // BigYByte is 1,000 SI z bytes in big.Ints - BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) -) - -var bigBytesSizeTable = map[string]*big.Int{ - "b": BigByte, - "kib": BigKiByte, - "kb": BigKByte, - "mib": BigMiByte, - "mb": BigMByte, - "gib": BigGiByte, - "gb": BigGByte, - "tib": BigTiByte, - "tb": BigTByte, - "pib": BigPiByte, - "pb": BigPByte, - "eib": BigEiByte, - "eb": BigEByte, - "zib": BigZiByte, - "zb": BigZByte, - "yib": BigYiByte, - "yb": BigYByte, - // Without suffix - "": BigByte, - "ki": BigKiByte, - "k": BigKByte, - "mi": BigMiByte, - "m": BigMByte, - "gi": BigGiByte, - "g": BigGByte, - "ti": BigTiByte, - "t": BigTByte, - "pi": BigPiByte, - "p": BigPByte, - "ei": BigEiByte, - "e": BigEByte, - "z": BigZByte, - "zi": BigZiByte, - "y": BigYByte, - "yi": BigYiByte, -} - -var ten = big.NewInt(10) - -func humanateBigBytes(s, base *big.Int, sizes []string) string { - if s.Cmp(ten) < 0 { - return fmt.Sprintf("%d B", s) - } - c := (&big.Int{}).Set(s) - val, mag := oomm(c, base, len(sizes)-1) - suffix := sizes[mag] - f := "%.0f %s" - if val < 10 { - f = "%.1f %s" - } - - return fmt.Sprintf(f, val, suffix) - -} - -// BigBytes produces a human readable representation of an SI size. -// -// See also: ParseBigBytes. -// -// BigBytes(82854982) -> 83 MB -func BigBytes(s *big.Int) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} - return humanateBigBytes(s, bigSIExp, sizes) -} - -// BigIBytes produces a human readable representation of an IEC size. -// -// See also: ParseBigBytes. -// -// BigIBytes(82854982) -> 79 MiB -func BigIBytes(s *big.Int) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} - return humanateBigBytes(s, bigIECExp, sizes) -} - -// ParseBigBytes parses a string representation of bytes into the number -// of bytes it represents. -// -// See also: BigBytes, BigIBytes. -// -// ParseBigBytes("42 MB") -> 42000000, nil -// ParseBigBytes("42 mib") -> 44040192, nil -func ParseBigBytes(s string) (*big.Int, error) { - lastDigit := 0 - hasComma := false - for _, r := range s { - if !(unicode.IsDigit(r) || r == '.' || r == ',') { - break - } - if r == ',' { - hasComma = true - } - lastDigit++ - } - - num := s[:lastDigit] - if hasComma { - num = strings.Replace(num, ",", "", -1) - } - - val := &big.Rat{} - _, err := fmt.Sscanf(num, "%f", val) - if err != nil { - return nil, err - } - - extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) - if m, ok := bigBytesSizeTable[extra]; ok { - mv := (&big.Rat{}).SetInt(m) - val.Mul(val, mv) - rv := &big.Int{} - rv.Div(val.Num(), val.Denom()) - return rv, nil - } - - return nil, fmt.Errorf("unhandled size name: %v", extra) -} diff --git a/vendor/github.com/dustin/go-humanize/bytes.go b/vendor/github.com/dustin/go-humanize/bytes.go deleted file mode 100644 index 0b498f488..000000000 --- a/vendor/github.com/dustin/go-humanize/bytes.go +++ /dev/null @@ -1,143 +0,0 @@ -package humanize - -import ( - "fmt" - "math" - "strconv" - "strings" - "unicode" -) - -// IEC Sizes. -// kibis of bits -const ( - Byte = 1 << (iota * 10) - KiByte - MiByte - GiByte - TiByte - PiByte - EiByte -) - -// SI Sizes. -const ( - IByte = 1 - KByte = IByte * 1000 - MByte = KByte * 1000 - GByte = MByte * 1000 - TByte = GByte * 1000 - PByte = TByte * 1000 - EByte = PByte * 1000 -) - -var bytesSizeTable = map[string]uint64{ - "b": Byte, - "kib": KiByte, - "kb": KByte, - "mib": MiByte, - "mb": MByte, - "gib": GiByte, - "gb": GByte, - "tib": TiByte, - "tb": TByte, - "pib": PiByte, - "pb": PByte, - "eib": EiByte, - "eb": EByte, - // Without suffix - "": Byte, - "ki": KiByte, - "k": KByte, - "mi": MiByte, - "m": MByte, - "gi": GiByte, - "g": GByte, - "ti": TiByte, - "t": TByte, - "pi": PiByte, - "p": PByte, - "ei": EiByte, - "e": EByte, -} - -func logn(n, b float64) float64 { - return math.Log(n) / math.Log(b) -} - -func humanateBytes(s uint64, base float64, sizes []string) string { - if s < 10 { - return fmt.Sprintf("%d B", s) - } - e := math.Floor(logn(float64(s), base)) - suffix := sizes[int(e)] - val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 - f := "%.0f %s" - if val < 10 { - f = "%.1f %s" - } - - return fmt.Sprintf(f, val, suffix) -} - -// Bytes produces a human readable representation of an SI size. -// -// See also: ParseBytes. -// -// Bytes(82854982) -> 83 MB -func Bytes(s uint64) string { - sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} - return humanateBytes(s, 1000, sizes) -} - -// IBytes produces a human readable representation of an IEC size. -// -// See also: ParseBytes. -// -// IBytes(82854982) -> 79 MiB -func IBytes(s uint64) string { - sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} - return humanateBytes(s, 1024, sizes) -} - -// ParseBytes parses a string representation of bytes into the number -// of bytes it represents. -// -// See Also: Bytes, IBytes. -// -// ParseBytes("42 MB") -> 42000000, nil -// ParseBytes("42 mib") -> 44040192, nil -func ParseBytes(s string) (uint64, error) { - lastDigit := 0 - hasComma := false - for _, r := range s { - if !(unicode.IsDigit(r) || r == '.' || r == ',') { - break - } - if r == ',' { - hasComma = true - } - lastDigit++ - } - - num := s[:lastDigit] - if hasComma { - num = strings.Replace(num, ",", "", -1) - } - - f, err := strconv.ParseFloat(num, 64) - if err != nil { - return 0, err - } - - extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) - if m, ok := bytesSizeTable[extra]; ok { - f *= float64(m) - if f >= math.MaxUint64 { - return 0, fmt.Errorf("too large: %v", s) - } - return uint64(f), nil - } - - return 0, fmt.Errorf("unhandled size name: %v", extra) -} diff --git a/vendor/github.com/dustin/go-humanize/comma.go b/vendor/github.com/dustin/go-humanize/comma.go deleted file mode 100644 index 520ae3e57..000000000 --- a/vendor/github.com/dustin/go-humanize/comma.go +++ /dev/null @@ -1,116 +0,0 @@ -package humanize - -import ( - "bytes" - "math" - "math/big" - "strconv" - "strings" -) - -// Comma produces a string form of the given number in base 10 with -// commas after every three orders of magnitude. -// -// e.g. Comma(834142) -> 834,142 -func Comma(v int64) string { - sign := "" - - // Min int64 can't be negated to a usable value, so it has to be special cased. - if v == math.MinInt64 { - return "-9,223,372,036,854,775,808" - } - - if v < 0 { - sign = "-" - v = 0 - v - } - - parts := []string{"", "", "", "", "", "", ""} - j := len(parts) - 1 - - for v > 999 { - parts[j] = strconv.FormatInt(v%1000, 10) - switch len(parts[j]) { - case 2: - parts[j] = "0" + parts[j] - case 1: - parts[j] = "00" + parts[j] - } - v = v / 1000 - j-- - } - parts[j] = strconv.Itoa(int(v)) - return sign + strings.Join(parts[j:], ",") -} - -// Commaf produces a string form of the given number in base 10 with -// commas after every three orders of magnitude. -// -// e.g. Commaf(834142.32) -> 834,142.32 -func Commaf(v float64) string { - buf := &bytes.Buffer{} - if v < 0 { - buf.Write([]byte{'-'}) - v = 0 - v - } - - comma := []byte{','} - - parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") - pos := 0 - if len(parts[0])%3 != 0 { - pos += len(parts[0]) % 3 - buf.WriteString(parts[0][:pos]) - buf.Write(comma) - } - for ; pos < len(parts[0]); pos += 3 { - buf.WriteString(parts[0][pos : pos+3]) - buf.Write(comma) - } - buf.Truncate(buf.Len() - 1) - - if len(parts) > 1 { - buf.Write([]byte{'.'}) - buf.WriteString(parts[1]) - } - return buf.String() -} - -// CommafWithDigits works like the Commaf but limits the resulting -// string to the given number of decimal places. -// -// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 -func CommafWithDigits(f float64, decimals int) string { - return stripTrailingDigits(Commaf(f), decimals) -} - -// BigComma produces a string form of the given big.Int in base 10 -// with commas after every three orders of magnitude. -func BigComma(b *big.Int) string { - sign := "" - if b.Sign() < 0 { - sign = "-" - b.Abs(b) - } - - athousand := big.NewInt(1000) - c := (&big.Int{}).Set(b) - _, m := oom(c, athousand) - parts := make([]string, m+1) - j := len(parts) - 1 - - mod := &big.Int{} - for b.Cmp(athousand) >= 0 { - b.DivMod(b, athousand, mod) - parts[j] = strconv.FormatInt(mod.Int64(), 10) - switch len(parts[j]) { - case 2: - parts[j] = "0" + parts[j] - case 1: - parts[j] = "00" + parts[j] - } - j-- - } - parts[j] = strconv.Itoa(int(b.Int64())) - return sign + strings.Join(parts[j:], ",") -} diff --git a/vendor/github.com/dustin/go-humanize/commaf.go b/vendor/github.com/dustin/go-humanize/commaf.go deleted file mode 100644 index 620690dec..000000000 --- a/vendor/github.com/dustin/go-humanize/commaf.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build go1.6 - -package humanize - -import ( - "bytes" - "math/big" - "strings" -) - -// BigCommaf produces a string form of the given big.Float in base 10 -// with commas after every three orders of magnitude. -func BigCommaf(v *big.Float) string { - buf := &bytes.Buffer{} - if v.Sign() < 0 { - buf.Write([]byte{'-'}) - v.Abs(v) - } - - comma := []byte{','} - - parts := strings.Split(v.Text('f', -1), ".") - pos := 0 - if len(parts[0])%3 != 0 { - pos += len(parts[0]) % 3 - buf.WriteString(parts[0][:pos]) - buf.Write(comma) - } - for ; pos < len(parts[0]); pos += 3 { - buf.WriteString(parts[0][pos : pos+3]) - buf.Write(comma) - } - buf.Truncate(buf.Len() - 1) - - if len(parts) > 1 { - buf.Write([]byte{'.'}) - buf.WriteString(parts[1]) - } - return buf.String() -} diff --git a/vendor/github.com/dustin/go-humanize/ftoa.go b/vendor/github.com/dustin/go-humanize/ftoa.go deleted file mode 100644 index 1c62b640d..000000000 --- a/vendor/github.com/dustin/go-humanize/ftoa.go +++ /dev/null @@ -1,46 +0,0 @@ -package humanize - -import ( - "strconv" - "strings" -) - -func stripTrailingZeros(s string) string { - offset := len(s) - 1 - for offset > 0 { - if s[offset] == '.' { - offset-- - break - } - if s[offset] != '0' { - break - } - offset-- - } - return s[:offset+1] -} - -func stripTrailingDigits(s string, digits int) string { - if i := strings.Index(s, "."); i >= 0 { - if digits <= 0 { - return s[:i] - } - i++ - if i+digits >= len(s) { - return s - } - return s[:i+digits] - } - return s -} - -// Ftoa converts a float to a string with no trailing zeros. -func Ftoa(num float64) string { - return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) -} - -// FtoaWithDigits converts a float to a string but limits the resulting string -// to the given number of decimal places, and no trailing zeros. -func FtoaWithDigits(num float64, digits int) string { - return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) -} diff --git a/vendor/github.com/dustin/go-humanize/humanize.go b/vendor/github.com/dustin/go-humanize/humanize.go deleted file mode 100644 index a2c2da31e..000000000 --- a/vendor/github.com/dustin/go-humanize/humanize.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package humanize converts boring ugly numbers to human-friendly strings and back. - -Durations can be turned into strings such as "3 days ago", numbers -representing sizes like 82854982 into useful strings like, "83 MB" or -"79 MiB" (whichever you prefer). -*/ -package humanize diff --git a/vendor/github.com/dustin/go-humanize/number.go b/vendor/github.com/dustin/go-humanize/number.go deleted file mode 100644 index dec618659..000000000 --- a/vendor/github.com/dustin/go-humanize/number.go +++ /dev/null @@ -1,192 +0,0 @@ -package humanize - -/* -Slightly adapted from the source to fit go-humanize. - -Author: https://github.com/gorhill -Source: https://gist.github.com/gorhill/5285193 - -*/ - -import ( - "math" - "strconv" -) - -var ( - renderFloatPrecisionMultipliers = [...]float64{ - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - } - - renderFloatPrecisionRounders = [...]float64{ - 0.5, - 0.05, - 0.005, - 0.0005, - 0.00005, - 0.000005, - 0.0000005, - 0.00000005, - 0.000000005, - 0.0000000005, - } -) - -// FormatFloat produces a formatted number as string based on the following user-specified criteria: -// * thousands separator -// * decimal separator -// * decimal precision -// -// Usage: s := RenderFloat(format, n) -// The format parameter tells how to render the number n. -// -// See examples: http://play.golang.org/p/LXc1Ddm1lJ -// -// Examples of format strings, given n = 12345.6789: -// "#,###.##" => "12,345.67" -// "#,###." => "12,345" -// "#,###" => "12345,678" -// "#\u202F###,##" => "12 345,68" -// "#.###,###### => 12.345,678900 -// "" (aka default format) => 12,345.67 -// -// The highest precision allowed is 9 digits after the decimal symbol. -// There is also a version for integer number, FormatInteger(), -// which is convenient for calls within template. -func FormatFloat(format string, n float64) string { - // Special cases: - // NaN = "NaN" - // +Inf = "+Infinity" - // -Inf = "-Infinity" - if math.IsNaN(n) { - return "NaN" - } - if n > math.MaxFloat64 { - return "Infinity" - } - if n < -math.MaxFloat64 { - return "-Infinity" - } - - // default format - precision := 2 - decimalStr := "." - thousandStr := "," - positiveStr := "" - negativeStr := "-" - - if len(format) > 0 { - format := []rune(format) - - // If there is an explicit format directive, - // then default values are these: - precision = 9 - thousandStr = "" - - // collect indices of meaningful formatting directives - formatIndx := []int{} - for i, char := range format { - if char != '#' && char != '0' { - formatIndx = append(formatIndx, i) - } - } - - if len(formatIndx) > 0 { - // Directive at index 0: - // Must be a '+' - // Raise an error if not the case - // index: 0123456789 - // +0.000,000 - // +000,000.0 - // +0000.00 - // +0000 - if formatIndx[0] == 0 { - if format[formatIndx[0]] != '+' { - panic("RenderFloat(): invalid positive sign directive") - } - positiveStr = "+" - formatIndx = formatIndx[1:] - } - - // Two directives: - // First is thousands separator - // Raise an error if not followed by 3-digit - // 0123456789 - // 0.000,000 - // 000,000.00 - if len(formatIndx) == 2 { - if (formatIndx[1] - formatIndx[0]) != 4 { - panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") - } - thousandStr = string(format[formatIndx[0]]) - formatIndx = formatIndx[1:] - } - - // One directive: - // Directive is decimal separator - // The number of digit-specifier following the separator indicates wanted precision - // 0123456789 - // 0.00 - // 000,0000 - if len(formatIndx) == 1 { - decimalStr = string(format[formatIndx[0]]) - precision = len(format) - formatIndx[0] - 1 - } - } - } - - // generate sign part - var signStr string - if n >= 0.000000001 { - signStr = positiveStr - } else if n <= -0.000000001 { - signStr = negativeStr - n = -n - } else { - signStr = "" - n = 0.0 - } - - // split number into integer and fractional parts - intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) - - // generate integer part string - intStr := strconv.FormatInt(int64(intf), 10) - - // add thousand separator if required - if len(thousandStr) > 0 { - for i := len(intStr); i > 3; { - i -= 3 - intStr = intStr[:i] + thousandStr + intStr[i:] - } - } - - // no fractional part, we can leave now - if precision == 0 { - return signStr + intStr - } - - // generate fractional part - fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) - // may need padding - if len(fracStr) < precision { - fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr - } - - return signStr + intStr + decimalStr + fracStr -} - -// FormatInteger produces a formatted number as string. -// See FormatFloat. -func FormatInteger(format string, n int) string { - return FormatFloat(format, float64(n)) -} diff --git a/vendor/github.com/dustin/go-humanize/ordinals.go b/vendor/github.com/dustin/go-humanize/ordinals.go deleted file mode 100644 index 43d88a861..000000000 --- a/vendor/github.com/dustin/go-humanize/ordinals.go +++ /dev/null @@ -1,25 +0,0 @@ -package humanize - -import "strconv" - -// Ordinal gives you the input number in a rank/ordinal format. -// -// Ordinal(3) -> 3rd -func Ordinal(x int) string { - suffix := "th" - switch x % 10 { - case 1: - if x%100 != 11 { - suffix = "st" - } - case 2: - if x%100 != 12 { - suffix = "nd" - } - case 3: - if x%100 != 13 { - suffix = "rd" - } - } - return strconv.Itoa(x) + suffix -} diff --git a/vendor/github.com/dustin/go-humanize/si.go b/vendor/github.com/dustin/go-humanize/si.go deleted file mode 100644 index ae659e0e4..000000000 --- a/vendor/github.com/dustin/go-humanize/si.go +++ /dev/null @@ -1,123 +0,0 @@ -package humanize - -import ( - "errors" - "math" - "regexp" - "strconv" -) - -var siPrefixTable = map[float64]string{ - -24: "y", // yocto - -21: "z", // zepto - -18: "a", // atto - -15: "f", // femto - -12: "p", // pico - -9: "n", // nano - -6: "µ", // micro - -3: "m", // milli - 0: "", - 3: "k", // kilo - 6: "M", // mega - 9: "G", // giga - 12: "T", // tera - 15: "P", // peta - 18: "E", // exa - 21: "Z", // zetta - 24: "Y", // yotta -} - -var revSIPrefixTable = revfmap(siPrefixTable) - -// revfmap reverses the map and precomputes the power multiplier -func revfmap(in map[float64]string) map[string]float64 { - rv := map[string]float64{} - for k, v := range in { - rv[v] = math.Pow(10, k) - } - return rv -} - -var riParseRegex *regexp.Regexp - -func init() { - ri := `^([\-0-9.]+)\s?([` - for _, v := range siPrefixTable { - ri += v - } - ri += `]?)(.*)` - - riParseRegex = regexp.MustCompile(ri) -} - -// ComputeSI finds the most appropriate SI prefix for the given number -// and returns the prefix along with the value adjusted to be within -// that prefix. -// -// See also: SI, ParseSI. -// -// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") -func ComputeSI(input float64) (float64, string) { - if input == 0 { - return 0, "" - } - mag := math.Abs(input) - exponent := math.Floor(logn(mag, 10)) - exponent = math.Floor(exponent/3) * 3 - - value := mag / math.Pow(10, exponent) - - // Handle special case where value is exactly 1000.0 - // Should return 1 M instead of 1000 k - if value == 1000.0 { - exponent += 3 - value = mag / math.Pow(10, exponent) - } - - value = math.Copysign(value, input) - - prefix := siPrefixTable[exponent] - return value, prefix -} - -// SI returns a string with default formatting. -// -// SI uses Ftoa to format float value, removing trailing zeros. -// -// See also: ComputeSI, ParseSI. -// -// e.g. SI(1000000, "B") -> 1 MB -// e.g. SI(2.2345e-12, "F") -> 2.2345 pF -func SI(input float64, unit string) string { - value, prefix := ComputeSI(input) - return Ftoa(value) + " " + prefix + unit -} - -// SIWithDigits works like SI but limits the resulting string to the -// given number of decimal places. -// -// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB -// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF -func SIWithDigits(input float64, decimals int, unit string) string { - value, prefix := ComputeSI(input) - return FtoaWithDigits(value, decimals) + " " + prefix + unit -} - -var errInvalid = errors.New("invalid input") - -// ParseSI parses an SI string back into the number and unit. -// -// See also: SI, ComputeSI. -// -// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) -func ParseSI(input string) (float64, string, error) { - found := riParseRegex.FindStringSubmatch(input) - if len(found) != 4 { - return 0, "", errInvalid - } - mag := revSIPrefixTable[found[2]] - unit := found[3] - - base, err := strconv.ParseFloat(found[1], 64) - return base * mag, unit, err -} diff --git a/vendor/github.com/dustin/go-humanize/times.go b/vendor/github.com/dustin/go-humanize/times.go deleted file mode 100644 index dd3fbf5ef..000000000 --- a/vendor/github.com/dustin/go-humanize/times.go +++ /dev/null @@ -1,117 +0,0 @@ -package humanize - -import ( - "fmt" - "math" - "sort" - "time" -) - -// Seconds-based time units -const ( - Day = 24 * time.Hour - Week = 7 * Day - Month = 30 * Day - Year = 12 * Month - LongTime = 37 * Year -) - -// Time formats a time into a relative string. -// -// Time(someT) -> "3 weeks ago" -func Time(then time.Time) string { - return RelTime(then, time.Now(), "ago", "from now") -} - -// A RelTimeMagnitude struct contains a relative time point at which -// the relative format of time will switch to a new format string. A -// slice of these in ascending order by their "D" field is passed to -// CustomRelTime to format durations. -// -// The Format field is a string that may contain a "%s" which will be -// replaced with the appropriate signed label (e.g. "ago" or "from -// now") and a "%d" that will be replaced by the quantity. -// -// The DivBy field is the amount of time the time difference must be -// divided by in order to display correctly. -// -// e.g. if D is 2*time.Minute and you want to display "%d minutes %s" -// DivBy should be time.Minute so whatever the duration is will be -// expressed in minutes. -type RelTimeMagnitude struct { - D time.Duration - Format string - DivBy time.Duration -} - -var defaultMagnitudes = []RelTimeMagnitude{ - {time.Second, "now", time.Second}, - {2 * time.Second, "1 second %s", 1}, - {time.Minute, "%d seconds %s", time.Second}, - {2 * time.Minute, "1 minute %s", 1}, - {time.Hour, "%d minutes %s", time.Minute}, - {2 * time.Hour, "1 hour %s", 1}, - {Day, "%d hours %s", time.Hour}, - {2 * Day, "1 day %s", 1}, - {Week, "%d days %s", Day}, - {2 * Week, "1 week %s", 1}, - {Month, "%d weeks %s", Week}, - {2 * Month, "1 month %s", 1}, - {Year, "%d months %s", Month}, - {18 * Month, "1 year %s", 1}, - {2 * Year, "2 years %s", 1}, - {LongTime, "%d years %s", Year}, - {math.MaxInt64, "a long while %s", 1}, -} - -// RelTime formats a time into a relative string. -// -// It takes two times and two labels. In addition to the generic time -// delta string (e.g. 5 minutes), the labels are used applied so that -// the label corresponding to the smaller time is applied. -// -// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" -func RelTime(a, b time.Time, albl, blbl string) string { - return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) -} - -// CustomRelTime formats a time into a relative string. -// -// It takes two times two labels and a table of relative time formats. -// In addition to the generic time delta string (e.g. 5 minutes), the -// labels are used applied so that the label corresponding to the -// smaller time is applied. -func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { - lbl := albl - diff := b.Sub(a) - - if a.After(b) { - lbl = blbl - diff = a.Sub(b) - } - - n := sort.Search(len(magnitudes), func(i int) bool { - return magnitudes[i].D > diff - }) - - if n >= len(magnitudes) { - n = len(magnitudes) - 1 - } - mag := magnitudes[n] - args := []interface{}{} - escaped := false - for _, ch := range mag.Format { - if escaped { - switch ch { - case 's': - args = append(args, lbl) - case 'd': - args = append(args, diff/mag.DivBy) - } - escaped = false - } else { - escaped = ch == '%' - } - } - return fmt.Sprintf(mag.Format, args...) -} diff --git a/vendor/github.com/flynn/go-shlex/Makefile b/vendor/github.com/flynn/go-shlex/Makefile deleted file mode 100644 index 038d9a489..000000000 --- a/vendor/github.com/flynn/go-shlex/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2011 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. - -include $(GOROOT)/src/Make.inc - -TARG=shlex -GOFILES=\ - shlex.go\ - -include $(GOROOT)/src/Make.pkg diff --git a/vendor/github.com/flynn/go-shlex/README.md b/vendor/github.com/flynn/go-shlex/README.md deleted file mode 100644 index c86bcc066..000000000 --- a/vendor/github.com/flynn/go-shlex/README.md +++ /dev/null @@ -1,2 +0,0 @@ -go-shlex is a simple lexer for go that supports shell-style quoting, -commenting, and escaping. diff --git a/vendor/github.com/flynn/go-shlex/shlex.go b/vendor/github.com/flynn/go-shlex/shlex.go deleted file mode 100644 index 7aeace801..000000000 --- a/vendor/github.com/flynn/go-shlex/shlex.go +++ /dev/null @@ -1,457 +0,0 @@ -/* -Copyright 2012 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 shlex - -/* -Package shlex implements a simple lexer which splits input in to tokens using -shell-style rules for quoting and commenting. -*/ -import ( - "bufio" - "errors" - "fmt" - "io" - "strings" -) - -/* -A TokenType is a top-level token; a word, space, comment, unknown. -*/ -type TokenType int - -/* -A RuneTokenType is the type of a UTF-8 character; a character, quote, space, escape. -*/ -type RuneTokenType int - -type lexerState int - -type Token struct { - tokenType TokenType - value string -} - -/* -Two tokens are equal if both their types and values are equal. A nil token can -never equal another token. -*/ -func (a *Token) Equal(b *Token) bool { - if a == nil || b == nil { - return false - } - if a.tokenType != b.tokenType { - return false - } - return a.value == b.value -} - -const ( - RUNE_CHAR string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._-,/@$*()+=><:;&^%~|!?[]{}" - RUNE_SPACE string = " \t\r\n" - RUNE_ESCAPING_QUOTE string = "\"" - RUNE_NONESCAPING_QUOTE string = "'" - RUNE_ESCAPE = "\\" - RUNE_COMMENT = "#" - - RUNETOKEN_UNKNOWN RuneTokenType = 0 - RUNETOKEN_CHAR RuneTokenType = 1 - RUNETOKEN_SPACE RuneTokenType = 2 - RUNETOKEN_ESCAPING_QUOTE RuneTokenType = 3 - RUNETOKEN_NONESCAPING_QUOTE RuneTokenType = 4 - RUNETOKEN_ESCAPE RuneTokenType = 5 - RUNETOKEN_COMMENT RuneTokenType = 6 - RUNETOKEN_EOF RuneTokenType = 7 - - TOKEN_UNKNOWN TokenType = 0 - TOKEN_WORD TokenType = 1 - TOKEN_SPACE TokenType = 2 - TOKEN_COMMENT TokenType = 3 - - STATE_START lexerState = 0 - STATE_INWORD lexerState = 1 - STATE_ESCAPING lexerState = 2 - STATE_ESCAPING_QUOTED lexerState = 3 - STATE_QUOTED_ESCAPING lexerState = 4 - STATE_QUOTED lexerState = 5 - STATE_COMMENT lexerState = 6 - - INITIAL_TOKEN_CAPACITY int = 100 -) - -/* -A type for classifying characters. This allows for different sorts of -classifiers - those accepting extended non-ascii chars, or strict posix -compatibility, for example. -*/ -type TokenClassifier struct { - typeMap map[int32]RuneTokenType -} - -func addRuneClass(typeMap *map[int32]RuneTokenType, runes string, tokenType RuneTokenType) { - for _, rune := range runes { - (*typeMap)[int32(rune)] = tokenType - } -} - -/* -Create a new classifier for basic ASCII characters. -*/ -func NewDefaultClassifier() *TokenClassifier { - typeMap := map[int32]RuneTokenType{} - addRuneClass(&typeMap, RUNE_CHAR, RUNETOKEN_CHAR) - addRuneClass(&typeMap, RUNE_SPACE, RUNETOKEN_SPACE) - addRuneClass(&typeMap, RUNE_ESCAPING_QUOTE, RUNETOKEN_ESCAPING_QUOTE) - addRuneClass(&typeMap, RUNE_NONESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE) - addRuneClass(&typeMap, RUNE_ESCAPE, RUNETOKEN_ESCAPE) - addRuneClass(&typeMap, RUNE_COMMENT, RUNETOKEN_COMMENT) - return &TokenClassifier{ - typeMap: typeMap} -} - -func (classifier *TokenClassifier) ClassifyRune(rune int32) RuneTokenType { - return classifier.typeMap[rune] -} - -/* -A type for turning an input stream in to a sequence of strings. Whitespace and -comments are skipped. -*/ -type Lexer struct { - tokenizer *Tokenizer -} - -/* -Create a new lexer. -*/ -func NewLexer(r io.Reader) (*Lexer, error) { - - tokenizer, err := NewTokenizer(r) - if err != nil { - return nil, err - } - lexer := &Lexer{tokenizer: tokenizer} - return lexer, nil -} - -/* -Return the next word, and an error value. If there are no more words, the error -will be io.EOF. -*/ -func (l *Lexer) NextWord() (string, error) { - var token *Token - var err error - for { - token, err = l.tokenizer.NextToken() - if err != nil { - return "", err - } - switch token.tokenType { - case TOKEN_WORD: - { - return token.value, nil - } - case TOKEN_COMMENT: - { - // skip comments - } - default: - { - panic(fmt.Sprintf("Unknown token type: %v", token.tokenType)) - } - } - } - return "", io.EOF -} - -/* -A type for turning an input stream in to a sequence of typed tokens. -*/ -type Tokenizer struct { - input *bufio.Reader - classifier *TokenClassifier -} - -/* -Create a new tokenizer. -*/ -func NewTokenizer(r io.Reader) (*Tokenizer, error) { - input := bufio.NewReader(r) - classifier := NewDefaultClassifier() - tokenizer := &Tokenizer{ - input: input, - classifier: classifier} - return tokenizer, nil -} - -/* -Scan the stream for the next token. - -This uses an internal state machine. It will panic if it encounters a character -which it does not know how to handle. -*/ -func (t *Tokenizer) scanStream() (*Token, error) { - state := STATE_START - var tokenType TokenType - value := make([]int32, 0, INITIAL_TOKEN_CAPACITY) - var ( - nextRune int32 - nextRuneType RuneTokenType - err error - ) -SCAN: - for { - nextRune, _, err = t.input.ReadRune() - nextRuneType = t.classifier.ClassifyRune(nextRune) - if err != nil { - if err == io.EOF { - nextRuneType = RUNETOKEN_EOF - err = nil - } else { - return nil, err - } - } - switch state { - case STATE_START: // no runes read yet - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - return nil, io.EOF - } - case RUNETOKEN_CHAR: - { - tokenType = TOKEN_WORD - value = append(value, nextRune) - state = STATE_INWORD - } - case RUNETOKEN_SPACE: - { - } - case RUNETOKEN_ESCAPING_QUOTE: - { - tokenType = TOKEN_WORD - state = STATE_QUOTED_ESCAPING - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - tokenType = TOKEN_WORD - state = STATE_QUOTED - } - case RUNETOKEN_ESCAPE: - { - tokenType = TOKEN_WORD - state = STATE_ESCAPING - } - case RUNETOKEN_COMMENT: - { - tokenType = TOKEN_COMMENT - state = STATE_COMMENT - } - default: - { - return nil, errors.New(fmt.Sprintf("Unknown rune: %v", nextRune)) - } - } - } - case STATE_INWORD: // in a regular word - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_SPACE: - { - t.input.UnreadRune() - break SCAN - } - case RUNETOKEN_ESCAPING_QUOTE: - { - state = STATE_QUOTED_ESCAPING - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - state = STATE_QUOTED - } - case RUNETOKEN_ESCAPE: - { - state = STATE_ESCAPING - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_ESCAPING: // the next rune after an escape character - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found after escape character") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - state = STATE_INWORD - value = append(value, nextRune) - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_ESCAPING_QUOTED: // the next rune after an escape character, in double quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found after escape character") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - state = STATE_QUOTED_ESCAPING - value = append(value, nextRune) - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_QUOTED_ESCAPING: // in escaping double quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found when expecting closing quote.") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_NONESCAPING_QUOTE, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_ESCAPING_QUOTE: - { - state = STATE_INWORD - } - case RUNETOKEN_ESCAPE: - { - state = STATE_ESCAPING_QUOTED - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_QUOTED: // in non-escaping single quotes - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - err = errors.New("EOF found when expecting closing quote.") - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_SPACE, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT: - { - value = append(value, nextRune) - } - case RUNETOKEN_NONESCAPING_QUOTE: - { - state = STATE_INWORD - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - case STATE_COMMENT: - { - switch nextRuneType { - case RUNETOKEN_EOF: - { - break SCAN - } - case RUNETOKEN_CHAR, RUNETOKEN_UNKNOWN, RUNETOKEN_ESCAPING_QUOTE, RUNETOKEN_ESCAPE, RUNETOKEN_COMMENT, RUNETOKEN_NONESCAPING_QUOTE: - { - value = append(value, nextRune) - } - case RUNETOKEN_SPACE: - { - if nextRune == '\n' { - state = STATE_START - break SCAN - } else { - value = append(value, nextRune) - } - } - default: - { - return nil, errors.New(fmt.Sprintf("Uknown rune: %v", nextRune)) - } - } - } - default: - { - panic(fmt.Sprintf("Unexpected state: %v", state)) - } - } - } - token := &Token{ - tokenType: tokenType, - value: string(value)} - return token, err -} - -/* -Return the next token in the stream, and an error value. If there are no more -tokens available, the error value will be io.EOF. -*/ -func (t *Tokenizer) NextToken() (*Token, error) { - return t.scanStream() -} - -/* -Split a string in to a slice of strings, based upon shell-style rules for -quoting, escaping, and spaces. -*/ -func Split(s string) ([]string, error) { - l, err := NewLexer(strings.NewReader(s)) - if err != nil { - return nil, err - } - subStrings := []string{} - for { - word, err := l.NextWord() - if err != nil { - if err == io.EOF { - return subStrings, nil - } - return subStrings, err - } - subStrings = append(subStrings, word) - } - return subStrings, nil -} diff --git a/vendor/github.com/go-acme/lego/LICENSE b/vendor/github.com/go-acme/lego/LICENSE deleted file mode 100644 index 270cba089..000000000 --- a/vendor/github.com/go-acme/lego/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015-2017 Sebastian Erhart - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/go-acme/lego/acme/api/account.go b/vendor/github.com/go-acme/lego/acme/api/account.go deleted file mode 100644 index fd2331424..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/account.go +++ /dev/null @@ -1,69 +0,0 @@ -package api - -import ( - "encoding/base64" - "errors" - "fmt" - - "github.com/go-acme/lego/acme" -) - -type AccountService service - -// New Creates a new account. -func (a *AccountService) New(req acme.Account) (acme.ExtendedAccount, error) { - var account acme.Account - resp, err := a.core.post(a.core.GetDirectory().NewAccountURL, req, &account) - location := getLocation(resp) - - if len(location) > 0 { - a.core.jws.SetKid(location) - } - - if err != nil { - return acme.ExtendedAccount{Location: location}, err - } - - return acme.ExtendedAccount{Account: account, Location: location}, nil -} - -// NewEAB Creates a new account with an External Account Binding. -func (a *AccountService) NewEAB(accMsg acme.Account, kid string, hmacEncoded string) (acme.ExtendedAccount, error) { - hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded) - if err != nil { - return acme.ExtendedAccount{}, fmt.Errorf("acme: could not decode hmac key: %v", err) - } - - eabJWS, err := a.core.signEABContent(a.core.GetDirectory().NewAccountURL, kid, hmac) - if err != nil { - return acme.ExtendedAccount{}, fmt.Errorf("acme: error signing eab content: %v", err) - } - accMsg.ExternalAccountBinding = eabJWS - - return a.New(accMsg) -} - -// Get Retrieves an account. -func (a *AccountService) Get(accountURL string) (acme.Account, error) { - if len(accountURL) == 0 { - return acme.Account{}, errors.New("account[get]: empty URL") - } - - var account acme.Account - _, err := a.core.post(accountURL, acme.Account{}, &account) - if err != nil { - return acme.Account{}, err - } - return account, nil -} - -// Deactivate Deactivates an account. -func (a *AccountService) Deactivate(accountURL string) error { - if len(accountURL) == 0 { - return errors.New("account[deactivate]: empty URL") - } - - req := acme.Account{Status: acme.StatusDeactivated} - _, err := a.core.post(accountURL, req, nil) - return err -} diff --git a/vendor/github.com/go-acme/lego/acme/api/api.go b/vendor/github.com/go-acme/lego/acme/api/api.go deleted file mode 100644 index 912e7c5ae..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/api.go +++ /dev/null @@ -1,166 +0,0 @@ -package api - -import ( - "bytes" - "context" - "crypto" - "encoding/json" - "errors" - "fmt" - "net/http" - "time" - - "github.com/cenkalti/backoff" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api/internal/nonces" - "github.com/go-acme/lego/acme/api/internal/secure" - "github.com/go-acme/lego/acme/api/internal/sender" - "github.com/go-acme/lego/log" -) - -// Core ACME/LE core API. -type Core struct { - doer *sender.Doer - nonceManager *nonces.Manager - jws *secure.JWS - directory acme.Directory - HTTPClient *http.Client - - common service // Reuse a single struct instead of allocating one for each service on the heap. - Accounts *AccountService - Authorizations *AuthorizationService - Certificates *CertificateService - Challenges *ChallengeService - Orders *OrderService -} - -// New Creates a new Core. -func New(httpClient *http.Client, userAgent string, caDirURL, kid string, privateKey crypto.PrivateKey) (*Core, error) { - doer := sender.NewDoer(httpClient, userAgent) - - dir, err := getDirectory(doer, caDirURL) - if err != nil { - return nil, err - } - - nonceManager := nonces.NewManager(doer, dir.NewNonceURL) - - jws := secure.NewJWS(privateKey, kid, nonceManager) - - c := &Core{doer: doer, nonceManager: nonceManager, jws: jws, directory: dir, HTTPClient: httpClient} - - c.common.core = c - c.Accounts = (*AccountService)(&c.common) - c.Authorizations = (*AuthorizationService)(&c.common) - c.Certificates = (*CertificateService)(&c.common) - c.Challenges = (*ChallengeService)(&c.common) - c.Orders = (*OrderService)(&c.common) - - return c, nil -} - -// post performs an HTTP POST request and parses the response body as JSON, -// into the provided respBody object. -func (a *Core) post(uri string, reqBody, response interface{}) (*http.Response, error) { - content, err := json.Marshal(reqBody) - if err != nil { - return nil, errors.New("failed to marshal message") - } - - return a.retrievablePost(uri, content, response) -} - -// postAsGet performs an HTTP POST ("POST-as-GET") request. -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.3 -func (a *Core) postAsGet(uri string, response interface{}) (*http.Response, error) { - return a.retrievablePost(uri, []byte{}, response) -} - -func (a *Core) retrievablePost(uri string, content []byte, response interface{}) (*http.Response, error) { - // during tests, allow to support ~90% of bad nonce with a minimum of attempts. - bo := backoff.NewExponentialBackOff() - bo.InitialInterval = 200 * time.Millisecond - bo.MaxInterval = 5 * time.Second - bo.MaxElapsedTime = 20 * time.Second - - ctx, cancel := context.WithCancel(context.Background()) - - var resp *http.Response - operation := func() error { - var err error - resp, err = a.signedPost(uri, content, response) - if err != nil { - switch err.(type) { - // Retry if the nonce was invalidated - case *acme.NonceError: - log.Infof("nonce error retry: %s", err) - return err - default: - cancel() - return err - } - } - - return nil - } - - err := backoff.Retry(operation, backoff.WithContext(bo, ctx)) - if err != nil { - return nil, err - } - - return resp, nil -} - -func (a *Core) signedPost(uri string, content []byte, response interface{}) (*http.Response, error) { - signedContent, err := a.jws.SignContent(uri, content) - if err != nil { - return nil, fmt.Errorf("failed to post JWS message -> failed to sign content -> %v", err) - } - - signedBody := bytes.NewBuffer([]byte(signedContent.FullSerialize())) - - resp, err := a.doer.Post(uri, signedBody, "application/jose+json", response) - - // nonceErr is ignored to keep the root error. - nonce, nonceErr := nonces.GetFromResponse(resp) - if nonceErr == nil { - a.nonceManager.Push(nonce) - } - - return resp, err -} - -func (a *Core) signEABContent(newAccountURL, kid string, hmac []byte) ([]byte, error) { - eabJWS, err := a.jws.SignEABContent(newAccountURL, kid, hmac) - if err != nil { - return nil, err - } - - return []byte(eabJWS.FullSerialize()), nil -} - -// GetKeyAuthorization Gets the key authorization -func (a *Core) GetKeyAuthorization(token string) (string, error) { - return a.jws.GetKeyAuthorization(token) -} - -func (a *Core) GetDirectory() acme.Directory { - return a.directory -} - -func getDirectory(do *sender.Doer, caDirURL string) (acme.Directory, error) { - var dir acme.Directory - if _, err := do.Get(caDirURL, &dir); err != nil { - return dir, fmt.Errorf("get directory at '%s': %v", caDirURL, err) - } - - if dir.NewAccountURL == "" { - return dir, errors.New("directory missing new registration URL") - } - if dir.NewOrderURL == "" { - return dir, errors.New("directory missing new order URL") - } - - return dir, nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/authorization.go b/vendor/github.com/go-acme/lego/acme/api/authorization.go deleted file mode 100644 index a59fa0d25..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/authorization.go +++ /dev/null @@ -1,34 +0,0 @@ -package api - -import ( - "errors" - - "github.com/go-acme/lego/acme" -) - -type AuthorizationService service - -// Get Gets an authorization. -func (c *AuthorizationService) Get(authzURL string) (acme.Authorization, error) { - if len(authzURL) == 0 { - return acme.Authorization{}, errors.New("authorization[get]: empty URL") - } - - var authz acme.Authorization - _, err := c.core.postAsGet(authzURL, &authz) - if err != nil { - return acme.Authorization{}, err - } - return authz, nil -} - -// Deactivate Deactivates an authorization. -func (c *AuthorizationService) Deactivate(authzURL string) error { - if len(authzURL) == 0 { - return errors.New("authorization[deactivate]: empty URL") - } - - var disabledAuth acme.Authorization - _, err := c.core.post(authzURL, acme.Authorization{Status: acme.StatusDeactivated}, &disabledAuth) - return err -} diff --git a/vendor/github.com/go-acme/lego/acme/api/certificate.go b/vendor/github.com/go-acme/lego/acme/api/certificate.go deleted file mode 100644 index 65904e76d..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/certificate.go +++ /dev/null @@ -1,99 +0,0 @@ -package api - -import ( - "crypto/x509" - "encoding/pem" - "errors" - "io/ioutil" - "net/http" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/log" -) - -// maxBodySize is the maximum size of body that we will read. -const maxBodySize = 1024 * 1024 - -type CertificateService service - -// Get Returns the certificate and the issuer certificate. -// 'bundle' is only applied if the issuer is provided by the 'up' link. -func (c *CertificateService) Get(certURL string, bundle bool) ([]byte, []byte, error) { - cert, up, err := c.get(certURL) - if err != nil { - return nil, nil, err - } - - // Get issuerCert from bundled response from Let's Encrypt - // See https://community.letsencrypt.org/t/acme-v2-no-up-link-in-response/64962 - _, issuer := pem.Decode(cert) - if issuer != nil { - return cert, issuer, nil - } - - issuer, err = c.getIssuerFromLink(up) - if err != nil { - // If we fail to acquire the issuer cert, return the issued certificate - do not fail. - log.Warnf("acme: Could not bundle issuer certificate [%s]: %v", certURL, err) - } else if len(issuer) > 0 { - // If bundle is true, we want to return a certificate bundle. - // To do this, we append the issuer cert to the issued cert. - if bundle { - cert = append(cert, issuer...) - } - } - - return cert, issuer, nil -} - -// Revoke Revokes a certificate. -func (c *CertificateService) Revoke(req acme.RevokeCertMessage) error { - _, err := c.core.post(c.core.GetDirectory().RevokeCertURL, req, nil) - return err -} - -// get Returns the certificate and the "up" link. -func (c *CertificateService) get(certURL string) ([]byte, string, error) { - if len(certURL) == 0 { - return nil, "", errors.New("certificate[get]: empty URL") - } - - resp, err := c.core.postAsGet(certURL, nil) - if err != nil { - return nil, "", err - } - - cert, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) - if err != nil { - return nil, "", err - } - - // The issuer certificate link may be supplied via an "up" link - // in the response headers of a new certificate. - // See https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4.2 - up := getLink(resp.Header, "up") - - return cert, up, err -} - -// getIssuerFromLink requests the issuer certificate -func (c *CertificateService) getIssuerFromLink(up string) ([]byte, error) { - if len(up) == 0 { - return nil, nil - } - - log.Infof("acme: Requesting issuer cert from %s", up) - - cert, _, err := c.get(up) - if err != nil { - return nil, err - } - - _, err = x509.ParseCertificate(cert) - if err != nil { - return nil, err - } - - return certcrypto.PEMEncode(certcrypto.DERCertificateBytes(cert)), nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/challenge.go b/vendor/github.com/go-acme/lego/acme/api/challenge.go deleted file mode 100644 index f4e8dbeeb..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/challenge.go +++ /dev/null @@ -1,45 +0,0 @@ -package api - -import ( - "errors" - - "github.com/go-acme/lego/acme" -) - -type ChallengeService service - -// New Creates a challenge. -func (c *ChallengeService) New(chlgURL string) (acme.ExtendedChallenge, error) { - if len(chlgURL) == 0 { - return acme.ExtendedChallenge{}, errors.New("challenge[new]: empty URL") - } - - // Challenge initiation is done by sending a JWS payload containing the trivial JSON object `{}`. - // We use an empty struct instance as the postJSON payload here to achieve this result. - var chlng acme.ExtendedChallenge - resp, err := c.core.post(chlgURL, struct{}{}, &chlng) - if err != nil { - return acme.ExtendedChallenge{}, err - } - - chlng.AuthorizationURL = getLink(resp.Header, "up") - chlng.RetryAfter = getRetryAfter(resp) - return chlng, nil -} - -// Get Gets a challenge. -func (c *ChallengeService) Get(chlgURL string) (acme.ExtendedChallenge, error) { - if len(chlgURL) == 0 { - return acme.ExtendedChallenge{}, errors.New("challenge[get]: empty URL") - } - - var chlng acme.ExtendedChallenge - resp, err := c.core.postAsGet(chlgURL, &chlng) - if err != nil { - return acme.ExtendedChallenge{}, err - } - - chlng.AuthorizationURL = getLink(resp.Header, "up") - chlng.RetryAfter = getRetryAfter(resp) - return chlng, nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go b/vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go deleted file mode 100644 index c08f6d493..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/internal/nonces/nonce_manager.go +++ /dev/null @@ -1,78 +0,0 @@ -package nonces - -import ( - "errors" - "fmt" - "net/http" - "sync" - - "github.com/go-acme/lego/acme/api/internal/sender" -) - -// Manager Manages nonces. -type Manager struct { - do *sender.Doer - nonceURL string - nonces []string - sync.Mutex -} - -// NewManager Creates a new Manager. -func NewManager(do *sender.Doer, nonceURL string) *Manager { - return &Manager{ - do: do, - nonceURL: nonceURL, - } -} - -// Pop Pops a nonce. -func (n *Manager) Pop() (string, bool) { - n.Lock() - defer n.Unlock() - - if len(n.nonces) == 0 { - return "", false - } - - nonce := n.nonces[len(n.nonces)-1] - n.nonces = n.nonces[:len(n.nonces)-1] - return nonce, true -} - -// Push Pushes a nonce. -func (n *Manager) Push(nonce string) { - n.Lock() - defer n.Unlock() - n.nonces = append(n.nonces, nonce) -} - -// Nonce implement jose.NonceSource -func (n *Manager) Nonce() (string, error) { - if nonce, ok := n.Pop(); ok { - return nonce, nil - } - return n.getNonce() -} - -func (n *Manager) getNonce() (string, error) { - resp, err := n.do.Head(n.nonceURL) - if err != nil { - return "", fmt.Errorf("failed to get nonce from HTTP HEAD -> %v", err) - } - - return GetFromResponse(resp) -} - -// GetFromResponse Extracts a nonce from a HTTP response. -func GetFromResponse(resp *http.Response) (string, error) { - if resp == nil { - return "", errors.New("nil response") - } - - nonce := resp.Header.Get("Replay-Nonce") - if nonce == "" { - return "", fmt.Errorf("server did not respond with a proper nonce header") - } - - return nonce, nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go b/vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go deleted file mode 100644 index 213aeda0a..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/internal/secure/jws.go +++ /dev/null @@ -1,130 +0,0 @@ -package secure - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "encoding/base64" - "fmt" - - "github.com/go-acme/lego/acme/api/internal/nonces" - jose "gopkg.in/square/go-jose.v2" -) - -// JWS Represents a JWS. -type JWS struct { - privKey crypto.PrivateKey - kid string // Key identifier - nonces *nonces.Manager -} - -// NewJWS Create a new JWS. -func NewJWS(privateKey crypto.PrivateKey, kid string, nonceManager *nonces.Manager) *JWS { - return &JWS{ - privKey: privateKey, - nonces: nonceManager, - kid: kid, - } -} - -// SetKid Sets a key identifier. -func (j *JWS) SetKid(kid string) { - j.kid = kid -} - -// SignContent Signs a content with the JWS. -func (j *JWS) SignContent(url string, content []byte) (*jose.JSONWebSignature, error) { - var alg jose.SignatureAlgorithm - switch k := j.privKey.(type) { - case *rsa.PrivateKey: - alg = jose.RS256 - case *ecdsa.PrivateKey: - if k.Curve == elliptic.P256() { - alg = jose.ES256 - } else if k.Curve == elliptic.P384() { - alg = jose.ES384 - } - } - - signKey := jose.SigningKey{ - Algorithm: alg, - Key: jose.JSONWebKey{Key: j.privKey, KeyID: j.kid}, - } - - options := jose.SignerOptions{ - NonceSource: j.nonces, - ExtraHeaders: map[jose.HeaderKey]interface{}{ - "url": url, - }, - } - - if j.kid == "" { - options.EmbedJWK = true - } - - signer, err := jose.NewSigner(signKey, &options) - if err != nil { - return nil, fmt.Errorf("failed to create jose signer -> %v", err) - } - - signed, err := signer.Sign(content) - if err != nil { - return nil, fmt.Errorf("failed to sign content -> %v", err) - } - return signed, nil -} - -// SignEABContent Signs an external account binding content with the JWS. -func (j *JWS) SignEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignature, error) { - jwk := jose.JSONWebKey{Key: j.privKey} - jwkJSON, err := jwk.Public().MarshalJSON() - if err != nil { - return nil, fmt.Errorf("acme: error encoding eab jwk key: %v", err) - } - - signer, err := jose.NewSigner( - jose.SigningKey{Algorithm: jose.HS256, Key: hmac}, - &jose.SignerOptions{ - EmbedJWK: false, - ExtraHeaders: map[jose.HeaderKey]interface{}{ - "kid": kid, - "url": url, - }, - }, - ) - if err != nil { - return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %v", err) - } - - signed, err := signer.Sign(jwkJSON) - if err != nil { - return nil, fmt.Errorf("failed to External Account Binding sign content -> %v", err) - } - - return signed, nil -} - -// GetKeyAuthorization Gets the key authorization for a token. -func (j *JWS) GetKeyAuthorization(token string) (string, error) { - var publicKey crypto.PublicKey - switch k := j.privKey.(type) { - case *ecdsa.PrivateKey: - publicKey = k.Public() - case *rsa.PrivateKey: - publicKey = k.Public() - } - - // Generate the Key Authorization for the challenge - jwk := &jose.JSONWebKey{Key: publicKey} - - thumbBytes, err := jwk.Thumbprint(crypto.SHA256) - if err != nil { - return "", err - } - - // unpad the base64URL - keyThumb := base64.RawURLEncoding.EncodeToString(thumbBytes) - - return token + "." + keyThumb, nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go b/vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go deleted file mode 100644 index e08f2ffba..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/internal/sender/sender.go +++ /dev/null @@ -1,146 +0,0 @@ -package sender - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "runtime" - "strings" - - "github.com/go-acme/lego/acme" -) - -type RequestOption func(*http.Request) error - -func contentType(ct string) RequestOption { - return func(req *http.Request) error { - req.Header.Set("Content-Type", ct) - return nil - } -} - -type Doer struct { - httpClient *http.Client - userAgent string -} - -// NewDoer Creates a new Doer. -func NewDoer(client *http.Client, userAgent string) *Doer { - return &Doer{ - httpClient: client, - userAgent: userAgent, - } -} - -// Get performs a GET request with a proper User-Agent string. -// If "response" is not provided, callers should close resp.Body when done reading from it. -func (d *Doer) Get(url string, response interface{}) (*http.Response, error) { - req, err := d.newRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - return d.do(req, response) -} - -// Head performs a HEAD request with a proper User-Agent string. -// The response body (resp.Body) is already closed when this function returns. -func (d *Doer) Head(url string) (*http.Response, error) { - req, err := d.newRequest(http.MethodHead, url, nil) - if err != nil { - return nil, err - } - - return d.do(req, nil) -} - -// Post performs a POST request with a proper User-Agent string. -// If "response" is not provided, callers should close resp.Body when done reading from it. -func (d *Doer) Post(url string, body io.Reader, bodyType string, response interface{}) (*http.Response, error) { - req, err := d.newRequest(http.MethodPost, url, body, contentType(bodyType)) - if err != nil { - return nil, err - } - - return d.do(req, response) -} - -func (d *Doer) newRequest(method, uri string, body io.Reader, opts ...RequestOption) (*http.Request, error) { - req, err := http.NewRequest(method, uri, body) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - - req.Header.Set("User-Agent", d.formatUserAgent()) - - for _, opt := range opts { - err = opt(req) - if err != nil { - return nil, fmt.Errorf("failed to create request: %v", err) - } - } - - return req, nil -} - -func (d *Doer) do(req *http.Request, response interface{}) (*http.Response, error) { - resp, err := d.httpClient.Do(req) - if err != nil { - return nil, err - } - - if err = checkError(req, resp); err != nil { - return resp, err - } - - if response != nil { - raw, err := ioutil.ReadAll(resp.Body) - if err != nil { - return resp, err - } - - defer resp.Body.Close() - - err = json.Unmarshal(raw, response) - if err != nil { - return resp, fmt.Errorf("failed to unmarshal %q to type %T: %v", raw, response, err) - } - } - - return resp, nil -} - -// formatUserAgent builds and returns the User-Agent string to use in requests. -func (d *Doer) formatUserAgent() string { - ua := fmt.Sprintf("%s %s (%s; %s; %s)", d.userAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH) - return strings.TrimSpace(ua) -} - -func checkError(req *http.Request, resp *http.Response) error { - if resp.StatusCode >= http.StatusBadRequest { - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("%d :: %s :: %s :: %v", resp.StatusCode, req.Method, req.URL, err) - } - - var errorDetails *acme.ProblemDetails - err = json.Unmarshal(body, &errorDetails) - if err != nil { - return fmt.Errorf("%d ::%s :: %s :: %v :: %s", resp.StatusCode, req.Method, req.URL, err, string(body)) - } - - errorDetails.Method = req.Method - errorDetails.URL = req.URL.String() - - // Check for errors we handle specifically - if errorDetails.HTTPStatus == http.StatusBadRequest && errorDetails.Type == acme.BadNonceErr { - return &acme.NonceError{ProblemDetails: errorDetails} - } - - return errorDetails - } - return nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go b/vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go deleted file mode 100644 index f01719547..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/internal/sender/useragent.go +++ /dev/null @@ -1,14 +0,0 @@ -package sender - -// CODE GENERATED AUTOMATICALLY -// THIS FILE MUST NOT BE EDITED BY HAND - -const ( - // ourUserAgent is the User-Agent of this underlying library package. - ourUserAgent = "xenolf-acme/2.5.0" - - // ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package. - // values: detach|release - // NOTE: Update this with each tagged release. - ourUserAgentComment = "release" -) diff --git a/vendor/github.com/go-acme/lego/acme/api/order.go b/vendor/github.com/go-acme/lego/acme/api/order.go deleted file mode 100644 index 11240b4cd..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/order.go +++ /dev/null @@ -1,65 +0,0 @@ -package api - -import ( - "encoding/base64" - "errors" - - "github.com/go-acme/lego/acme" -) - -type OrderService service - -// New Creates a new order. -func (o *OrderService) New(domains []string) (acme.ExtendedOrder, error) { - var identifiers []acme.Identifier - for _, domain := range domains { - identifiers = append(identifiers, acme.Identifier{Type: "dns", Value: domain}) - } - - orderReq := acme.Order{Identifiers: identifiers} - - var order acme.Order - resp, err := o.core.post(o.core.GetDirectory().NewOrderURL, orderReq, &order) - if err != nil { - return acme.ExtendedOrder{}, err - } - - return acme.ExtendedOrder{ - Location: resp.Header.Get("Location"), - Order: order, - }, nil -} - -// Get Gets an order. -func (o *OrderService) Get(orderURL string) (acme.Order, error) { - if len(orderURL) == 0 { - return acme.Order{}, errors.New("order[get]: empty URL") - } - - var order acme.Order - _, err := o.core.postAsGet(orderURL, &order) - if err != nil { - return acme.Order{}, err - } - - return order, nil -} - -// UpdateForCSR Updates an order for a CSR. -func (o *OrderService) UpdateForCSR(orderURL string, csr []byte) (acme.Order, error) { - csrMsg := acme.CSRMessage{ - Csr: base64.RawURLEncoding.EncodeToString(csr), - } - - var order acme.Order - _, err := o.core.post(orderURL, csrMsg, &order) - if err != nil { - return acme.Order{}, err - } - - if order.Status == acme.StatusInvalid { - return acme.Order{}, order.Error - } - - return order, nil -} diff --git a/vendor/github.com/go-acme/lego/acme/api/service.go b/vendor/github.com/go-acme/lego/acme/api/service.go deleted file mode 100644 index ff043bc7b..000000000 --- a/vendor/github.com/go-acme/lego/acme/api/service.go +++ /dev/null @@ -1,45 +0,0 @@ -package api - -import ( - "net/http" - "regexp" -) - -type service struct { - core *Core -} - -// getLink get a rel into the Link header -func getLink(header http.Header, rel string) string { - var linkExpr = regexp.MustCompile(`<(.+?)>;\s*rel="(.+?)"`) - - for _, link := range header["Link"] { - for _, m := range linkExpr.FindAllStringSubmatch(link, -1) { - if len(m) != 3 { - continue - } - if m[2] == rel { - return m[1] - } - } - } - return "" -} - -// getLocation get the value of the header Location -func getLocation(resp *http.Response) string { - if resp == nil { - return "" - } - - return resp.Header.Get("Location") -} - -// getRetryAfter get the value of the header Retry-After -func getRetryAfter(resp *http.Response) string { - if resp == nil { - return "" - } - - return resp.Header.Get("Retry-After") -} diff --git a/vendor/github.com/go-acme/lego/acme/commons.go b/vendor/github.com/go-acme/lego/acme/commons.go deleted file mode 100644 index c4493696a..000000000 --- a/vendor/github.com/go-acme/lego/acme/commons.go +++ /dev/null @@ -1,284 +0,0 @@ -// Package acme contains all objects related the ACME endpoints. -// https://tools.ietf.org/html/draft-ietf-acme-acme-16 -package acme - -import ( - "encoding/json" - "time" -) - -// Challenge statuses -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.6 -const ( - StatusPending = "pending" - StatusInvalid = "invalid" - StatusValid = "valid" - StatusProcessing = "processing" - StatusDeactivated = "deactivated" - StatusExpired = "expired" - StatusRevoked = "revoked" -) - -// Directory the ACME directory object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 -type Directory struct { - NewNonceURL string `json:"newNonce"` - NewAccountURL string `json:"newAccount"` - NewOrderURL string `json:"newOrder"` - NewAuthzURL string `json:"newAuthz"` - RevokeCertURL string `json:"revokeCert"` - KeyChangeURL string `json:"keyChange"` - Meta Meta `json:"meta"` -} - -// Meta the ACME meta object (related to Directory). -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.1 -type Meta struct { - // termsOfService (optional, string): - // A URL identifying the current terms of service. - TermsOfService string `json:"termsOfService"` - - // website (optional, string): - // An HTTP or HTTPS URL locating a website providing more information about the ACME server. - Website string `json:"website"` - - // caaIdentities (optional, array of string): - // The hostnames that the ACME server recognizes as referring to itself - // for the purposes of CAA record validation as defined in [RFC6844]. - // Each string MUST represent the same sequence of ASCII code points - // that the server will expect to see as the "Issuer Domain Name" in a CAA issue or issuewild property tag. - // This allows clients to determine the correct issuer domain name to use when configuring CAA records. - CaaIdentities []string `json:"caaIdentities"` - - // externalAccountRequired (optional, boolean): - // If this field is present and set to "true", - // then the CA requires that all new- account requests include an "externalAccountBinding" field - // associating the new account with an external account. - ExternalAccountRequired bool `json:"externalAccountRequired"` -} - -// ExtendedAccount a extended Account. -type ExtendedAccount struct { - Account - // Contains the value of the response header `Location` - Location string `json:"-"` -} - -// Account the ACME account Object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.2 -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3 -type Account struct { - // status (required, string): - // The status of this account. - // Possible values are: "valid", "deactivated", and "revoked". - // The value "deactivated" should be used to indicate client-initiated deactivation - // whereas "revoked" should be used to indicate server- initiated deactivation. (See Section 7.1.6) - Status string `json:"status,omitempty"` - - // contact (optional, array of string): - // An array of URLs that the server can use to contact the client for issues related to this account. - // For example, the server may wish to notify the client about server-initiated revocation or certificate expiration. - // For information on supported URL schemes, see Section 7.3 - Contact []string `json:"contact,omitempty"` - - // termsOfServiceAgreed (optional, boolean): - // Including this field in a new-account request, - // with a value of true, indicates the client's agreement with the terms of service. - // This field is not updateable by the client. - TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"` - - // orders (required, string): - // A URL from which a list of orders submitted by this account can be fetched via a POST-as-GET request, - // as described in Section 7.1.2.1. - Orders string `json:"orders,omitempty"` - - // onlyReturnExisting (optional, boolean): - // If this field is present with the value "true", - // then the server MUST NOT create a new account if one does not already exist. - // This allows a client to look up an account URL based on an account key (see Section 7.3.1). - OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"` - - // externalAccountBinding (optional, object): - // An optional field for binding the new account with an existing non-ACME account (see Section 7.3.4). - ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"` -} - -// ExtendedOrder a extended Order. -type ExtendedOrder struct { - Order - // The order URL, contains the value of the response header `Location` - Location string `json:"-"` -} - -// Order the ACME order Object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.3 -type Order struct { - // status (required, string): - // The status of this order. - // Possible values are: "pending", "ready", "processing", "valid", and "invalid". - Status string `json:"status,omitempty"` - - // expires (optional, string): - // The timestamp after which the server will consider this order invalid, - // encoded in the format specified in RFC 3339 [RFC3339]. - // This field is REQUIRED for objects with "pending" or "valid" in the status field. - Expires string `json:"expires,omitempty"` - - // identifiers (required, array of object): - // An array of identifier objects that the order pertains to. - Identifiers []Identifier `json:"identifiers"` - - // notBefore (optional, string): - // The requested value of the notBefore field in the certificate, - // in the date format defined in [RFC3339]. - NotBefore string `json:"notBefore,omitempty"` - - // notAfter (optional, string): - // The requested value of the notAfter field in the certificate, - // in the date format defined in [RFC3339]. - NotAfter string `json:"notAfter,omitempty"` - - // error (optional, object): - // The error that occurred while processing the order, if any. - // This field is structured as a problem document [RFC7807]. - Error *ProblemDetails `json:"error,omitempty"` - - // authorizations (required, array of string): - // For pending orders, - // the authorizations that the client needs to complete before the requested certificate can be issued (see Section 7.5), - // including unexpired authorizations that the client has completed in the past for identifiers specified in the order. - // The authorizations required are dictated by server policy - // and there may not be a 1:1 relationship between the order identifiers and the authorizations required. - // For final orders (in the "valid" or "invalid" state), the authorizations that were completed. - // Each entry is a URL from which an authorization can be fetched with a POST-as-GET request. - Authorizations []string `json:"authorizations,omitempty"` - - // finalize (required, string): - // A URL that a CSR must be POSTed to once all of the order's authorizations are satisfied to finalize the order. - // The result of a successful finalization will be the population of the certificate URL for the order. - Finalize string `json:"finalize,omitempty"` - - // certificate (optional, string): - // A URL for the certificate that has been issued in response to this order - Certificate string `json:"certificate,omitempty"` -} - -// Authorization the ACME authorization object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 -type Authorization struct { - // status (required, string): - // The status of this authorization. - // Possible values are: "pending", "valid", "invalid", "deactivated", "expired", and "revoked". - Status string `json:"status"` - - // expires (optional, string): - // The timestamp after which the server will consider this authorization invalid, - // encoded in the format specified in RFC 3339 [RFC3339]. - // This field is REQUIRED for objects with "valid" in the "status" field. - Expires time.Time `json:"expires,omitempty"` - - // identifier (required, object): - // The identifier that the account is authorized to represent - Identifier Identifier `json:"identifier,omitempty"` - - // challenges (required, array of objects): - // For pending authorizations, the challenges that the client can fulfill in order to prove possession of the identifier. - // For valid authorizations, the challenge that was validated. - // For invalid authorizations, the challenge that was attempted and failed. - // Each array entry is an object with parameters required to validate the challenge. - // A client should attempt to fulfill one of these challenges, - // and a server should consider any one of the challenges sufficient to make the authorization valid. - Challenges []Challenge `json:"challenges,omitempty"` - - // wildcard (optional, boolean): - // For authorizations created as a result of a newOrder request containing a DNS identifier - // with a value that contained a wildcard prefix this field MUST be present, and true. - Wildcard bool `json:"wildcard,omitempty"` -} - -// ExtendedChallenge a extended Challenge. -type ExtendedChallenge struct { - Challenge - // Contains the value of the response header `Retry-After` - RetryAfter string `json:"-"` - // Contains the value of the response header `Link` rel="up" - AuthorizationURL string `json:"-"` -} - -// Challenge the ACME challenge object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.5 -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8 -type Challenge struct { - // type (required, string): - // The type of challenge encoded in the object. - Type string `json:"type"` - - // url (required, string): - // The URL to which a response can be posted. - URL string `json:"url"` - - // status (required, string): - // The status of this challenge. Possible values are: "pending", "processing", "valid", and "invalid". - Status string `json:"status"` - - // validated (optional, string): - // The time at which the server validated this challenge, - // encoded in the format specified in RFC 3339 [RFC3339]. - // This field is REQUIRED if the "status" field is "valid". - Validated time.Time `json:"validated,omitempty"` - - // error (optional, object): - // Error that occurred while the server was validating the challenge, if any, - // structured as a problem document [RFC7807]. - // Multiple errors can be indicated by using subproblems Section 6.7.1. - // A challenge object with an error MUST have status equal to "invalid". - Error *ProblemDetails `json:"error,omitempty"` - - // token (required, string): - // A random value that uniquely identifies the challenge. - // This value MUST have at least 128 bits of entropy. - // It MUST NOT contain any characters outside the base64url alphabet, - // and MUST NOT include base64 padding characters ("="). - // See [RFC4086] for additional information on randomness requirements. - // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3 - // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4 - Token string `json:"token"` - - // https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.1 - KeyAuthorization string `json:"keyAuthorization"` -} - -// Identifier the ACME identifier object. -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-9.7.7 -type Identifier struct { - Type string `json:"type"` - Value string `json:"value"` -} - -// CSRMessage Certificate Signing Request -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.4 -type CSRMessage struct { - // csr (required, string): - // A CSR encoding the parameters for the certificate being requested [RFC2986]. - // The CSR is sent in the base64url-encoded version of the DER format. - // (Note: Because this field uses base64url, and does not include headers, it is different from PEM.). - Csr string `json:"csr"` -} - -// RevokeCertMessage a certificate revocation message -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.6 -// - https://tools.ietf.org/html/rfc5280#section-5.3.1 -type RevokeCertMessage struct { - // certificate (required, string): - // The certificate to be revoked, in the base64url-encoded version of the DER format. - // (Note: Because this field uses base64url, and does not include headers, it is different from PEM.) - Certificate string `json:"certificate"` - - // reason (optional, int): - // One of the revocation reasonCodes defined in Section 5.3.1 of [RFC5280] to be used when generating OCSP responses and CRLs. - // If this field is not set the server SHOULD omit the reasonCode CRL entry extension when generating OCSP responses and CRLs. - // The server MAY disallow a subset of reasonCodes from being used by the user. - // If a request contains a disallowed reasonCode the server MUST reject it with the error type "urn:ietf:params:acme:error:badRevocationReason". - // The problem document detail SHOULD indicate which reasonCodes are allowed. - Reason *uint `json:"reason,omitempty"` -} diff --git a/vendor/github.com/go-acme/lego/acme/errors.go b/vendor/github.com/go-acme/lego/acme/errors.go deleted file mode 100644 index 1658fe8d1..000000000 --- a/vendor/github.com/go-acme/lego/acme/errors.go +++ /dev/null @@ -1,58 +0,0 @@ -package acme - -import ( - "fmt" -) - -// Errors types -const ( - errNS = "urn:ietf:params:acme:error:" - BadNonceErr = errNS + "badNonce" -) - -// ProblemDetails the problem details object -// - https://tools.ietf.org/html/rfc7807#section-3.1 -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.3.3 -type ProblemDetails struct { - Type string `json:"type,omitempty"` - Detail string `json:"detail,omitempty"` - HTTPStatus int `json:"status,omitempty"` - Instance string `json:"instance,omitempty"` - SubProblems []SubProblem `json:"subproblems,omitempty"` - - // additional values to have a better error message (Not defined by the RFC) - Method string `json:"method,omitempty"` - URL string `json:"url,omitempty"` -} - -// SubProblem a "subproblems" -// - https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-6.7.1 -type SubProblem struct { - Type string `json:"type,omitempty"` - Detail string `json:"detail,omitempty"` - Identifier Identifier `json:"identifier,omitempty"` -} - -func (p ProblemDetails) Error() string { - msg := fmt.Sprintf("acme: error: %d", p.HTTPStatus) - if len(p.Method) != 0 || len(p.URL) != 0 { - msg += fmt.Sprintf(" :: %s :: %s", p.Method, p.URL) - } - msg += fmt.Sprintf(" :: %s :: %s", p.Type, p.Detail) - - for _, sub := range p.SubProblems { - msg += fmt.Sprintf(", problem: %q :: %s", sub.Type, sub.Detail) - } - - if len(p.Instance) == 0 { - msg += ", url: " + p.Instance - } - - return msg -} - -// NonceError represents the error which is returned -// if the nonce sent by the client was not accepted by the server. -type NonceError struct { - *ProblemDetails -} diff --git a/vendor/github.com/go-acme/lego/certcrypto/crypto.go b/vendor/github.com/go-acme/lego/certcrypto/crypto.go deleted file mode 100644 index c9d0c1098..000000000 --- a/vendor/github.com/go-acme/lego/certcrypto/crypto.go +++ /dev/null @@ -1,256 +0,0 @@ -package certcrypto - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/pem" - "errors" - "fmt" - "math/big" - "time" - - "golang.org/x/crypto/ocsp" -) - -// Constants for all key types we support. -const ( - EC256 = KeyType("P256") - EC384 = KeyType("P384") - RSA2048 = KeyType("2048") - RSA4096 = KeyType("4096") - RSA8192 = KeyType("8192") -) - -const ( - // OCSPGood means that the certificate is valid. - OCSPGood = ocsp.Good - // OCSPRevoked means that the certificate has been deliberately revoked. - OCSPRevoked = ocsp.Revoked - // OCSPUnknown means that the OCSP responder doesn't know about the certificate. - OCSPUnknown = ocsp.Unknown - // OCSPServerFailed means that the OCSP responder failed to process the request. - OCSPServerFailed = ocsp.ServerFailed -) - -// Constants for OCSP must staple -var ( - tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24} - ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05} -) - -// KeyType represents the key algo as well as the key size or curve to use. -type KeyType string - -type DERCertificateBytes []byte - -// ParsePEMBundle parses a certificate bundle from top to bottom and returns -// a slice of x509 certificates. This function will error if no certificates are found. -func ParsePEMBundle(bundle []byte) ([]*x509.Certificate, error) { - var certificates []*x509.Certificate - var certDERBlock *pem.Block - - for { - certDERBlock, bundle = pem.Decode(bundle) - if certDERBlock == nil { - break - } - - if certDERBlock.Type == "CERTIFICATE" { - cert, err := x509.ParseCertificate(certDERBlock.Bytes) - if err != nil { - return nil, err - } - certificates = append(certificates, cert) - } - } - - if len(certificates) == 0 { - return nil, errors.New("no certificates were found while parsing the bundle") - } - - return certificates, nil -} - -func ParsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(key) - - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - default: - return nil, errors.New("unknown PEM header value") - } -} - -func GeneratePrivateKey(keyType KeyType) (crypto.PrivateKey, error) { - switch keyType { - case EC256: - return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case EC384: - return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case RSA2048: - return rsa.GenerateKey(rand.Reader, 2048) - case RSA4096: - return rsa.GenerateKey(rand.Reader, 4096) - case RSA8192: - return rsa.GenerateKey(rand.Reader, 8192) - } - - return nil, fmt.Errorf("invalid KeyType: %s", keyType) -} - -func GenerateCSR(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) { - template := x509.CertificateRequest{ - Subject: pkix.Name{CommonName: domain}, - DNSNames: san, - } - - if mustStaple { - template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{ - Id: tlsFeatureExtensionOID, - Value: ocspMustStapleFeature, - }) - } - - return x509.CreateCertificateRequest(rand.Reader, &template, privateKey) -} - -func PEMEncode(data interface{}) []byte { - return pem.EncodeToMemory(PEMBlock(data)) -} - -func PEMBlock(data interface{}) *pem.Block { - var pemBlock *pem.Block - switch key := data.(type) { - case *ecdsa.PrivateKey: - keyBytes, _ := x509.MarshalECPrivateKey(key) - pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes} - case *rsa.PrivateKey: - pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)} - case *x509.CertificateRequest: - pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw} - case DERCertificateBytes: - pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(DERCertificateBytes))} - } - - return pemBlock -} - -func pemDecode(data []byte) (*pem.Block, error) { - pemBlock, _ := pem.Decode(data) - if pemBlock == nil { - return nil, fmt.Errorf("PEM decode did not yield a valid block. Is the certificate in the right format?") - } - - return pemBlock, nil -} - -func PemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) { - pemBlock, err := pemDecode(pem) - if pemBlock == nil { - return nil, err - } - - if pemBlock.Type != "CERTIFICATE REQUEST" { - return nil, fmt.Errorf("PEM block is not a certificate request") - } - - return x509.ParseCertificateRequest(pemBlock.Bytes) -} - -// ParsePEMCertificate returns Certificate from a PEM encoded certificate. -// The certificate has to be PEM encoded. Any other encodings like DER will fail. -func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) { - pemBlock, err := pemDecode(cert) - if pemBlock == nil { - return nil, err - } - - // from a DER encoded certificate - return x509.ParseCertificate(pemBlock.Bytes) -} - -func ExtractDomains(cert *x509.Certificate) []string { - domains := []string{cert.Subject.CommonName} - - // Check for SAN certificate - for _, sanDomain := range cert.DNSNames { - if sanDomain == cert.Subject.CommonName { - continue - } - domains = append(domains, sanDomain) - } - - return domains -} - -func ExtractDomainsCSR(csr *x509.CertificateRequest) []string { - domains := []string{csr.Subject.CommonName} - - // loop over the SubjectAltName DNS names - for _, sanName := range csr.DNSNames { - if containsSAN(domains, sanName) { - // Duplicate; skip this name - continue - } - - // Name is unique - domains = append(domains, sanName) - } - - return domains -} - -func containsSAN(domains []string, sanName string) bool { - for _, existingName := range domains { - if existingName == sanName { - return true - } - } - return false -} - -func GeneratePemCert(privateKey *rsa.PrivateKey, domain string, extensions []pkix.Extension) ([]byte, error) { - derBytes, err := generateDerCert(privateKey, time.Time{}, domain, extensions) - if err != nil { - return nil, err - } - - return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil -} - -func generateDerCert(privateKey *rsa.PrivateKey, expiration time.Time, domain string, extensions []pkix.Extension) ([]byte, error) { - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return nil, err - } - - if expiration.IsZero() { - expiration = time.Now().Add(365) - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - CommonName: "ACME Challenge TEMP", - }, - NotBefore: time.Now(), - NotAfter: expiration, - - KeyUsage: x509.KeyUsageKeyEncipherment, - BasicConstraintsValid: true, - DNSNames: []string{domain}, - ExtraExtensions: extensions, - } - - return x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) -} diff --git a/vendor/github.com/go-acme/lego/certificate/authorization.go b/vendor/github.com/go-acme/lego/certificate/authorization.go deleted file mode 100644 index 00062504b..000000000 --- a/vendor/github.com/go-acme/lego/certificate/authorization.go +++ /dev/null @@ -1,69 +0,0 @@ -package certificate - -import ( - "time" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/log" -) - -const ( - // overallRequestLimit is the overall number of request per second - // limited on the "new-reg", "new-authz" and "new-cert" endpoints. - // From the documentation the limitation is 20 requests per second, - // but using 20 as value doesn't work but 18 do - overallRequestLimit = 18 -) - -func (c *Certifier) getAuthorizations(order acme.ExtendedOrder) ([]acme.Authorization, error) { - resc, errc := make(chan acme.Authorization), make(chan domainError) - - delay := time.Second / overallRequestLimit - - for _, authzURL := range order.Authorizations { - time.Sleep(delay) - - go func(authzURL string) { - authz, err := c.core.Authorizations.Get(authzURL) - if err != nil { - errc <- domainError{Domain: authz.Identifier.Value, Error: err} - return - } - - resc <- authz - }(authzURL) - } - - var responses []acme.Authorization - failures := make(obtainError) - for i := 0; i < len(order.Authorizations); i++ { - select { - case res := <-resc: - responses = append(responses, res) - case err := <-errc: - failures[err.Domain] = err.Error - } - } - - for i, auth := range order.Authorizations { - log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth) - } - - close(resc) - close(errc) - - // be careful to not return an empty failures map; - // even if empty, they become non-nil error values - if len(failures) > 0 { - return responses, failures - } - return responses, nil -} - -func (c *Certifier) deactivateAuthorizations(order acme.ExtendedOrder) { - for _, auth := range order.Authorizations { - if err := c.core.Authorizations.Deactivate(auth); err != nil { - log.Infof("Unable to deactivated authorizations: %s", auth) - } - } -} diff --git a/vendor/github.com/go-acme/lego/certificate/certificates.go b/vendor/github.com/go-acme/lego/certificate/certificates.go deleted file mode 100644 index b0327d5c1..000000000 --- a/vendor/github.com/go-acme/lego/certificate/certificates.go +++ /dev/null @@ -1,495 +0,0 @@ -package certificate - -import ( - "bytes" - "crypto" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strings" - "time" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" - "github.com/go-acme/lego/platform/wait" - "golang.org/x/crypto/ocsp" - "golang.org/x/net/idna" -) - -// maxBodySize is the maximum size of body that we will read. -const maxBodySize = 1024 * 1024 - -// Resource represents a CA issued certificate. -// PrivateKey, Certificate and IssuerCertificate are all -// already PEM encoded and can be directly written to disk. -// Certificate may be a certificate bundle, -// depending on the options supplied to create it. -type Resource struct { - Domain string `json:"domain"` - CertURL string `json:"certUrl"` - CertStableURL string `json:"certStableUrl"` - PrivateKey []byte `json:"-"` - Certificate []byte `json:"-"` - IssuerCertificate []byte `json:"-"` - CSR []byte `json:"-"` -} - -// ObtainRequest The request to obtain certificate. -// -// 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. -// -// A new private key is generated for every invocation of the function Obtain. -// If you do not want that you can supply your own private key in the privateKey parameter. -// If this parameter is non-nil it will be used instead of generating a new one. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -type ObtainRequest struct { - Domains []string - Bundle bool - PrivateKey crypto.PrivateKey - MustStaple bool -} - -type resolver interface { - Solve(authorizations []acme.Authorization) error -} - -type CertifierOptions struct { - KeyType certcrypto.KeyType - Timeout time.Duration -} - -// Certifier A service to obtain/renew/revoke certificates. -type Certifier struct { - core *api.Core - resolver resolver - options CertifierOptions -} - -// NewCertifier creates a Certifier. -func NewCertifier(core *api.Core, resolver resolver, options CertifierOptions) *Certifier { - return &Certifier{ - core: core, - resolver: resolver, - options: options, - } -} - -// Obtain tries to obtain a single certificate using all domains passed into it. -// -// This function will never return a partial certificate. -// If one domain in the list fails, the whole certificate will fail. -func (c *Certifier) Obtain(request ObtainRequest) (*Resource, error) { - if len(request.Domains) == 0 { - return nil, errors.New("no domains to obtain a certificate for") - } - - domains := sanitizeDomain(request.Domains) - - if request.Bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", ")) - } - - order, err := c.core.Orders.New(domains) - if err != nil { - return nil, err - } - - authz, err := c.getAuthorizations(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - err = c.resolver.Solve(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(obtainError) - cert, err := c.getForOrder(domains, order, request.Bundle, request.PrivateKey, request.MustStaple) - if err != nil { - for _, auth := range authz { - failures[challenge.GetTargetedDomain(auth)] = err - } - } - - // Do not return an empty failures map, because - // it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -// ObtainForCSR tries to obtain a certificate matching the CSR passed into it. -// -// The domains are inferred from the CommonName and SubjectAltNames, if any. -// The private key for this CSR is not required. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -// -// This function will never return a partial certificate. -// If one domain in the list fails, the whole certificate will fail. -func (c *Certifier) ObtainForCSR(csr x509.CertificateRequest, bundle bool) (*Resource, error) { - // figure out what domains it concerns - // start with the common name - domains := certcrypto.ExtractDomainsCSR(&csr) - - if bundle { - log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", ")) - } else { - log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", ")) - } - - order, err := c.core.Orders.New(domains) - if err != nil { - return nil, err - } - - authz, err := c.getAuthorizations(order) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - err = c.resolver.Solve(authz) - if err != nil { - // If any challenge fails, return. Do not generate partial SAN certificates. - c.deactivateAuthorizations(order) - return nil, err - } - - log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", ")) - - failures := make(obtainError) - cert, err := c.getForCSR(domains, order, bundle, csr.Raw, nil) - if err != nil { - for _, auth := range authz { - failures[challenge.GetTargetedDomain(auth)] = err - } - } - - if cert != nil { - // Add the CSR to the certificate so that it can be used for renewals. - cert.CSR = certcrypto.PEMEncode(&csr) - } - - // Do not return an empty failures map, - // because it would still be a non-nil error value - if len(failures) > 0 { - return cert, failures - } - return cert, nil -} - -func (c *Certifier) getForOrder(domains []string, order acme.ExtendedOrder, bundle bool, privateKey crypto.PrivateKey, mustStaple bool) (*Resource, error) { - if privateKey == nil { - var err error - privateKey, err = certcrypto.GeneratePrivateKey(c.options.KeyType) - if err != nil { - return nil, err - } - } - - // Determine certificate name(s) based on the authorization resources - commonName := domains[0] - - // ACME draft Section 7.4 "Applying for Certificate Issuance" - // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.4 - // says: - // Clients SHOULD NOT make any assumptions about the sort order of - // "identifiers" or "authorizations" elements in the returned order - // object. - san := []string{commonName} - for _, auth := range order.Identifiers { - if auth.Value != commonName { - san = append(san, auth.Value) - } - } - - // TODO: should the CSR be customizable? - csr, err := certcrypto.GenerateCSR(privateKey, commonName, san, mustStaple) - if err != nil { - return nil, err - } - - return c.getForCSR(domains, order, bundle, csr, certcrypto.PEMEncode(privateKey)) -} - -func (c *Certifier) getForCSR(domains []string, order acme.ExtendedOrder, bundle bool, csr []byte, privateKeyPem []byte) (*Resource, error) { - respOrder, err := c.core.Orders.UpdateForCSR(order.Finalize, csr) - if err != nil { - return nil, err - } - - commonName := domains[0] - certRes := &Resource{ - Domain: commonName, - CertURL: respOrder.Certificate, - PrivateKey: privateKeyPem, - } - - if respOrder.Status == acme.StatusValid { - // if the certificate is available right away, short cut! - ok, errR := c.checkResponse(respOrder, certRes, bundle) - if errR != nil { - return nil, errR - } - - if ok { - return certRes, nil - } - } - - timeout := c.options.Timeout - if c.options.Timeout <= 0 { - timeout = 30 * time.Second - } - - err = wait.For("certificate", timeout, timeout/60, func() (bool, error) { - ord, errW := c.core.Orders.Get(order.Location) - if errW != nil { - return false, errW - } - - done, errW := c.checkResponse(ord, certRes, bundle) - if errW != nil { - return false, errW - } - - return done, nil - }) - - return certRes, err -} - -// checkResponse checks to see if the certificate is ready and a link is contained in the response. -// -// If so, loads it into certRes and returns true. -// If the cert is not yet ready, it returns false. -// -// The certRes input should already have the Domain (common name) field populated. -// -// If bundle is true, the certificate will be bundled with the issuer's cert. -func (c *Certifier) checkResponse(order acme.Order, certRes *Resource, bundle bool) (bool, error) { - valid, err := checkOrderStatus(order) - if err != nil || !valid { - return valid, err - } - - cert, issuer, err := c.core.Certificates.Get(order.Certificate, bundle) - if err != nil { - return false, err - } - - log.Infof("[%s] Server responded with a certificate.", certRes.Domain) - - certRes.IssuerCertificate = issuer - certRes.Certificate = cert - certRes.CertURL = order.Certificate - certRes.CertStableURL = order.Certificate - - return true, nil -} - -// Revoke takes a PEM encoded certificate or bundle and tries to revoke it at the CA. -func (c *Certifier) Revoke(cert []byte) error { - certificates, err := certcrypto.ParsePEMBundle(cert) - if err != nil { - return err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return fmt.Errorf("certificate bundle starts with a CA certificate") - } - - revokeMsg := acme.RevokeCertMessage{ - Certificate: base64.RawURLEncoding.EncodeToString(x509Cert.Raw), - } - - return c.core.Certificates.Revoke(revokeMsg) -} - -// Renew takes a Resource and tries to renew the certificate. -// -// If the renewal process succeeds, the new certificate will ge returned in a new CertResource. -// Please be aware that this function will return a new certificate in ANY case that is not an error. -// If the server does not provide us with a new cert on a GET request to the CertURL -// this function will start a new-cert flow where a new certificate gets generated. -// -// If bundle is true, the []byte contains both the issuer certificate and your issued certificate as a bundle. -// -// For private key reuse the PrivateKey property of the passed in Resource should be non-nil. -func (c *Certifier) Renew(certRes Resource, bundle, mustStaple bool) (*Resource, error) { - // Input certificate is PEM encoded. - // Decode it here as we may need the decoded cert later on in the renewal process. - // The input may be a bundle or a single certificate. - certificates, err := certcrypto.ParsePEMBundle(certRes.Certificate) - if err != nil { - return nil, err - } - - x509Cert := certificates[0] - if x509Cert.IsCA { - return nil, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", certRes.Domain) - } - - // This is just meant to be informal for the user. - timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC()) - log.Infof("[%s] acme: Trying renewal with %d hours remaining", certRes.Domain, int(timeLeft.Hours())) - - // We always need to request a new certificate to renew. - // Start by checking to see if the certificate was based off a CSR, - // and use that if it's defined. - if len(certRes.CSR) > 0 { - csr, errP := certcrypto.PemDecodeTox509CSR(certRes.CSR) - if errP != nil { - return nil, errP - } - - return c.ObtainForCSR(*csr, bundle) - } - - var privateKey crypto.PrivateKey - if certRes.PrivateKey != nil { - privateKey, err = certcrypto.ParsePEMPrivateKey(certRes.PrivateKey) - if err != nil { - return nil, err - } - } - - query := ObtainRequest{ - Domains: certcrypto.ExtractDomains(x509Cert), - Bundle: bundle, - PrivateKey: privateKey, - MustStaple: mustStaple, - } - return c.Obtain(query) -} - -// GetOCSP takes a PEM encoded cert or cert bundle returning the raw OCSP response, -// the parsed response, and an error, if any. -// -// The returned []byte can be passed directly into the OCSPStaple property of a tls.Certificate. -// If the bundle only contains the issued certificate, -// this function will try to get the issuer certificate from the IssuingCertificateURL in the certificate. -// -// If the []byte and/or ocsp.Response return values are nil, the OCSP status may be assumed OCSPUnknown. -func (c *Certifier) GetOCSP(bundle []byte) ([]byte, *ocsp.Response, error) { - certificates, err := certcrypto.ParsePEMBundle(bundle) - if err != nil { - return nil, nil, err - } - - // We expect the certificate slice to be ordered downwards the chain. - // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it, - // which should always be the first two certificates. - // If there's no OCSP server listed in the leaf cert, there's nothing to do. - // And if we have only one certificate so far, we need to get the issuer cert. - - issuedCert := certificates[0] - - if len(issuedCert.OCSPServer) == 0 { - return nil, nil, errors.New("no OCSP server specified in cert") - } - - if len(certificates) == 1 { - // TODO: build fallback. If this fails, check the remaining array entries. - if len(issuedCert.IssuingCertificateURL) == 0 { - return nil, nil, errors.New("no issuing certificate URL") - } - - resp, errC := c.core.HTTPClient.Get(issuedCert.IssuingCertificateURL[0]) - if errC != nil { - return nil, nil, errC - } - defer resp.Body.Close() - - issuerBytes, errC := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) - if errC != nil { - return nil, nil, errC - } - - issuerCert, errC := x509.ParseCertificate(issuerBytes) - if errC != nil { - return nil, nil, errC - } - - // Insert it into the slice on position 0 - // We want it ordered right SRV CRT -> CA - certificates = append(certificates, issuerCert) - } - - issuerCert := certificates[1] - - // Finally kick off the OCSP request. - ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) - if err != nil { - return nil, nil, err - } - - resp, err := c.core.HTTPClient.Post(issuedCert.OCSPServer[0], "application/ocsp-request", bytes.NewReader(ocspReq)) - if err != nil { - return nil, nil, err - } - defer resp.Body.Close() - - ocspResBytes, err := ioutil.ReadAll(http.MaxBytesReader(nil, resp.Body, maxBodySize)) - if err != nil { - return nil, nil, err - } - - ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) - if err != nil { - return nil, nil, err - } - - return ocspResBytes, ocspRes, nil -} - -func checkOrderStatus(order acme.Order) (bool, error) { - switch order.Status { - case acme.StatusValid: - return true, nil - case acme.StatusInvalid: - return false, order.Error - default: - return false, nil - } -} - -// https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-7.1.4 -// The domain name MUST be encoded -// in the form in which it would appear in a certificate. That is, it -// MUST be encoded according to the rules in Section 7 of [RFC5280]. -// -// https://tools.ietf.org/html/rfc5280#section-7 -func sanitizeDomain(domains []string) []string { - var sanitizedDomains []string - for _, domain := range domains { - sanitizedDomain, err := idna.ToASCII(domain) - if err != nil { - log.Infof("skip domain %q: unable to sanitize (punnycode): %v", domain, err) - } else { - sanitizedDomains = append(sanitizedDomains, sanitizedDomain) - } - } - return sanitizedDomains -} diff --git a/vendor/github.com/go-acme/lego/certificate/errors.go b/vendor/github.com/go-acme/lego/certificate/errors.go deleted file mode 100644 index 0fec7c16a..000000000 --- a/vendor/github.com/go-acme/lego/certificate/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -package certificate - -import ( - "bytes" - "fmt" - "sort" -) - -// obtainError is returned when there are specific errors available per domain. -type obtainError map[string]error - -func (e obtainError) Error() string { - buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") - - var domains []string - for domain := range e { - domains = append(domains, domain) - } - sort.Strings(domains) - - for _, domain := range domains { - buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) - } - return buffer.String() -} - -type domainError struct { - Domain string - Error error -} diff --git a/vendor/github.com/go-acme/lego/challenge/challenges.go b/vendor/github.com/go-acme/lego/challenge/challenges.go deleted file mode 100644 index b3281402f..000000000 --- a/vendor/github.com/go-acme/lego/challenge/challenges.go +++ /dev/null @@ -1,44 +0,0 @@ -package challenge - -import ( - "fmt" - - "github.com/go-acme/lego/acme" -) - -// Type is a string that identifies a particular challenge type and version of ACME challenge. -type Type string - -const ( - // HTTP01 is the "http-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.3 - // Note: ChallengePath returns the URL path to fulfill this challenge - HTTP01 = Type("http-01") - - // DNS01 is the "dns-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-acme-16#section-8.4 - // Note: GetRecord returns a DNS record which will fulfill this challenge - DNS01 = Type("dns-01") - - // TLSALPN01 is the "tls-alpn-01" ACME challenge https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05 - TLSALPN01 = Type("tls-alpn-01") -) - -func (t Type) String() string { - return string(t) -} - -func FindChallenge(chlgType Type, authz acme.Authorization) (acme.Challenge, error) { - for _, chlg := range authz.Challenges { - if chlg.Type == string(chlgType) { - return chlg, nil - } - } - - return acme.Challenge{}, fmt.Errorf("[%s] acme: unable to find challenge %s", GetTargetedDomain(authz), chlgType) -} - -func GetTargetedDomain(authz acme.Authorization) string { - if authz.Wildcard { - return "*." + authz.Identifier.Value - } - return authz.Identifier.Value -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/cname.go b/vendor/github.com/go-acme/lego/challenge/dns01/cname.go deleted file mode 100644 index 619c84763..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/cname.go +++ /dev/null @@ -1,16 +0,0 @@ -package dns01 - -import "github.com/miekg/dns" - -// Update FQDN with CNAME if any -func updateDomainWithCName(r *dns.Msg, fqdn string) string { - for _, rr := range r.Answer { - if cn, ok := rr.(*dns.CNAME); ok { - if cn.Hdr.Name == fqdn { - return cn.Target - } - } - } - - return fqdn -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go b/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go deleted file mode 100644 index 9500305f8..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge.go +++ /dev/null @@ -1,188 +0,0 @@ -package dns01 - -import ( - "crypto/sha256" - "encoding/base64" - "fmt" - "os" - "strconv" - "time" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" - "github.com/go-acme/lego/platform/wait" - "github.com/miekg/dns" -) - -const ( - // DefaultPropagationTimeout default propagation timeout - DefaultPropagationTimeout = 60 * time.Second - - // DefaultPollingInterval default polling interval - DefaultPollingInterval = 2 * time.Second - - // DefaultTTL default TTL - DefaultTTL = 120 -) - -type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error - -type ChallengeOption func(*Challenge) error - -// CondOption Conditional challenge option. -func CondOption(condition bool, opt ChallengeOption) ChallengeOption { - if !condition { - // NoOp options - return func(*Challenge) error { - return nil - } - } - return opt -} - -// Challenge implements the dns-01 challenge -type Challenge struct { - core *api.Core - validate ValidateFunc - provider challenge.Provider - preCheck preCheck - dnsTimeout time.Duration -} - -func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider, opts ...ChallengeOption) *Challenge { - chlg := &Challenge{ - core: core, - validate: validate, - provider: provider, - preCheck: newPreCheck(), - dnsTimeout: 10 * time.Second, - } - - for _, opt := range opts { - err := opt(chlg) - if err != nil { - log.Infof("challenge option error: %v", err) - } - } - - return chlg -} - -// PreSolve just submits the txt record to the dns provider. -// It does not validate record propagation, or do anything at all with the acme server. -func (c *Challenge) PreSolve(authz acme.Authorization) error { - domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Preparing to solve DNS-01", domain) - - chlng, err := challenge.FindChallenge(challenge.DNS01, authz) - if err != nil { - return err - } - - if c.provider == nil { - return fmt.Errorf("[%s] acme: no DNS Provider configured", domain) - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %s", domain, err) - } - - return nil -} - -func (c *Challenge) Solve(authz acme.Authorization) error { - domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Trying to solve DNS-01", domain) - - chlng, err := challenge.FindChallenge(challenge.DNS01, authz) - if err != nil { - return err - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - fqdn, value := GetRecord(authz.Identifier.Value, keyAuth) - - var timeout, interval time.Duration - switch provider := c.provider.(type) { - case challenge.ProviderTimeout: - timeout, interval = provider.Timeout() - default: - timeout, interval = DefaultPropagationTimeout, DefaultPollingInterval - } - - log.Infof("[%s] acme: Checking DNS record propagation using %+v", domain, recursiveNameservers) - - err = wait.For("propagation", timeout, interval, func() (bool, error) { - stop, errP := c.preCheck.call(domain, fqdn, value) - if !stop || errP != nil { - log.Infof("[%s] acme: Waiting for DNS record propagation.", domain) - } - return stop, errP - }) - if err != nil { - return err - } - - chlng.KeyAuthorization = keyAuth - return c.validate(c.core, domain, chlng) -} - -// CleanUp cleans the challenge. -func (c *Challenge) CleanUp(authz acme.Authorization) error { - log.Infof("[%s] acme: Cleaning DNS-01 challenge", challenge.GetTargetedDomain(authz)) - - chlng, err := challenge.FindChallenge(challenge.DNS01, authz) - if err != nil { - return err - } - - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - return c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) -} - -func (c *Challenge) Sequential() (bool, time.Duration) { - if p, ok := c.provider.(sequential); ok { - return ok, p.Sequential() - } - return false, 0 -} - -type sequential interface { - Sequential() time.Duration -} - -// GetRecord returns a DNS record which will fulfill the `dns-01` challenge -func GetRecord(domain, keyAuth string) (fqdn string, value string) { - keyAuthShaBytes := sha256.Sum256([]byte(keyAuth)) - // base64URL encoding without padding - value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size]) - fqdn = fmt.Sprintf("_acme-challenge.%s.", domain) - - if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_CNAME_SUPPORT")); ok { - r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true) - // Check if the domain has CNAME then return that - if err == nil && r.Rcode == dns.RcodeSuccess { - fqdn = updateDomainWithCName(r, fqdn) - } - } - - return -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge_manual.go b/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge_manual.go deleted file mode 100644 index 490108dd1..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/dns_challenge_manual.go +++ /dev/null @@ -1,52 +0,0 @@ -package dns01 - -import ( - "bufio" - "fmt" - "os" -) - -const ( - dnsTemplate = `%s %d IN TXT "%s"` -) - -// DNSProviderManual is an implementation of the ChallengeProvider interface -type DNSProviderManual struct{} - -// NewDNSProviderManual returns a DNSProviderManual instance. -func NewDNSProviderManual() (*DNSProviderManual, error) { - return &DNSProviderManual{}, nil -} - -// Present prints instructions for manually creating the TXT record -func (*DNSProviderManual) Present(domain, token, keyAuth string) error { - fqdn, value := GetRecord(domain, keyAuth) - - authZone, err := FindZoneByFqdn(fqdn) - if err != nil { - return err - } - - fmt.Printf("lego: Please create the following TXT record in your %s zone:\n", authZone) - fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, value) - fmt.Printf("lego: Press 'Enter' when you are done\n") - - _, err = bufio.NewReader(os.Stdin).ReadBytes('\n') - - return err -} - -// CleanUp prints instructions for manually removing the TXT record -func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error { - fqdn, _ := GetRecord(domain, keyAuth) - - authZone, err := FindZoneByFqdn(fqdn) - if err != nil { - return err - } - - fmt.Printf("lego: You can now remove this TXT record from your %s zone:\n", authZone) - fmt.Printf(dnsTemplate+"\n", fqdn, DefaultTTL, "...") - - return nil -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/fqdn.go b/vendor/github.com/go-acme/lego/challenge/dns01/fqdn.go deleted file mode 100644 index c238c8cf5..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/fqdn.go +++ /dev/null @@ -1,19 +0,0 @@ -package dns01 - -// ToFqdn converts the name into a fqdn appending a trailing dot. -func ToFqdn(name string) string { - n := len(name) - if n == 0 || name[n-1] == '.' { - return name - } - return name + "." -} - -// UnFqdn converts the fqdn into a name removing the trailing dot. -func UnFqdn(name string) string { - n := len(name) - if n != 0 && name[n-1] == '.' { - return name[:n-1] - } - return name -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go b/vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go deleted file mode 100644 index 03f1a8d12..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/nameserver.go +++ /dev/null @@ -1,232 +0,0 @@ -package dns01 - -import ( - "fmt" - "net" - "strings" - "sync" - "time" - - "github.com/miekg/dns" -) - -const defaultResolvConf = "/etc/resolv.conf" - -// dnsTimeout is used to override the default DNS timeout of 10 seconds. -var dnsTimeout = 10 * time.Second - -var ( - fqdnToZone = map[string]string{} - muFqdnToZone sync.Mutex -) - -var defaultNameservers = []string{ - "google-public-dns-a.google.com:53", - "google-public-dns-b.google.com:53", -} - -// recursiveNameservers are used to pre-check DNS propagation -var recursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers) - -// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing. -func ClearFqdnCache() { - muFqdnToZone.Lock() - fqdnToZone = map[string]string{} - muFqdnToZone.Unlock() -} - -func AddDNSTimeout(timeout time.Duration) ChallengeOption { - return func(_ *Challenge) error { - dnsTimeout = timeout - return nil - } -} - -func AddRecursiveNameservers(nameservers []string) ChallengeOption { - return func(_ *Challenge) error { - recursiveNameservers = ParseNameservers(nameservers) - return nil - } -} - -// getNameservers attempts to get systems nameservers before falling back to the defaults -func getNameservers(path string, defaults []string) []string { - config, err := dns.ClientConfigFromFile(path) - if err != nil || len(config.Servers) == 0 { - return defaults - } - - return ParseNameservers(config.Servers) -} - -func ParseNameservers(servers []string) []string { - var resolvers []string - for _, resolver := range servers { - // ensure all servers have a port number - if _, _, err := net.SplitHostPort(resolver); err != nil { - resolvers = append(resolvers, net.JoinHostPort(resolver, "53")) - } else { - resolvers = append(resolvers, resolver) - } - } - return resolvers -} - -// lookupNameservers returns the authoritative nameservers for the given fqdn. -func lookupNameservers(fqdn string) ([]string, error) { - var authoritativeNss []string - - zone, err := FindZoneByFqdn(fqdn) - if err != nil { - return nil, fmt.Errorf("could not determine the zone: %v", err) - } - - r, err := dnsQuery(zone, dns.TypeNS, recursiveNameservers, true) - if err != nil { - return nil, err - } - - for _, rr := range r.Answer { - if ns, ok := rr.(*dns.NS); ok { - authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns)) - } - } - - if len(authoritativeNss) > 0 { - return authoritativeNss, nil - } - return nil, fmt.Errorf("could not determine authoritative nameservers") -} - -// FindZoneByFqdn determines the zone apex for the given fqdn -// by recursing up the domain labels until the nameserver returns a SOA record in the answer section. -func FindZoneByFqdn(fqdn string) (string, error) { - return FindZoneByFqdnCustom(fqdn, recursiveNameservers) -} - -// FindZoneByFqdnCustom determines the zone apex for the given fqdn -// by recursing up the domain labels until the nameserver returns a SOA record in the answer section. -func FindZoneByFqdnCustom(fqdn string, nameservers []string) (string, error) { - muFqdnToZone.Lock() - defer muFqdnToZone.Unlock() - - // Do we have it cached? - if zone, ok := fqdnToZone[fqdn]; ok { - return zone, nil - } - - var err error - var in *dns.Msg - - labelIndexes := dns.Split(fqdn) - for _, index := range labelIndexes { - domain := fqdn[index:] - - in, err = dnsQuery(domain, dns.TypeSOA, nameservers, true) - if err != nil { - continue - } - - if in == nil { - continue - } - - switch in.Rcode { - case dns.RcodeSuccess: - // Check if we got a SOA RR in the answer section - - if len(in.Answer) == 0 { - continue - } - - // CNAME records cannot/should not exist at the root of a zone. - // So we skip a domain when a CNAME is found. - if dnsMsgContainsCNAME(in) { - continue - } - - for _, ans := range in.Answer { - if soa, ok := ans.(*dns.SOA); ok { - zone := soa.Hdr.Name - fqdnToZone[fqdn] = zone - return zone, nil - } - } - case dns.RcodeNameError: - // NXDOMAIN - default: - // Any response code other than NOERROR and NXDOMAIN is treated as error - return "", fmt.Errorf("unexpected response code '%s' for %s", dns.RcodeToString[in.Rcode], domain) - } - } - - return "", fmt.Errorf("could not find the start of authority for %s%s", fqdn, formatDNSError(in, err)) -} - -// dnsMsgContainsCNAME checks for a CNAME answer in msg -func dnsMsgContainsCNAME(msg *dns.Msg) bool { - for _, ans := range msg.Answer { - if _, ok := ans.(*dns.CNAME); ok { - return true - } - } - return false -} - -func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (*dns.Msg, error) { - m := createDNSMsg(fqdn, rtype, recursive) - - var in *dns.Msg - var err error - - for _, ns := range nameservers { - in, err = sendDNSQuery(m, ns) - if err == nil && len(in.Answer) > 0 { - break - } - } - return in, err -} - -func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg { - m := new(dns.Msg) - m.SetQuestion(fqdn, rtype) - m.SetEdns0(4096, false) - - if !recursive { - m.RecursionDesired = false - } - - return m -} - -func sendDNSQuery(m *dns.Msg, ns string) (*dns.Msg, error) { - udp := &dns.Client{Net: "udp", Timeout: dnsTimeout} - in, _, err := udp.Exchange(m, ns) - - if in != nil && in.Truncated { - tcp := &dns.Client{Net: "tcp", Timeout: dnsTimeout} - // If the TCP request succeeds, the err will reset to nil - in, _, err = tcp.Exchange(m, ns) - } - - return in, err -} - -func formatDNSError(msg *dns.Msg, err error) string { - var parts []string - - if msg != nil { - parts = append(parts, dns.RcodeToString[msg.Rcode]) - } - - if err != nil { - parts = append(parts, fmt.Sprintf("%v", err)) - } - - if len(parts) > 0 { - return ": " + strings.Join(parts, " ") - } - - return "" -} diff --git a/vendor/github.com/go-acme/lego/challenge/dns01/precheck.go b/vendor/github.com/go-acme/lego/challenge/dns01/precheck.go deleted file mode 100644 index 00e09854d..000000000 --- a/vendor/github.com/go-acme/lego/challenge/dns01/precheck.go +++ /dev/null @@ -1,127 +0,0 @@ -package dns01 - -import ( - "errors" - "fmt" - "net" - "strings" - - "github.com/miekg/dns" -) - -// PreCheckFunc checks DNS propagation before notifying ACME that the DNS challenge is ready. -type PreCheckFunc func(fqdn, value string) (bool, error) - -// WrapPreCheckFunc wraps a PreCheckFunc in order to do extra operations before or after -// the main check, put it in a loop, etc. -type WrapPreCheckFunc func(domain, fqdn, value string, check PreCheckFunc) (bool, error) - -// WrapPreCheck Allow to define checks before notifying ACME that the DNS challenge is ready. -func WrapPreCheck(wrap WrapPreCheckFunc) ChallengeOption { - return func(chlg *Challenge) error { - chlg.preCheck.checkFunc = wrap - return nil - } -} - -// AddPreCheck Allow to define checks before notifying ACME that the DNS challenge is ready. -// Deprecated: use WrapPreCheck instead. -func AddPreCheck(preCheck PreCheckFunc) ChallengeOption { - // Prevent race condition - check := preCheck - return func(chlg *Challenge) error { - chlg.preCheck.checkFunc = func(_, fqdn, value string, _ PreCheckFunc) (bool, error) { - if check == nil { - return false, errors.New("invalid preCheck: preCheck is nil") - } - return check(fqdn, value) - } - return nil - } -} - -func DisableCompletePropagationRequirement() ChallengeOption { - return func(chlg *Challenge) error { - chlg.preCheck.requireCompletePropagation = false - return nil - } -} - -type preCheck struct { - // checks DNS propagation before notifying ACME that the DNS challenge is ready. - checkFunc WrapPreCheckFunc - // require the TXT record to be propagated to all authoritative name servers - requireCompletePropagation bool -} - -func newPreCheck() preCheck { - return preCheck{ - requireCompletePropagation: true, - } -} - -func (p preCheck) call(domain, fqdn, value string) (bool, error) { - if p.checkFunc == nil { - return p.checkDNSPropagation(fqdn, value) - } - - return p.checkFunc(domain, fqdn, value, p.checkDNSPropagation) -} - -// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers. -func (p preCheck) checkDNSPropagation(fqdn, value string) (bool, error) { - // Initial attempt to resolve at the recursive NS - r, err := dnsQuery(fqdn, dns.TypeTXT, recursiveNameservers, true) - if err != nil { - return false, err - } - - if !p.requireCompletePropagation { - return true, nil - } - - if r.Rcode == dns.RcodeSuccess { - fqdn = updateDomainWithCName(r, fqdn) - } - - authoritativeNss, err := lookupNameservers(fqdn) - if err != nil { - return false, err - } - - return checkAuthoritativeNss(fqdn, value, authoritativeNss) -} - -// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record. -func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) { - for _, ns := range nameservers { - r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false) - if err != nil { - return false, err - } - - if r.Rcode != dns.RcodeSuccess { - return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn) - } - - var records []string - - var found bool - for _, rr := range r.Answer { - if txt, ok := rr.(*dns.TXT); ok { - record := strings.Join(txt.Txt, "") - records = append(records, record) - if record == value { - found = true - break - } - } - } - - if !found { - return false, fmt.Errorf("NS %s did not return the expected TXT record [fqdn: %s, value: %s]: %s", ns, fqdn, value, strings.Join(records, " ,")) - } - } - - return true, nil -} diff --git a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go b/vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go deleted file mode 100644 index c1cf3d45e..000000000 --- a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge.go +++ /dev/null @@ -1,65 +0,0 @@ -package http01 - -import ( - "fmt" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" -) - -type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error - -// ChallengePath returns the URL path for the `http-01` challenge -func ChallengePath(token string) string { - return "/.well-known/acme-challenge/" + token -} - -type Challenge struct { - core *api.Core - validate ValidateFunc - provider challenge.Provider -} - -func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { - return &Challenge{ - core: core, - validate: validate, - provider: provider, - } -} - -func (c *Challenge) SetProvider(provider challenge.Provider) { - c.provider = provider -} - -func (c *Challenge) Solve(authz acme.Authorization) error { - domain := challenge.GetTargetedDomain(authz) - log.Infof("[%s] acme: Trying to solve HTTP-01", domain) - - chlng, err := challenge.FindChallenge(challenge.HTTP01, authz) - if err != nil { - return err - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - err = c.provider.Present(authz.Identifier.Value, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %v", domain, err) - } - defer func() { - err := c.provider.CleanUp(authz.Identifier.Value, chlng.Token, keyAuth) - if err != nil { - log.Warnf("[%s] acme: error cleaning up: %v", domain, err) - } - }() - - chlng.KeyAuthorization = keyAuth - return c.validate(c.core, domain, chlng) -} diff --git a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go b/vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go deleted file mode 100644 index 9f4429111..000000000 --- a/vendor/github.com/go-acme/lego/challenge/http01/http_challenge_server.go +++ /dev/null @@ -1,96 +0,0 @@ -package http01 - -import ( - "fmt" - "net" - "net/http" - "strings" - - "github.com/go-acme/lego/log" -) - -// ProviderServer implements ChallengeProvider for `http-01` challenge -// It may be instantiated without using the NewProviderServer function if -// you want only to use the default values. -type ProviderServer struct { - iface string - port string - done chan bool - listener net.Listener -} - -// NewProviderServer creates a new ProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 80 respectively. -func NewProviderServer(iface, port string) *ProviderServer { - return &ProviderServer{iface: iface, port: port} -} - -// Present starts a web server and makes the token available at `ChallengePath(token)` for web requests. -func (s *ProviderServer) Present(domain, token, keyAuth string) error { - if s.port == "" { - s.port = "80" - } - - var err error - s.listener, err = net.Listen("tcp", s.GetAddress()) - if err != nil { - return fmt.Errorf("could not start HTTP server for challenge -> %v", err) - } - - s.done = make(chan bool) - go s.serve(domain, token, keyAuth) - return nil -} - -func (s *ProviderServer) GetAddress() string { - return net.JoinHostPort(s.iface, s.port) -} - -// CleanUp closes the HTTP server and removes the token from `ChallengePath(token)` -func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - s.listener.Close() - <-s.done - return nil -} - -func (s *ProviderServer) serve(domain, token, keyAuth string) { - path := ChallengePath(token) - - // The handler validates the HOST header and request type. - // For validation it then writes the token the server returned with the challenge - mux := http.NewServeMux() - mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet { - w.Header().Add("Content-Type", "text/plain") - _, err := w.Write([]byte(keyAuth)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - log.Infof("[%s] Served key authentication", domain) - } else { - log.Warnf("Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method) - _, err := w.Write([]byte("TEST")) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } - }) - - httpServer := &http.Server{Handler: mux} - - // Once httpServer is shut down - // we don't want any lingering connections, so disable KeepAlives. - httpServer.SetKeepAlivesEnabled(false) - - err := httpServer.Serve(s.listener) - if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) - } - s.done <- true -} diff --git a/vendor/github.com/go-acme/lego/challenge/provider.go b/vendor/github.com/go-acme/lego/challenge/provider.go deleted file mode 100644 index d7cc213f7..000000000 --- a/vendor/github.com/go-acme/lego/challenge/provider.go +++ /dev/null @@ -1,28 +0,0 @@ -package challenge - -import "time" - -// Provider enables implementing a custom challenge -// provider. Present presents the solution to a challenge available to -// be solved. CleanUp will be called by the challenge if Present ends -// in a non-error state. -type Provider interface { - Present(domain, token, keyAuth string) error - CleanUp(domain, token, keyAuth string) error -} - -// ProviderTimeout allows for implementing a -// Provider where an unusually long timeout is required when -// waiting for an ACME challenge to be satisfied, such as when -// checking for DNS record propagation. If an implementor of a -// Provider provides a Timeout method, then the return values -// of the Timeout method will be used when appropriate by the acme -// package. The interval value is the time between checks. -// -// The default values used for timeout and interval are 60 seconds and -// 2 seconds respectively. These are used when no Timeout method is -// defined for the Provider. -type ProviderTimeout interface { - Provider - Timeout() (timeout, interval time.Duration) -} diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/errors.go b/vendor/github.com/go-acme/lego/challenge/resolver/errors.go deleted file mode 100644 index 9d6091432..000000000 --- a/vendor/github.com/go-acme/lego/challenge/resolver/errors.go +++ /dev/null @@ -1,25 +0,0 @@ -package resolver - -import ( - "bytes" - "fmt" - "sort" -) - -// obtainError is returned when there are specific errors available per domain. -type obtainError map[string]error - -func (e obtainError) Error() string { - buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n") - - var domains []string - for domain := range e { - domains = append(domains, domain) - } - sort.Strings(domains) - - for _, domain := range domains { - buffer.WriteString(fmt.Sprintf("[%s] %s\n", domain, e[domain])) - } - return buffer.String() -} diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/prober.go b/vendor/github.com/go-acme/lego/challenge/resolver/prober.go deleted file mode 100644 index b787caf13..000000000 --- a/vendor/github.com/go-acme/lego/challenge/resolver/prober.go +++ /dev/null @@ -1,173 +0,0 @@ -package resolver - -import ( - "fmt" - "time" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" -) - -// Interface for all challenge solvers to implement. -type solver interface { - Solve(authorization acme.Authorization) error -} - -// Interface for challenges like dns, where we can set a record in advance for ALL challenges. -// This saves quite a bit of time vs creating the records and solving them serially. -type preSolver interface { - PreSolve(authorization acme.Authorization) error -} - -// Interface for challenges like dns, where we can solve all the challenges before to delete them. -type cleanup interface { - CleanUp(authorization acme.Authorization) error -} - -type sequential interface { - Sequential() (bool, time.Duration) -} - -// an authz with the solver we have chosen and the index of the challenge associated with it -type selectedAuthSolver struct { - authz acme.Authorization - solver solver -} - -type Prober struct { - solverManager *SolverManager -} - -func NewProber(solverManager *SolverManager) *Prober { - return &Prober{ - solverManager: solverManager, - } -} - -// Solve Looks through the challenge combinations to find a solvable match. -// Then solves the challenges in series and returns. -func (p *Prober) Solve(authorizations []acme.Authorization) error { - failures := make(obtainError) - - var authSolvers []*selectedAuthSolver - var authSolversSequential []*selectedAuthSolver - - // Loop through the resources, basically through the domains. - // First pass just selects a solver for each authz. - for _, authz := range authorizations { - domain := challenge.GetTargetedDomain(authz) - if authz.Status == acme.StatusValid { - // Boulder might recycle recent validated authz (see issue #267) - log.Infof("[%s] acme: authorization already valid; skipping challenge", domain) - continue - } - - if solvr := p.solverManager.chooseSolver(authz); solvr != nil { - authSolver := &selectedAuthSolver{authz: authz, solver: solvr} - - switch s := solvr.(type) { - case sequential: - if ok, _ := s.Sequential(); ok { - authSolversSequential = append(authSolversSequential, authSolver) - } else { - authSolvers = append(authSolvers, authSolver) - } - default: - authSolvers = append(authSolvers, authSolver) - } - } else { - failures[domain] = fmt.Errorf("[%s] acme: could not determine solvers", domain) - } - } - - parallelSolve(authSolvers, failures) - - sequentialSolve(authSolversSequential, failures) - - // Be careful not to return an empty failures map, - // for even an empty obtainError is a non-nil error value - if len(failures) > 0 { - return failures - } - return nil -} - -func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) { - for i, authSolver := range authSolvers { - // Submit the challenge - domain := challenge.GetTargetedDomain(authSolver.authz) - - if solvr, ok := authSolver.solver.(preSolver); ok { - err := solvr.PreSolve(authSolver.authz) - if err != nil { - failures[domain] = err - cleanUp(authSolver.solver, authSolver.authz) - continue - } - } - - // Solve challenge - err := authSolver.solver.Solve(authSolver.authz) - if err != nil { - failures[domain] = err - cleanUp(authSolver.solver, authSolver.authz) - continue - } - - // Clean challenge - cleanUp(authSolver.solver, authSolver.authz) - - if len(authSolvers)-1 > i { - solvr := authSolver.solver.(sequential) - _, interval := solvr.Sequential() - log.Infof("sequence: wait for %s", interval) - time.Sleep(interval) - } - } -} - -func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) { - // For all valid preSolvers, first submit the challenges so they have max time to propagate - for _, authSolver := range authSolvers { - authz := authSolver.authz - if solvr, ok := authSolver.solver.(preSolver); ok { - err := solvr.PreSolve(authz) - if err != nil { - failures[challenge.GetTargetedDomain(authz)] = err - } - } - } - - defer func() { - // Clean all created TXT records - for _, authSolver := range authSolvers { - cleanUp(authSolver.solver, authSolver.authz) - } - }() - - // Finally solve all challenges for real - for _, authSolver := range authSolvers { - authz := authSolver.authz - domain := challenge.GetTargetedDomain(authz) - if failures[domain] != nil { - // already failed in previous loop - continue - } - - err := authSolver.solver.Solve(authz) - if err != nil { - failures[domain] = err - } - } -} - -func cleanUp(solvr solver, authz acme.Authorization) { - if solvr, ok := solvr.(cleanup); ok { - domain := challenge.GetTargetedDomain(authz) - err := solvr.CleanUp(authz) - if err != nil { - log.Warnf("[%s] acme: error cleaning up: %v ", domain, err) - } - } -} diff --git a/vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go b/vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go deleted file mode 100644 index de6f02db8..000000000 --- a/vendor/github.com/go-acme/lego/challenge/resolver/solver_manager.go +++ /dev/null @@ -1,169 +0,0 @@ -package resolver - -import ( - "context" - "errors" - "fmt" - "sort" - "strconv" - "time" - - "github.com/cenkalti/backoff" - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/dns01" - "github.com/go-acme/lego/challenge/http01" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/log" -) - -type byType []acme.Challenge - -func (a byType) Len() int { return len(a) } -func (a byType) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byType) Less(i, j int) bool { return a[i].Type > a[j].Type } - -type SolverManager struct { - core *api.Core - solvers map[challenge.Type]solver -} - -func NewSolversManager(core *api.Core) *SolverManager { - return &SolverManager{ - solvers: map[challenge.Type]solver{}, - core: core, - } -} - -// SetHTTP01Provider specifies a custom provider p that can solve the given HTTP-01 challenge. -func (c *SolverManager) SetHTTP01Provider(p challenge.Provider) error { - c.solvers[challenge.HTTP01] = http01.NewChallenge(c.core, validate, p) - return nil -} - -// SetTLSALPN01Provider specifies a custom provider p that can solve the given TLS-ALPN-01 challenge. -func (c *SolverManager) SetTLSALPN01Provider(p challenge.Provider) error { - c.solvers[challenge.TLSALPN01] = tlsalpn01.NewChallenge(c.core, validate, p) - return nil -} - -// SetDNS01Provider specifies a custom provider p that can solve the given DNS-01 challenge. -func (c *SolverManager) SetDNS01Provider(p challenge.Provider, opts ...dns01.ChallengeOption) error { - c.solvers[challenge.DNS01] = dns01.NewChallenge(c.core, validate, p, opts...) - return nil -} - -// Remove Remove a challenge type from the available solvers. -func (c *SolverManager) Remove(chlgType challenge.Type) { - delete(c.solvers, chlgType) -} - -// Checks all challenges from the server in order and returns the first matching solver. -func (c *SolverManager) chooseSolver(authz acme.Authorization) solver { - // Allow to have a deterministic challenge order - sort.Sort(byType(authz.Challenges)) - - domain := challenge.GetTargetedDomain(authz) - for _, chlg := range authz.Challenges { - if solvr, ok := c.solvers[challenge.Type(chlg.Type)]; ok { - log.Infof("[%s] acme: use %s solver", domain, chlg.Type) - return solvr - } - log.Infof("[%s] acme: Could not find solver for: %s", domain, chlg.Type) - } - - return nil -} - -func validate(core *api.Core, domain string, chlg acme.Challenge) error { - chlng, err := core.Challenges.New(chlg.URL) - if err != nil { - return fmt.Errorf("failed to initiate challenge: %v", err) - } - - valid, err := checkChallengeStatus(chlng) - if err != nil { - return err - } - - if valid { - log.Infof("[%s] The server validated our request", domain) - return nil - } - - ra, err := strconv.Atoi(chlng.RetryAfter) - if err != nil { - // The ACME server MUST return a Retry-After. - // If it doesn't, we'll just poll hard. - // Boulder does not implement the ability to retry challenges or the Retry-After header. - // https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82 - ra = 5 - } - initialInterval := time.Duration(ra) * time.Second - - bo := backoff.NewExponentialBackOff() - bo.InitialInterval = initialInterval - bo.MaxInterval = 10 * initialInterval - bo.MaxElapsedTime = 100 * initialInterval - - ctx, cancel := context.WithCancel(context.Background()) - - // After the path is sent, the ACME server will access our server. - // Repeatedly check the server for an updated status on our request. - operation := func() error { - authz, err := core.Authorizations.Get(chlng.AuthorizationURL) - if err != nil { - cancel() - return err - } - - valid, err := checkAuthorizationStatus(authz) - if err != nil { - cancel() - return err - } - - if valid { - log.Infof("[%s] The server validated our request", domain) - return nil - } - - return errors.New("the server didn't respond to our request") - } - - return backoff.Retry(operation, backoff.WithContext(bo, ctx)) -} - -func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) { - switch chlng.Status { - case acme.StatusValid: - return true, nil - case acme.StatusPending, acme.StatusProcessing: - return false, nil - case acme.StatusInvalid: - return false, chlng.Error - default: - return false, errors.New("the server returned an unexpected state") - } -} - -func checkAuthorizationStatus(authz acme.Authorization) (bool, error) { - switch authz.Status { - case acme.StatusValid: - return true, nil - case acme.StatusPending, acme.StatusProcessing: - return false, nil - case acme.StatusDeactivated, acme.StatusExpired, acme.StatusRevoked: - return false, fmt.Errorf("the authorization state %s", authz.Status) - case acme.StatusInvalid: - for _, chlg := range authz.Challenges { - if chlg.Status == acme.StatusInvalid && chlg.Error != nil { - return false, chlg.Error - } - } - return false, fmt.Errorf("the authorization state %s", authz.Status) - default: - return false, errors.New("the server returned an unexpected state") - } -} diff --git a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go b/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go deleted file mode 100644 index a3fa7ef46..000000000 --- a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge.go +++ /dev/null @@ -1,129 +0,0 @@ -package tlsalpn01 - -import ( - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "crypto/x509/pkix" - "encoding/asn1" - "fmt" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/log" -) - -// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension. -// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1 -var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} - -type ValidateFunc func(core *api.Core, domain string, chlng acme.Challenge) error - -type Challenge struct { - core *api.Core - validate ValidateFunc - provider challenge.Provider -} - -func NewChallenge(core *api.Core, validate ValidateFunc, provider challenge.Provider) *Challenge { - return &Challenge{ - core: core, - validate: validate, - provider: provider, - } -} - -func (c *Challenge) SetProvider(provider challenge.Provider) { - c.provider = provider -} - -// Solve manages the provider to validate and solve the challenge. -func (c *Challenge) Solve(authz acme.Authorization) error { - domain := authz.Identifier.Value - log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", challenge.GetTargetedDomain(authz)) - - chlng, err := challenge.FindChallenge(challenge.TLSALPN01, authz) - if err != nil { - return err - } - - // Generate the Key Authorization for the challenge - keyAuth, err := c.core.GetKeyAuthorization(chlng.Token) - if err != nil { - return err - } - - err = c.provider.Present(domain, chlng.Token, keyAuth) - if err != nil { - return fmt.Errorf("[%s] acme: error presenting token: %v", challenge.GetTargetedDomain(authz), err) - } - defer func() { - err := c.provider.CleanUp(domain, chlng.Token, keyAuth) - if err != nil { - log.Warnf("[%s] acme: error cleaning up: %v", challenge.GetTargetedDomain(authz), err) - } - }() - - chlng.KeyAuthorization = keyAuth - return c.validate(c.core, domain, chlng) -} - -// ChallengeBlocks returns PEM blocks (certPEMBlock, keyPEMBlock) with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func ChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) { - // Compute the SHA-256 digest of the key authorization. - zBytes := sha256.Sum256([]byte(keyAuth)) - - value, err := asn1.Marshal(zBytes[:sha256.Size]) - if err != nil { - return nil, nil, err - } - - // Add the keyAuth digest as the acmeValidation-v1 extension - // (marked as critical such that it won't be used by non-ACME software). - // Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3 - extensions := []pkix.Extension{ - { - Id: idPeAcmeIdentifierV1, - Critical: true, - Value: value, - }, - } - - // Generate a new RSA key for the certificates. - tempPrivateKey, err := certcrypto.GeneratePrivateKey(certcrypto.RSA2048) - if err != nil { - return nil, nil, err - } - - rsaPrivateKey := tempPrivateKey.(*rsa.PrivateKey) - - // Generate the PEM certificate using the provided private key, domain, and extra extensions. - tempCertPEM, err := certcrypto.GeneratePemCert(rsaPrivateKey, domain, extensions) - if err != nil { - return nil, nil, err - } - - // Encode the private key into a PEM format. We'll need to use it to generate the x509 keypair. - rsaPrivatePEM := certcrypto.PEMEncode(rsaPrivateKey) - - return tempCertPEM, rsaPrivatePEM, nil -} - -// ChallengeCert returns a certificate with the acmeValidation-v1 extension -// and domain name for the `tls-alpn-01` challenge. -func ChallengeCert(domain, keyAuth string) (*tls.Certificate, error) { - tempCertPEM, rsaPrivatePEM, err := ChallengeBlocks(domain, keyAuth) - if err != nil { - return nil, err - } - - cert, err := tls.X509KeyPair(tempCertPEM, rsaPrivatePEM) - if err != nil { - return nil, err - } - - return &cert, nil -} diff --git a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go b/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go deleted file mode 100644 index 61e353bef..000000000 --- a/vendor/github.com/go-acme/lego/challenge/tlsalpn01/tls_alpn_challenge_server.go +++ /dev/null @@ -1,95 +0,0 @@ -package tlsalpn01 - -import ( - "crypto/tls" - "fmt" - "net" - "net/http" - "strings" - - "github.com/go-acme/lego/log" -) - -const ( - // ACMETLS1Protocol is the ALPN Protocol ID for the ACME-TLS/1 Protocol. - ACMETLS1Protocol = "acme-tls/1" - - // defaultTLSPort is the port that the ProviderServer will default to - // when no other port is provided. - defaultTLSPort = "443" -) - -// ProviderServer implements ChallengeProvider for `TLS-ALPN-01` challenge. -// It may be instantiated without using the NewProviderServer -// if you want only to use the default values. -type ProviderServer struct { - iface string - port string - listener net.Listener -} - -// NewProviderServer creates a new ProviderServer on the selected interface and port. -// Setting iface and / or port to an empty string will make the server fall back to -// the "any" interface and port 443 respectively. -func NewProviderServer(iface, port string) *ProviderServer { - return &ProviderServer{iface: iface, port: port} -} - -func (s *ProviderServer) GetAddress() string { - return net.JoinHostPort(s.iface, s.port) -} - -// Present generates a certificate with a SHA-256 digest of the keyAuth provided -// as the acmeValidation-v1 extension value to conform to the ACME-TLS-ALPN spec. -func (s *ProviderServer) Present(domain, token, keyAuth string) error { - if s.port == "" { - // Fallback to port 443 if the port was not provided. - s.port = defaultTLSPort - } - - // Generate the challenge certificate using the provided keyAuth and domain. - cert, err := ChallengeCert(domain, keyAuth) - if err != nil { - return err - } - - // Place the generated certificate with the extension into the TLS config - // so that it can serve the correct details. - tlsConf := new(tls.Config) - tlsConf.Certificates = []tls.Certificate{*cert} - - // We must set that the `acme-tls/1` application level protocol is supported - // so that the protocol negotiation can succeed. Reference: - // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.2 - tlsConf.NextProtos = []string{ACMETLS1Protocol} - - // Create the listener with the created tls.Config. - s.listener, err = tls.Listen("tcp", s.GetAddress(), tlsConf) - if err != nil { - return fmt.Errorf("could not start HTTPS server for challenge -> %v", err) - } - - // Shut the server down when we're finished. - go func() { - err := http.Serve(s.listener, nil) - if err != nil && !strings.Contains(err.Error(), "use of closed network connection") { - log.Println(err) - } - }() - - return nil -} - -// CleanUp closes the HTTPS server. -func (s *ProviderServer) CleanUp(domain, token, keyAuth string) error { - if s.listener == nil { - return nil - } - - // Server was created, close it. - if err := s.listener.Close(); err != nil && err != http.ErrServerClosed { - return err - } - - return nil -} diff --git a/vendor/github.com/go-acme/lego/lego/client.go b/vendor/github.com/go-acme/lego/lego/client.go deleted file mode 100644 index c55dd25d3..000000000 --- a/vendor/github.com/go-acme/lego/lego/client.go +++ /dev/null @@ -1,74 +0,0 @@ -package lego - -import ( - "errors" - "net/url" - - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge/resolver" - "github.com/go-acme/lego/registration" -) - -// Client is the user-friendly way to ACME -type Client struct { - Certificate *certificate.Certifier - Challenge *resolver.SolverManager - Registration *registration.Registrar - core *api.Core -} - -// NewClient creates a new ACME client on behalf of the user. -// The client will depend on the ACME directory located at CADirURL for the rest of its actions. -// A private key of type keyType (see KeyType constants) will be generated when requesting a new certificate if one isn't provided. -func NewClient(config *Config) (*Client, error) { - if config == nil { - return nil, errors.New("a configuration must be provided") - } - - _, err := url.Parse(config.CADirURL) - if err != nil { - return nil, err - } - - if config.HTTPClient == nil { - return nil, errors.New("the HTTP client cannot be nil") - } - - privateKey := config.User.GetPrivateKey() - if privateKey == nil { - return nil, errors.New("private key was nil") - } - - var kid string - if reg := config.User.GetRegistration(); reg != nil { - kid = reg.URI - } - - core, err := api.New(config.HTTPClient, config.UserAgent, config.CADirURL, kid, privateKey) - if err != nil { - return nil, err - } - - solversManager := resolver.NewSolversManager(core) - - prober := resolver.NewProber(solversManager) - certifier := certificate.NewCertifier(core, prober, certificate.CertifierOptions{KeyType: config.Certificate.KeyType, Timeout: config.Certificate.Timeout}) - - return &Client{ - Certificate: certifier, - Challenge: solversManager, - Registration: registration.NewRegistrar(core, config.User), - core: core, - }, nil -} - -// GetToSURL returns the current ToS URL from the Directory -func (c *Client) GetToSURL() string { - return c.core.GetDirectory().Meta.TermsOfService -} - -// GetExternalAccountRequired returns the External Account Binding requirement of the Directory -func (c *Client) GetExternalAccountRequired() bool { - return c.core.GetDirectory().Meta.ExternalAccountRequired -} diff --git a/vendor/github.com/go-acme/lego/lego/client_config.go b/vendor/github.com/go-acme/lego/lego/client_config.go deleted file mode 100644 index 2421d7fa3..000000000 --- a/vendor/github.com/go-acme/lego/lego/client_config.go +++ /dev/null @@ -1,104 +0,0 @@ -package lego - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "net" - "net/http" - "os" - "time" - - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/registration" -) - -const ( - // caCertificatesEnvVar is the environment variable name that can be used to - // specify the path to PEM encoded CA Certificates that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caCertificatesEnvVar = "LEGO_CA_CERTIFICATES" - - // caServerNameEnvVar is the environment variable name that can be used to - // specify the CA server name that can be used to - // authenticate an ACME server with a HTTPS certificate not issued by a CA in - // the system-wide trusted root list. - caServerNameEnvVar = "LEGO_CA_SERVER_NAME" - - // LEDirectoryProduction URL to the Let's Encrypt production - LEDirectoryProduction = "https://acme-v02.api.letsencrypt.org/directory" - - // LEDirectoryStaging URL to the Let's Encrypt staging - LEDirectoryStaging = "https://acme-staging-v02.api.letsencrypt.org/directory" -) - -type Config struct { - CADirURL string - User registration.User - UserAgent string - HTTPClient *http.Client - Certificate CertificateConfig -} - -func NewConfig(user registration.User) *Config { - return &Config{ - CADirURL: LEDirectoryProduction, - User: user, - HTTPClient: createDefaultHTTPClient(), - Certificate: CertificateConfig{ - KeyType: certcrypto.RSA2048, - Timeout: 30 * time.Second, - }, - } -} - -type CertificateConfig struct { - KeyType certcrypto.KeyType - Timeout time.Duration -} - -// createDefaultHTTPClient Creates an HTTP client with a reasonable timeout value -// and potentially a custom *x509.CertPool -// based on the caCertificatesEnvVar environment variable (see the `initCertPool` function) -func createDefaultHTTPClient() *http.Client { - return &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).DialContext, - TLSHandshakeTimeout: 15 * time.Second, - ResponseHeaderTimeout: 15 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{ - ServerName: os.Getenv(caServerNameEnvVar), - RootCAs: initCertPool(), - }, - }, - } -} - -// initCertPool creates a *x509.CertPool populated with the PEM certificates -// found in the filepath specified in the caCertificatesEnvVar OS environment -// variable. If the caCertificatesEnvVar is not set then initCertPool will -// return nil. If there is an error creating a *x509.CertPool from the provided -// caCertificatesEnvVar value then initCertPool will panic. -func initCertPool() *x509.CertPool { - if customCACertsPath := os.Getenv(caCertificatesEnvVar); customCACertsPath != "" { - customCAs, err := ioutil.ReadFile(customCACertsPath) - if err != nil { - panic(fmt.Sprintf("error reading %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - certPool := x509.NewCertPool() - if ok := certPool.AppendCertsFromPEM(customCAs); !ok { - panic(fmt.Sprintf("error creating x509 cert pool from %s=%q: %v", - caCertificatesEnvVar, customCACertsPath, err)) - } - return certPool - } - return nil -} diff --git a/vendor/github.com/go-acme/lego/log/logger.go b/vendor/github.com/go-acme/lego/log/logger.go deleted file mode 100644 index 22ec98f0a..000000000 --- a/vendor/github.com/go-acme/lego/log/logger.go +++ /dev/null @@ -1,59 +0,0 @@ -package log - -import ( - "log" - "os" -) - -// Logger is an optional custom logger. -var Logger StdLogger = log.New(os.Stdout, "", log.LstdFlags) - -// StdLogger interface for Standard Logger. -type StdLogger interface { - Fatal(args ...interface{}) - Fatalln(args ...interface{}) - Fatalf(format string, args ...interface{}) - Print(args ...interface{}) - Println(args ...interface{}) - Printf(format string, args ...interface{}) -} - -// Fatal writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Fatal(args ...interface{}) { - Logger.Fatal(args...) -} - -// Fatalf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Fatalf(format string, args ...interface{}) { - Logger.Fatalf(format, args...) -} - -// Print writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Print(args ...interface{}) { - Logger.Print(args...) -} - -// Println writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Println(args ...interface{}) { - Logger.Println(args...) -} - -// Printf writes a log entry. -// It uses Logger if not nil, otherwise it uses the default log.Logger. -func Printf(format string, args ...interface{}) { - Logger.Printf(format, args...) -} - -// Warnf writes a log entry. -func Warnf(format string, args ...interface{}) { - Printf("[WARN] "+format, args...) -} - -// Infof writes a log entry. -func Infof(format string, args ...interface{}) { - Printf("[INFO] "+format, args...) -} diff --git a/vendor/github.com/go-acme/lego/platform/wait/wait.go b/vendor/github.com/go-acme/lego/platform/wait/wait.go deleted file mode 100644 index 97af5dceb..000000000 --- a/vendor/github.com/go-acme/lego/platform/wait/wait.go +++ /dev/null @@ -1,33 +0,0 @@ -package wait - -import ( - "fmt" - "time" - - "github.com/go-acme/lego/log" -) - -// For polls the given function 'f', once every 'interval', up to 'timeout'. -func For(msg string, timeout, interval time.Duration, f func() (bool, error)) error { - log.Infof("Wait for %s [timeout: %s, interval: %s]", msg, timeout, interval) - - var lastErr string - timeUp := time.After(timeout) - for { - select { - case <-timeUp: - return fmt.Errorf("time limit exceeded: last error: %s", lastErr) - default: - } - - stop, err := f() - if stop { - return nil - } - if err != nil { - lastErr = err.Error() - } - - time.Sleep(interval) - } -} diff --git a/vendor/github.com/go-acme/lego/registration/registar.go b/vendor/github.com/go-acme/lego/registration/registar.go deleted file mode 100644 index 09e866574..000000000 --- a/vendor/github.com/go-acme/lego/registration/registar.go +++ /dev/null @@ -1,146 +0,0 @@ -package registration - -import ( - "errors" - "net/http" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/acme/api" - "github.com/go-acme/lego/log" -) - -// Resource represents all important information about a registration -// of which the client needs to keep track itself. -// Deprecated: will be remove in the future (acme.ExtendedAccount). -type Resource struct { - Body acme.Account `json:"body,omitempty"` - URI string `json:"uri,omitempty"` -} - -type RegisterOptions struct { - TermsOfServiceAgreed bool -} - -type RegisterEABOptions struct { - TermsOfServiceAgreed bool - Kid string - HmacEncoded string -} - -type Registrar struct { - core *api.Core - user User -} - -func NewRegistrar(core *api.Core, user User) *Registrar { - return &Registrar{ - core: core, - user: user, - } -} - -// Register the current account to the ACME server. -func (r *Registrar) Register(options RegisterOptions) (*Resource, error) { - if r == nil || r.user == nil { - return nil, errors.New("acme: cannot register a nil client or user") - } - - accMsg := acme.Account{ - TermsOfServiceAgreed: options.TermsOfServiceAgreed, - Contact: []string{}, - } - - if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) - accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} - } - - account, err := r.core.Accounts.New(accMsg) - if err != nil { - // FIXME seems impossible - errorDetails, ok := err.(acme.ProblemDetails) - if !ok || errorDetails.HTTPStatus != http.StatusConflict { - return nil, err - } - } - - return &Resource{URI: account.Location, Body: account.Account}, nil -} - -// RegisterWithExternalAccountBinding Register the current account to the ACME server. -func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOptions) (*Resource, error) { - accMsg := acme.Account{ - TermsOfServiceAgreed: options.TermsOfServiceAgreed, - Contact: []string{}, - } - - if r.user.GetEmail() != "" { - log.Infof("acme: Registering account for %s", r.user.GetEmail()) - accMsg.Contact = []string{"mailto:" + r.user.GetEmail()} - } - - account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded) - if err != nil { - errorDetails, ok := err.(acme.ProblemDetails) - // FIXME seems impossible - if !ok || errorDetails.HTTPStatus != http.StatusConflict { - return nil, err - } - } - - return &Resource{URI: account.Location, Body: account.Account}, nil -} - -// QueryRegistration runs a POST request on the client's registration and returns the result. -// -// This is similar to the Register function, -// but acting on an existing registration link and resource. -func (r *Registrar) QueryRegistration() (*Resource, error) { - if r == nil || r.user == nil { - return nil, errors.New("acme: cannot query the registration of a nil client or user") - } - - // Log the URL here instead of the email as the email may not be set - log.Infof("acme: Querying account for %s", r.user.GetRegistration().URI) - - account, err := r.core.Accounts.Get(r.user.GetRegistration().URI) - if err != nil { - return nil, err - } - - return &Resource{ - Body: account, - // Location: header is not returned so this needs to be populated off of existing URI - URI: r.user.GetRegistration().URI, - }, nil -} - -// DeleteRegistration deletes the client's user registration from the ACME server. -func (r *Registrar) DeleteRegistration() error { - if r == nil || r.user == nil { - return errors.New("acme: cannot unregister a nil client or user") - } - - log.Infof("acme: Deleting account for %s", r.user.GetEmail()) - - return r.core.Accounts.Deactivate(r.user.GetRegistration().URI) -} - -// ResolveAccountByKey will attempt to look up an account using the given account key -// and return its registration resource. -func (r *Registrar) ResolveAccountByKey() (*Resource, error) { - log.Infof("acme: Trying to resolve account by key") - - accMsg := acme.Account{OnlyReturnExisting: true} - accountTransit, err := r.core.Accounts.New(accMsg) - if err != nil { - return nil, err - } - - account, err := r.core.Accounts.Get(accountTransit.Location) - if err != nil { - return nil, err - } - - return &Resource{URI: accountTransit.Location, Body: account}, nil -} diff --git a/vendor/github.com/go-acme/lego/registration/user.go b/vendor/github.com/go-acme/lego/registration/user.go deleted file mode 100644 index 1e29300e2..000000000 --- a/vendor/github.com/go-acme/lego/registration/user.go +++ /dev/null @@ -1,13 +0,0 @@ -package registration - -import ( - "crypto" -) - -// User interface is to be implemented by users of this library. -// It is used by the client type to get user specific information. -type User interface { - GetEmail() string - GetRegistration() *Resource - GetPrivateKey() crypto.PrivateKey -} diff --git a/vendor/github.com/go-logr/zapr/.gitignore b/vendor/github.com/go-logr/zapr/.gitignore deleted file mode 100644 index 5ba77727f..000000000 --- a/vendor/github.com/go-logr/zapr/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*~ -*.swp -/vendor diff --git a/vendor/github.com/go-logr/zapr/Gopkg.lock b/vendor/github.com/go-logr/zapr/Gopkg.lock deleted file mode 100644 index 8da0a8f76..000000000 --- a/vendor/github.com/go-logr/zapr/Gopkg.lock +++ /dev/null @@ -1,52 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - digest = "1:edd2fa4578eb086265db78a9201d15e76b298dfd0d5c379da83e9c61712cf6df" - name = "github.com/go-logr/logr" - packages = ["."] - pruneopts = "UT" - revision = "9fb12b3b21c5415d16ac18dc5cd42c1cfdd40c4e" - version = "v0.1.0" - -[[projects]] - digest = "1:3c1a69cdae3501bf75e76d0d86dc6f2b0a7421bc205c0cb7b96b19eed464a34d" - name = "go.uber.org/atomic" - packages = ["."] - pruneopts = "UT" - revision = "1ea20fb1cbb1cc08cbd0d913a96dead89aa18289" - version = "v1.3.2" - -[[projects]] - digest = "1:60bf2a5e347af463c42ed31a493d817f8a72f102543060ed992754e689805d1a" - name = "go.uber.org/multierr" - packages = ["."] - pruneopts = "UT" - revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" - version = "v1.1.0" - -[[projects]] - digest = "1:9580b1b079114140ade8cec957685344d14f00119e0241f6b369633cb346eeb3" - name = "go.uber.org/zap" - packages = [ - ".", - "buffer", - "internal/bufferpool", - "internal/color", - "internal/exit", - "zapcore", - ] - pruneopts = "UT" - revision = "eeedf312bc6c57391d84767a4cd413f02a917974" - version = "v1.8.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/go-logr/logr", - "go.uber.org/zap", - "go.uber.org/zap/zapcore", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/go-logr/zapr/Gopkg.toml b/vendor/github.com/go-logr/zapr/Gopkg.toml deleted file mode 100644 index ae475d72e..000000000 --- a/vendor/github.com/go-logr/zapr/Gopkg.toml +++ /dev/null @@ -1,38 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - - -[[constraint]] - name = "github.com/go-logr/logr" - version = "0.1.0" - -[[constraint]] - name = "go.uber.org/zap" - version = "1.8.0" - -[prune] - go-tests = true - unused-packages = true diff --git a/vendor/github.com/go-logr/zapr/README.md b/vendor/github.com/go-logr/zapr/README.md deleted file mode 100644 index 8472875fa..000000000 --- a/vendor/github.com/go-logr/zapr/README.md +++ /dev/null @@ -1,45 +0,0 @@ -Zapr :zap: -========== - -A [logr](https://github.com/go-logr/logr) implementation using -[Zap](go.uber.org/zap). - -Usage ------ - -```go -import ( - "fmt" - - "go.uber.org/zap" - "github.com/go-logr/logr" - "github.com/directxman12/zapr" -) - -func main() { - var log logr.Logger - - zapLog, err := zap.NewDevelopment() - if err != nil { - panic(fmt.Sprintf("who watches the watchmen (%v)?", err)) - } - log = zapr.NewLogger(zapLog) - - log.Info("Logr in action!", "the answer", 42) -} -``` - -Implementation Details ----------------------- - -For the most part, concepts in Zap correspond directly with those in logr. - -Unlike Zap, all fields *must* be in the form of suggared fields -- -it's illegal to pass a strongly-typed Zap field in a key position to any -of the logging methods (`Log`, `Error`). - -Levels in logr correspond to custom debug levels in Zap. Any given level -in logr is represents by its inverse in Zap (`zapLevel = -1*logrLevel`). - -For example `V(2)` is equivalent to log level -2 in Zap, while `V(1)` is -equivalent to Zap's `DebugLevel`. diff --git a/vendor/github.com/go-logr/zapr/zapr.go b/vendor/github.com/go-logr/zapr/zapr.go deleted file mode 100644 index a9a10ae2e..000000000 --- a/vendor/github.com/go-logr/zapr/zapr.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2018 Solly Ross -// -// 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 zapr defines an implementation of the github.com/go-logr/logr -// interfaces built on top of Zap (go.uber.org/zap). -// -// Usage -// -// A new logr.Logger can be constructed from an existing zap.Logger using -// the NewLogger function: -// -// log := zapr.NewLogger(someZapLogger) -// -// Implementation Details -// -// For the most part, concepts in Zap correspond directly with those in -// logr. -// -// Unlike Zap, all fields *must* be in the form of suggared fields -- -// it's illegal to pass a strongly-typed Zap field in a key position -// to any of the log methods. -// -// Levels in logr correspond to custom debug levels in Zap. Any given level -// in logr is represents by its inverse in zap (`zapLevel = -1*logrLevel`). -// For example V(2) is equivalent to log level -2 in Zap, while V(1) is -// equivalent to Zap's DebugLevel. -package zapr - -import ( - "github.com/go-logr/logr" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// noopInfoLogger is a logr.InfoLogger that's always disabled, and does nothing. -type noopInfoLogger struct{} - -func (l *noopInfoLogger) Enabled() bool { return false } -func (l *noopInfoLogger) Info(_ string, _ ...interface{}) {} - -var disabledInfoLogger = &noopInfoLogger{} - -// NB: right now, we always use the equivalent of sugared logging. -// This is necessary, since logr doesn't define non-suggared types, -// and using zap-specific non-suggared types would make uses tied -// directly to Zap. - -// infoLogger is a logr.InfoLogger that uses Zap to log at a particular -// level. The level has already been converted to a Zap level, which -// is to say that `logrLevel = -1*zapLevel`. -type infoLogger struct { - lvl zapcore.Level - l *zap.Logger -} - -func (l *infoLogger) Enabled() bool { return true } -func (l *infoLogger) Info(msg string, keysAndVals ...interface{}) { - if checkedEntry := l.l.Check(l.lvl, msg); checkedEntry != nil { - checkedEntry.Write(handleFields(l.l, keysAndVals)...) - } -} - -// zapLogger is a logr.Logger that uses Zap to log. -type zapLogger struct { - // NB: this looks very similar to zap.SugaredLogger, but - // deals with our desire to have multiple verbosity levels. - l *zap.Logger - infoLogger -} - -// handleFields converts a bunch of arbitrary key-value pairs into Zap fields. It takes -// additional pre-converted Zap fields, for use with automatically attached fields, like -// `error`. -func handleFields(l *zap.Logger, args []interface{}, additional ...zap.Field) []zap.Field { - // a slightly modified version of zap.SugaredLogger.sweetenFields - if len(args) == 0 { - // fast-return if we have no suggared fields. - return additional - } - - // unlike Zap, we can be pretty sure users aren't passing structured - // fields (since logr has no concept of that), so guess that we need a - // little less space. - fields := make([]zap.Field, 0, len(args)/2+len(additional)) - for i := 0; i < len(args); { - // check just in case for strongly-typed Zap fields, which is illegal (since - // it breaks implementation agnosticism), so we can give a better error message. - if _, ok := args[i].(zap.Field); ok { - l.DPanic("strongly-typed Zap Field passed to logr", zap.Any("zap field", args[i])) - break - } - - // make sure this isn't a mismatched key - if i == len(args)-1 { - l.DPanic("odd number of arguments passed as key-value pairs for logging", zap.Any("ignored key", args[i])) - break - } - - // process a key-value pair, - // ensuring that the key is a string - key, val := args[i], args[i+1] - keyStr, isString := key.(string) - if !isString { - // if the key isn't a string, DPanic and stop logging - l.DPanic("non-string key argument passed to logging, ignoring all later arguments", zap.Any("invalid key", key)) - break - } - - fields = append(fields, zap.Any(keyStr, val)) - i += 2 - } - - return append(fields, additional...) -} - -func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) { - if checkedEntry := l.l.Check(zap.ErrorLevel, msg); checkedEntry != nil { - checkedEntry.Write(handleFields(l.l, keysAndVals, zap.Error(err))...) - } -} - -func (l *zapLogger) V(level int) logr.InfoLogger { - lvl := zapcore.Level(-1 * level) - if l.l.Core().Enabled(lvl) { - return &infoLogger{ - lvl: lvl, - l: l.l, - } - } - return disabledInfoLogger -} - -func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger { - newLogger := l.l.With(handleFields(l.l, keysAndValues)...) - return NewLogger(newLogger) -} - -func (l *zapLogger) WithName(name string) logr.Logger { - newLogger := l.l.Named(name) - return NewLogger(newLogger) -} - -// NewLogger creates a new logr.Logger using the given Zap Logger to log. -func NewLogger(l *zap.Logger) logr.Logger { - return &zapLogger{ - l: l, - infoLogger: infoLogger{ - l: l, - lvl: zap.InfoLevel, - }, - } -} diff --git a/vendor/github.com/go-openapi/analysis/.gitignore b/vendor/github.com/go-openapi/analysis/.gitignore new file mode 100644 index 000000000..87c3bd3e6 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +coverage.txt +*.cov +.idea diff --git a/vendor/github.com/go-openapi/analysis/.golangci.yml b/vendor/github.com/go-openapi/analysis/.golangci.yml new file mode 100644 index 000000000..212c13bf9 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.golangci.yml @@ -0,0 +1,21 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - gochecknoglobals + - gochecknoinits diff --git a/vendor/github.com/go-openapi/analysis/.travis.yml b/vendor/github.com/go-openapi/analysis/.travis.yml new file mode 100644 index 000000000..7ecf865c2 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: Sf7kZf7ZGbnwWUMpffHwMu5A0cHkLK2MYY32LNTPj4+/3qC3Ghl7+9v4TSLOqOlCwdRNjOGblAq7s+GDJed6/xgRQl1JtCi1klzZNrYX4q01pgTPvvGcwbBkIYgeMaPeIRcK9OZnud7sRXdttozgTOpytps2U6Js32ip7uj5mHSg2ub0FwoSJwlS6dbezZ8+eDhoha0F/guY99BEwx8Bd+zROrT2TFGsSGOFGN6wFc7moCqTHO/YkWib13a2QNXqOxCCVBy/lt76Wp+JkeFppjHlzs/2lP3EAk13RIUAaesdEUHvIHrzCyNJEd3/+KO2DzsWOYfpktd+KBCvgaYOsoo7ubdT3IROeAegZdCgo/6xgCEsmFc9ZcqCfN5yNx2A+BZ2Vwmpws+bQ1E1+B5HDzzaiLcYfG4X2O210QVGVDLWsv1jqD+uPYeHY2WRfh5ZsIUFvaqgUEnwHwrK44/8REAhQavt1QAj5uJpsRd7CkRVPWRNK+yIky+wgbVUFEchRNmS55E7QWf+W4+4QZkQi7vUTMc9nbTUu2Es9NfvfudOpM2wZbn98fjpb/qq/nRv6Bk+ca+7XD5/IgNLMbWp2ouDdzbiHLCOfDUiHiDJhLfFZx9Bwo7ZwfzeOlbrQX66bx7xRKYmOe4DLrXhNcpbsMa8qbfxlZRCmYbubB/Y8h4= +script: +- gotestsum -f short-verbose -- -race -timeout=20m -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/cloud.google.com/go/LICENSE b/vendor/github.com/go-openapi/analysis/LICENSE similarity index 100% rename from vendor/cloud.google.com/go/LICENSE rename to vendor/github.com/go-openapi/analysis/LICENSE diff --git a/vendor/github.com/go-openapi/analysis/README.md b/vendor/github.com/go-openapi/analysis/README.md new file mode 100644 index 000000000..3724bfc48 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/README.md @@ -0,0 +1,9 @@ +# OpenAPI initiative analysis [![Build Status](https://travis-ci.org/go-openapi/analysis.svg?branch=master)](https://travis-ci.org/go-openapi/analysis) [![codecov](https://codecov.io/gh/go-openapi/analysis/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/analysis) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/analysis/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/analysis?status.svg)](http://godoc.org/github.com/go-openapi/analysis) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/analysis.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/analysis)](https://goreportcard.com/report/github.com/go-openapi/analysis) + + +A foundational library to analyze an OAI specification document for easier reasoning about the content. diff --git a/vendor/github.com/go-openapi/analysis/analyzer.go b/vendor/github.com/go-openapi/analysis/analyzer.go new file mode 100644 index 000000000..85711d577 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/analyzer.go @@ -0,0 +1,996 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis + +import ( + "fmt" + slashpath "path" + "strconv" + "strings" + + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +type referenceAnalysis struct { + schemas map[string]spec.Ref + responses map[string]spec.Ref + parameters map[string]spec.Ref + items map[string]spec.Ref + headerItems map[string]spec.Ref + parameterItems map[string]spec.Ref + allRefs map[string]spec.Ref + pathItems map[string]spec.Ref +} + +func (r *referenceAnalysis) addRef(key string, ref spec.Ref) { + r.allRefs["#"+key] = ref +} + +func (r *referenceAnalysis) addItemsRef(key string, items *spec.Items, location string) { + r.items["#"+key] = items.Ref + r.addRef(key, items.Ref) + if location == "header" { + // NOTE: in swagger 2.0, headers and parameters (but not body param schemas) are simple schemas + // and $ref are not supported here. However it is possible to analyze this. + r.headerItems["#"+key] = items.Ref + } else { + r.parameterItems["#"+key] = items.Ref + } +} + +func (r *referenceAnalysis) addSchemaRef(key string, ref SchemaRef) { + r.schemas["#"+key] = ref.Schema.Ref + r.addRef(key, ref.Schema.Ref) +} + +func (r *referenceAnalysis) addResponseRef(key string, resp *spec.Response) { + r.responses["#"+key] = resp.Ref + r.addRef(key, resp.Ref) +} + +func (r *referenceAnalysis) addParamRef(key string, param *spec.Parameter) { + r.parameters["#"+key] = param.Ref + r.addRef(key, param.Ref) +} + +func (r *referenceAnalysis) addPathItemRef(key string, pathItem *spec.PathItem) { + r.pathItems["#"+key] = pathItem.Ref + r.addRef(key, pathItem.Ref) +} + +type patternAnalysis struct { + parameters map[string]string + headers map[string]string + items map[string]string + schemas map[string]string + allPatterns map[string]string +} + +func (p *patternAnalysis) addPattern(key, pattern string) { + p.allPatterns["#"+key] = pattern +} + +func (p *patternAnalysis) addParameterPattern(key, pattern string) { + p.parameters["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addHeaderPattern(key, pattern string) { + p.headers["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addItemsPattern(key, pattern string) { + p.items["#"+key] = pattern + p.addPattern(key, pattern) +} + +func (p *patternAnalysis) addSchemaPattern(key, pattern string) { + p.schemas["#"+key] = pattern + p.addPattern(key, pattern) +} + +type enumAnalysis struct { + parameters map[string][]interface{} + headers map[string][]interface{} + items map[string][]interface{} + schemas map[string][]interface{} + allEnums map[string][]interface{} +} + +func (p *enumAnalysis) addEnum(key string, enum []interface{}) { + p.allEnums["#"+key] = enum +} + +func (p *enumAnalysis) addParameterEnum(key string, enum []interface{}) { + p.parameters["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addHeaderEnum(key string, enum []interface{}) { + p.headers["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addItemsEnum(key string, enum []interface{}) { + p.items["#"+key] = enum + p.addEnum(key, enum) +} + +func (p *enumAnalysis) addSchemaEnum(key string, enum []interface{}) { + p.schemas["#"+key] = enum + p.addEnum(key, enum) +} + +// New takes a swagger spec object and returns an analyzed spec document. +// The analyzed document contains a number of indices that make it easier to +// reason about semantics of a swagger specification for use in code generation +// or validation etc. +func New(doc *spec.Swagger) *Spec { + a := &Spec{ + spec: doc, + consumes: make(map[string]struct{}, 150), + produces: make(map[string]struct{}, 150), + authSchemes: make(map[string]struct{}, 150), + operations: make(map[string]map[string]*spec.Operation, 150), + allSchemas: make(map[string]SchemaRef, 150), + allOfs: make(map[string]SchemaRef, 150), + references: referenceAnalysis{ + schemas: make(map[string]spec.Ref, 150), + pathItems: make(map[string]spec.Ref, 150), + responses: make(map[string]spec.Ref, 150), + parameters: make(map[string]spec.Ref, 150), + items: make(map[string]spec.Ref, 150), + headerItems: make(map[string]spec.Ref, 150), + parameterItems: make(map[string]spec.Ref, 150), + allRefs: make(map[string]spec.Ref, 150), + }, + patterns: patternAnalysis{ + parameters: make(map[string]string, 150), + headers: make(map[string]string, 150), + items: make(map[string]string, 150), + schemas: make(map[string]string, 150), + allPatterns: make(map[string]string, 150), + }, + enums: enumAnalysis{ + parameters: make(map[string][]interface{}, 150), + headers: make(map[string][]interface{}, 150), + items: make(map[string][]interface{}, 150), + schemas: make(map[string][]interface{}, 150), + allEnums: make(map[string][]interface{}, 150), + }, + } + a.initialize() + return a +} + +// Spec is an analyzed specification object. It takes a swagger spec object and turns it into a registry +// with a bunch of utility methods to act on the information in the spec. +type Spec struct { + spec *spec.Swagger + consumes map[string]struct{} + produces map[string]struct{} + authSchemes map[string]struct{} + operations map[string]map[string]*spec.Operation + references referenceAnalysis + patterns patternAnalysis + enums enumAnalysis + allSchemas map[string]SchemaRef + allOfs map[string]SchemaRef +} + +func (s *Spec) reset() { + s.consumes = make(map[string]struct{}, 150) + s.produces = make(map[string]struct{}, 150) + s.authSchemes = make(map[string]struct{}, 150) + s.operations = make(map[string]map[string]*spec.Operation, 150) + s.allSchemas = make(map[string]SchemaRef, 150) + s.allOfs = make(map[string]SchemaRef, 150) + s.references.schemas = make(map[string]spec.Ref, 150) + s.references.pathItems = make(map[string]spec.Ref, 150) + s.references.responses = make(map[string]spec.Ref, 150) + s.references.parameters = make(map[string]spec.Ref, 150) + s.references.items = make(map[string]spec.Ref, 150) + s.references.headerItems = make(map[string]spec.Ref, 150) + s.references.parameterItems = make(map[string]spec.Ref, 150) + s.references.allRefs = make(map[string]spec.Ref, 150) + s.patterns.parameters = make(map[string]string, 150) + s.patterns.headers = make(map[string]string, 150) + s.patterns.items = make(map[string]string, 150) + s.patterns.schemas = make(map[string]string, 150) + s.patterns.allPatterns = make(map[string]string, 150) + s.enums.parameters = make(map[string][]interface{}, 150) + s.enums.headers = make(map[string][]interface{}, 150) + s.enums.items = make(map[string][]interface{}, 150) + s.enums.schemas = make(map[string][]interface{}, 150) + s.enums.allEnums = make(map[string][]interface{}, 150) +} + +func (s *Spec) reload() { + s.reset() + s.initialize() +} + +func (s *Spec) initialize() { + for _, c := range s.spec.Consumes { + s.consumes[c] = struct{}{} + } + for _, c := range s.spec.Produces { + s.produces[c] = struct{}{} + } + for _, ss := range s.spec.Security { + for k := range ss { + s.authSchemes[k] = struct{}{} + } + } + for path, pathItem := range s.AllPaths() { + s.analyzeOperations(path, &pathItem) + } + + for name, parameter := range s.spec.Parameters { + refPref := slashpath.Join("/parameters", jsonpointer.Escape(name)) + if parameter.Items != nil { + s.analyzeItems("items", parameter.Items, refPref, "parameter") + } + if parameter.In == "body" && parameter.Schema != nil { + s.analyzeSchema("schema", *parameter.Schema, refPref) + } + if parameter.Pattern != "" { + s.patterns.addParameterPattern(refPref, parameter.Pattern) + } + if len(parameter.Enum) > 0 { + s.enums.addParameterEnum(refPref, parameter.Enum) + } + } + + for name, response := range s.spec.Responses { + refPref := slashpath.Join("/responses", jsonpointer.Escape(name)) + for k, v := range response.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + if v.Items != nil { + s.analyzeItems("items", v.Items, hRefPref, "header") + } + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + if len(v.Enum) > 0 { + s.enums.addHeaderEnum(hRefPref, v.Enum) + } + } + if response.Schema != nil { + s.analyzeSchema("schema", *response.Schema, refPref) + } + } + + for name, schema := range s.spec.Definitions { + s.analyzeSchema(name, schema, "/definitions") + } + // TODO: after analyzing all things and flattening schemas etc + // resolve all the collected references to their final representations + // best put in a separate method because this could get expensive +} + +func (s *Spec) analyzeOperations(path string, pi *spec.PathItem) { + // TODO: resolve refs here? + // Currently, operations declared via pathItem $ref are known only after expansion + op := pi + if pi.Ref.String() != "" { + key := slashpath.Join("/paths", jsonpointer.Escape(path)) + s.references.addPathItemRef(key, pi) + } + s.analyzeOperation("GET", path, op.Get) + s.analyzeOperation("PUT", path, op.Put) + s.analyzeOperation("POST", path, op.Post) + s.analyzeOperation("PATCH", path, op.Patch) + s.analyzeOperation("DELETE", path, op.Delete) + s.analyzeOperation("HEAD", path, op.Head) + s.analyzeOperation("OPTIONS", path, op.Options) + for i, param := range op.Parameters { + refPref := slashpath.Join("/paths", jsonpointer.Escape(path), "parameters", strconv.Itoa(i)) + if param.Ref.String() != "" { + s.references.addParamRef(refPref, ¶m) + } + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } + if len(param.Enum) > 0 { + s.enums.addParameterEnum(refPref, param.Enum) + } + if param.Items != nil { + s.analyzeItems("items", param.Items, refPref, "parameter") + } + if param.Schema != nil { + s.analyzeSchema("schema", *param.Schema, refPref) + } + } +} + +func (s *Spec) analyzeItems(name string, items *spec.Items, prefix, location string) { + if items == nil { + return + } + refPref := slashpath.Join(prefix, name) + s.analyzeItems(name, items.Items, refPref, location) + if items.Ref.String() != "" { + s.references.addItemsRef(refPref, items, location) + } + if items.Pattern != "" { + s.patterns.addItemsPattern(refPref, items.Pattern) + } + if len(items.Enum) > 0 { + s.enums.addItemsEnum(refPref, items.Enum) + } +} + +func (s *Spec) analyzeOperation(method, path string, op *spec.Operation) { + if op == nil { + return + } + + for _, c := range op.Consumes { + s.consumes[c] = struct{}{} + } + for _, c := range op.Produces { + s.produces[c] = struct{}{} + } + for _, ss := range op.Security { + for k := range ss { + s.authSchemes[k] = struct{}{} + } + } + if _, ok := s.operations[method]; !ok { + s.operations[method] = make(map[string]*spec.Operation) + } + s.operations[method][path] = op + prefix := slashpath.Join("/paths", jsonpointer.Escape(path), strings.ToLower(method)) + for i, param := range op.Parameters { + refPref := slashpath.Join(prefix, "parameters", strconv.Itoa(i)) + if param.Ref.String() != "" { + s.references.addParamRef(refPref, ¶m) + } + if param.Pattern != "" { + s.patterns.addParameterPattern(refPref, param.Pattern) + } + if len(param.Enum) > 0 { + s.enums.addParameterEnum(refPref, param.Enum) + } + s.analyzeItems("items", param.Items, refPref, "parameter") + if param.In == "body" && param.Schema != nil { + s.analyzeSchema("schema", *param.Schema, refPref) + } + } + if op.Responses != nil { + if op.Responses.Default != nil { + refPref := slashpath.Join(prefix, "responses", "default") + if op.Responses.Default.Ref.String() != "" { + s.references.addResponseRef(refPref, op.Responses.Default) + } + for k, v := range op.Responses.Default.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + } + if op.Responses.Default.Schema != nil { + s.analyzeSchema("schema", *op.Responses.Default.Schema, refPref) + } + } + for k, res := range op.Responses.StatusCodeResponses { + refPref := slashpath.Join(prefix, "responses", strconv.Itoa(k)) + if res.Ref.String() != "" { + s.references.addResponseRef(refPref, &res) + } + for k, v := range res.Headers { + hRefPref := slashpath.Join(refPref, "headers", k) + s.analyzeItems("items", v.Items, hRefPref, "header") + if v.Pattern != "" { + s.patterns.addHeaderPattern(hRefPref, v.Pattern) + } + if len(v.Enum) > 0 { + s.enums.addHeaderEnum(hRefPref, v.Enum) + } + } + if res.Schema != nil { + s.analyzeSchema("schema", *res.Schema, refPref) + } + } + } +} + +func (s *Spec) analyzeSchema(name string, schema spec.Schema, prefix string) { + refURI := slashpath.Join(prefix, jsonpointer.Escape(name)) + schRef := SchemaRef{ + Name: name, + Schema: &schema, + Ref: spec.MustCreateRef("#" + refURI), + TopLevel: prefix == "/definitions", + } + + s.allSchemas["#"+refURI] = schRef + + if schema.Ref.String() != "" { + s.references.addSchemaRef(refURI, schRef) + } + if schema.Pattern != "" { + s.patterns.addSchemaPattern(refURI, schema.Pattern) + } + if len(schema.Enum) > 0 { + s.enums.addSchemaEnum(refURI, schema.Enum) + } + + for k, v := range schema.Definitions { + s.analyzeSchema(k, v, slashpath.Join(refURI, "definitions")) + } + for k, v := range schema.Properties { + s.analyzeSchema(k, v, slashpath.Join(refURI, "properties")) + } + for k, v := range schema.PatternProperties { + // NOTE: swagger 2.0 does not support PatternProperties. + // However it is possible to analyze this in a schema + s.analyzeSchema(k, v, slashpath.Join(refURI, "patternProperties")) + } + for i, v := range schema.AllOf { + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf")) + } + if len(schema.AllOf) > 0 { + s.allOfs["#"+refURI] = schRef + } + for i, v := range schema.AnyOf { + // NOTE: swagger 2.0 does not support anyOf constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf")) + } + for i, v := range schema.OneOf { + // NOTE: swagger 2.0 does not support oneOf constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "oneOf")) + } + if schema.Not != nil { + // NOTE: swagger 2.0 does not support "not" constructs. + // However it is possible to analyze this in a schema + s.analyzeSchema("not", *schema.Not, refURI) + } + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + s.analyzeSchema("additionalProperties", *schema.AdditionalProperties.Schema, refURI) + } + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + // NOTE: swagger 2.0 does not support AdditionalItems. + // However it is possible to analyze this in a schema + s.analyzeSchema("additionalItems", *schema.AdditionalItems.Schema, refURI) + } + if schema.Items != nil { + if schema.Items.Schema != nil { + s.analyzeSchema("items", *schema.Items.Schema, refURI) + } + for i, sch := range schema.Items.Schemas { + s.analyzeSchema(strconv.Itoa(i), sch, slashpath.Join(refURI, "items")) + } + } +} + +// SecurityRequirement is a representation of a security requirement for an operation +type SecurityRequirement struct { + Name string + Scopes []string +} + +// SecurityRequirementsFor gets the security requirements for the operation +func (s *Spec) SecurityRequirementsFor(operation *spec.Operation) [][]SecurityRequirement { + if s.spec.Security == nil && operation.Security == nil { + return nil + } + + schemes := s.spec.Security + if operation.Security != nil { + schemes = operation.Security + } + + result := [][]SecurityRequirement{} + for _, scheme := range schemes { + if len(scheme) == 0 { + // append a zero object for anonymous + result = append(result, []SecurityRequirement{{}}) + continue + } + var reqs []SecurityRequirement + for k, v := range scheme { + if v == nil { + v = []string{} + } + reqs = append(reqs, SecurityRequirement{Name: k, Scopes: v}) + } + result = append(result, reqs) + } + return result +} + +// SecurityDefinitionsForRequirements gets the matching security definitions for a set of requirements +func (s *Spec) SecurityDefinitionsForRequirements(requirements []SecurityRequirement) map[string]spec.SecurityScheme { + result := make(map[string]spec.SecurityScheme) + + for _, v := range requirements { + if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok { + if definition != nil { + result[v.Name] = *definition + } + } + } + return result +} + +// SecurityDefinitionsFor gets the matching security definitions for a set of requirements +func (s *Spec) SecurityDefinitionsFor(operation *spec.Operation) map[string]spec.SecurityScheme { + requirements := s.SecurityRequirementsFor(operation) + if len(requirements) == 0 { + return nil + } + + result := make(map[string]spec.SecurityScheme) + for _, reqs := range requirements { + for _, v := range reqs { + if v.Name == "" { + // optional requirement + continue + } + if _, ok := result[v.Name]; ok { + // duplicate requirement + continue + } + if definition, ok := s.spec.SecurityDefinitions[v.Name]; ok { + if definition != nil { + result[v.Name] = *definition + } + } + } + } + return result +} + +// ConsumesFor gets the mediatypes for the operation +func (s *Spec) ConsumesFor(operation *spec.Operation) []string { + + if len(operation.Consumes) == 0 { + cons := make(map[string]struct{}, len(s.spec.Consumes)) + for _, k := range s.spec.Consumes { + cons[k] = struct{}{} + } + return s.structMapKeys(cons) + } + + cons := make(map[string]struct{}, len(operation.Consumes)) + for _, c := range operation.Consumes { + cons[c] = struct{}{} + } + return s.structMapKeys(cons) +} + +// ProducesFor gets the mediatypes for the operation +func (s *Spec) ProducesFor(operation *spec.Operation) []string { + if len(operation.Produces) == 0 { + prod := make(map[string]struct{}, len(s.spec.Produces)) + for _, k := range s.spec.Produces { + prod[k] = struct{}{} + } + return s.structMapKeys(prod) + } + + prod := make(map[string]struct{}, len(operation.Produces)) + for _, c := range operation.Produces { + prod[c] = struct{}{} + } + return s.structMapKeys(prod) +} + +func mapKeyFromParam(param *spec.Parameter) string { + return fmt.Sprintf("%s#%s", param.In, fieldNameFromParam(param)) +} + +func fieldNameFromParam(param *spec.Parameter) string { + // TODO: this should be x-go-name + if nm, ok := param.Extensions.GetString("go-name"); ok { + return nm + } + return swag.ToGoName(param.Name) +} + +// ErrorOnParamFunc is a callback function to be invoked +// whenever an error is encountered while resolving references +// on parameters. +// +// This function takes as input the spec.Parameter which triggered the +// error and the error itself. +// +// If the callback function returns false, the calling function should bail. +// +// If it returns true, the calling function should continue evaluating parameters. +// A nil ErrorOnParamFunc must be evaluated as equivalent to panic(). +type ErrorOnParamFunc func(spec.Parameter, error) bool + +func (s *Spec) paramsAsMap(parameters []spec.Parameter, res map[string]spec.Parameter, callmeOnError ErrorOnParamFunc) { + for _, param := range parameters { + pr := param + if pr.Ref.String() != "" { + obj, _, err := pr.Ref.GetPointer().Get(s.spec) + if err != nil { + if callmeOnError != nil { + if callmeOnError(param, fmt.Errorf("invalid reference: %q", pr.Ref.String())) { + continue + } + break + } else { + panic(fmt.Sprintf("invalid reference: %q", pr.Ref.String())) + } + } + if objAsParam, ok := obj.(spec.Parameter); ok { + pr = objAsParam + } else { + if callmeOnError != nil { + if callmeOnError(param, fmt.Errorf("resolved reference is not a parameter: %q", pr.Ref.String())) { + continue + } + break + } else { + panic(fmt.Sprintf("resolved reference is not a parameter: %q", pr.Ref.String())) + } + } + } + res[mapKeyFromParam(&pr)] = pr + } +} + +// ParametersFor the specified operation id. +// +// Assumes parameters properly resolve references if any and that +// such references actually resolve to a parameter object. +// Otherwise, panics. +func (s *Spec) ParametersFor(operationID string) []spec.Parameter { + return s.SafeParametersFor(operationID, nil) +} + +// SafeParametersFor the specified operation id. +// +// Does not assume parameters properly resolve references or that +// such references actually resolve to a parameter object. +// +// Upon error, invoke a ErrorOnParamFunc callback with the erroneous +// parameters. If the callback is set to nil, panics upon errors. +func (s *Spec) SafeParametersFor(operationID string, callmeOnError ErrorOnParamFunc) []spec.Parameter { + gatherParams := func(pi *spec.PathItem, op *spec.Operation) []spec.Parameter { + bag := make(map[string]spec.Parameter) + s.paramsAsMap(pi.Parameters, bag, callmeOnError) + s.paramsAsMap(op.Parameters, bag, callmeOnError) + + var res []spec.Parameter + for _, v := range bag { + res = append(res, v) + } + return res + } + for _, pi := range s.spec.Paths.Paths { + if pi.Get != nil && pi.Get.ID == operationID { + return gatherParams(&pi, pi.Get) + } + if pi.Head != nil && pi.Head.ID == operationID { + return gatherParams(&pi, pi.Head) + } + if pi.Options != nil && pi.Options.ID == operationID { + return gatherParams(&pi, pi.Options) + } + if pi.Post != nil && pi.Post.ID == operationID { + return gatherParams(&pi, pi.Post) + } + if pi.Patch != nil && pi.Patch.ID == operationID { + return gatherParams(&pi, pi.Patch) + } + if pi.Put != nil && pi.Put.ID == operationID { + return gatherParams(&pi, pi.Put) + } + if pi.Delete != nil && pi.Delete.ID == operationID { + return gatherParams(&pi, pi.Delete) + } + } + return nil +} + +// ParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that +// apply for the method and path. +// +// Assumes parameters properly resolve references if any and that +// such references actually resolve to a parameter object. +// Otherwise, panics. +func (s *Spec) ParamsFor(method, path string) map[string]spec.Parameter { + return s.SafeParamsFor(method, path, nil) +} + +// SafeParamsFor the specified method and path. Aggregates them with the defaults etc, so it's all the params that +// apply for the method and path. +// +// Does not assume parameters properly resolve references or that +// such references actually resolve to a parameter object. +// +// Upon error, invoke a ErrorOnParamFunc callback with the erroneous +// parameters. If the callback is set to nil, panics upon errors. +func (s *Spec) SafeParamsFor(method, path string, callmeOnError ErrorOnParamFunc) map[string]spec.Parameter { + res := make(map[string]spec.Parameter) + if pi, ok := s.spec.Paths.Paths[path]; ok { + s.paramsAsMap(pi.Parameters, res, callmeOnError) + s.paramsAsMap(s.operations[strings.ToUpper(method)][path].Parameters, res, callmeOnError) + } + return res +} + +// OperationForName gets the operation for the given id +func (s *Spec) OperationForName(operationID string) (string, string, *spec.Operation, bool) { + for method, pathItem := range s.operations { + for path, op := range pathItem { + if operationID == op.ID { + return method, path, op, true + } + } + } + return "", "", nil, false +} + +// OperationFor the given method and path +func (s *Spec) OperationFor(method, path string) (*spec.Operation, bool) { + if mp, ok := s.operations[strings.ToUpper(method)]; ok { + op, fn := mp[path] + return op, fn + } + return nil, false +} + +// Operations gathers all the operations specified in the spec document +func (s *Spec) Operations() map[string]map[string]*spec.Operation { + return s.operations +} + +func (s *Spec) structMapKeys(mp map[string]struct{}) []string { + if len(mp) == 0 { + return nil + } + + result := make([]string, 0, len(mp)) + for k := range mp { + result = append(result, k) + } + return result +} + +// AllPaths returns all the paths in the swagger spec +func (s *Spec) AllPaths() map[string]spec.PathItem { + if s.spec == nil || s.spec.Paths == nil { + return nil + } + return s.spec.Paths.Paths +} + +// OperationIDs gets all the operation ids based on method an dpath +func (s *Spec) OperationIDs() []string { + if len(s.operations) == 0 { + return nil + } + result := make([]string, 0, len(s.operations)) + for method, v := range s.operations { + for p, o := range v { + if o.ID != "" { + result = append(result, o.ID) + } else { + result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p)) + } + } + } + return result +} + +// OperationMethodPaths gets all the operation ids based on method an dpath +func (s *Spec) OperationMethodPaths() []string { + if len(s.operations) == 0 { + return nil + } + result := make([]string, 0, len(s.operations)) + for method, v := range s.operations { + for p := range v { + result = append(result, fmt.Sprintf("%s %s", strings.ToUpper(method), p)) + } + } + return result +} + +// RequiredConsumes gets all the distinct consumes that are specified in the specification document +func (s *Spec) RequiredConsumes() []string { + return s.structMapKeys(s.consumes) +} + +// RequiredProduces gets all the distinct produces that are specified in the specification document +func (s *Spec) RequiredProduces() []string { + return s.structMapKeys(s.produces) +} + +// RequiredSecuritySchemes gets all the distinct security schemes that are specified in the swagger spec +func (s *Spec) RequiredSecuritySchemes() []string { + return s.structMapKeys(s.authSchemes) +} + +// SchemaRef is a reference to a schema +type SchemaRef struct { + Name string + Ref spec.Ref + Schema *spec.Schema + TopLevel bool +} + +// SchemasWithAllOf returns schema references to all schemas that are defined +// with an allOf key +func (s *Spec) SchemasWithAllOf() (result []SchemaRef) { + for _, v := range s.allOfs { + result = append(result, v) + } + return +} + +// AllDefinitions returns schema references for all the definitions that were discovered +func (s *Spec) AllDefinitions() (result []SchemaRef) { + for _, v := range s.allSchemas { + result = append(result, v) + } + return +} + +// AllDefinitionReferences returns json refs for all the discovered schemas +func (s *Spec) AllDefinitionReferences() (result []string) { + for _, v := range s.references.schemas { + result = append(result, v.String()) + } + return +} + +// AllParameterReferences returns json refs for all the discovered parameters +func (s *Spec) AllParameterReferences() (result []string) { + for _, v := range s.references.parameters { + result = append(result, v.String()) + } + return +} + +// AllResponseReferences returns json refs for all the discovered responses +func (s *Spec) AllResponseReferences() (result []string) { + for _, v := range s.references.responses { + result = append(result, v.String()) + } + return +} + +// AllPathItemReferences returns the references for all the items +func (s *Spec) AllPathItemReferences() (result []string) { + for _, v := range s.references.pathItems { + result = append(result, v.String()) + } + return +} + +// AllItemsReferences returns the references for all the items in simple schemas (parameters or headers). +// +// NOTE: since Swagger 2.0 forbids $ref in simple params, this should always yield an empty slice for a valid +// Swagger 2.0 spec. +func (s *Spec) AllItemsReferences() (result []string) { + for _, v := range s.references.items { + result = append(result, v.String()) + } + return +} + +// AllReferences returns all the references found in the document, with possible duplicates +func (s *Spec) AllReferences() (result []string) { + for _, v := range s.references.allRefs { + result = append(result, v.String()) + } + return +} + +// AllRefs returns all the unique references found in the document +func (s *Spec) AllRefs() (result []spec.Ref) { + set := make(map[string]struct{}) + for _, v := range s.references.allRefs { + a := v.String() + if a == "" { + continue + } + if _, ok := set[a]; !ok { + set[a] = struct{}{} + result = append(result, v) + } + } + return +} + +func cloneStringMap(source map[string]string) map[string]string { + res := make(map[string]string, len(source)) + for k, v := range source { + res[k] = v + } + return res +} + +func cloneEnumMap(source map[string][]interface{}) map[string][]interface{} { + res := make(map[string][]interface{}, len(source)) + for k, v := range source { + res[k] = v + } + return res +} + +// ParameterPatterns returns all the patterns found in parameters +// the map is cloned to avoid accidental changes +func (s *Spec) ParameterPatterns() map[string]string { + return cloneStringMap(s.patterns.parameters) +} + +// HeaderPatterns returns all the patterns found in response headers +// the map is cloned to avoid accidental changes +func (s *Spec) HeaderPatterns() map[string]string { + return cloneStringMap(s.patterns.headers) +} + +// ItemsPatterns returns all the patterns found in simple array items +// the map is cloned to avoid accidental changes +func (s *Spec) ItemsPatterns() map[string]string { + return cloneStringMap(s.patterns.items) +} + +// SchemaPatterns returns all the patterns found in schemas +// the map is cloned to avoid accidental changes +func (s *Spec) SchemaPatterns() map[string]string { + return cloneStringMap(s.patterns.schemas) +} + +// AllPatterns returns all the patterns found in the spec +// the map is cloned to avoid accidental changes +func (s *Spec) AllPatterns() map[string]string { + return cloneStringMap(s.patterns.allPatterns) +} + +// ParameterEnums returns all the enums found in parameters +// the map is cloned to avoid accidental changes +func (s *Spec) ParameterEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.parameters) +} + +// HeaderEnums returns all the enums found in response headers +// the map is cloned to avoid accidental changes +func (s *Spec) HeaderEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.headers) +} + +// ItemsEnums returns all the enums found in simple array items +// the map is cloned to avoid accidental changes +func (s *Spec) ItemsEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.items) +} + +// SchemaEnums returns all the enums found in schemas +// the map is cloned to avoid accidental changes +func (s *Spec) SchemaEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.schemas) +} + +// AllEnums returns all the enums found in the spec +// the map is cloned to avoid accidental changes +func (s *Spec) AllEnums() map[string][]interface{} { + return cloneEnumMap(s.enums.allEnums) +} diff --git a/vendor/github.com/go-openapi/analysis/debug.go b/vendor/github.com/go-openapi/analysis/debug.go new file mode 100644 index 000000000..84cc4e54c --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/debug.go @@ -0,0 +1,47 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + // Debug is true when the SWAGGER_DEBUG env var is not empty. + // It enables a more verbose logging of the spec analyzer. + Debug = os.Getenv("SWAGGER_DEBUG") != "" + // analysisLogger is a debug logger for this package + analysisLogger *log.Logger +) + +func init() { + debugOptions() +} + +func debugOptions() { + analysisLogger = log.New(os.Stdout, "analysis:", log.LstdFlags) +} + +func debugLog(msg string, args ...interface{}) { + // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() + if Debug { + _, file1, pos1, _ := runtime.Caller(1) + analysisLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } +} diff --git a/vendor/github.com/go-openapi/analysis/doc.go b/vendor/github.com/go-openapi/analysis/doc.go new file mode 100644 index 000000000..d5294c095 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/doc.go @@ -0,0 +1,43 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis provides methods to work with a Swagger specification document from +package go-openapi/spec. + +Analyzing a specification + +An analysed specification object (type Spec) provides methods to work with swagger definition. + +Flattening or expanding a specification + +Flattening a specification bundles all remote $ref in the main spec document. +Depending on flattening options, additional preprocessing may take place: + - full flattening: replacing all inline complex constructs by a named entry in #/definitions + - expand: replace all $ref's in the document by their expanded content + +Merging several specifications + +Mixin several specifications merges all Swagger constructs, and warns about found conflicts. + +Fixing a specification + +Unmarshalling a specification with golang json unmarshalling may lead to +some unwanted result on present but empty fields. + +Analyzing a Swagger schema + +Swagger schemas are analyzed to determine their complexity and qualify their content. +*/ +package analysis diff --git a/vendor/github.com/go-openapi/analysis/fixer.go b/vendor/github.com/go-openapi/analysis/fixer.go new file mode 100644 index 000000000..bfe014ca5 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/fixer.go @@ -0,0 +1,76 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis + +import "github.com/go-openapi/spec" + +// FixEmptyResponseDescriptions replaces empty ("") response +// descriptions in the input with "(empty)" to ensure that the +// resulting Swagger is stays valid. The problem appears to arise +// from reading in valid specs that have a explicit response +// description of "" (valid, response.description is required), but +// due to zero values being omitted upon re-serializing (omitempty) we +// lose them unless we stick some chars in there. +func FixEmptyResponseDescriptions(s *spec.Swagger) { + if s.Paths != nil { + for _, v := range s.Paths.Paths { + if v.Get != nil { + FixEmptyDescs(v.Get.Responses) + } + if v.Put != nil { + FixEmptyDescs(v.Put.Responses) + } + if v.Post != nil { + FixEmptyDescs(v.Post.Responses) + } + if v.Delete != nil { + FixEmptyDescs(v.Delete.Responses) + } + if v.Options != nil { + FixEmptyDescs(v.Options.Responses) + } + if v.Head != nil { + FixEmptyDescs(v.Head.Responses) + } + if v.Patch != nil { + FixEmptyDescs(v.Patch.Responses) + } + } + } + for k, v := range s.Responses { + FixEmptyDesc(&v) + s.Responses[k] = v + } +} + +// FixEmptyDescs adds "(empty)" as the description for any Response in +// the given Responses object that doesn't already have one. +func FixEmptyDescs(rs *spec.Responses) { + FixEmptyDesc(rs.Default) + for k, v := range rs.StatusCodeResponses { + FixEmptyDesc(&v) + rs.StatusCodeResponses[k] = v + } +} + +// FixEmptyDesc adds "(empty)" as the description to the given +// Response object if it doesn't already have one and isn't a +// ref. No-op on nil input. +func FixEmptyDesc(rs *spec.Response) { + if rs == nil || rs.Description != "" || rs.Ref.Ref.GetURL() != nil { + return + } + rs.Description = "(empty)" +} diff --git a/vendor/github.com/go-openapi/analysis/flatten.go b/vendor/github.com/go-openapi/analysis/flatten.go new file mode 100644 index 000000000..0095e8258 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/flatten.go @@ -0,0 +1,1525 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis + +import ( + "fmt" + "log" + "net/http" + "net/url" + "os" + slashpath "path" + "path/filepath" + "sort" + "strings" + + "strconv" + + "github.com/go-openapi/analysis/internal" + "github.com/go-openapi/jsonpointer" + swspec "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// FlattenOpts configuration for flattening a swagger specification. +type FlattenOpts struct { + Spec *Spec // The analyzed spec to work with + flattenContext *context // Internal context to track flattening activity + + BasePath string + + // Flattening options + Expand bool // If Expand is true, we skip flattening the spec and expand it instead + Minimal bool + Verbose bool + RemoveUnused bool + + /* Extra keys */ + _ struct{} // require keys +} + +// ExpandOpts creates a spec.ExpandOptions to configure expanding a specification document. +func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *swspec.ExpandOptions { + return &swspec.ExpandOptions{RelativeBase: f.BasePath, SkipSchemas: skipSchemas} +} + +// Swagger gets the swagger specification for this flatten operation +func (f *FlattenOpts) Swagger() *swspec.Swagger { + return f.Spec.spec +} + +// newRef stores information about refs created during the flattening process +type newRef struct { + key string + newName string + path string + isOAIGen bool + resolved bool + schema *swspec.Schema + parents []string +} + +// context stores intermediary results from flatten +type context struct { + newRefs map[string]*newRef + warnings []string + resolved map[string]string +} + +func newContext() *context { + return &context{ + newRefs: make(map[string]*newRef, 150), + warnings: make([]string, 0), + resolved: make(map[string]string, 50), + } +} + +// Flatten an analyzed spec and produce a self-contained spec bundle. +// +// There is a minimal and a full flattening mode. +// +// Minimally flattening a spec means: +// - Expanding parameters, responses, path items, parameter items and header items (references to schemas are left +// unscathed) +// - Importing external (http, file) references so they become internal to the document +// - Moving every JSON pointer to a $ref to a named definition (i.e. the reworked spec does not contain pointers +// like "$ref": "#/definitions/myObject/allOfs/1") +// +// A minimally flattened spec thus guarantees the following properties: +// - all $refs point to a local definition (i.e. '#/definitions/...') +// - definitions are unique +// +// NOTE: arbitrary JSON pointers (other than $refs to top level definitions) are rewritten as definitions if they +// represent a complex schema or express commonality in the spec. +// Otherwise, they are simply expanded. +// +// Minimal flattening is necessary and sufficient for codegen rendering using go-swagger. +// +// Fully flattening a spec means: +// - Moving every complex inline schema to be a definition with an auto-generated name in a depth-first fashion. +// +// By complex, we mean every JSON object with some properties. +// Arrays, when they do not define a tuple, +// or empty objects with or without additionalProperties, are not considered complex and remain inline. +// +// NOTE: rewritten schemas get a vendor extension x-go-gen-location so we know from which part of the spec definitions +// have been created. +// +// Available flattening options: +// - Minimal: stops flattening after minimal $ref processing, leaving schema constructs untouched +// - Expand: expand all $ref's in the document (inoperant if Minimal set to true) +// - Verbose: croaks about name conflicts detected +// - RemoveUnused: removes unused parameters, responses and definitions after expansion/flattening +// +// NOTE: expansion removes all $ref save circular $ref, which remain in place +// +// TODO: additional options +// - ProgagateNameExtensions: ensure that created entries properly follow naming rules when their parent have set a +// x-go-name extension +// - LiftAllOfs: +// - limit the flattening of allOf members when simple objects +// - merge allOf with validation only +// - merge allOf with extensions only +// - ... +// +func Flatten(opts FlattenOpts) error { + // Make sure opts.BasePath is an absolute path + if !filepath.IsAbs(opts.BasePath) { + cwd, _ := os.Getwd() + opts.BasePath = filepath.Join(cwd, opts.BasePath) + } + + opts.flattenContext = newContext() + + // recursively expand responses, parameters, path items and items in simple schemas. + // This simplifies the spec and leaves $ref only into schema objects. + // + if err := swspec.ExpandSpec(opts.Swagger(), opts.ExpandOpts(!opts.Expand)); err != nil { + return err + } + + // strip current file from $ref's, so we can recognize them as proper definitions + // In particular, this works around for issue go-openapi/spec#76: leading absolute file in $ref is stripped + if err := normalizeRef(&opts); err != nil { + return err + } + + if opts.RemoveUnused { + // optionally removes shared parameters and responses already expanded (now unused) + // default parameters (i.e. under paths) remain. + opts.Swagger().Parameters = nil + opts.Swagger().Responses = nil + } + + opts.Spec.reload() // re-analyze + + // at this point there are no other references left but schemas + for imported := false; !imported; { + var err error + if imported, err = importExternalReferences(&opts); err != nil { + return err + } + opts.Spec.reload() // re-analyze + } + + if !opts.Minimal && !opts.Expand { + // full flattening: rewrite inline schemas (schemas that aren't simple types or arrays or maps) + if err := nameInlinedSchemas(&opts); err != nil { + return err + } + + opts.Spec.reload() // re-analyze + } + // rewrite JSON pointers other than $ref to named definitions + // and attempts to resolve conflicting names + if err := stripPointersAndOAIGen(&opts); err != nil { + return err + } + + if opts.RemoveUnused { + // remove unused definitions + expected := make(map[string]struct{}) + for k := range opts.Swagger().Definitions { + expected[slashpath.Join(definitionsPath, jsonpointer.Escape(k))] = struct{}{} + } + for _, k := range opts.Spec.AllDefinitionReferences() { + if _, ok := expected[k]; ok { + delete(expected, k) + } + } + for k := range expected { + debugLog("removing unused definition %s", slashpath.Base(k)) + if opts.Verbose { + log.Printf("info: removing unused definition: %s", slashpath.Base(k)) + } + delete(opts.Swagger().Definitions, slashpath.Base(k)) + } + opts.Spec.reload() // re-analyze + } + + // TODO: simplify known schema patterns to flat objects with properties + // examples: + // - lift simple allOf object, + // - empty allOf with validation only or extensions only + // - rework allOf arrays + // - rework allOf additionalProperties + + if opts.Verbose { + // issue notifications + croak(&opts) + } + return nil +} + +// isAnalyzedAsComplex determines if an analyzed schema is eligible to flattening (i.e. it is "complex"). +// +// Complex means the schema is any of: +// - a simple type (primitive) +// - an array of something (items are possibly complex ; if this is the case, items will generate a definition) +// - a map of something (additionalProperties are possibly complex ; if this is the case, additionalProperties will +// generate a definition) +func isAnalyzedAsComplex(asch *AnalyzedSchema) bool { + if !asch.IsSimpleSchema && !asch.IsArray && !asch.IsMap { + return true + } + return false +} + +// nameInlinedSchemas replaces every complex inline construct by a named definition. +func nameInlinedSchemas(opts *FlattenOpts) error { + debugLog("nameInlinedSchemas") + namer := &inlineSchemaNamer{ + Spec: opts.Swagger(), + Operations: opRefsByRef(gatherOperations(opts.Spec, nil)), + flattenContext: opts.flattenContext, + opts: opts, + } + depthFirst := sortDepthFirst(opts.Spec.allSchemas) + for _, key := range depthFirst { + sch := opts.Spec.allSchemas[key] + if sch.Schema != nil && sch.Schema.Ref.String() == "" && !sch.TopLevel { // inline schema + asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if err != nil { + return fmt.Errorf("schema analysis [%s]: %v", key, err) + } + + if isAnalyzedAsComplex(asch) { // move complex schemas to definitions + if err := namer.Name(key, sch.Schema, asch); err != nil { + return err + } + } + } + } + return nil +} + +var depthGroupOrder = []string{ + "sharedParam", "sharedResponse", "sharedOpParam", "opParam", "codeResponse", "defaultResponse", "definition", +} + +func sortDepthFirst(data map[string]SchemaRef) []string { + // group by category (shared params, op param, statuscode response, default response, definitions) + // sort groups internally by number of parts in the key and lexical names + // flatten groups into a single list of keys + sorted := make([]string, 0, len(data)) + grouped := make(map[string]keys, len(data)) + for k := range data { + split := keyParts(k) + var pk string + if split.IsSharedOperationParam() { + pk = "sharedOpParam" + } + if split.IsOperationParam() { + pk = "opParam" + } + if split.IsStatusCodeResponse() { + pk = "codeResponse" + } + if split.IsDefaultResponse() { + pk = "defaultResponse" + } + if split.IsDefinition() { + pk = "definition" + } + if split.IsSharedParam() { + pk = "sharedParam" + } + if split.IsSharedResponse() { + pk = "sharedResponse" + } + grouped[pk] = append(grouped[pk], key{Segments: len(split), Key: k}) + } + + for _, pk := range depthGroupOrder { + res := grouped[pk] + sort.Sort(res) + for _, v := range res { + sorted = append(sorted, v.Key) + } + } + return sorted +} + +type key struct { + Segments int + Key string +} +type keys []key + +func (k keys) Len() int { return len(k) } +func (k keys) Swap(i, j int) { k[i], k[j] = k[j], k[i] } +func (k keys) Less(i, j int) bool { + return k[i].Segments > k[j].Segments || (k[i].Segments == k[j].Segments && k[i].Key < k[j].Key) +} + +type inlineSchemaNamer struct { + Spec *swspec.Swagger + Operations map[string]opRef + flattenContext *context + opts *FlattenOpts +} + +func opRefsByRef(oprefs map[string]opRef) map[string]opRef { + result := make(map[string]opRef, len(oprefs)) + for _, v := range oprefs { + result[v.Ref.String()] = v + } + return result +} + +func (isn *inlineSchemaNamer) Name(key string, schema *swspec.Schema, aschema *AnalyzedSchema) error { + debugLog("naming inlined schema at %s", key) + + parts := keyParts(key) + for _, name := range namesFromKey(parts, aschema, isn.Operations) { + if name != "" { + // create unique name + newName, isOAIGen := uniqifyName(isn.Spec.Definitions, swag.ToJSONName(name)) + + // clone schema + sch, err := cloneSchema(schema) + if err != nil { + return err + } + + // replace values on schema + if err := rewriteSchemaToRef(isn.Spec, key, + swspec.MustCreateRef(slashpath.Join(definitionsPath, newName))); err != nil { + return fmt.Errorf("error while creating definition %q from inline schema: %v", newName, err) + } + + // rewrite any dependent $ref pointing to this place, + // when not already pointing to a top-level definition. + // NOTE: this is important if such referers use arbitrary JSON pointers. + an := New(isn.Spec) + for k, v := range an.references.allRefs { + r, _, erd := deepestRef(isn.opts, v) + if erd != nil { + return fmt.Errorf("at %s, %v", k, erd) + } + if r.String() == key || + r.String() == slashpath.Join(definitionsPath, newName) && + slashpath.Dir(v.String()) != definitionsPath { + debugLog("found a $ref to a rewritten schema: %s points to %s", k, v.String()) + // rewrite $ref to the new target + if err := updateRef(isn.Spec, k, + swspec.MustCreateRef(slashpath.Join(definitionsPath, newName))); err != nil { + return err + } + } + } + + // NOTE: this extension is currently not used by go-swagger (provided for information only) + sch.AddExtension("x-go-gen-location", genLocation(parts)) + // save cloned schema to definitions + saveSchema(isn.Spec, newName, sch) + + // keep track of created refs + if isn.flattenContext != nil { + debugLog("track created ref: key=%s, newName=%s, isOAIGen=%t", key, newName, isOAIGen) + resolved := false + if _, ok := isn.flattenContext.newRefs[key]; ok { + resolved = isn.flattenContext.newRefs[key].resolved + } + isn.flattenContext.newRefs[key] = &newRef{ + key: key, + newName: newName, + path: slashpath.Join(definitionsPath, newName), + isOAIGen: isOAIGen, + resolved: resolved, + schema: sch, + } + } + } + } + return nil +} + +// genLocation indicates from which section of the specification (models or operations) a definition has been created. +// This is reflected in the output spec with a "x-go-gen-location" extension. At the moment, this is is provided +// for information only. +func genLocation(parts splitKey) string { + if parts.IsOperation() { + return "operations" + } + if parts.IsDefinition() { + return "models" + } + return "" +} + +func uniqifyName(definitions swspec.Definitions, name string) (string, bool) { + isOAIGen := false + if name == "" { + name = "oaiGen" + isOAIGen = true + } + if len(definitions) == 0 { + return name, isOAIGen + } + + unq := true + for k := range definitions { + if strings.ToLower(k) == strings.ToLower(name) { + unq = false + break + } + } + + if unq { + return name, isOAIGen + } + + name += "OAIGen" + isOAIGen = true + var idx int + unique := name + _, known := definitions[unique] + for known { + idx++ + unique = fmt.Sprintf("%s%d", name, idx) + _, known = definitions[unique] + } + return unique, isOAIGen +} + +func namesFromKey(parts splitKey, aschema *AnalyzedSchema, operations map[string]opRef) []string { + var baseNames [][]string + var startIndex int + if parts.IsOperation() { + // params + if parts.IsOperationParam() || parts.IsSharedOperationParam() { + piref := parts.PathItemRef() + if piref.String() != "" && parts.IsOperationParam() { + if op, ok := operations[piref.String()]; ok { + startIndex = 5 + baseNames = append(baseNames, []string{op.ID, "params", "body"}) + } + } else if parts.IsSharedOperationParam() { + pref := parts.PathRef() + for k, v := range operations { + if strings.HasPrefix(k, pref.String()) { + startIndex = 4 + baseNames = append(baseNames, []string{v.ID, "params", "body"}) + } + } + } + } + // responses + if parts.IsOperationResponse() { + piref := parts.PathItemRef() + if piref.String() != "" { + if op, ok := operations[piref.String()]; ok { + startIndex = 6 + baseNames = append(baseNames, []string{op.ID, parts.ResponseName(), "body"}) + } + } + } + } + + // definitions + if parts.IsDefinition() { + nm := parts.DefinitionName() + if nm != "" { + startIndex = 2 + baseNames = append(baseNames, []string{parts.DefinitionName()}) + } + } + + var result []string + for _, segments := range baseNames { + nm := parts.BuildName(segments, startIndex, aschema) + if nm != "" { + result = append(result, nm) + } + } + sort.Strings(result) + return result +} + +const ( + paths = "paths" + responses = "responses" + parameters = "parameters" + definitions = "definitions" + definitionsPath = "#/definitions" +) + +var ( + ignoredKeys map[string]struct{} + validMethods map[string]struct{} +) + +func init() { + ignoredKeys = map[string]struct{}{ + "schema": {}, + "properties": {}, + "not": {}, + "anyOf": {}, + "oneOf": {}, + } + + validMethods = map[string]struct{}{ + "GET": {}, + "HEAD": {}, + "OPTIONS": {}, + "PATCH": {}, + "POST": {}, + "PUT": {}, + "DELETE": {}, + } +} + +type splitKey []string + +func (s splitKey) IsDefinition() bool { + return len(s) > 1 && s[0] == definitions +} + +func (s splitKey) DefinitionName() string { + if !s.IsDefinition() { + return "" + } + return s[1] +} + +func (s splitKey) isKeyName(i int) bool { + if i <= 0 { + return false + } + count := 0 + for idx := i - 1; idx > 0; idx-- { + if s[idx] != "properties" { + break + } + count++ + } + + return count%2 != 0 +} + +func (s splitKey) BuildName(segments []string, startIndex int, aschema *AnalyzedSchema) string { + for i, part := range s[startIndex:] { + if _, ignored := ignoredKeys[part]; !ignored || s.isKeyName(startIndex+i) { + if part == "items" || part == "additionalItems" { + if aschema.IsTuple || aschema.IsTupleWithExtra { + segments = append(segments, "tuple") + } else { + segments = append(segments, "items") + } + if part == "additionalItems" { + segments = append(segments, part) + } + continue + } + segments = append(segments, part) + } + } + return strings.Join(segments, " ") +} + +func (s splitKey) IsOperation() bool { + return len(s) > 1 && s[0] == paths +} + +func (s splitKey) IsSharedOperationParam() bool { + return len(s) > 2 && s[0] == paths && s[2] == parameters +} + +func (s splitKey) IsSharedParam() bool { + return len(s) > 1 && s[0] == parameters +} + +func (s splitKey) IsOperationParam() bool { + return len(s) > 3 && s[0] == paths && s[3] == parameters +} + +func (s splitKey) IsOperationResponse() bool { + return len(s) > 3 && s[0] == paths && s[3] == responses +} + +func (s splitKey) IsSharedResponse() bool { + return len(s) > 1 && s[0] == responses +} + +func (s splitKey) IsDefaultResponse() bool { + return len(s) > 4 && s[0] == paths && s[3] == responses && s[4] == "default" +} + +func (s splitKey) IsStatusCodeResponse() bool { + isInt := func() bool { + _, err := strconv.Atoi(s[4]) + return err == nil + } + return len(s) > 4 && s[0] == paths && s[3] == responses && isInt() +} + +func (s splitKey) ResponseName() string { + if s.IsStatusCodeResponse() { + code, _ := strconv.Atoi(s[4]) + return http.StatusText(code) + } + if s.IsDefaultResponse() { + return "Default" + } + return "" +} + +func (s splitKey) PathItemRef() swspec.Ref { + if len(s) < 3 { + return swspec.Ref{} + } + pth, method := s[1], s[2] + if _, isValidMethod := validMethods[strings.ToUpper(method)]; !isValidMethod && !strings.HasPrefix(method, "x-") { + return swspec.Ref{} + } + return swspec.MustCreateRef("#" + slashpath.Join("/", paths, jsonpointer.Escape(pth), strings.ToUpper(method))) +} + +func (s splitKey) PathRef() swspec.Ref { + if !s.IsOperation() { + return swspec.Ref{} + } + return swspec.MustCreateRef("#" + slashpath.Join("/", paths, jsonpointer.Escape(s[1]))) +} + +func keyParts(key string) splitKey { + var res []string + for _, part := range strings.Split(key[1:], "/") { + if part != "" { + res = append(res, jsonpointer.Unescape(part)) + } + } + return res +} + +func rewriteSchemaToRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + debugLog("rewriting schema to ref for %s with %s", key, ref.String()) + _, value, err := getPointerFromKey(spec, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *swspec.Schema: + return rewriteParentRef(spec, key, ref) + + case swspec.Schema: + return rewriteParentRef(spec, key, ref) + + case *swspec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + } + + case *swspec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + } + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +func rewriteParentRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + parent, entry, pvalue, err := getParentFromKey(spec, key) + if err != nil { + return err + } + + debugLog("rewriting holder for %T", pvalue) + switch container := pvalue.(type) { + case swspec.Response: + if err := rewriteParentRef(spec, "#"+parent, ref); err != nil { + return err + } + + case *swspec.Response: + container.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.Responses: + statusCode, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", key[1:], err) + } + resp := container.StatusCodeResponses[statusCode] + resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container.StatusCodeResponses[statusCode] = resp + + case map[string]swspec.Response: + resp := container[entry] + resp.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[entry] = resp + + case swspec.Parameter: + if err := rewriteParentRef(spec, "#"+parent, ref); err != nil { + return err + } + + case map[string]swspec.Parameter: + param := container[entry] + param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[entry] = param + + case []swspec.Parameter: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", key[1:], err) + } + param := container[idx] + param.Schema = &swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + container[idx] = param + + case swspec.Definitions: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case map[string]swspec.Schema: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case []swspec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", key[1:], err) + } + container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", key[1:], err) + } + container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + // NOTE: can't have case *swspec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled parent schema rewrite %s (%T)", key, pvalue) + } + return nil +} + +func cloneSchema(schema *swspec.Schema) (*swspec.Schema, error) { + var sch swspec.Schema + if err := swag.FromDynamicJSON(schema, &sch); err != nil { + return nil, fmt.Errorf("cannot clone schema: %v", err) + } + return &sch, nil +} + +func importExternalReferences(opts *FlattenOpts) (bool, error) { + groupedRefs := reverseIndexForSchemaRefs(opts) + sortedRefStr := make([]string, 0, len(groupedRefs)) + if opts.flattenContext == nil { + opts.flattenContext = newContext() + } + + // sort $ref resolution to ensure deterministic name conflict resolution + for refStr := range groupedRefs { + sortedRefStr = append(sortedRefStr, refStr) + } + sort.Strings(sortedRefStr) + + complete := true + for _, refStr := range sortedRefStr { + entry := groupedRefs[refStr] + if !entry.Ref.HasFragmentOnly { + complete = false + var isOAIGen bool + + newName := opts.flattenContext.resolved[refStr] + if newName != "" { + // rewrite ref with already resolved external ref (useful for cyclicali refs) + debugLog("resolving known ref [%s] to %s", refStr, newName) + // rewrite the external refs to local ones + for _, key := range entry.Keys { + if err := updateRef(opts.Swagger(), key, + swspec.MustCreateRef(slashpath.Join(definitionsPath, newName))); err != nil { + return false, err + } + } + } else { + debugLog("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) + + // resolve to actual schema + sch := new(swspec.Schema) + sch.Ref = entry.Ref + if err := swspec.ExpandSchemaWithBasePath(sch, nil, opts.ExpandOpts(false)); err != nil { + return false, err + } + if sch == nil { + return false, fmt.Errorf("no schema found at %s for [%s]", refStr, strings.Join(entry.Keys, ", ")) + } + debugLog("importing external schema for [%s] from %s", strings.Join(entry.Keys, ", "), refStr) + + // generate a unique name - isOAIGen means that a naming conflict was resolved by changing the name + newName, isOAIGen = uniqifyName(opts.Swagger().Definitions, nameFromRef(entry.Ref)) + debugLog("new name for [%s]: %s - with name conflict:%t", + strings.Join(entry.Keys, ", "), newName, isOAIGen) + + opts.flattenContext.resolved[refStr] = newName + + // rewrite the external refs to local ones + for _, key := range entry.Keys { + if err := updateRef(opts.Swagger(), key, + swspec.MustCreateRef(slashpath.Join(definitionsPath, newName))); err != nil { + return false, err + } + + // keep track of created refs + resolved := false + if _, ok := opts.flattenContext.newRefs[key]; ok { + resolved = opts.flattenContext.newRefs[key].resolved + } + opts.flattenContext.newRefs[key] = &newRef{ + key: key, + newName: newName, + path: slashpath.Join(definitionsPath, newName), + isOAIGen: isOAIGen, + resolved: resolved, + schema: sch, + } + } + + // add the resolved schema to the definitions + saveSchema(opts.Swagger(), newName, sch) + } + } + } + return complete, nil +} + +type refRevIdx struct { + Ref swspec.Ref + Keys []string +} + +// normalizePath renders absolute path on remote file refs +func normalizePath(ref swspec.Ref, opts *FlattenOpts) (normalizedPath string) { + if ref.HasFragmentOnly || filepath.IsAbs(ref.String()) { + normalizedPath = ref.String() + return + } + + refURL, _ := url.Parse(ref.String()) + if refURL.Host != "" { + normalizedPath = ref.String() + return + } + + parts := strings.Split(ref.String(), "#") + parts[0] = filepath.Join(filepath.Dir(opts.BasePath), parts[0]) + normalizedPath = strings.Join(parts, "#") + return +} + +func reverseIndexForSchemaRefs(opts *FlattenOpts) map[string]refRevIdx { + collected := make(map[string]refRevIdx) + for key, schRef := range opts.Spec.references.schemas { + // normalize paths before sorting, + // so we get together keys in same external file + normalizedPath := normalizePath(schRef, opts) + if entry, ok := collected[normalizedPath]; ok { + entry.Keys = append(entry.Keys, key) + collected[normalizedPath] = entry + } else { + collected[normalizedPath] = refRevIdx{ + Ref: schRef, + Keys: []string{key}, + } + } + } + return collected +} + +func nameFromRef(ref swspec.Ref) string { + u := ref.GetURL() + if u.Fragment != "" { + return swag.ToJSONName(slashpath.Base(u.Fragment)) + } + if u.Path != "" { + bn := slashpath.Base(u.Path) + if bn != "" && bn != "/" { + ext := slashpath.Ext(bn) + if ext != "" { + return swag.ToJSONName(bn[:len(bn)-len(ext)]) + } + return swag.ToJSONName(bn) + } + } + return swag.ToJSONName(strings.Replace(u.Host, ".", " ", -1)) +} + +func saveSchema(spec *swspec.Swagger, name string, schema *swspec.Schema) { + if schema == nil { + return + } + if spec.Definitions == nil { + spec.Definitions = make(map[string]swspec.Schema, 150) + } + spec.Definitions[name] = *schema +} + +// getPointerFromKey retrieves the content of the JSON pointer "key" +func getPointerFromKey(spec *swspec.Swagger, key string) (string, interface{}, error) { + // unescape chars in key, e.g. "{}" from path params + pth, _ := internal.PathUnescape(key[1:]) + ptr, err := jsonpointer.New(pth) + if err != nil { + return "", nil, err + } + + value, _, err := ptr.Get(spec) + if err != nil { + debugLog("error when getting key: %s with path: %s", key, pth) + return "", nil, err + } + return pth, value, nil +} + +// getParentFromKey retrieves the container of the JSON pointer "key" +func getParentFromKey(spec *swspec.Swagger, key string) (string, string, interface{}, error) { + // unescape chars in key, e.g. "{}" from path params + pth, _ := internal.PathUnescape(key[1:]) + + parent, entry := slashpath.Dir(pth), slashpath.Base(pth) + debugLog("getting schema holder at: %s, with entry: %s", parent, entry) + + pptr, err := jsonpointer.New(parent) + if err != nil { + return "", "", nil, err + } + pvalue, _, err := pptr.Get(spec) + if err != nil { + return "", "", nil, fmt.Errorf("can't get parent for %s: %v", parent, err) + } + return parent, entry, pvalue, nil +} + +// updateRef replaces a ref by another one +func updateRef(spec *swspec.Swagger, key string, ref swspec.Ref) error { + debugLog("updating ref for %s with %s", key, ref.String()) + pth, value, err := getPointerFromKey(spec, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *swspec.Schema: + refable.Ref = ref + case *swspec.SchemaOrArray: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case *swspec.SchemaOrBool: + if refable.Schema != nil { + refable.Schema.Ref = ref + } + case swspec.Schema: + debugLog("rewriting holder for %T", refable) + _, entry, pvalue, erp := getParentFromKey(spec, key) + if erp != nil { + return err + } + switch container := pvalue.(type) { + case swspec.Definitions: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case map[string]swspec.Schema: + container[entry] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case []swspec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + case *swspec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container.Schemas[idx] = swspec.Schema{SchemaProps: swspec.SchemaProps{Ref: ref}} + + // NOTE: can't have case *swspec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled container type at %s: %T", key, value) + } + + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +// updateRefWithSchema replaces a ref with a schema (i.e. re-inline schema) +func updateRefWithSchema(spec *swspec.Swagger, key string, sch *swspec.Schema) error { + debugLog("updating ref for %s with schema", key) + pth, value, err := getPointerFromKey(spec, key) + if err != nil { + return err + } + + switch refable := value.(type) { + case *swspec.Schema: + *refable = *sch + case swspec.Schema: + _, entry, pvalue, erp := getParentFromKey(spec, key) + if erp != nil { + return err + } + switch container := pvalue.(type) { + case swspec.Definitions: + container[entry] = *sch + + case map[string]swspec.Schema: + container[entry] = *sch + + case []swspec.Schema: + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container[idx] = *sch + + case *swspec.SchemaOrArray: + // NOTE: this is necessarily an array - otherwise, the parent would be *Schema + idx, err := strconv.Atoi(entry) + if err != nil { + return fmt.Errorf("%s not a number: %v", pth, err) + } + container.Schemas[idx] = *sch + + // NOTE: can't have case *swspec.SchemaOrBool = parent in this case is *Schema + + default: + return fmt.Errorf("unhandled type for parent of [%s]: %T", key, value) + } + case *swspec.SchemaOrArray: + *refable.Schema = *sch + // NOTE: can't have case *swspec.SchemaOrBool = parent in this case is *Schema + case *swspec.SchemaOrBool: + *refable.Schema = *sch + default: + return fmt.Errorf("no schema with ref found at %s for %T", key, value) + } + + return nil +} + +func containsString(names []string, name string) bool { + for _, nm := range names { + if nm == name { + return true + } + } + return false +} + +type opRef struct { + Method string + Path string + Key string + ID string + Op *swspec.Operation + Ref swspec.Ref +} + +type opRefs []opRef + +func (o opRefs) Len() int { return len(o) } +func (o opRefs) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o opRefs) Less(i, j int) bool { return o[i].Key < o[j].Key } + +func gatherOperations(specDoc *Spec, operationIDs []string) map[string]opRef { + var oprefs opRefs + + for method, pathItem := range specDoc.Operations() { + for pth, operation := range pathItem { + vv := *operation + oprefs = append(oprefs, opRef{ + Key: swag.ToGoName(strings.ToLower(method) + " " + pth), + Method: method, + Path: pth, + ID: vv.ID, + Op: &vv, + Ref: swspec.MustCreateRef("#" + slashpath.Join("/paths", jsonpointer.Escape(pth), method)), + }) + } + } + + sort.Sort(oprefs) + + operations := make(map[string]opRef) + for _, opr := range oprefs { + nm := opr.ID + if nm == "" { + nm = opr.Key + } + + oo, found := operations[nm] + if found && oo.Method != opr.Method && oo.Path != opr.Path { + nm = opr.Key + } + if len(operationIDs) == 0 || containsString(operationIDs, opr.ID) || containsString(operationIDs, nm) { + opr.ID = nm + opr.Op.ID = nm + operations[nm] = opr + } + } + return operations +} + +// stripPointersAndOAIGen removes anonymous JSON pointers from spec and chain with name conflicts handler. +// This loops until the spec has no such pointer and all name conflicts have been reduced as much as possible. +func stripPointersAndOAIGen(opts *FlattenOpts) error { + // name all JSON pointers to anonymous documents + if err := namePointers(opts); err != nil { + return err + } + + // remove unnecessary OAIGen ref (created when flattening external refs creates name conflicts) + hasIntroducedPointerOrInline, ers := stripOAIGen(opts) + if ers != nil { + return ers + } + + // iterate as pointer or OAIGen resolution may introduce inline schemas or pointers + for hasIntroducedPointerOrInline { + if !opts.Minimal { + if err := nameInlinedSchemas(opts); err != nil { + return err + } + } + + if err := namePointers(opts); err != nil { + return err + } + + // restrip + if hasIntroducedPointerOrInline, ers = stripOAIGen(opts); ers != nil { + return ers + } + } + return nil +} + +// stripOAIGen strips the spec from unnecessary OAIGen constructs, initially created to dedupe flattened definitions. +// A dedupe is deemed unnecessary whenever: +// - the only conflict is with its (single) parent: OAIGen is merged into its parent +// - there is a conflict with multiple parents: merge OAIGen in first parent, the rewrite other parents to point to +// the first parent. +// +// This function returns a true bool whenever it re-inlined a complex schema, so the caller may chose to iterate +// flattening again. +// +// NOTE: the OAIGen definition cannot be itself a $ref. +func stripOAIGen(opts *FlattenOpts) (bool, error) { + debugLog("stripOAIGen") + replacedWithComplex := false + for k, v := range opts.Spec.references.allRefs { + // figure out referers of OAIGen definitions + for _, r := range opts.flattenContext.newRefs { + if r.isOAIGen && !r.resolved && r.path == v.String() { // bail on already resolved entries (avoid looping) + r.parents = append(r.parents, k) + } + } + } + + for _, r := range opts.flattenContext.newRefs { + if r.isOAIGen && len(r.parents) >= 1 && r.schema.Ref.String() == "" { + pr := r.parents + sort.Strings(pr) + // rewrite first parent schema in lexicographical order + debugLog("rewrite first parent %s with schema", pr[0]) + if err := updateRefWithSchema(opts.Swagger(), pr[0], r.schema); err != nil { + return false, err + } + // rewrite other parents to point to first parent + if len(pr) > 1 { + for _, p := range pr[1:] { + replacingRef := swspec.MustCreateRef(pr[0]) + // Set complex when replacing ref is an anonymous jsonpointer: further processing may be required + replacedWithComplex = replacedWithComplex || + slashpath.Dir(replacingRef.String()) != definitionsPath + debugLog("rewrite parent with ref: %s", replacingRef.String()) + // NOTE: it is possible at this stage to introduce json pointers (to non-definitions places). + // Those are stripped later on. + if err := updateRef(opts.Swagger(), p, replacingRef); err != nil { + return false, err + } + } + } + // remove OAIGen definition + debugLog("removing definition %s", slashpath.Base(r.path)) + delete(opts.Swagger().Definitions, slashpath.Base(r.path)) + // mark naming conflict as resolved + opts.flattenContext.newRefs[r.key].isOAIGen = false + opts.flattenContext.newRefs[r.key].resolved = true + + // determine if the previous substitution did inline a complex schema + if r.schema != nil && r.schema.Ref.String() == "" { // inline schema + asch, err := Schema(SchemaOpts{Schema: r.schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if err != nil { + return false, err + } + debugLog("re-inline schema: parent: %s, %t", pr[0], isAnalyzedAsComplex(asch)) + replacedWithComplex = replacedWithComplex || + !(slashpath.Dir(pr[0]) == definitionsPath) && isAnalyzedAsComplex(asch) + } + } + } + opts.Spec.reload() // re-analyze + return replacedWithComplex, nil +} + +// croak logs notifications and warnings about valid, but possibly unwanted constructs resulting +// from flattening a spec +func croak(opts *FlattenOpts) { + reported := make(map[string]bool, len(opts.flattenContext.newRefs)) + for _, v := range opts.Spec.references.allRefs { + // warns about duplicate handling + for _, r := range opts.flattenContext.newRefs { + if r.isOAIGen && r.path == v.String() { + reported[r.newName] = true + } + } + } + for k := range reported { + log.Printf("warning: duplicate flattened definition name resolved as %s", k) + } + // warns about possible type mismatches + uniqueMsg := make(map[string]bool) + for _, msg := range opts.flattenContext.warnings { + if _, ok := uniqueMsg[msg]; ok { + continue + } + log.Printf("warning: %s", msg) + uniqueMsg[msg] = true + } +} + +// namePointers replaces all JSON pointers to anonymous documents by a $ref to a new named definitions. +// +// This is carried on depth-first. Pointers to $refs which are top level definitions are replaced by the $ref itself. +// Pointers to simple types are expanded, unless they express commonality (i.e. several such $ref are used). +func namePointers(opts *FlattenOpts) error { + debugLog("name pointers") + refsToReplace := make(map[string]SchemaRef, len(opts.Spec.references.schemas)) + for k, ref := range opts.Spec.references.allRefs { + if slashpath.Dir(ref.String()) == definitionsPath { + // this a ref to a top-level definition: ok + continue + } + replacingRef, sch, erd := deepestRef(opts, ref) + if erd != nil { + return fmt.Errorf("at %s, %v", k, erd) + } + debugLog("planning pointer to replace at %s: %s, resolved to: %s", k, ref.String(), replacingRef.String()) + refsToReplace[k] = SchemaRef{ + Name: k, // caller + Ref: replacingRef, // callee + Schema: sch, + TopLevel: slashpath.Dir(replacingRef.String()) == definitionsPath, + } + } + depthFirst := sortDepthFirst(refsToReplace) + namer := &inlineSchemaNamer{ + Spec: opts.Swagger(), + Operations: opRefsByRef(gatherOperations(opts.Spec, nil)), + flattenContext: opts.flattenContext, + opts: opts, + } + + for _, key := range depthFirst { + v := refsToReplace[key] + // update current replacement, which may have been updated by previous changes of deeper elements + replacingRef, sch, erd := deepestRef(opts, v.Ref) + if erd != nil { + return fmt.Errorf("at %s, %v", key, erd) + } + v.Ref = replacingRef + v.Schema = sch + v.TopLevel = slashpath.Dir(replacingRef.String()) == definitionsPath + debugLog("replacing pointer at %s: resolved to: %s", key, v.Ref.String()) + + if v.TopLevel { + debugLog("replace pointer %s by canonical definition: %s", key, v.Ref.String()) + // if the schema is a $ref to a top level definition, just rewrite the pointer to this $ref + if err := updateRef(opts.Swagger(), key, v.Ref); err != nil { + return err + } + } else { + // this is a JSON pointer to an anonymous document (internal or external): + // create a definition for this schema when: + // - it is a complex schema + // - or it is pointed by more than one $ref (i.e. expresses commonality) + // otherwise, expand the pointer (single reference to a simple type) + // + // The named definition for this follows the target's key, not the caller's + debugLog("namePointers at %s for %s", key, v.Ref.String()) + + // qualify the expanded schema + asch, ers := Schema(SchemaOpts{Schema: v.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + if ers != nil { + return fmt.Errorf("schema analysis [%s]: %v", key, ers) + } + callers := make([]string, 0, 64) + + debugLog("looking for callers") + an := New(opts.Swagger()) + for k, w := range an.references.allRefs { + r, _, erd := deepestRef(opts, w) + if erd != nil { + return fmt.Errorf("at %s, %v", key, erd) + } + if r.String() == v.Ref.String() { + callers = append(callers, k) + } + } + debugLog("callers for %s: %d", v.Ref.String(), len(callers)) + if len(callers) == 0 { + // has already been updated and resolved + continue + } + + parts := keyParts(v.Ref.String()) + debugLog("number of callers for %s: %d", v.Ref.String(), len(callers)) + // identifying edge case when the namer did nothing because we point to a non-schema object + // no definition is created and we expand the $ref for all callers + if (!asch.IsSimpleSchema || len(callers) > 1) && !parts.IsSharedParam() && !parts.IsSharedResponse() { + debugLog("replace JSON pointer at [%s] by definition: %s", key, v.Ref.String()) + if err := namer.Name(v.Ref.String(), v.Schema, asch); err != nil { + return err + } + + // regular case: we named the $ref as a definition, and we move all callers to this new $ref + for _, caller := range callers { + if caller != key { + // move $ref for next to resolve + debugLog("identified caller of %s at [%s]", v.Ref.String(), caller) + c := refsToReplace[caller] + c.Ref = v.Ref + refsToReplace[caller] = c + } + } + } else { + debugLog("expand JSON pointer for key=%s", key) + if err := updateRefWithSchema(opts.Swagger(), key, v.Schema); err != nil { + return err + } + // NOTE: there is no other caller to update + } + } + } + opts.Spec.reload() // re-analyze + return nil +} + +// deepestRef finds the first definition ref, from a cascade of nested refs which are not definitions. +// - if no definition is found, returns the deepest ref. +// - pointers to external files are expanded +// +// NOTE: all external $ref's are assumed to be already expanded at this stage. +func deepestRef(opts *FlattenOpts, ref swspec.Ref) (swspec.Ref, *swspec.Schema, error) { + if !ref.HasFragmentOnly { + // we found an external $ref, which is odd + // does nothing on external $refs + return ref, nil, nil + } + currentRef := ref + visited := make(map[string]bool, 64) +DOWNREF: + for currentRef.String() != "" { + if slashpath.Dir(currentRef.String()) == definitionsPath { + // this is a top-level definition: stop here and return this ref + return currentRef, nil, nil + } + if _, beenThere := visited[currentRef.String()]; beenThere { + return swspec.Ref{}, nil, + fmt.Errorf("cannot resolve cyclic chain of pointers under %s", currentRef.String()) + } + visited[currentRef.String()] = true + value, _, err := currentRef.GetPointer().Get(opts.Swagger()) + if err != nil { + return swspec.Ref{}, nil, err + } + switch refable := value.(type) { + case *swspec.Schema: + if refable.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Ref + + case swspec.Schema: + if refable.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Ref + + case *swspec.SchemaOrArray: + if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Schema.Ref + + case *swspec.SchemaOrBool: + if refable.Schema == nil || refable.Schema != nil && refable.Schema.Ref.String() == "" { + break DOWNREF + } + currentRef = refable.Schema.Ref + + case swspec.Response: + // a pointer points to a schema initially marshalled in responses section... + // Attempt to convert this to a schema. If this fails, the spec is invalid + asJSON, _ := refable.MarshalJSON() + var asSchema swspec.Schema + err := asSchema.UnmarshalJSON(asJSON) + if err != nil { + return swspec.Ref{}, nil, + fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T", + currentRef.String(), value) + + } + opts.flattenContext.warnings = append(opts.flattenContext.warnings, + fmt.Sprintf("found $ref %q (response) interpreted as schema", currentRef.String())) + + if asSchema.Ref.String() == "" { + break DOWNREF + } + currentRef = asSchema.Ref + + case swspec.Parameter: + // a pointer points to a schema initially marshalled in parameters section... + // Attempt to convert this to a schema. If this fails, the spec is invalid + asJSON, _ := refable.MarshalJSON() + var asSchema swspec.Schema + err := asSchema.UnmarshalJSON(asJSON) + if err != nil { + return swspec.Ref{}, nil, + fmt.Errorf("invalid type for resolved JSON pointer %s. Expected a schema a, got: %T", + currentRef.String(), value) + + } + opts.flattenContext.warnings = append(opts.flattenContext.warnings, + fmt.Sprintf("found $ref %q (parameter) interpreted as schema", currentRef.String())) + + if asSchema.Ref.String() == "" { + break DOWNREF + } + currentRef = asSchema.Ref + + default: + return swspec.Ref{}, nil, + fmt.Errorf("unhandled type to resolve JSON pointer %s. Expected a Schema, got: %T", + currentRef.String(), value) + + } + } + // assess what schema we're ending with + sch, erv := swspec.ResolveRefWithBase(opts.Swagger(), ¤tRef, opts.ExpandOpts(false)) + if erv != nil { + return swspec.Ref{}, nil, erv + } + if sch == nil { + return swspec.Ref{}, nil, fmt.Errorf("no schema found at %s", currentRef.String()) + } + return currentRef, sch, nil +} + +// normalizeRef strips the current file from any $ref. This works around issue go-openapi/spec#76: +// leading absolute file in $ref is stripped +func normalizeRef(opts *FlattenOpts) error { + debugLog("normalizeRef") + opts.Spec.reload() // re-analyze + for k, w := range opts.Spec.references.allRefs { + if strings.HasPrefix(w.String(), opts.BasePath+definitionsPath) { // may be a mix of / and \, depending on OS + // strip base path from definition + debugLog("stripping absolute path for: %s", w.String()) + if err := updateRef(opts.Swagger(), k, + swspec.MustCreateRef(slashpath.Join(definitionsPath, slashpath.Base(w.String())))); err != nil { + return err + } + } + } + opts.Spec.reload() // re-analyze + return nil +} diff --git a/vendor/github.com/go-openapi/analysis/go.mod b/vendor/github.com/go-openapi/analysis/go.mod new file mode 100644 index 000000000..95652cbe3 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/go.mod @@ -0,0 +1,13 @@ +module github.com/go-openapi/analysis + +require ( + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect + github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 // indirect + github.com/go-openapi/errors v0.19.2 // indirect + github.com/go-openapi/jsonpointer v0.19.2 + github.com/go-openapi/loads v0.19.0 + github.com/go-openapi/spec v0.19.2 + github.com/go-openapi/strfmt v0.19.0 + github.com/go-openapi/swag v0.19.2 + github.com/stretchr/testify v1.3.0 +) diff --git a/vendor/github.com/go-openapi/analysis/go.sum b/vendor/github.com/go-openapi/analysis/go.sum new file mode 100644 index 000000000..5a8c0dbee --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/go.sum @@ -0,0 +1,79 @@ +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/loads v0.19.0 h1:wCOBNscACI8L93tt5tvB2zOMkJ098XCw3fP0BY2ybDA= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/analysis/internal/post_go18.go b/vendor/github.com/go-openapi/analysis/internal/post_go18.go new file mode 100644 index 000000000..f96f55c08 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/post_go18.go @@ -0,0 +1,29 @@ +// +build go1.8 + +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import "net/url" + +// PathUnescape provides url.PathUnescape(), with seamless +// go version support for pre-go1.8 +// +// TODO: this function is currently defined in go-openapi/swag, +// but unexported. We might chose to export it, or simple phase +// out pre-go1.8 support. +func PathUnescape(path string) (string, error) { + return url.PathUnescape(path) +} diff --git a/vendor/github.com/go-openapi/analysis/internal/pre_go18.go b/vendor/github.com/go-openapi/analysis/internal/pre_go18.go new file mode 100644 index 000000000..4cc644182 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/internal/pre_go18.go @@ -0,0 +1,29 @@ +// +build !go1.8 + +// Copyright 2015 go-swagger maintainers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import "net/url" + +// PathUnescape provides url.PathUnescape(), with seamless +// go version support for pre-go1.8 +// +// TODO: this function is currently defined in go-openapi/swag, +// but unexported. We might chose to export it, or simple phase +// out pre-go1.8 support. +func PathUnescape(path string) (string, error) { + return url.QueryUnescape(path) +} diff --git a/vendor/github.com/go-openapi/analysis/mixin.go b/vendor/github.com/go-openapi/analysis/mixin.go new file mode 100644 index 000000000..49806b4f7 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/mixin.go @@ -0,0 +1,334 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 analysis + +import ( + "fmt" + "reflect" + + "github.com/go-openapi/spec" +) + +// Mixin modifies the primary swagger spec by adding the paths and +// definitions from the mixin specs. Top level parameters and +// responses from the mixins are also carried over. Operation id +// collisions are avoided by appending "Mixin" but only if +// needed. +// +// The following parts of primary are never modified by merging: +// - Info +// - BasePath +// - Host +// - ExternalDocs +// +// Consider calling FixEmptyResponseDescriptions() on the modified primary +// if you read them from storage and they are valid to start with. +// +// Entries in "paths", "definitions", "parameters" and "responses" are +// added to the primary in the order of the given mixins. If the entry +// already exists in primary it is skipped with a warning message. +// +// The count of skipped entries (from collisions) is returned so any +// deviation from the number expected can flag a warning in your build +// scripts. Carefully review the collisions before accepting them; +// consider renaming things if possible. +// +// No key normalization takes place (paths, type defs, +// etc). Ensure they are canonical if your downstream tools do +// key normalization of any form. +// +// Merging schemes (http, https), and consumers/producers do not account for +// collisions. +func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string { + skipped := make([]string, 0, len(mixins)) + opIds := getOpIds(primary) + initPrimary(primary) + + for i, m := range mixins { + skipped = append(skipped, mergeConsumes(primary, m)...) + + skipped = append(skipped, mergeProduces(primary, m)...) + + skipped = append(skipped, mergeTags(primary, m)...) + + skipped = append(skipped, mergeSchemes(primary, m)...) + + skipped = append(skipped, mergeSecurityDefinitions(primary, m)...) + + skipped = append(skipped, mergeSecurityRequirements(primary, m)...) + + skipped = append(skipped, mergeDefinitions(primary, m)...) + + // merging paths requires a map of operationIDs to work with + skipped = append(skipped, mergePaths(primary, m, opIds, i)...) + + skipped = append(skipped, mergeParameters(primary, m)...) + + skipped = append(skipped, mergeResponses(primary, m)...) + } + return skipped +} + +// getOpIds extracts all the paths..operationIds from the given +// spec and returns them as the keys in a map with 'true' values. +func getOpIds(s *spec.Swagger) map[string]bool { + rv := make(map[string]bool) + if s.Paths == nil { + return rv + } + for _, v := range s.Paths.Paths { + piops := pathItemOps(v) + for _, op := range piops { + rv[op.ID] = true + } + } + return rv +} + +func pathItemOps(p spec.PathItem) []*spec.Operation { + var rv []*spec.Operation + rv = appendOp(rv, p.Get) + rv = appendOp(rv, p.Put) + rv = appendOp(rv, p.Post) + rv = appendOp(rv, p.Delete) + rv = appendOp(rv, p.Head) + rv = appendOp(rv, p.Patch) + return rv +} + +func appendOp(ops []*spec.Operation, op *spec.Operation) []*spec.Operation { + if op == nil { + return ops + } + return append(ops, op) +} + +func mergeSecurityDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.SecurityDefinitions { + if _, exists := primary.SecurityDefinitions[k]; exists { + warn := fmt.Sprintf( + "SecurityDefinitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.SecurityDefinitions[k] = v + } + return +} + +func mergeSecurityRequirements(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Security { + found := false + for _, vv := range primary.Security { + if reflect.DeepEqual(v, vv) { + found = true + break + } + } + if found { + warn := fmt.Sprintf( + "Security requirement: '%v' already exists in primary or higher priority mixin, skipping\n", v) + skipped = append(skipped, warn) + continue + } + primary.Security = append(primary.Security, v) + } + return +} + +func mergeDefinitions(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Definitions { + // assume name collisions represent IDENTICAL type. careful. + if _, exists := primary.Definitions[k]; exists { + warn := fmt.Sprintf( + "definitions entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Definitions[k] = v + } + return +} + +func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIds map[string]bool, mixIndex int) (skipped []string) { + if m.Paths != nil { + for k, v := range m.Paths.Paths { + if _, exists := primary.Paths.Paths[k]; exists { + warn := fmt.Sprintf( + "paths entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + + // Swagger requires that operationIds be + // unique within a spec. If we find a + // collision we append "Mixin0" to the + // operatoinId we are adding, where 0 is mixin + // index. We assume that operationIds with + // all the proivded specs are already unique. + piops := pathItemOps(v) + for _, piop := range piops { + if opIds[piop.ID] { + piop.ID = fmt.Sprintf("%v%v%v", piop.ID, "Mixin", mixIndex) + } + opIds[piop.ID] = true + } + primary.Paths.Paths[k] = v + } + } + return +} + +func mergeParameters(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Parameters { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Parameters[k]; exists { + warn := fmt.Sprintf( + "top level parameters entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Parameters[k] = v + } + return +} + +func mergeResponses(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for k, v := range m.Responses { + // could try to rename on conflict but would + // have to fix $refs in the mixin. Complain + // for now + if _, exists := primary.Responses[k]; exists { + warn := fmt.Sprintf( + "top level responses entry '%v' already exists in primary or higher priority mixin, skipping\n", k) + skipped = append(skipped, warn) + continue + } + primary.Responses[k] = v + } + return +} + +func mergeConsumes(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Consumes { + found := false + for _, vv := range primary.Consumes { + if v == vv { + found = true + break + } + } + if found { + // no warning here: we just skip it + continue + } + primary.Consumes = append(primary.Consumes, v) + } + return +} + +func mergeProduces(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Produces { + found := false + for _, vv := range primary.Produces { + if v == vv { + found = true + break + } + } + if found { + // no warning here: we just skip it + continue + } + primary.Produces = append(primary.Produces, v) + } + return +} + +func mergeTags(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Tags { + found := false + for _, vv := range primary.Tags { + if v.Name == vv.Name { + found = true + break + } + } + if found { + warn := fmt.Sprintf( + "top level tags entry with name '%v' already exists in primary or higher priority mixin, skipping\n", v.Name) + skipped = append(skipped, warn) + continue + } + primary.Tags = append(primary.Tags, v) + } + return +} + +func mergeSchemes(primary *spec.Swagger, m *spec.Swagger) (skipped []string) { + for _, v := range m.Schemes { + found := false + for _, vv := range primary.Schemes { + if v == vv { + found = true + break + } + } + if found { + // no warning here: we just skip it + continue + } + primary.Schemes = append(primary.Schemes, v) + } + return +} + +func initPrimary(primary *spec.Swagger) { + if primary.SecurityDefinitions == nil { + primary.SecurityDefinitions = make(map[string]*spec.SecurityScheme) + } + if primary.Security == nil { + primary.Security = make([]map[string][]string, 0, 10) + } + if primary.Produces == nil { + primary.Produces = make([]string, 0, 10) + } + if primary.Consumes == nil { + primary.Consumes = make([]string, 0, 10) + } + if primary.Tags == nil { + primary.Tags = make([]spec.Tag, 0, 10) + } + if primary.Schemes == nil { + primary.Schemes = make([]string, 0, 10) + } + if primary.Paths == nil { + primary.Paths = &spec.Paths{Paths: make(map[string]spec.PathItem)} + } + if primary.Paths.Paths == nil { + primary.Paths.Paths = make(map[string]spec.PathItem) + } + if primary.Definitions == nil { + primary.Definitions = make(spec.Definitions) + } + if primary.Parameters == nil { + primary.Parameters = make(map[string]spec.Parameter) + } + if primary.Responses == nil { + primary.Responses = make(map[string]spec.Response) + } +} diff --git a/vendor/github.com/go-openapi/analysis/schema.go b/vendor/github.com/go-openapi/analysis/schema.go new file mode 100644 index 000000000..afbf932ff --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/schema.go @@ -0,0 +1,240 @@ +package analysis + +import ( + "fmt" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// SchemaOpts configures the schema analyzer +type SchemaOpts struct { + Schema *spec.Schema + Root interface{} + BasePath string + _ struct{} +} + +// Schema analysis, will classify the schema according to known +// patterns. +func Schema(opts SchemaOpts) (*AnalyzedSchema, error) { + if opts.Schema == nil { + return nil, fmt.Errorf("no schema to analyze") + } + + a := &AnalyzedSchema{ + schema: opts.Schema, + root: opts.Root, + basePath: opts.BasePath, + } + + a.initializeFlags() + a.inferKnownType() + a.inferEnum() + a.inferBaseType() + + if err := a.inferMap(); err != nil { + return nil, err + } + if err := a.inferArray(); err != nil { + return nil, err + } + + if err := a.inferTuple(); err != nil { + // NOTE(fredbi): currently, inferTuple() never returns an error + return nil, err + } + + if err := a.inferFromRef(); err != nil { + return nil, err + } + + a.inferSimpleSchema() + return a, nil +} + +// AnalyzedSchema indicates what the schema represents +type AnalyzedSchema struct { + schema *spec.Schema + root interface{} + basePath string + + hasProps bool + hasAllOf bool + hasItems bool + hasAdditionalProps bool + hasAdditionalItems bool + hasRef bool + + IsKnownType bool + IsSimpleSchema bool + IsArray bool + IsSimpleArray bool + IsMap bool + IsSimpleMap bool + IsExtendedObject bool + IsTuple bool + IsTupleWithExtra bool + IsBaseType bool + IsEnum bool +} + +// Inherits copies value fields from other onto this schema +func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) { + if other == nil { + return + } + a.hasProps = other.hasProps + a.hasAllOf = other.hasAllOf + a.hasItems = other.hasItems + a.hasAdditionalItems = other.hasAdditionalItems + a.hasAdditionalProps = other.hasAdditionalProps + a.hasRef = other.hasRef + + a.IsKnownType = other.IsKnownType + a.IsSimpleSchema = other.IsSimpleSchema + a.IsArray = other.IsArray + a.IsSimpleArray = other.IsSimpleArray + a.IsMap = other.IsMap + a.IsSimpleMap = other.IsSimpleMap + a.IsExtendedObject = other.IsExtendedObject + a.IsTuple = other.IsTuple + a.IsTupleWithExtra = other.IsTupleWithExtra + a.IsBaseType = other.IsBaseType + a.IsEnum = other.IsEnum +} + +func (a *AnalyzedSchema) inferFromRef() error { + if a.hasRef { + sch := new(spec.Schema) + sch.Ref = a.schema.Ref + err := spec.ExpandSchema(sch, a.root, nil) + if err != nil { + return err + } + if sch != nil { + // NOTE(fredbi): currently the only cause for errors in + // unresolved ref. Since spec.ExpandSchema() expands the + // schema recursively, there is no chance to get there, + // until we add more causes for error in this schema analysis. + rsch, err := Schema(SchemaOpts{ + Schema: sch, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.inherits(rsch) + } + } + return nil +} + +func (a *AnalyzedSchema) inferSimpleSchema() { + a.IsSimpleSchema = a.IsKnownType || a.IsSimpleArray || a.IsSimpleMap +} + +func (a *AnalyzedSchema) inferKnownType() { + tpe := a.schema.Type + format := a.schema.Format + a.IsKnownType = tpe.Contains("boolean") || + tpe.Contains("integer") || + tpe.Contains("number") || + tpe.Contains("string") || + (format != "" && strfmt.Default.ContainsName(format)) || + (a.isObjectType() && !a.hasProps && !a.hasAllOf && !a.hasAdditionalProps && !a.hasAdditionalItems) +} + +func (a *AnalyzedSchema) inferMap() error { + if a.isObjectType() { + hasExtra := a.hasProps || a.hasAllOf + a.IsMap = a.hasAdditionalProps && !hasExtra + a.IsExtendedObject = a.hasAdditionalProps && hasExtra + if a.IsMap { + if a.schema.AdditionalProperties.Schema != nil { + msch, err := Schema(SchemaOpts{ + Schema: a.schema.AdditionalProperties.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleMap = msch.IsSimpleSchema + } else if a.schema.AdditionalProperties.Allows { + a.IsSimpleMap = true + } + } + } + return nil +} + +func (a *AnalyzedSchema) inferArray() error { + // an array has Items defined as an object schema, otherwise we qualify this JSON array as a tuple + // (yes, even if the Items array contains only one element). + // arrays in JSON schema may be unrestricted (i.e no Items specified). + // Note that arrays in Swagger MUST have Items. Nonetheless, we analyze unrestricted arrays. + // + // NOTE: the spec package misses the distinction between: + // items: [] and items: {}, so we consider both arrays here. + a.IsArray = a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Schemas == nil) + if a.IsArray && a.hasItems { + if a.schema.Items.Schema != nil { + itsch, err := Schema(SchemaOpts{ + Schema: a.schema.Items.Schema, + Root: a.root, + BasePath: a.basePath, + }) + if err != nil { + return err + } + a.IsSimpleArray = itsch.IsSimpleSchema + } + } + if a.IsArray && !a.hasItems { + a.IsSimpleArray = true + } + return nil +} + +func (a *AnalyzedSchema) inferTuple() error { + tuple := a.hasItems && a.schema.Items.Schemas != nil + a.IsTuple = tuple && !a.hasAdditionalItems + a.IsTupleWithExtra = tuple && a.hasAdditionalItems + return nil +} + +func (a *AnalyzedSchema) inferBaseType() { + if a.isObjectType() { + a.IsBaseType = a.schema.Discriminator != "" + } +} + +func (a *AnalyzedSchema) inferEnum() { + a.IsEnum = len(a.schema.Enum) > 0 +} + +func (a *AnalyzedSchema) initializeFlags() { + a.hasProps = len(a.schema.Properties) > 0 + a.hasAllOf = len(a.schema.AllOf) > 0 + a.hasRef = a.schema.Ref.String() != "" + + a.hasItems = a.schema.Items != nil && + (a.schema.Items.Schema != nil || len(a.schema.Items.Schemas) > 0) + + a.hasAdditionalProps = a.schema.AdditionalProperties != nil && + (a.schema.AdditionalProperties != nil || a.schema.AdditionalProperties.Allows) + + a.hasAdditionalItems = a.schema.AdditionalItems != nil && + (a.schema.AdditionalItems.Schema != nil || a.schema.AdditionalItems.Allows) + +} + +func (a *AnalyzedSchema) isObjectType() bool { + return !a.hasRef && (a.schema.Type == nil || a.schema.Type.Contains("") || a.schema.Type.Contains("object")) +} + +func (a *AnalyzedSchema) isArrayType() bool { + return !a.hasRef && (a.schema.Type != nil && a.schema.Type.Contains("array")) +} diff --git a/vendor/github.com/go-openapi/loads/.editorconfig b/vendor/github.com/go-openapi/loads/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/loads/.gitignore b/vendor/github.com/go-openapi/loads/.gitignore new file mode 100644 index 000000000..e4f15f17b --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.gitignore @@ -0,0 +1,4 @@ +secrets.yml +coverage.out +profile.cov +profile.out diff --git a/vendor/github.com/go-openapi/loads/.golangci.yml b/vendor/github.com/go-openapi/loads/.golangci.yml new file mode 100644 index 000000000..1932914e6 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.golangci.yml @@ -0,0 +1,22 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 30 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 4 + +linters: + enable-all: true + disable: + - maligned + - lll + - gochecknoglobals + - gochecknoinits diff --git a/vendor/github.com/go-openapi/loads/.travis.yml b/vendor/github.com/go-openapi/loads/.travis.yml new file mode 100644 index 000000000..8a7e05d91 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/.travis.yml @@ -0,0 +1,15 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: OxkPwVp35qBTUilgWC8xykSj+sGMcj0h8IIOKD+Rflx2schZVlFfdYdyVBM+s9OqeOfvtuvnR9v1Ye2rPKAvcjWdC4LpRGUsgmItZaI6Um8Aj6+K9udCw5qrtZVfOVmRu8LieH//XznWWKdOultUuniW0MLqw5+II87Gd00RWbCGi0hk0PykHe7uK+PDA2BEbqyZ2WKKYCvfB3j+0nrFOHScXqnh0V05l2E83J4+Sgy1fsPy+1WdX58ZlNBG333ibaC1FS79XvKSmTgKRkx3+YBo97u6ZtUmJa5WZjf2OdLG3KIckGWAv6R5xgxeU31N0Ng8L332w/Edpp2O/M2bZwdnKJ8hJQikXIAQbICbr+lTDzsoNzMdEIYcHpJ5hjPbiUl3Bmd+Jnsjf5McgAZDiWIfpCKZ29tPCEkVwRsOCqkyPRMNMzHHmoja495P5jR+ODS7+J8RFg5xgcnOgpP9D4Wlhztlf5WyZMpkLxTUD+bZq2SRf50HfHFXTkfq22zPl3d1eq0yrLwh/Z/fWKkfb6SyysROL8y6s8u3dpFX1YHSg0BR6i913h4aoZw9B2BG27cafLLTwKYsp2dFo1PWl4O6u9giFJIeqwloZHLKKrwh0cBFhB7RH0I58asxkZpCH6uWjJierahmHe7iS+E6i+9oCHkOZ59hmCYNimIs3hM= +script: +- gotestsum -f short-verbose -- -race -timeout=20m -coverprofile=coverage.txt -covermode=atomic ./... diff --git a/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/loads/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/gopkg.in/square/go-jose.v2/LICENSE b/vendor/github.com/go-openapi/loads/LICENSE similarity index 100% rename from vendor/gopkg.in/square/go-jose.v2/LICENSE rename to vendor/github.com/go-openapi/loads/LICENSE diff --git a/vendor/github.com/go-openapi/loads/README.md b/vendor/github.com/go-openapi/loads/README.md new file mode 100644 index 000000000..071cf69ab --- /dev/null +++ b/vendor/github.com/go-openapi/loads/README.md @@ -0,0 +1,7 @@ +# Loads OAI specs [![Build Status](https://travis-ci.org/go-openapi/loads.svg?branch=master)](https://travis-ci.org/go-openapi/loads) [![codecov](https://codecov.io/gh/go-openapi/loads/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/loads) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/loads/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/loads?status.svg)](http://godoc.org/github.com/go-openapi/loads) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/loads.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/loads)](https://goreportcard.com/report/github.com/go-openapi/loads) + +Loading of OAI specification documents from local or remote locations. Supports JSON and YAML documents. diff --git a/vendor/github.com/go-openapi/loads/doc.go b/vendor/github.com/go-openapi/loads/doc.go new file mode 100644 index 000000000..3046da4ce --- /dev/null +++ b/vendor/github.com/go-openapi/loads/doc.go @@ -0,0 +1,21 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 loads provides document loading methods for swagger (OAI) specifications. + +It is used by other go-openapi packages to load and run analysis on local or remote spec documents. + +*/ +package loads diff --git a/vendor/github.com/go-openapi/loads/go.mod b/vendor/github.com/go-openapi/loads/go.mod new file mode 100644 index 000000000..e83c6ec30 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/go.mod @@ -0,0 +1,9 @@ +module github.com/go-openapi/loads + +require ( + github.com/go-openapi/analysis v0.19.2 + github.com/go-openapi/spec v0.19.2 + github.com/go-openapi/swag v0.19.2 + github.com/stretchr/testify v1.3.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/github.com/go-openapi/loads/go.sum b/vendor/github.com/go-openapi/loads/go.sum new file mode 100644 index 000000000..b0658b2cd --- /dev/null +++ b/vendor/github.com/go-openapi/loads/go.sum @@ -0,0 +1,79 @@ +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.19.0 h1:sYEyyO7OKQvJX0z4OyHWoGt0uLuALxB/ZJ4Jb3I6KNU= +github.com/go-openapi/analysis v0.19.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/loads/spec.go b/vendor/github.com/go-openapi/loads/spec.go new file mode 100644 index 000000000..e4b4a3cf7 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/spec.go @@ -0,0 +1,298 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 loads + +import ( + "bytes" + "encoding/gob" + "encoding/json" + "fmt" + "net/url" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/spec" + "github.com/go-openapi/swag" +) + +// JSONDoc loads a json document from either a file or a remote url +func JSONDoc(path string) (json.RawMessage, error) { + data, err := swag.LoadFromFileOrHTTP(path) + if err != nil { + return nil, err + } + return json.RawMessage(data), nil +} + +// DocLoader represents a doc loader type +type DocLoader func(string) (json.RawMessage, error) + +// DocMatcher represents a predicate to check if a loader matches +type DocMatcher func(string) bool + +var ( + loaders *loader + defaultLoader *loader +) + +func init() { + defaultLoader = &loader{Match: func(_ string) bool { return true }, Fn: JSONDoc} + loaders = defaultLoader + spec.PathLoader = loaders.Fn + AddLoader(swag.YAMLMatcher, swag.YAMLDoc) + + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}{}) + //gob.Register(spec.Refable{}) +} + +// AddLoader for a document +func AddLoader(predicate DocMatcher, load DocLoader) { + prev := loaders + loaders = &loader{ + Match: predicate, + Fn: load, + Next: prev, + } + spec.PathLoader = loaders.Fn +} + +type loader struct { + Fn DocLoader + Match DocMatcher + Next *loader +} + +// JSONSpec loads a spec from a json document +func JSONSpec(path string) (*Document, error) { + data, err := JSONDoc(path) + if err != nil { + return nil, err + } + // convert to json + return Analyzed(data, "") +} + +// Document represents a swagger spec document +type Document struct { + // specAnalyzer + Analyzer *analysis.Spec + spec *spec.Swagger + specFilePath string + origSpec *spec.Swagger + schema *spec.Schema + raw json.RawMessage +} + +// Embedded returns a Document based on embedded specs. No analysis is required +func Embedded(orig, flat json.RawMessage) (*Document, error) { + var origSpec, flatSpec spec.Swagger + if err := json.Unmarshal(orig, &origSpec); err != nil { + return nil, err + } + if err := json.Unmarshal(flat, &flatSpec); err != nil { + return nil, err + } + return &Document{ + raw: orig, + origSpec: &origSpec, + spec: &flatSpec, + }, nil +} + +// Spec loads a new spec document +func Spec(path string) (*Document, error) { + specURL, err := url.Parse(path) + if err != nil { + return nil, err + } + var lastErr error + for l := loaders.Next; l != nil; l = l.Next { + if loaders.Match(specURL.Path) { + b, err2 := loaders.Fn(path) + if err2 != nil { + lastErr = err2 + continue + } + doc, err3 := Analyzed(b, "") + if err3 != nil { + return nil, err3 + } + if doc != nil { + doc.specFilePath = path + } + return doc, nil + } + } + if lastErr != nil { + return nil, lastErr + } + b, err := defaultLoader.Fn(path) + if err != nil { + return nil, err + } + + document, err := Analyzed(b, "") + if document != nil { + document.specFilePath = path + } + + return document, err +} + +// Analyzed creates a new analyzed spec document +func Analyzed(data json.RawMessage, version string) (*Document, error) { + if version == "" { + version = "2.0" + } + if version != "2.0" { + return nil, fmt.Errorf("spec version %q is not supported", version) + } + + raw := data + trimmed := bytes.TrimSpace(data) + if len(trimmed) > 0 { + if trimmed[0] != '{' && trimmed[0] != '[' { + yml, err := swag.BytesToYAMLDoc(trimmed) + if err != nil { + return nil, fmt.Errorf("analyzed: %v", err) + } + d, err := swag.YAMLToJSON(yml) + if err != nil { + return nil, fmt.Errorf("analyzed: %v", err) + } + raw = d + } + } + + swspec := new(spec.Swagger) + if err := json.Unmarshal(raw, swspec); err != nil { + return nil, err + } + + origsqspec, err := cloneSpec(swspec) + if err != nil { + return nil, err + } + + d := &Document{ + Analyzer: analysis.New(swspec), + schema: spec.MustLoadSwagger20Schema(), + spec: swspec, + raw: raw, + origSpec: origsqspec, + } + return d, nil +} + +// Expanded expands the ref fields in the spec document and returns a new spec document +func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { + swspec := new(spec.Swagger) + if err := json.Unmarshal(d.raw, swspec); err != nil { + return nil, err + } + + var expandOptions *spec.ExpandOptions + if len(options) > 0 { + expandOptions = options[0] + } else { + expandOptions = &spec.ExpandOptions{ + RelativeBase: d.specFilePath, + } + } + + if err := spec.ExpandSpec(swspec, expandOptions); err != nil { + return nil, err + } + + dd := &Document{ + Analyzer: analysis.New(swspec), + spec: swspec, + specFilePath: d.specFilePath, + schema: spec.MustLoadSwagger20Schema(), + raw: d.raw, + origSpec: d.origSpec, + } + return dd, nil +} + +// BasePath the base path for this spec +func (d *Document) BasePath() string { + return d.spec.BasePath +} + +// Version returns the version of this spec +func (d *Document) Version() string { + return d.spec.Swagger +} + +// Schema returns the swagger 2.0 schema +func (d *Document) Schema() *spec.Schema { + return d.schema +} + +// Spec returns the swagger spec object model +func (d *Document) Spec() *spec.Swagger { + return d.spec +} + +// Host returns the host for the API +func (d *Document) Host() string { + return d.spec.Host +} + +// Raw returns the raw swagger spec as json bytes +func (d *Document) Raw() json.RawMessage { + return d.raw +} + +// OrigSpec yields the original spec +func (d *Document) OrigSpec() *spec.Swagger { + return d.origSpec +} + +// ResetDefinitions gives a shallow copy with the models reset +func (d *Document) ResetDefinitions() *Document { + defs := make(map[string]spec.Schema, len(d.origSpec.Definitions)) + for k, v := range d.origSpec.Definitions { + defs[k] = v + } + + d.spec.Definitions = defs + return d +} + +// Pristine creates a new pristine document instance based on the input data +func (d *Document) Pristine() *Document { + dd, _ := Analyzed(d.Raw(), d.Version()) + return dd +} + +// SpecFilePath returns the file path of the spec if one is defined +func (d *Document) SpecFilePath() string { + return d.specFilePath +} + +func cloneSpec(src *spec.Swagger) (*spec.Swagger, error) { + var b bytes.Buffer + if err := gob.NewEncoder(&b).Encode(src); err != nil { + return nil, err + } + + var dst spec.Swagger + if err := gob.NewDecoder(&b).Decode(&dst); err != nil { + return nil, err + } + return &dst, nil +} diff --git a/vendor/github.com/go-openapi/runtime/.editorconfig b/vendor/github.com/go-openapi/runtime/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/runtime/.gitignore b/vendor/github.com/go-openapi/runtime/.gitignore new file mode 100644 index 000000000..fea8b84ec --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +*.cov +*.out +playground diff --git a/vendor/github.com/go-openapi/runtime/.travis.yml b/vendor/github.com/go-openapi/runtime/.travis.yml new file mode 100644 index 000000000..87228c6c2 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.travis.yml @@ -0,0 +1,23 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- go get -u github.com/axw/gocov/gocov +- go get -u gopkg.in/matm/v1/gocov-html +- go get -u github.com/cee-dub/go-junit-report +- go get -u github.com/stretchr/testify/assert +- go get -u gopkg.in/yaml.v2 +- go get -u github.com/go-openapi/analysis +- go get -u github.com/go-openapi/errors +- go get -u github.com/go-openapi/loads +- go get -u github.com/go-openapi/strfmt +- go get -u github.com/go-openapi/validate +- go get -u github.com/docker/go-units +language: go +notifications: + slack: + secure: EmObnQuM9Mw8J9vpFaKKHqSMN4Wsr/A9+v7ewAD5cEhA0T1P4m7MbJMiJOhxUhj/X+BFh2DamW+P2lT8mybj5wg8wnkQ2BteKA8Tawi6f9PRw2NRheO8tAi8o/npLnlmet0kc93mn+oLuqHw36w4+j5mkOl2FghkfGiUVhwrhkCP7KXQN+3TU87e+/HzQumlJ3nsE+6terVxkH3PmaUTsS5ONaODZfuxFpfb7RsoEl3skHf6d+tr+1nViLxxly7558Nc33C+W1mr0qiEvMLZ+kJ/CpGWBJ6CUJM3jm6hNe2eMuIPwEK2hxZob8c7n22VPap4K6a0bBRoydoDXaba+2sD7Ym6ivDO/DVyL44VeBBLyIiIBylDGQdZH+6SoWm90Qe/i7tnY/T5Ao5igT8f3cfQY1c3EsTfqmlDfrhmACBmwSlgkdVBLTprHL63JMY24LWmh4jhxsmMRZhCL4dze8su1w6pLN/pD1pGHtKYCEVbdTmaM3PblNRFf12XB7qosmQsgUndH4Vq3bTbU0s1pKjeDhRyLvFzvR0TBbo0pDLEoF1A/i5GVFWa7yLZNUDudQERRh7qv/xBl2excIaQ1sV4DSVm7bAE9l6Kp+yeHQJW2uN6Y3X8wu9gB9nv9l5HBze7wh8KE6PyWAOLYYqZg9/sAtsv/2GcQqXcKFF1zcA= +script: +- ./hack/coverage diff --git a/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/flynn/go-shlex/COPYING b/vendor/github.com/go-openapi/runtime/LICENSE similarity index 100% rename from vendor/github.com/flynn/go-shlex/COPYING rename to vendor/github.com/go-openapi/runtime/LICENSE diff --git a/vendor/github.com/go-openapi/runtime/README.md b/vendor/github.com/go-openapi/runtime/README.md new file mode 100644 index 000000000..5b1ec6494 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/README.md @@ -0,0 +1,7 @@ +# runtime [![Build Status](https://travis-ci.org/go-openapi/runtime.svg?branch=client-context)](https://travis-ci.org/go-openapi/runtime) [![codecov](https://codecov.io/gh/go-openapi/runtime/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/runtime) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/runtime/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/runtime?status.svg)](http://godoc.org/github.com/go-openapi/runtime) + +# golang Open-API toolkit - runtime + +The runtime component for use in codegeneration or as untyped usage. diff --git a/vendor/github.com/go-openapi/runtime/bytestream.go b/vendor/github.com/go-openapi/runtime/bytestream.go new file mode 100644 index 000000000..9845e10cf --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/bytestream.go @@ -0,0 +1,151 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "bytes" + "encoding" + "errors" + "fmt" + "io" + "reflect" + + "github.com/go-openapi/swag" +) + +func defaultCloser() error { return nil } + +type byteStreamOpt func(opts *byteStreamOpts) + +// ClosesStream when the bytestream consumer or producer is finished +func ClosesStream(opts *byteStreamOpts) { + opts.Close = true +} + +type byteStreamOpts struct { + Close bool +} + +// ByteStreamConsumer creates a consmer for byte streams, +// takes a Writer/BinaryUnmarshaler interface or binary slice by reference, +// and reads from the provided reader +func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { + var vals byteStreamOpts + for _, opt := range opts { + opt(&vals) + } + + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("ByteStreamConsumer requires a reader") // early exit + } + + close := defaultCloser + if vals.Close { + if cl, ok := reader.(io.Closer); ok { + close = cl.Close + } + } + defer close() + + if wrtr, ok := data.(io.Writer); ok { + _, err := io.Copy(wrtr, reader) + return err + } + + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(reader) + if err != nil { + return err + } + b := buf.Bytes() + + if bu, ok := data.(encoding.BinaryUnmarshaler); ok { + return bu.UnmarshalBinary(b) + } + + if t := reflect.TypeOf(data); data != nil && t.Kind() == reflect.Ptr { + v := reflect.Indirect(reflect.ValueOf(data)) + if t = v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + v.SetBytes(b) + return nil + } + } + + return fmt.Errorf("%v (%T) is not supported by the ByteStreamConsumer, %s", + data, data, "can be resolved by supporting Writer/BinaryUnmarshaler interface") + }) +} + +// ByteStreamProducer creates a producer for byte streams, +// takes a Reader/BinaryMarshaler interface or binary slice, +// and writes to a writer (essentially a pipe) +func ByteStreamProducer(opts ...byteStreamOpt) Producer { + var vals byteStreamOpts + for _, opt := range opts { + opt(&vals) + } + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("ByteStreamProducer requires a writer") // early exit + } + close := defaultCloser + if vals.Close { + if cl, ok := writer.(io.Closer); ok { + close = cl.Close + } + } + defer close() + + if rdr, ok := data.(io.Reader); ok { + _, err := io.Copy(writer, rdr) + return err + } + + if bm, ok := data.(encoding.BinaryMarshaler); ok { + bytes, err := bm.MarshalBinary() + if err != nil { + return err + } + + _, err = writer.Write(bytes) + return err + } + + if data != nil { + if e, ok := data.(error); ok { + _, err := writer.Write([]byte(e.Error())) + return err + } + + v := reflect.Indirect(reflect.ValueOf(data)) + if t := v.Type(); t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { + _, err := writer.Write(v.Bytes()) + return err + } + if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { + b, err := swag.WriteJSON(data) + if err != nil { + return err + } + _, err = writer.Write(b) + return err + } + } + + return fmt.Errorf("%v (%T) is not supported by the ByteStreamProducer, %s", + data, data, "can be resolved by supporting Reader/BinaryMarshaler interface") + }) +} diff --git a/vendor/github.com/go-openapi/runtime/client_auth_info.go b/vendor/github.com/go-openapi/runtime/client_auth_info.go new file mode 100644 index 000000000..c6c97d9a7 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_auth_info.go @@ -0,0 +1,30 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import "github.com/go-openapi/strfmt" + +// A ClientAuthInfoWriterFunc converts a function to a request writer interface +type ClientAuthInfoWriterFunc func(ClientRequest, strfmt.Registry) error + +// AuthenticateRequest adds authentication data to the request +func (fn ClientAuthInfoWriterFunc) AuthenticateRequest(req ClientRequest, reg strfmt.Registry) error { + return fn(req, reg) +} + +// A ClientAuthInfoWriter implementor knows how to write authentication info to a request +type ClientAuthInfoWriter interface { + AuthenticateRequest(ClientRequest, strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/client_operation.go b/vendor/github.com/go-openapi/runtime/client_operation.go new file mode 100644 index 000000000..fa21eacf3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_operation.go @@ -0,0 +1,41 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "context" + "net/http" +) + +// ClientOperation represents the context for a swagger operation to be submitted to the transport +type ClientOperation struct { + ID string + Method string + PathPattern string + ProducesMediaTypes []string + ConsumesMediaTypes []string + Schemes []string + AuthInfo ClientAuthInfoWriter + Params ClientRequestWriter + Reader ClientResponseReader + Context context.Context + Client *http.Client +} + +// A ClientTransport implementor knows how to submit Request objects to some destination +type ClientTransport interface { + //Submit(string, RequestWriter, ResponseReader, AuthInfoWriter) (interface{}, error) + Submit(*ClientOperation) (interface{}, error) +} diff --git a/vendor/github.com/go-openapi/runtime/client_request.go b/vendor/github.com/go-openapi/runtime/client_request.go new file mode 100644 index 000000000..904196ae3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_request.go @@ -0,0 +1,103 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "io" + "io/ioutil" + "net/http" + "net/url" + "time" + + "github.com/go-openapi/strfmt" +) + +// ClientRequestWriterFunc converts a function to a request writer interface +type ClientRequestWriterFunc func(ClientRequest, strfmt.Registry) error + +// WriteToRequest adds data to the request +func (fn ClientRequestWriterFunc) WriteToRequest(req ClientRequest, reg strfmt.Registry) error { + return fn(req, reg) +} + +// ClientRequestWriter is an interface for things that know how to write to a request +type ClientRequestWriter interface { + WriteToRequest(ClientRequest, strfmt.Registry) error +} + +// ClientRequest is an interface for things that know how to +// add information to a swagger client request +type ClientRequest interface { + SetHeaderParam(string, ...string) error + + GetHeaderParams() http.Header + + SetQueryParam(string, ...string) error + + SetFormParam(string, ...string) error + + SetPathParam(string, string) error + + GetQueryParams() url.Values + + SetFileParam(string, ...NamedReadCloser) error + + SetBodyParam(interface{}) error + + SetTimeout(time.Duration) error + + GetMethod() string + + GetPath() string + + GetBody() []byte + + GetBodyParam() interface{} + + GetFileParam() map[string][]NamedReadCloser +} + +// NamedReadCloser represents a named ReadCloser interface +type NamedReadCloser interface { + io.ReadCloser + Name() string +} + +// NamedReader creates a NamedReadCloser for use as file upload +func NamedReader(name string, rdr io.Reader) NamedReadCloser { + rc, ok := rdr.(io.ReadCloser) + if !ok { + rc = ioutil.NopCloser(rdr) + } + return &namedReadCloser{ + name: name, + cr: rc, + } +} + +type namedReadCloser struct { + name string + cr io.ReadCloser +} + +func (n *namedReadCloser) Close() error { + return n.cr.Close() +} +func (n *namedReadCloser) Read(p []byte) (int, error) { + return n.cr.Read(p) +} +func (n *namedReadCloser) Name() string { + return n.name +} diff --git a/vendor/github.com/go-openapi/runtime/client_response.go b/vendor/github.com/go-openapi/runtime/client_response.go new file mode 100644 index 000000000..729e18b22 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client_response.go @@ -0,0 +1,63 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "fmt" + "io" +) + +// A ClientResponse represents a client response +// This bridges between responses obtained from different transports +type ClientResponse interface { + Code() int + Message() string + GetHeader(string) string + Body() io.ReadCloser +} + +// A ClientResponseReaderFunc turns a function into a ClientResponseReader interface implementation +type ClientResponseReaderFunc func(ClientResponse, Consumer) (interface{}, error) + +// ReadResponse reads the response +func (read ClientResponseReaderFunc) ReadResponse(resp ClientResponse, consumer Consumer) (interface{}, error) { + return read(resp, consumer) +} + +// A ClientResponseReader is an interface for things want to read a response. +// An application of this is to create structs from response values +type ClientResponseReader interface { + ReadResponse(ClientResponse, Consumer) (interface{}, error) +} + +// NewAPIError creates a new API error +func NewAPIError(opName string, payload interface{}, code int) *APIError { + return &APIError{ + OperationName: opName, + Response: payload, + Code: code, + } +} + +// APIError wraps an error model and captures the status code +type APIError struct { + OperationName string + Response interface{} + Code int +} + +func (a *APIError) Error() string { + return fmt.Sprintf("%s (status %d): %+v ", a.OperationName, a.Code, a.Response) +} diff --git a/vendor/github.com/go-openapi/runtime/constants.go b/vendor/github.com/go-openapi/runtime/constants.go new file mode 100644 index 000000000..a4de897ad --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/constants.go @@ -0,0 +1,47 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +const ( + // HeaderContentType represents a http content-type header, it's value is supposed to be a mime type + HeaderContentType = "Content-Type" + + // HeaderTransferEncoding represents a http transfer-encoding header. + HeaderTransferEncoding = "Transfer-Encoding" + + // HeaderAccept the Accept header + HeaderAccept = "Accept" + + charsetKey = "charset" + + // DefaultMime the default fallback mime type + DefaultMime = "application/octet-stream" + // JSONMime the json mime type + JSONMime = "application/json" + // YAMLMime the yaml mime type + YAMLMime = "application/x-yaml" + // XMLMime the xml mime type + XMLMime = "application/xml" + // TextMime the text mime type + TextMime = "text/plain" + // HTMLMime the html mime type + HTMLMime = "text/html" + // CSVMime the csv mime type + CSVMime = "text/csv" + // MultipartFormMime the multipart form mime type + MultipartFormMime = "multipart/form-data" + // URLencodedFormMime the url encoded form mime type + URLencodedFormMime = "application/x-www-form-urlencoded" +) diff --git a/vendor/github.com/go-openapi/runtime/csv.go b/vendor/github.com/go-openapi/runtime/csv.go new file mode 100644 index 000000000..d807bd915 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/csv.go @@ -0,0 +1,77 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "bytes" + "encoding/csv" + "errors" + "io" +) + +// CSVConsumer creates a new CSV consumer +func CSVConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("CSVConsumer requires a reader") + } + + csvReader := csv.NewReader(reader) + writer, ok := data.(io.Writer) + if !ok { + return errors.New("data type must be io.Writer") + } + csvWriter := csv.NewWriter(writer) + records, err := csvReader.ReadAll() + if err != nil { + return err + } + for _, r := range records { + if err := csvWriter.Write(r); err != nil { + return err + } + } + csvWriter.Flush() + return nil + }) +} + +// CSVProducer creates a new CSV producer +func CSVProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("CSVProducer requires a writer") + } + + dataBytes, ok := data.([]byte) + if !ok { + return errors.New("data type must be byte array") + } + + csvReader := csv.NewReader(bytes.NewBuffer(dataBytes)) + records, err := csvReader.ReadAll() + if err != nil { + return err + } + csvWriter := csv.NewWriter(writer) + for _, r := range records { + if err := csvWriter.Write(r); err != nil { + return err + } + } + csvWriter.Flush() + return nil + }) +} diff --git a/vendor/github.com/go-openapi/runtime/discard.go b/vendor/github.com/go-openapi/runtime/discard.go new file mode 100644 index 000000000..0d390cfd6 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/discard.go @@ -0,0 +1,9 @@ +package runtime + +import "io" + +// DiscardConsumer does absolutely nothing, it's a black hole. +var DiscardConsumer = ConsumerFunc(func(_ io.Reader, _ interface{}) error { return nil }) + +// DiscardProducer does absolutely nothing, it's a black hole. +var DiscardProducer = ProducerFunc(func(_ io.Writer, _ interface{}) error { return nil }) diff --git a/vendor/github.com/go-openapi/runtime/file.go b/vendor/github.com/go-openapi/runtime/file.go new file mode 100644 index 000000000..85971c18c --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/file.go @@ -0,0 +1,33 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import "mime/multipart" + +// File represents an uploaded file. +type File struct { + Data multipart.File + Header *multipart.FileHeader +} + +// Read bytes from the file +func (f *File) Read(p []byte) (n int, err error) { + return f.Data.Read(p) +} + +// Close the file +func (f *File) Close() error { + return f.Data.Close() +} diff --git a/vendor/github.com/go-openapi/runtime/go.mod b/vendor/github.com/go-openapi/runtime/go.mod new file mode 100644 index 000000000..51e4d721e --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/go.mod @@ -0,0 +1,25 @@ +module github.com/go-openapi/runtime + +require ( + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/docker/go-units v0.3.3 + github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 // indirect + github.com/go-openapi/analysis v0.18.0 + github.com/go-openapi/errors v0.18.0 + github.com/go-openapi/jsonpointer v0.18.0 // indirect + github.com/go-openapi/jsonreference v0.18.0 // indirect + github.com/go-openapi/loads v0.18.0 + github.com/go-openapi/spec v0.18.0 + github.com/go-openapi/strfmt v0.18.0 + github.com/go-openapi/swag v0.18.0 + github.com/go-openapi/validate v0.18.0 + github.com/google/uuid v1.1.1 // indirect + github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe // indirect + github.com/stretchr/objx v0.1.1 // indirect + github.com/stretchr/testify v1.3.0 + golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 // indirect + golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 // indirect + golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/github.com/go-openapi/runtime/go.sum b/vendor/github.com/go-openapi/runtime/go.sum new file mode 100644 index 000000000..5ac448f0a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/go.sum @@ -0,0 +1,87 @@ +github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.17.2 h1:eYp14J1o8TTSCzndHBtsNuckikV1PfZOSnx4BcBeu0c= +github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.17.2 h1:azEQ8Fnx0jmtFF2fxsnmd6I0x6rsweUF63qqSO1NmKk= +github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.17.2 h1:3ekBy41gar/iJi2KSh/au/PrC2vpLr85upF/UZmm3W0= +github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.17.2 h1:lF3z7AH8dd0IKXc1zEBi1dj0B4XgVb5cVjn39dCK3Ls= +github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.17.2 h1:tEXYu6Xc0pevpzzQx5ghrMN9F7IVpN/+u4iD3rkYE5o= +github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.17.2 h1:eb2NbuCnoe8cWAxhtK6CfMWUYmiFEZJ9Hx3Z2WRwJ5M= +github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.17.2 h1:2KDns36DMHXG9/iYkOjiX+/8fKK9GCU5ELZ+J6qcRVA= +github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.17.2 h1:K/ycE/XTUDFltNHSO32cGRUhrVGJD64o8WgAIZNyc3k= +github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.17.2 h1:lwFfiS4sv5DvOrsYDsYq4N7UU8ghXiYtPJ+VcQnC3Xg= +github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/runtime/headers.go b/vendor/github.com/go-openapi/runtime/headers.go new file mode 100644 index 000000000..4d111db4f --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/headers.go @@ -0,0 +1,45 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "mime" + "net/http" + + "github.com/go-openapi/errors" +) + +// ContentType parses a content type header +func ContentType(headers http.Header) (string, string, error) { + ct := headers.Get(HeaderContentType) + orig := ct + if ct == "" { + ct = DefaultMime + } + if ct == "" { + return "", "", nil + } + + mt, opts, err := mime.ParseMediaType(ct) + if err != nil { + return "", "", errors.NewParseError(HeaderContentType, "header", orig, err) + } + + if cs, ok := opts[charsetKey]; ok { + return mt, cs, nil + } + + return mt, "", nil +} diff --git a/vendor/github.com/go-openapi/runtime/interfaces.go b/vendor/github.com/go-openapi/runtime/interfaces.go new file mode 100644 index 000000000..65de0aa44 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/interfaces.go @@ -0,0 +1,103 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "io" + "net/http" + + "github.com/go-openapi/strfmt" +) + +// OperationHandlerFunc an adapter for a function to the OperationHandler interface +type OperationHandlerFunc func(interface{}) (interface{}, error) + +// Handle implements the operation handler interface +func (s OperationHandlerFunc) Handle(data interface{}) (interface{}, error) { + return s(data) +} + +// OperationHandler a handler for a swagger operation +type OperationHandler interface { + Handle(interface{}) (interface{}, error) +} + +// ConsumerFunc represents a function that can be used as a consumer +type ConsumerFunc func(io.Reader, interface{}) error + +// Consume consumes the reader into the data parameter +func (fn ConsumerFunc) Consume(reader io.Reader, data interface{}) error { + return fn(reader, data) +} + +// Consumer implementations know how to bind the values on the provided interface to +// data provided by the request body +type Consumer interface { + // Consume performs the binding of request values + Consume(io.Reader, interface{}) error +} + +// ProducerFunc represents a function that can be used as a producer +type ProducerFunc func(io.Writer, interface{}) error + +// Produce produces the response for the provided data +func (f ProducerFunc) Produce(writer io.Writer, data interface{}) error { + return f(writer, data) +} + +// Producer implementations know how to turn the provided interface into a valid +// HTTP response +type Producer interface { + // Produce writes to the http response + Produce(io.Writer, interface{}) error +} + +// AuthenticatorFunc turns a function into an authenticator +type AuthenticatorFunc func(interface{}) (bool, interface{}, error) + +// Authenticate authenticates the request with the provided data +func (f AuthenticatorFunc) Authenticate(params interface{}) (bool, interface{}, error) { + return f(params) +} + +// Authenticator represents an authentication strategy +// implementations of Authenticator know how to authenticate the +// request data and translate that into a valid principal object or an error +type Authenticator interface { + Authenticate(interface{}) (bool, interface{}, error) +} + +// AuthorizerFunc turns a function into an authorizer +type AuthorizerFunc func(*http.Request, interface{}) error + +// Authorize authorizes the processing of the request for the principal +func (f AuthorizerFunc) Authorize(r *http.Request, principal interface{}) error { + return f(r, principal) +} + +// Authorizer represents an authorization strategy +// implementations of Authorizer know how to authorize the principal object +// using the request data and returns error if unauthorized +type Authorizer interface { + Authorize(*http.Request, interface{}) error +} + +// Validatable types implementing this interface allow customizing their validation +// this will be used instead of the reflective validation based on the spec document. +// the implementations are assumed to have been generated by the swagger tool so they should +// contain all the validations obtained from the spec +type Validatable interface { + Validate(strfmt.Registry) error +} diff --git a/vendor/github.com/go-openapi/runtime/json.go b/vendor/github.com/go-openapi/runtime/json.go new file mode 100644 index 000000000..5a690559c --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/json.go @@ -0,0 +1,38 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "encoding/json" + "io" +) + +// JSONConsumer creates a new JSON consumer +func JSONConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + dec := json.NewDecoder(reader) + dec.UseNumber() // preserve number formats + return dec.Decode(data) + }) +} + +// JSONProducer creates a new JSON producer +func JSONProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + enc := json.NewEncoder(writer) + enc.SetEscapeHTML(false) + return enc.Encode(data) + }) +} diff --git a/vendor/github.com/go-openapi/runtime/request.go b/vendor/github.com/go-openapi/runtime/request.go new file mode 100644 index 000000000..87b73da45 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/request.go @@ -0,0 +1,77 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "io" + "net/http" + "strings" + + "github.com/go-openapi/swag" +) + +// CanHaveBody returns true if this method can have a body +func CanHaveBody(method string) bool { + mn := strings.ToUpper(method) + return mn == "POST" || mn == "PUT" || mn == "PATCH" || mn == "DELETE" +} + +// IsSafe returns true if this is a request with a safe method +func IsSafe(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn == "GET" || mn == "HEAD" +} + +// AllowsBody returns true if the request allows for a body +func AllowsBody(r *http.Request) bool { + mn := strings.ToUpper(r.Method) + return mn != "HEAD" +} + +// HasBody returns true if this method needs a content-type +func HasBody(r *http.Request) bool { + return len(r.TransferEncoding) > 0 || r.ContentLength > 0 +} + +// JSONRequest creates a new http request with json headers set +func JSONRequest(method, urlStr string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest(method, urlStr, body) + if err != nil { + return nil, err + } + req.Header.Add(HeaderContentType, JSONMime) + req.Header.Add(HeaderAccept, JSONMime) + return req, nil +} + +// Gettable for things with a method GetOK(string) (data string, hasKey bool, hasValue bool) +type Gettable interface { + GetOK(string) ([]string, bool, bool) +} + +// ReadSingleValue reads a single value from the source +func ReadSingleValue(values Gettable, name string) string { + vv, _, hv := values.GetOK(name) + if hv { + return vv[len(vv)-1] + } + return "" +} + +// ReadCollectionValue reads a collection value from a string data source +func ReadCollectionValue(values Gettable, name, collectionFormat string) []string { + v := ReadSingleValue(values, name) + return swag.SplitByFormat(v, collectionFormat) +} diff --git a/vendor/github.com/go-openapi/runtime/statuses.go b/vendor/github.com/go-openapi/runtime/statuses.go new file mode 100644 index 000000000..3b011a0bf --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/statuses.go @@ -0,0 +1,90 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +// Statuses lists the most common HTTP status codes to default message +// taken from https://httpstatuses.com/ +var Statuses = map[int]string{ + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Checkpoint", + 122: "URI too long", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Request Processed", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Request Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 420: "Enhance Your Calm", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 444: "No Response", + 449: "Retry With", + 450: "Blocked by Windows Parental Controls", + 451: "Wrong Exchange Server", + 499: "Client Closed Request", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 509: "Bandwidth Limit Exceeded", + 510: "Not Extended", + 511: "Network Authentication Required", + 598: "Network read timeout error", + 599: "Network connect timeout error", +} diff --git a/vendor/github.com/go-openapi/runtime/text.go b/vendor/github.com/go-openapi/runtime/text.go new file mode 100644 index 000000000..77099feda --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/text.go @@ -0,0 +1,111 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "bytes" + "encoding" + "errors" + "fmt" + "io" + "reflect" + + "github.com/go-openapi/swag" +) + +// TextConsumer creates a new text consumer +func TextConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + if reader == nil { + return errors.New("TextConsumer requires a reader") // early exit + } + + buf := new(bytes.Buffer) + _, err := buf.ReadFrom(reader) + if err != nil { + return err + } + b := buf.Bytes() + + if tu, ok := data.(encoding.TextUnmarshaler); ok { + err := tu.UnmarshalText(b) + if err != nil { + return fmt.Errorf("text consumer: %v", err) + } + + return nil + } + + t := reflect.TypeOf(data) + if data != nil && t.Kind() == reflect.Ptr { + v := reflect.Indirect(reflect.ValueOf(data)) + if t.Elem().Kind() == reflect.String { + v.SetString(string(b)) + return nil + } + } + + return fmt.Errorf("%v (%T) is not supported by the TextConsumer, %s", + data, data, "can be resolved by supporting TextUnmarshaler interface") + }) +} + +// TextProducer creates a new text producer +func TextProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + if writer == nil { + return errors.New("TextProducer requires a writer") // early exit + } + + if data == nil { + return errors.New("no data given to produce text from") + } + + if tm, ok := data.(encoding.TextMarshaler); ok { + txt, err := tm.MarshalText() + if err != nil { + return fmt.Errorf("text producer: %v", err) + } + _, err = writer.Write(txt) + return err + } + + if str, ok := data.(error); ok { + _, err := writer.Write([]byte(str.Error())) + return err + } + + if str, ok := data.(fmt.Stringer); ok { + _, err := writer.Write([]byte(str.String())) + return err + } + + v := reflect.Indirect(reflect.ValueOf(data)) + if t := v.Type(); t.Kind() == reflect.Struct || t.Kind() == reflect.Slice { + b, err := swag.WriteJSON(data) + if err != nil { + return err + } + _, err = writer.Write(b) + return err + } + if v.Kind() != reflect.String { + return fmt.Errorf("%T is not a supported type by the TextProducer", data) + } + + _, err := writer.Write([]byte(v.String())) + return err + }) +} diff --git a/vendor/github.com/go-openapi/runtime/values.go b/vendor/github.com/go-openapi/runtime/values.go new file mode 100644 index 000000000..11f5732af --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/values.go @@ -0,0 +1,19 @@ +package runtime + +// Values typically represent parameters on a http request. +type Values map[string][]string + +// GetOK returns the values collection for the given key. +// When the key is present in the map it will return true for hasKey. +// When the value is not empty it will return true for hasValue. +func (v Values) GetOK(key string) (value []string, hasKey bool, hasValue bool) { + value, hasKey = v[key] + if !hasKey { + return + } + if len(value) == 0 { + return + } + hasValue = true + return +} diff --git a/vendor/github.com/go-openapi/runtime/xml.go b/vendor/github.com/go-openapi/runtime/xml.go new file mode 100644 index 000000000..821c7393d --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/xml.go @@ -0,0 +1,36 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 runtime + +import ( + "encoding/xml" + "io" +) + +// XMLConsumer creates a new XML consumer +func XMLConsumer() Consumer { + return ConsumerFunc(func(reader io.Reader, data interface{}) error { + dec := xml.NewDecoder(reader) + return dec.Decode(data) + }) +} + +// XMLProducer creates a new XML producer +func XMLProducer() Producer { + return ProducerFunc(func(writer io.Writer, data interface{}) error { + enc := xml.NewEncoder(writer) + return enc.Encode(data) + }) +} diff --git a/vendor/github.com/go-openapi/validate/.editorconfig b/vendor/github.com/go-openapi/validate/.editorconfig new file mode 100644 index 000000000..3152da69a --- /dev/null +++ b/vendor/github.com/go-openapi/validate/.editorconfig @@ -0,0 +1,26 @@ +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +# Set default charset +[*.{js,py,go,scala,rb,java,html,css,less,sass,md}] +charset = utf-8 + +# Tab indentation (no size specified) +[*.go] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/vendor/github.com/go-openapi/validate/.gitignore b/vendor/github.com/go-openapi/validate/.gitignore new file mode 100644 index 000000000..fea8b84ec --- /dev/null +++ b/vendor/github.com/go-openapi/validate/.gitignore @@ -0,0 +1,5 @@ +secrets.yml +coverage.out +*.cov +*.out +playground diff --git a/vendor/github.com/go-openapi/validate/.golangci.yml b/vendor/github.com/go-openapi/validate/.golangci.yml new file mode 100644 index 000000000..c2c507132 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/.golangci.yml @@ -0,0 +1,20 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 25 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + +linters: + enable-all: true + disable: + - maligned + - lll diff --git a/vendor/github.com/go-openapi/validate/.travis.yml b/vendor/github.com/go-openapi/validate/.travis.yml new file mode 100644 index 000000000..dd3a4b29f --- /dev/null +++ b/vendor/github.com/go-openapi/validate/.travis.yml @@ -0,0 +1,19 @@ +after_success: +- bash <(curl -s https://codecov.io/bash) +go: +- 1.11.x +- 1.12.x +install: +- GO111MODULE=off go get -u gotest.tools/gotestsum +env: +- GO111MODULE=on +language: go +notifications: + slack: + secure: EmObnQuM9Mw8J9vpFaKKHqSMN4Wsr/A9+v7ewAD5cEhA0T1P4m7MbJMiJOhxUhj/X+BFh2DamW+P2lT8mybj5wg8wnkQ2BteKA8Tawi6f9PRw2NRheO8tAi8o/npLnlmet0kc93mn+oLuqHw36w4+j5mkOl2FghkfGiUVhwrhkCP7KXQN+3TU87e+/HzQumlJ3nsE+6terVxkH3PmaUTsS5ONaODZfuxFpfb7RsoEl3skHf6d+tr+1nViLxxly7558Nc33C+W1mr0qiEvMLZ+kJ/CpGWBJ6CUJM3jm6hNe2eMuIPwEK2hxZob8c7n22VPap4K6a0bBRoydoDXaba+2sD7Ym6ivDO/DVyL44VeBBLyIiIBylDGQdZH+6SoWm90Qe/i7tnY/T5Ao5igT8f3cfQY1c3EsTfqmlDfrhmACBmwSlgkdVBLTprHL63JMY24LWmh4jhxsmMRZhCL4dze8su1w6pLN/pD1pGHtKYCEVbdTmaM3PblNRFf12XB7qosmQsgUndH4Vq3bTbU0s1pKjeDhRyLvFzvR0TBbo0pDLEoF1A/i5GVFWa7yLZNUDudQERRh7qv/xBl2excIaQ1sV4DSVm7bAE9l6Kp+yeHQJW2uN6Y3X8wu9gB9nv9l5HBze7wh8KE6PyWAOLYYqZg9/sAtsv/2GcQqXcKFF1zcA= +script: +- gotestsum -f short-verbose -- -race ./... +- gotestsum -f short-verbose -- -timeout=20m -coverprofile=coverage.txt -covermode=atomic -args -enable-long ./... +- gotestsum -f short-verbose -- -timeout=30m -args -enable-go-swagger ./... +- go get -u github.com/go-openapi/runtime@master +- gotestsum -f short-verbose -- -timeout=30m github.com/go-openapi/runtime/... diff --git a/vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md b/vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..9322b065e --- /dev/null +++ b/vendor/github.com/go-openapi/validate/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ivan+abuse@flanders.co.nz. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/vendor/github.com/go-openapi/validate/LICENSE b/vendor/github.com/go-openapi/validate/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/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/github.com/go-openapi/validate/README.md b/vendor/github.com/go-openapi/validate/README.md new file mode 100644 index 000000000..08fb352bc --- /dev/null +++ b/vendor/github.com/go-openapi/validate/README.md @@ -0,0 +1,6 @@ +# Validation helpers [![Build Status](https://travis-ci.org/go-openapi/validate.svg?branch=master)](https://travis-ci.org/go-openapi/validate) [![codecov](https://codecov.io/gh/go-openapi/validate/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/validate) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) + +[![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/validate/master/LICENSE) +[![GoDoc](https://godoc.org/github.com/go-openapi/validate?status.svg)](http://godoc.org/github.com/go-openapi/validate) +[![GolangCI](https://golangci.com/badges/github.com/go-openapi/validate.svg)](https://golangci.com) +[![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/validate)](https://goreportcard.com/report/github.com/go-openapi/validate) diff --git a/vendor/github.com/go-openapi/validate/debug.go b/vendor/github.com/go-openapi/validate/debug.go new file mode 100644 index 000000000..8815fd935 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/debug.go @@ -0,0 +1,47 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" +) + +var ( + // Debug is true when the SWAGGER_DEBUG env var is not empty. + // It enables a more verbose logging of validators. + Debug = os.Getenv("SWAGGER_DEBUG") != "" + // validateLogger is a debug logger for this package + validateLogger *log.Logger +) + +func init() { + debugOptions() +} + +func debugOptions() { + validateLogger = log.New(os.Stdout, "validate:", log.LstdFlags) +} + +func debugLog(msg string, args ...interface{}) { + // A private, trivial trace logger, based on go-openapi/spec/expander.go:debugLog() + if Debug { + _, file1, pos1, _ := runtime.Caller(1) + validateLogger.Printf("%s:%d: %s", filepath.Base(file1), pos1, fmt.Sprintf(msg, args...)) + } +} diff --git a/vendor/github.com/go-openapi/validate/default_validator.go b/vendor/github.com/go-openapi/validate/default_validator.go new file mode 100644 index 000000000..35c631be6 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/default_validator.go @@ -0,0 +1,278 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" +) + +// defaultValidator validates default values in a spec. +// According to Swagger spec, default values MUST validate their schema. +type defaultValidator struct { + SpecValidator *SpecValidator + visitedSchemas map[string]bool +} + +// resetVisited resets the internal state of visited schemas +func (d *defaultValidator) resetVisited() { + d.visitedSchemas = map[string]bool{} +} + +// beingVisited asserts a schema is being visited +func (d *defaultValidator) beingVisited(path string) { + d.visitedSchemas[path] = true +} + +// isVisited tells if a path has already been visited +func (d *defaultValidator) isVisited(path string) bool { + found := d.visitedSchemas[path] + if !found { + // search for overlapping paths + frags := strings.Split(path, ".") + if len(frags) < 2 { + // shortcut exit on smaller paths + return found + } + last := len(frags) - 1 + var currentFragStr, parent string + for i := range frags { + if i == 0 { + currentFragStr = frags[last] + } else { + currentFragStr = strings.Join([]string{frags[last-i], currentFragStr}, ".") + } + if i < last { + parent = strings.Join(frags[0:last-i], ".") + } else { + parent = "" + } + if strings.HasSuffix(parent, currentFragStr) { + found = true + break + } + } + } + return found +} + +// Validate validates the default values declared in the swagger spec +func (d *defaultValidator) Validate() (errs *Result) { + errs = new(Result) + if d == nil || d.SpecValidator == nil { + return errs + } + d.resetVisited() + errs.Merge(d.validateDefaultValueValidAgainstSchema()) // error - + return errs +} + +func (d *defaultValidator) validateDefaultValueValidAgainstSchema() *Result { + // every default value that is specified must validate against the schema for that property + // headers, items, parameters, schema + + res := new(Result) + s := d.SpecValidator + + for method, pathItem := range s.analyzer.Operations() { + if pathItem != nil { // Safeguard + for path, op := range pathItem { + // parameters + for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { + if param.Default != nil && param.Required { + res.AddWarnings(requiredHasDefaultMsg(param.Name, param.In)) + } + + // reset explored schemas to get depth-first recursive-proof exploration + d.resetVisited() + + // Check simple parameters first + // default values provided must validate against their inline definition (no explicit schema) + if param.Default != nil && param.Schema == nil { + // check param default value is valid + red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Default) + if red.HasErrorsOrWarnings() { + res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In)) + res.Merge(red) + } + } + + // Recursively follows Items and Schemas + if param.Items != nil { + red := d.validateDefaultValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items) + if red.HasErrorsOrWarnings() { + res.AddErrors(defaultValueItemsDoesNotValidateMsg(param.Name, param.In)) + res.Merge(red) + } + } + + if param.Schema != nil { + // Validate default value against schema + red := d.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema) + if red.HasErrorsOrWarnings() { + res.AddErrors(defaultValueDoesNotValidateMsg(param.Name, param.In)) + res.Merge(red) + } + } + } + + if op.Responses != nil { + if op.Responses.Default != nil { + // Same constraint on default Response + res.Merge(d.validateDefaultInResponse(op.Responses.Default, "default", path, 0, op.ID)) + } + // Same constraint on regular Responses + if op.Responses.StatusCodeResponses != nil { // Safeguard + for code, r := range op.Responses.StatusCodeResponses { + res.Merge(d.validateDefaultInResponse(&r, "response", path, code, op.ID)) + } + } + } else { + // Empty op.ID means there is no meaningful operation: no need to report a specific message + if op.ID != "" { + res.AddErrors(noValidResponseMsg(op.ID)) + } + } + } + } + } + if s.spec.Spec().Definitions != nil { // Safeguard + // reset explored schemas to get depth-first recursive-proof exploration + d.resetVisited() + for nm, sch := range s.spec.Spec().Definitions { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) + } + } + return res +} + +func (d *defaultValidator) validateDefaultInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result { + s := d.SpecValidator + + response, res := responseHelp.expandResponseRef(resp, path, s) + if !res.IsValid() { + return res + } + + responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode) + + if response.Headers != nil { // Safeguard + for nm, h := range response.Headers { + // reset explored schemas to get depth-first recursive-proof exploration + d.resetVisited() + + if h.Default != nil { + red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default) + if red.HasErrorsOrWarnings() { + res.AddErrors(defaultValueHeaderDoesNotValidateMsg(operationID, nm, responseName)) + res.Merge(red) + } + } + + // Headers have inline definition, like params + if h.Items != nil { + red := d.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items) + if red.HasErrorsOrWarnings() { + res.AddErrors(defaultValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName)) + res.Merge(red) + } + } + + if _, err := compileRegexp(h.Pattern); err != nil { + res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err)) + } + + // Headers don't have schema + } + } + if response.Schema != nil { + // reset explored schemas to get depth-first recursive-proof exploration + d.resetVisited() + + red := d.validateDefaultValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema) + if red.HasErrorsOrWarnings() { + // Additional message to make sure the context of the error is not lost + res.AddErrors(defaultValueInDoesNotValidateMsg(operationID, responseName)) + res.Merge(red) + } + } + return res +} + +func (d *defaultValidator) validateDefaultValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result { + if schema == nil || d.isVisited(path) { + // Avoids recursing if we are already done with that check + return nil + } + d.beingVisited(path) + res := new(Result) + s := d.SpecValidator + + if schema.Default != nil { + res.Merge(NewSchemaValidator(schema, s.spec.Spec(), path+".default", s.KnownFormats).Validate(schema.Default)) + } + if schema.Items != nil { + if schema.Items.Schema != nil { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+".items.default", in, schema.Items.Schema)) + } + // Multiple schemas in items + if schema.Items.Schemas != nil { // Safeguard + for i, sch := range schema.Items.Schemas { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].default", path, i), in, &sch)) + } + } + } + if _, err := compileRegexp(schema.Pattern); err != nil { + res.AddErrors(invalidPatternInMsg(path, in, schema.Pattern)) + } + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + // NOTE: we keep validating values, even though additionalItems is not supported by Swagger 2.0 (and 3.0 as well) + res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema)) + } + for propName, prop := range schema.Properties { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) + } + for propName, prop := range schema.PatternProperties { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(path+"."+propName, in, &prop)) + } + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema)) + } + if schema.AllOf != nil { + for i, aoSch := range schema.AllOf { + res.Merge(d.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch)) + } + } + return res +} + +func (d *defaultValidator) validateDefaultValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result { + res := new(Result) + s := d.SpecValidator + if items != nil { + if items.Default != nil { + res.Merge(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Default)) + } + if items.Items != nil { + res.Merge(d.validateDefaultValueItemsAgainstSchema(path+"[0].default", in, root, items.Items)) + } + if _, err := compileRegexp(items.Pattern); err != nil { + res.AddErrors(invalidPatternInMsg(path, in, items.Pattern)) + } + } + return res +} diff --git a/vendor/github.com/go-openapi/validate/doc.go b/vendor/github.com/go-openapi/validate/doc.go new file mode 100644 index 000000000..f5ca9a5d5 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/doc.go @@ -0,0 +1,85 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate provides methods to validate a swagger specification, +as well as tools to validate data against their schema. + +This package follows Swagger 2.0. specification (aka OpenAPI 2.0). Reference +can be found here: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md. + +Validating a specification + +Validates a spec document (from JSON or YAML) against the JSON schema for swagger, +then checks a number of extra rules that can't be expressed in JSON schema. + +Entry points: + - Spec() + - NewSpecValidator() + - SpecValidator.Validate() + +Reported as errors: + [x] definition can't declare a property that's already defined by one of its ancestors + [x] definition's ancestor can't be a descendant of the same model + [x] path uniqueness: each api path should be non-verbatim (account for path param names) unique per method + [x] each security reference should contain only unique scopes + [x] each security scope in a security definition should be unique + [x] parameters in path must be unique + [x] each path parameter must correspond to a parameter placeholder and vice versa + [x] each referenceable definition must have references + [x] each definition property listed in the required array must be defined in the properties of the model + [x] each parameter should have a unique `name` and `type` combination + [x] each operation should have only 1 parameter of type body + [x] each reference must point to a valid object + [x] every default value that is specified must validate against the schema for that property + [x] items property is required for all schemas/definitions of type `array` + [x] path parameters must be declared a required + [x] headers must not contain $ref + [x] schema and property examples provided must validate against their respective object's schema + [x] examples provided must validate their schema + +Reported as warnings: + [x] path parameters should not contain any of [{,},\w] + [x] empty path + [x] unused definitions + [x] unsupported validation of examples on non-JSON media types + [x] examples in response without schema + [x] readOnly properties should not be required + +Validating a schema + +The schema validation toolkit validates data against JSON-schema-draft 04 schema. + +It is tested against the full json-schema-testing-suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +except for the optional part (bignum, ECMA regexp, ...). + +It supports the complete JSON-schema vocabulary, including keywords not supported by Swagger (e.g. additionalItems, ...) + +Entry points: + - AgainstSchema() + - ... + +Known limitations + +With the current version of this package, the following aspects of swagger are not yet supported: + [ ] errors and warnings are not reported with key/line number in spec + [ ] default values and examples on responses only support application/json producer type + [ ] invalid numeric constraints (such as Minimum, etc..) are not checked except for default and example values + [ ] rules for collectionFormat are not implemented + [ ] no validation rule for polymorphism support (discriminator) [not done here] + [ ] valid js ECMA regexp not supported by Go regexp engine are considered invalid + [ ] arbitrary large numbers are not supported: max is math.MaxFloat64 + +*/ +package validate diff --git a/vendor/github.com/go-openapi/validate/example_validator.go b/vendor/github.com/go-openapi/validate/example_validator.go new file mode 100644 index 000000000..b2acf1055 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/example_validator.go @@ -0,0 +1,299 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "strings" + + "github.com/go-openapi/spec" +) + +// ExampleValidator validates example values defined in a spec +type exampleValidator struct { + SpecValidator *SpecValidator + visitedSchemas map[string]bool +} + +// resetVisited resets the internal state of visited schemas +func (ex *exampleValidator) resetVisited() { + ex.visitedSchemas = map[string]bool{} +} + +// beingVisited asserts a schema is being visited +func (ex *exampleValidator) beingVisited(path string) { + ex.visitedSchemas[path] = true +} + +// isVisited tells if a path has already been visited +func (ex *exampleValidator) isVisited(path string) bool { + found := ex.visitedSchemas[path] + if !found { + // search for overlapping paths + frags := strings.Split(path, ".") + if len(frags) < 2 { + // shortcut exit on smaller paths + return found + } + last := len(frags) - 1 + var currentFragStr, parent string + for i := range frags { + if i == 0 { + currentFragStr = frags[last] + } else { + currentFragStr = strings.Join([]string{frags[last-i], currentFragStr}, ".") + } + if i < last { + parent = strings.Join(frags[0:last-i], ".") + } else { + parent = "" + } + if strings.HasSuffix(parent, currentFragStr) { + found = true + break + } + } + } + return found +} + +// Validate validates the example values declared in the swagger spec +// Example values MUST conform to their schema. +// +// With Swagger 2.0, examples are supported in: +// - schemas +// - individual property +// - responses +// +func (ex *exampleValidator) Validate() (errs *Result) { + errs = new(Result) + if ex == nil || ex.SpecValidator == nil { + return errs + } + ex.resetVisited() + errs.Merge(ex.validateExampleValueValidAgainstSchema()) // error - + + return errs +} + +func (ex *exampleValidator) validateExampleValueValidAgainstSchema() *Result { + // every example value that is specified must validate against the schema for that property + // in: schemas, properties, object, items + // not in: headers, parameters without schema + + res := new(Result) + s := ex.SpecValidator + + for method, pathItem := range s.analyzer.Operations() { + if pathItem != nil { // Safeguard + for path, op := range pathItem { + // parameters + for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { + + // As of swagger 2.0, Examples are not supported in simple parameters + // However, it looks like it is supported by go-openapi + + // reset explored schemas to get depth-first recursive-proof exploration + ex.resetVisited() + + // Check simple parameters first + // default values provided must validate against their inline definition (no explicit schema) + if param.Example != nil && param.Schema == nil { + // check param default value is valid + red := NewParamValidator(¶m, s.KnownFormats).Validate(param.Example) + if red.HasErrorsOrWarnings() { + res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In)) + res.MergeAsWarnings(red) + } + } + + // Recursively follows Items and Schemas + if param.Items != nil { + red := ex.validateExampleValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items) + if red.HasErrorsOrWarnings() { + res.AddWarnings(exampleValueItemsDoesNotValidateMsg(param.Name, param.In)) + res.Merge(red) + } + } + + if param.Schema != nil { + // Validate example value against schema + red := ex.validateExampleValueSchemaAgainstSchema(param.Name, param.In, param.Schema) + if red.HasErrorsOrWarnings() { + res.AddWarnings(exampleValueDoesNotValidateMsg(param.Name, param.In)) + res.Merge(red) + } + } + } + + if op.Responses != nil { + if op.Responses.Default != nil { + // Same constraint on default Response + res.Merge(ex.validateExampleInResponse(op.Responses.Default, "default", path, 0, op.ID)) + } + // Same constraint on regular Responses + if op.Responses.StatusCodeResponses != nil { // Safeguard + for code, r := range op.Responses.StatusCodeResponses { + res.Merge(ex.validateExampleInResponse(&r, "response", path, code, op.ID)) + } + } + } else { + // Empty op.ID means there is no meaningful operation: no need to report a specific message + if op.ID != "" { + res.AddErrors(noValidResponseMsg(op.ID)) + } + } + } + } + } + if s.spec.Spec().Definitions != nil { // Safeguard + // reset explored schemas to get depth-first recursive-proof exploration + ex.resetVisited() + for nm, sch := range s.spec.Spec().Definitions { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) + } + } + return res +} + +func (ex *exampleValidator) validateExampleInResponse(resp *spec.Response, responseType, path string, responseCode int, operationID string) *Result { + s := ex.SpecValidator + + response, res := responseHelp.expandResponseRef(resp, path, s) + if !res.IsValid() { // Safeguard + return res + } + + responseName, responseCodeAsStr := responseHelp.responseMsgVariants(responseType, responseCode) + + if response.Headers != nil { // Safeguard + for nm, h := range response.Headers { + // reset explored schemas to get depth-first recursive-proof exploration + ex.resetVisited() + + if h.Example != nil { + red := NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Example) + if red.HasErrorsOrWarnings() { + res.AddWarnings(exampleValueHeaderDoesNotValidateMsg(operationID, nm, responseName)) + res.MergeAsWarnings(red) + } + } + + // Headers have inline definition, like params + if h.Items != nil { + red := ex.validateExampleValueItemsAgainstSchema(nm, "header", &h, h.Items) + if red.HasErrorsOrWarnings() { + res.AddWarnings(exampleValueHeaderItemsDoesNotValidateMsg(operationID, nm, responseName)) + res.MergeAsWarnings(red) + } + } + + if _, err := compileRegexp(h.Pattern); err != nil { + res.AddErrors(invalidPatternInHeaderMsg(operationID, nm, responseName, h.Pattern, err)) + } + + // Headers don't have schema + } + } + if response.Schema != nil { + // reset explored schemas to get depth-first recursive-proof exploration + ex.resetVisited() + + red := ex.validateExampleValueSchemaAgainstSchema(responseCodeAsStr, "response", response.Schema) + if red.HasErrorsOrWarnings() { + // Additional message to make sure the context of the error is not lost + res.AddWarnings(exampleValueInDoesNotValidateMsg(operationID, responseName)) + res.Merge(red) + } + } + + if response.Examples != nil { + if response.Schema != nil { + if example, ok := response.Examples["application/json"]; ok { + res.MergeAsWarnings(NewSchemaValidator(response.Schema, s.spec.Spec(), path, s.KnownFormats).Validate(example)) + } else { + // TODO: validate other media types too + res.AddWarnings(examplesMimeNotSupportedMsg(operationID, responseName)) + } + } else { + res.AddWarnings(examplesWithoutSchemaMsg(operationID, responseName)) + } + } + return res +} + +func (ex *exampleValidator) validateExampleValueSchemaAgainstSchema(path, in string, schema *spec.Schema) *Result { + if schema == nil || ex.isVisited(path) { + // Avoids recursing if we are already done with that check + return nil + } + ex.beingVisited(path) + s := ex.SpecValidator + res := new(Result) + + if schema.Example != nil { + res.MergeAsWarnings(NewSchemaValidator(schema, s.spec.Spec(), path+".example", s.KnownFormats).Validate(schema.Example)) + } + if schema.Items != nil { + if schema.Items.Schema != nil { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+".items.example", in, schema.Items.Schema)) + } + // Multiple schemas in items + if schema.Items.Schemas != nil { // Safeguard + for i, sch := range schema.Items.Schemas { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.items[%d].example", path, i), in, &sch)) + } + } + } + if _, err := compileRegexp(schema.Pattern); err != nil { + res.AddErrors(invalidPatternInMsg(path, in, schema.Pattern)) + } + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + // NOTE: we keep validating values, even though additionalItems is unsupported in Swagger 2.0 (and 3.0 as well) + res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalItems", path), in, schema.AdditionalItems.Schema)) + } + for propName, prop := range schema.Properties { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) + } + for propName, prop := range schema.PatternProperties { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(path+"."+propName, in, &prop)) + } + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.additionalProperties", path), in, schema.AdditionalProperties.Schema)) + } + if schema.AllOf != nil { + for i, aoSch := range schema.AllOf { + res.Merge(ex.validateExampleValueSchemaAgainstSchema(fmt.Sprintf("%s.allOf[%d]", path, i), in, &aoSch)) + } + } + return res +} + +func (ex *exampleValidator) validateExampleValueItemsAgainstSchema(path, in string, root interface{}, items *spec.Items) *Result { + res := new(Result) + s := ex.SpecValidator + if items != nil { + if items.Example != nil { + res.MergeAsWarnings(newItemsValidator(path, in, items, root, s.KnownFormats).Validate(0, items.Example)) + } + if items.Items != nil { + res.Merge(ex.validateExampleValueItemsAgainstSchema(path+"[0].example", in, root, items.Items)) + } + if _, err := compileRegexp(items.Pattern); err != nil { + res.AddErrors(invalidPatternInMsg(path, in, items.Pattern)) + } + } + return res +} diff --git a/vendor/github.com/go-openapi/validate/formats.go b/vendor/github.com/go-openapi/validate/formats.go new file mode 100644 index 000000000..b7afe981b --- /dev/null +++ b/vendor/github.com/go-openapi/validate/formats.go @@ -0,0 +1,73 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "reflect" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +type formatValidator struct { + Format string + Path string + In string + KnownFormats strfmt.Registry +} + +func (f *formatValidator) SetPath(path string) { + f.Path = path +} + +func (f *formatValidator) Applies(source interface{}, kind reflect.Kind) bool { + doit := func() bool { + if source == nil { + return false + } + switch source.(type) { + case *spec.Items: + it := source.(*spec.Items) + return kind == reflect.String && f.KnownFormats.ContainsName(it.Format) + case *spec.Parameter: + par := source.(*spec.Parameter) + return kind == reflect.String && f.KnownFormats.ContainsName(par.Format) + case *spec.Schema: + sch := source.(*spec.Schema) + return kind == reflect.String && f.KnownFormats.ContainsName(sch.Format) + case *spec.Header: + hdr := source.(*spec.Header) + return kind == reflect.String && f.KnownFormats.ContainsName(hdr.Format) + } + return false + } + r := doit() + debugLog("format validator for %q applies %t for %T (kind: %v)\n", f.Path, r, source, kind) + return r +} + +func (f *formatValidator) Validate(val interface{}) *Result { + result := new(Result) + debugLog("validating \"%v\" against format: %s", val, f.Format) + + if err := FormatOf(f.Path, f.In, f.Format, val.(string), f.KnownFormats); err != nil { + result.AddErrors(err) + } + + if result.HasErrors() { + return result + } + return nil +} diff --git a/vendor/github.com/go-openapi/validate/go.mod b/vendor/github.com/go-openapi/validate/go.mod new file mode 100644 index 000000000..1197fcd64 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/go.mod @@ -0,0 +1,14 @@ +module github.com/go-openapi/validate + +require ( + github.com/go-openapi/analysis v0.19.2 + github.com/go-openapi/errors v0.19.2 + github.com/go-openapi/jsonpointer v0.19.2 + github.com/go-openapi/loads v0.19.2 + github.com/go-openapi/runtime v0.19.0 + github.com/go-openapi/spec v0.19.2 + github.com/go-openapi/strfmt v0.19.0 + github.com/go-openapi/swag v0.19.2 + github.com/stretchr/testify v1.3.0 + gopkg.in/yaml.v2 v2.2.2 +) diff --git a/vendor/github.com/go-openapi/validate/go.sum b/vendor/github.com/go-openapi/validate/go.sum new file mode 100644 index 000000000..7b65517fd --- /dev/null +++ b/vendor/github.com/go-openapi/validate/go.sum @@ -0,0 +1,103 @@ +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0 h1:wCOBNscACI8L93tt5tvB2zOMkJ098XCw3fP0BY2ybDA= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63 h1:nTT4s92Dgz2HlrB2NaMgvlfqHH39OgMhA7z3PK7PGD4= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/go-openapi/validate/helpers.go b/vendor/github.com/go-openapi/validate/helpers.go new file mode 100644 index 000000000..7ac877109 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/helpers.go @@ -0,0 +1,265 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +// TODO: define this as package validate/internal +// This must be done while keeping CI intact with all tests and test coverage + +import ( + "reflect" + "strconv" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/spec" +) + +const swaggerBody = "body" +const objectType = "object" + +// Helpers available at the package level +var ( + pathHelp *pathHelper + valueHelp *valueHelper + errorHelp *errorHelper + paramHelp *paramHelper + responseHelp *responseHelper +) + +type errorHelper struct { + // A collection of unexported helpers for error construction +} + +func (h *errorHelper) sErr(err errors.Error) *Result { + // Builds a Result from standard errors.Error + return &Result{Errors: []error{err}} +} + +func (h *errorHelper) addPointerError(res *Result, err error, ref string, fromPath string) *Result { + // Provides more context on error messages + // reported by the jsoinpointer package by altering the passed Result + if err != nil { + res.AddErrors(cannotResolveRefMsg(fromPath, ref, err)) + } + return res +} + +type pathHelper struct { + // A collection of unexported helpers for path validation +} + +func (h *pathHelper) stripParametersInPath(path string) string { + // Returns a path stripped from all path parameters, with multiple or trailing slashes removed. + // + // Stripping is performed on a slash-separated basis, e.g '/a{/b}' remains a{/b} and not /a. + // - Trailing "/" make a difference, e.g. /a/ !~ /a (ex: canary/bitbucket.org/swagger.json) + // - presence or absence of a parameter makes a difference, e.g. /a/{log} !~ /a/ (ex: canary/kubernetes/swagger.json) + + // Regexp to extract parameters from path, with surrounding {}. + // NOTE: important non-greedy modifier + rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`) + strippedSegments := []string{} + + for _, segment := range strings.Split(path, "/") { + strippedSegments = append(strippedSegments, rexParsePathParam.ReplaceAllString(segment, "X")) + } + return strings.Join(strippedSegments, "/") +} + +func (h *pathHelper) extractPathParams(path string) (params []string) { + // Extracts all params from a path, with surrounding "{}" + rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`) + + for _, segment := range strings.Split(path, "/") { + for _, v := range rexParsePathParam.FindAllStringSubmatch(segment, -1) { + params = append(params, v...) + } + } + return +} + +type valueHelper struct { + // A collection of unexported helpers for value validation +} + +func (h *valueHelper) asInt64(val interface{}) int64 { + // Number conversion function for int64, without error checking + // (implements an implicit type upgrade). + v := reflect.ValueOf(val) + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return int64(v.Uint()) + case reflect.Float32, reflect.Float64: + return int64(v.Float()) + default: + //panic("Non numeric value in asInt64()") + return 0 + } +} + +func (h *valueHelper) asUint64(val interface{}) uint64 { + // Number conversion function for uint64, without error checking + // (implements an implicit type upgrade). + v := reflect.ValueOf(val) + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return uint64(v.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return v.Uint() + case reflect.Float32, reflect.Float64: + return uint64(v.Float()) + default: + //panic("Non numeric value in asUint64()") + return 0 + } +} + +// Same for unsigned floats +func (h *valueHelper) asFloat64(val interface{}) float64 { + // Number conversion function for float64, without error checking + // (implements an implicit type upgrade). + v := reflect.ValueOf(val) + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return float64(v.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return float64(v.Uint()) + case reflect.Float32, reflect.Float64: + return v.Float() + default: + //panic("Non numeric value in asFloat64()") + return 0 + } +} + +type paramHelper struct { + // A collection of unexported helpers for parameters resolution +} + +func (h *paramHelper) safeExpandedParamsFor(path, method, operationID string, res *Result, s *SpecValidator) (params []spec.Parameter) { + operation, ok := s.analyzer.OperationFor(method, path) + if ok { + // expand parameters first if necessary + resolvedParams := []spec.Parameter{} + for _, ppr := range operation.Parameters { + resolvedParam, red := h.resolveParam(path, method, operationID, &ppr, s) + res.Merge(red) + if resolvedParam != nil { + resolvedParams = append(resolvedParams, *resolvedParam) + } + } + // remove params with invalid expansion from Slice + operation.Parameters = resolvedParams + + for _, ppr := range s.analyzer.SafeParamsFor(method, path, + func(p spec.Parameter, err error) bool { + // since params have already been expanded, there are few causes for error + res.AddErrors(someParametersBrokenMsg(path, method, operationID)) + // original error from analyzer + res.AddErrors(err) + return true + }) { + params = append(params, ppr) + } + } + return +} + +func (h *paramHelper) resolveParam(path, method, operationID string, param *spec.Parameter, s *SpecValidator) (*spec.Parameter, *Result) { + // Ensure parameter is expanded + var err error + res := new(Result) + isRef := param.Ref.String() != "" + if s.spec.SpecFilePath() == "" { + err = spec.ExpandParameterWithRoot(param, s.spec.Spec(), nil) + } else { + err = spec.ExpandParameter(param, s.spec.SpecFilePath()) + + } + if err != nil { // Safeguard + // NOTE: we may enter enter here when the whole parameter is an unresolved $ref + refPath := strings.Join([]string{"\"" + path + "\"", method}, ".") + errorHelp.addPointerError(res, err, param.Ref.String(), refPath) + return nil, res + } + res.Merge(h.checkExpandedParam(param, param.Name, param.In, operationID, isRef)) + return param, res +} + +func (h *paramHelper) checkExpandedParam(pr *spec.Parameter, path, in, operation string, isRef bool) *Result { + // Secure parameter structure after $ref resolution + res := new(Result) + simpleZero := spec.SimpleSchema{} + // Try to explain why... best guess + if pr.In == swaggerBody && (pr.SimpleSchema != simpleZero && pr.SimpleSchema.Type != objectType) { + if isRef { + // Most likely, a $ref with a sibling is an unwanted situation: in itself this is a warning... + // but we detect it because of the following error: + // schema took over Parameter for an unexplained reason + res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation)) + } + res.AddErrors(invalidParameterDefinitionMsg(path, in, operation)) + } else if pr.In != swaggerBody && pr.Schema != nil { + if isRef { + res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation)) + } + res.AddErrors(invalidParameterDefinitionAsSchemaMsg(path, in, operation)) + } else if (pr.In == swaggerBody && pr.Schema == nil) || + (pr.In != swaggerBody && pr.SimpleSchema == simpleZero) { // Safeguard + // Other unexpected mishaps + res.AddErrors(invalidParameterDefinitionMsg(path, in, operation)) + } + return res +} + +type responseHelper struct { + // A collection of unexported helpers for response resolution +} + +func (r *responseHelper) expandResponseRef( + response *spec.Response, + path string, s *SpecValidator) (*spec.Response, *Result) { + // Ensure response is expanded + var err error + res := new(Result) + if s.spec.SpecFilePath() == "" { + // there is no physical document to resolve $ref in response + err = spec.ExpandResponseWithRoot(response, s.spec.Spec(), nil) + } else { + err = spec.ExpandResponse(response, s.spec.SpecFilePath()) + } + if err != nil { // Safeguard + // NOTE: we may enter here when the whole response is an unresolved $ref. + errorHelp.addPointerError(res, err, response.Ref.String(), path) + return nil, res + } + return response, res +} + +func (r *responseHelper) responseMsgVariants( + responseType string, + responseCode int) (responseName, responseCodeAsStr string) { + // Path variants for messages + if responseType == "default" { + responseCodeAsStr = "default" + responseName = "default response" + } else { + responseCodeAsStr = strconv.Itoa(responseCode) + responseName = "response " + responseCodeAsStr + } + return +} diff --git a/vendor/github.com/go-openapi/validate/object_validator.go b/vendor/github.com/go-openapi/validate/object_validator.go new file mode 100644 index 000000000..df0c5c14e --- /dev/null +++ b/vendor/github.com/go-openapi/validate/object_validator.go @@ -0,0 +1,268 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "reflect" + "regexp" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +type objectValidator struct { + Path string + In string + MaxProperties *int64 + MinProperties *int64 + Required []string + Properties map[string]spec.Schema + AdditionalProperties *spec.SchemaOrBool + PatternProperties map[string]spec.Schema + Root interface{} + KnownFormats strfmt.Registry + Options SchemaValidatorOptions +} + +func (o *objectValidator) SetPath(path string) { + o.Path = path +} + +func (o *objectValidator) Applies(source interface{}, kind reflect.Kind) bool { + // TODO: this should also work for structs + // there is a problem in the type validator where it will be unhappy about null values + // so that requires more testing + r := reflect.TypeOf(source) == specSchemaType && (kind == reflect.Map || kind == reflect.Struct) + debugLog("object validator for %q applies %t for %T (kind: %v)\n", o.Path, r, source, kind) + return r +} + +func (o *objectValidator) isPropertyName() bool { + p := strings.Split(o.Path, ".") + return p[len(p)-1] == "properties" && p[len(p)-2] != "properties" +} + +func (o *objectValidator) checkArrayMustHaveItems(res *Result, val map[string]interface{}) { + if t, typeFound := val["type"]; typeFound { + if tpe, ok := t.(string); ok && tpe == "array" { + if _, itemsKeyFound := val["items"]; !itemsKeyFound { + res.AddErrors(errors.Required("items", o.Path)) + } + } + } +} + +func (o *objectValidator) checkItemsMustBeTypeArray(res *Result, val map[string]interface{}) { + if !o.isPropertyName() { + if _, itemsKeyFound := val["items"]; itemsKeyFound { + t, typeFound := val["type"] + if typeFound { + if tpe, ok := t.(string); !ok || tpe != "array" { + res.AddErrors(errors.InvalidType(o.Path, o.In, "array", nil)) + } + } else { + // there is no type + res.AddErrors(errors.Required("type", o.Path)) + } + } + } +} + +func (o *objectValidator) precheck(res *Result, val map[string]interface{}) { + o.checkArrayMustHaveItems(res, val) + if !o.Options.DisableObjectArrayTypeCheck { + o.checkItemsMustBeTypeArray(res, val) + } +} + +func (o *objectValidator) Validate(data interface{}) *Result { + val := data.(map[string]interface{}) + // TODO: guard against nil data + numKeys := int64(len(val)) + + if o.MinProperties != nil && numKeys < *o.MinProperties { + return errorHelp.sErr(errors.TooFewProperties(o.Path, o.In, *o.MinProperties)) + } + if o.MaxProperties != nil && numKeys > *o.MaxProperties { + return errorHelp.sErr(errors.TooManyProperties(o.Path, o.In, *o.MaxProperties)) + } + + res := new(Result) + + o.precheck(res, val) + + // check validity of field names + if o.AdditionalProperties != nil && !o.AdditionalProperties.Allows { + // Case: additionalProperties: false + for k := range val { + _, regularProperty := o.Properties[k] + matched := false + + for pk := range o.PatternProperties { + if matches, _ := regexp.MatchString(pk, k); matches { + matched = true + break + } + } + + if !regularProperty && k != "$schema" && k != "id" && !matched { + // Special properties "$schema" and "id" are ignored + res.AddErrors(errors.PropertyNotAllowed(o.Path, o.In, k)) + + // BUG(fredbi): This section should move to a part dedicated to spec validation as + // it will conflict with regular schemas where a property "headers" is defined. + + // + // Croaks a more explicit message on top of the standard one + // on some recognized cases. + // + // NOTE: edge cases with invalid type assertion are simply ignored here. + // NOTE: prefix your messages here by "IMPORTANT!" so there are not filtered + // by higher level callers (the IMPORTANT! tag will be eventually + // removed). + switch k { + // $ref is forbidden in header + case "headers": + if val[k] != nil { + if headers, mapOk := val[k].(map[string]interface{}); mapOk { + for headerKey, headerBody := range headers { + if headerBody != nil { + if headerSchema, mapOfMapOk := headerBody.(map[string]interface{}); mapOfMapOk { + if _, found := headerSchema["$ref"]; found { + var msg string + if refString, stringOk := headerSchema["$ref"].(string); stringOk { + msg = strings.Join([]string{", one may not use $ref=\":", refString, "\""}, "") + } + res.AddErrors(refNotAllowedInHeaderMsg(o.Path, headerKey, msg)) + } + } + } + } + } + } + /* + case "$ref": + if val[k] != nil { + // TODO: check context of that ref: warn about siblings, check against invalid context + } + */ + } + } + } + } else { + // Cases: no additionalProperties (implying: true), or additionalProperties: true, or additionalProperties: { <> } + for key, value := range val { + _, regularProperty := o.Properties[key] + + // Validates property against "patternProperties" if applicable + // BUG(fredbi): succeededOnce is always false + + // NOTE: how about regular properties which do not match patternProperties? + matched, succeededOnce, _ := o.validatePatternProperty(key, value, res) + + if !(regularProperty || matched || succeededOnce) { + + // Cases: properties which are not regular properties and have not been matched by the PatternProperties validator + if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil { + // AdditionalProperties as Schema + r := NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value) + res.mergeForField(data.(map[string]interface{}), key, r) + } else if regularProperty && !(matched || succeededOnce) { + // TODO: this is dead code since regularProperty=false here + res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key)) + } + } + } + // Valid cases: additionalProperties: true or undefined + } + + createdFromDefaults := map[string]bool{} + + // Property types: + // - regular Property + for pName := range o.Properties { + pSchema := o.Properties[pName] // one instance per iteration + rName := pName + if o.Path != "" { + rName = o.Path + "." + pName + } + + // Recursively validates each property against its schema + if v, ok := val[pName]; ok { + r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v) + res.mergeForField(data.(map[string]interface{}), pName, r) + } else if pSchema.Default != nil { + // If a default value is defined, creates the property from defaults + // NOTE: JSON schema does not enforce default values to be valid against schema. Swagger does. + createdFromDefaults[pName] = true + res.addPropertySchemata(data.(map[string]interface{}), pName, &pSchema) + } + } + + // Check required properties + if len(o.Required) > 0 { + for _, k := range o.Required { + if _, ok := val[k]; !ok && !createdFromDefaults[k] { + res.AddErrors(errors.Required(o.Path+"."+k, o.In)) + continue + } + } + } + + // Check patternProperties + // TODO: it looks like we have done that twice in many cases + for key, value := range val { + _, regularProperty := o.Properties[key] + matched, _ /*succeededOnce*/, patterns := o.validatePatternProperty(key, value, res) + if !regularProperty && (matched /*|| succeededOnce*/) { + for _, pName := range patterns { + if v, ok := o.PatternProperties[pName]; ok { + r := NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value) + res.mergeForField(data.(map[string]interface{}), key, r) + } + } + } + } + return res +} + +// TODO: succeededOnce is not used anywhere +func (o *objectValidator) validatePatternProperty(key string, value interface{}, result *Result) (bool, bool, []string) { + matched := false + succeededOnce := false + var patterns []string + + for k, schema := range o.PatternProperties { + sch := schema + if match, _ := regexp.MatchString(k, key); match { + patterns = append(patterns, k) + matched = true + validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...) + + res := validator.Validate(value) + result.Merge(res) + } + } + + // BUG(fredbi): can't get to here. Should remove dead code (commented out). + + //if succeededOnce { + // result.Inc() + //} + + return matched, succeededOnce, patterns +} diff --git a/vendor/github.com/go-openapi/validate/options.go b/vendor/github.com/go-openapi/validate/options.go new file mode 100644 index 000000000..deeec2f2e --- /dev/null +++ b/vendor/github.com/go-openapi/validate/options.go @@ -0,0 +1,43 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import "sync" + +// Opts specifies validation options for a SpecValidator. +// +// NOTE: other options might be needed, for example a go-swagger specific mode. +type Opts struct { + ContinueOnErrors bool // true: continue reporting errors, even if spec is invalid +} + +var ( + defaultOpts = Opts{ContinueOnErrors: false} // default is to stop validation on errors + defaultOptsMutex = &sync.Mutex{} +) + +// SetContinueOnErrors sets global default behavior regarding spec validation errors reporting. +// +// For extended error reporting, you most likely want to set it to true. +// For faster validation, it's better to give up early when a spec is detected as invalid: set it to false (this is the default). +// +// Setting this mode does NOT affect the validation status. +// +// NOTE: this method affects global defaults. It is not suitable for a concurrent usage. +func SetContinueOnErrors(c bool) { + defer defaultOptsMutex.Unlock() + defaultOptsMutex.Lock() + defaultOpts.ContinueOnErrors = c +} diff --git a/vendor/github.com/go-openapi/validate/result.go b/vendor/github.com/go-openapi/validate/result.go new file mode 100644 index 000000000..ae9b8dbf2 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/result.go @@ -0,0 +1,484 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "reflect" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/spec" +) + +// Result represents a validation result set, composed of +// errors and warnings. +// +// It is used to keep track of all detected errors and warnings during +// the validation of a specification. +// +// Matchcount is used to determine +// which errors are relevant in the case of AnyOf, OneOf +// schema validation. Results from the validation branch +// with most matches get eventually selected. +// +// TODO: keep path of key originating the error +type Result struct { + Errors []error + Warnings []error + MatchCount int + + // the object data + data interface{} + + // Schemata for the root object + rootObjectSchemata schemata + // Schemata for object fields + fieldSchemata []fieldSchemata + // Schemata for slice items + itemSchemata []itemSchemata + + cachedFieldSchemta map[FieldKey][]*spec.Schema + cachedItemSchemata map[ItemKey][]*spec.Schema +} + +// FieldKey is a pair of an object and a field, usable as a key for a map. +type FieldKey struct { + object reflect.Value // actually a map[string]interface{}, but the latter cannot be a key + field string +} + +// ItemKey is a pair of a slice and an index, usable as a key for a map. +type ItemKey struct { + slice reflect.Value // actually a []interface{}, but the latter cannot be a key + index int +} + +// NewFieldKey returns a pair of an object and field usable as a key of a map. +func NewFieldKey(obj map[string]interface{}, field string) FieldKey { + return FieldKey{object: reflect.ValueOf(obj), field: field} +} + +// Object returns the underlying object of this key. +func (fk *FieldKey) Object() map[string]interface{} { + return fk.object.Interface().(map[string]interface{}) +} + +// Field returns the underlying field of this key. +func (fk *FieldKey) Field() string { + return fk.field +} + +// NewItemKey returns a pair of a slice and index usable as a key of a map. +func NewItemKey(slice interface{}, i int) ItemKey { + return ItemKey{slice: reflect.ValueOf(slice), index: i} +} + +// Slice returns the underlying slice of this key. +func (ik *ItemKey) Slice() []interface{} { + return ik.slice.Interface().([]interface{}) +} + +// Index returns the underlying index of this key. +func (ik *ItemKey) Index() int { + return ik.index +} + +type fieldSchemata struct { + obj map[string]interface{} + field string + schemata schemata +} + +type itemSchemata struct { + slice reflect.Value + index int + schemata schemata +} + +// Merge merges this result with the other one(s), preserving match counts etc. +func (r *Result) Merge(others ...*Result) *Result { + for _, other := range others { + if other == nil { + continue + } + r.mergeWithoutRootSchemata(other) + r.rootObjectSchemata.Append(other.rootObjectSchemata) + } + return r +} + +// Data returns the original data object used for validation. Mutating this renders +// the result invalid. +func (r *Result) Data() interface{} { + return r.data +} + +// RootObjectSchemata returns the schemata which apply to the root object. +func (r *Result) RootObjectSchemata() []*spec.Schema { + return r.rootObjectSchemata.Slice() +} + +// FieldSchemata returns the schemata which apply to fields in objects. +func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema { + if r.cachedFieldSchemta != nil { + return r.cachedFieldSchemta + } + + ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata)) + for _, fs := range r.fieldSchemata { + key := NewFieldKey(fs.obj, fs.field) + if fs.schemata.one != nil { + ret[key] = append(ret[key], fs.schemata.one) + } else if len(fs.schemata.multiple) > 0 { + ret[key] = append(ret[key], fs.schemata.multiple...) + } + } + r.cachedFieldSchemta = ret + return ret +} + +// ItemSchemata returns the schemata which apply to items in slices. +func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema { + if r.cachedItemSchemata != nil { + return r.cachedItemSchemata + } + + ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata)) + for _, ss := range r.itemSchemata { + key := NewItemKey(ss.slice, ss.index) + if ss.schemata.one != nil { + ret[key] = append(ret[key], ss.schemata.one) + } else if len(ss.schemata.multiple) > 0 { + ret[key] = append(ret[key], ss.schemata.multiple...) + } + } + r.cachedItemSchemata = ret + return ret +} + +func (r *Result) resetCaches() { + r.cachedFieldSchemta = nil + r.cachedItemSchemata = nil +} + +// mergeForField merges other into r, assigning other's root schemata to the given Object and field name. +func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result { + if other == nil { + return r + } + r.mergeWithoutRootSchemata(other) + + if other.rootObjectSchemata.Len() > 0 { + if r.fieldSchemata == nil { + r.fieldSchemata = make([]fieldSchemata, len(obj)) + } + r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{ + obj: obj, + field: field, + schemata: other.rootObjectSchemata, + }) + } + + return r +} + +// mergeForSlice merges other into r, assigning other's root schemata to the given slice and index. +func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result { + if other == nil { + return r + } + r.mergeWithoutRootSchemata(other) + + if other.rootObjectSchemata.Len() > 0 { + if r.itemSchemata == nil { + r.itemSchemata = make([]itemSchemata, slice.Len()) + } + r.itemSchemata = append(r.itemSchemata, itemSchemata{ + slice: slice, + index: i, + schemata: other.rootObjectSchemata, + }) + } + + return r +} + +// addRootObjectSchemata adds the given schemata for the root object of the result. +// The slice schemata might be reused. I.e. do not modify it after being added to a result. +func (r *Result) addRootObjectSchemata(s *spec.Schema) { + r.rootObjectSchemata.Append(schemata{one: s}) +} + +// addPropertySchemata adds the given schemata for the object and field. +// The slice schemata might be reused. I.e. do not modify it after being added to a result. +func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) { + if r.fieldSchemata == nil { + r.fieldSchemata = make([]fieldSchemata, 0, len(obj)) + } + r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}}) +} + +// addSliceSchemata adds the given schemata for the slice and index. +// The slice schemata might be reused. I.e. do not modify it after being added to a result. +func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) { + if r.itemSchemata == nil { + r.itemSchemata = make([]itemSchemata, 0, slice.Len()) + } + r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}}) +} + +// mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata. +func (r *Result) mergeWithoutRootSchemata(other *Result) { + r.resetCaches() + r.AddErrors(other.Errors...) + r.AddWarnings(other.Warnings...) + r.MatchCount += other.MatchCount + + if other.fieldSchemata != nil { + if r.fieldSchemata == nil { + r.fieldSchemata = other.fieldSchemata + } else { + for _, x := range other.fieldSchemata { + r.fieldSchemata = append(r.fieldSchemata, x) + } + } + } + + if other.itemSchemata != nil { + if r.itemSchemata == nil { + r.itemSchemata = other.itemSchemata + } else { + for _, x := range other.itemSchemata { + r.itemSchemata = append(r.itemSchemata, x) + } + } + } +} + +// MergeAsErrors merges this result with the other one(s), preserving match counts etc. +// +// Warnings from input are merged as Errors in the returned merged Result. +func (r *Result) MergeAsErrors(others ...*Result) *Result { + for _, other := range others { + if other != nil { + r.resetCaches() + r.AddErrors(other.Errors...) + r.AddErrors(other.Warnings...) + r.MatchCount += other.MatchCount + } + } + return r +} + +// MergeAsWarnings merges this result with the other one(s), preserving match counts etc. +// +// Errors from input are merged as Warnings in the returned merged Result. +func (r *Result) MergeAsWarnings(others ...*Result) *Result { + for _, other := range others { + if other != nil { + r.resetCaches() + r.AddWarnings(other.Errors...) + r.AddWarnings(other.Warnings...) + r.MatchCount += other.MatchCount + } + } + return r +} + +// AddErrors adds errors to this validation result (if not already reported). +// +// Since the same check may be passed several times while exploring the +// spec structure (via $ref, ...) reported messages are kept +// unique. +func (r *Result) AddErrors(errors ...error) { + for _, e := range errors { + found := false + if e != nil { + for _, isReported := range r.Errors { + if e.Error() == isReported.Error() { + found = true + break + } + } + if !found { + r.Errors = append(r.Errors, e) + } + } + } +} + +// AddWarnings adds warnings to this validation result (if not already reported). +func (r *Result) AddWarnings(warnings ...error) { + for _, e := range warnings { + found := false + if e != nil { + for _, isReported := range r.Warnings { + if e.Error() == isReported.Error() { + found = true + break + } + } + if !found { + r.Warnings = append(r.Warnings, e) + } + } + } +} + +func (r *Result) keepRelevantErrors() *Result { + // TODO: this one is going to disapear... + // keepRelevantErrors strips a result from standard errors and keeps + // the ones which are supposedly more accurate. + // + // The original result remains unaffected (creates a new instance of Result). + // This method is used to work around the "matchCount" filter which would otherwise + // strip our result from some accurate error reporting from lower level validators. + // + // NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor + // very efficient. On the other hand, relying on go-openapi/errors to manipulate + // codes would require to change a lot here. So, for the moment, let's go with + // placeholders. + strippedErrors := []error{} + for _, e := range r.Errors { + if strings.HasPrefix(e.Error(), "IMPORTANT!") { + strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!"))) + } + } + strippedWarnings := []error{} + for _, e := range r.Warnings { + if strings.HasPrefix(e.Error(), "IMPORTANT!") { + strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!"))) + } + } + strippedResult := new(Result) + strippedResult.Errors = strippedErrors + strippedResult.Warnings = strippedWarnings + return strippedResult +} + +// IsValid returns true when this result is valid. +// +// Returns true on a nil *Result. +func (r *Result) IsValid() bool { + if r == nil { + return true + } + return len(r.Errors) == 0 +} + +// HasErrors returns true when this result is invalid. +// +// Returns false on a nil *Result. +func (r *Result) HasErrors() bool { + if r == nil { + return false + } + return !r.IsValid() +} + +// HasWarnings returns true when this result contains warnings. +// +// Returns false on a nil *Result. +func (r *Result) HasWarnings() bool { + if r == nil { + return false + } + return len(r.Warnings) > 0 +} + +// HasErrorsOrWarnings returns true when this result contains +// either errors or warnings. +// +// Returns false on a nil *Result. +func (r *Result) HasErrorsOrWarnings() bool { + if r == nil { + return false + } + return len(r.Errors) > 0 || len(r.Warnings) > 0 +} + +// Inc increments the match count +func (r *Result) Inc() { + r.MatchCount++ +} + +// AsError renders this result as an error interface +// +// TODO: reporting / pretty print with path ordered and indented +func (r *Result) AsError() error { + if r.IsValid() { + return nil + } + return errors.CompositeValidationError(r.Errors...) +} + +// schemata is an arbitrary number of schemata. It does a distinction between zero, +// one and many schemata to avoid slice allocations. +type schemata struct { + // one is set if there is exactly one schema. In that case multiple must be nil. + one *spec.Schema + // multiple is an arbitrary number of schemas. If it is set, one must be nil. + multiple []*spec.Schema +} + +func (s *schemata) Len() int { + if s.one != nil { + return 1 + } + return len(s.multiple) +} + +func (s *schemata) Slice() []*spec.Schema { + if s == nil { + return nil + } + if s.one != nil { + return []*spec.Schema{s.one} + } + return s.multiple +} + +// appendSchemata appends the schemata in other to s. It mutated s in-place. +func (s *schemata) Append(other schemata) { + if other.one == nil && len(other.multiple) == 0 { + return + } + if s.one == nil && len(s.multiple) == 0 { + *s = other + return + } + + if s.one != nil { + if other.one != nil { + s.multiple = []*spec.Schema{s.one, other.one} + } else { + t := make([]*spec.Schema, 0, 1+len(other.multiple)) + s.multiple = append(append(t, s.one), other.multiple...) + } + s.one = nil + } else { + if other.one != nil { + s.multiple = append(s.multiple, other.one) + } else { + if cap(s.multiple) >= len(s.multiple)+len(other.multiple) { + s.multiple = append(s.multiple, other.multiple...) + } else { + t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple)) + s.multiple = append(append(t, s.multiple...), other.multiple...) + } + } + } +} diff --git a/vendor/github.com/go-openapi/validate/rexp.go b/vendor/github.com/go-openapi/validate/rexp.go new file mode 100644 index 000000000..5a0824395 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/rexp.go @@ -0,0 +1,71 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + re "regexp" + "sync" + "sync/atomic" +) + +// Cache for compiled regular expressions +var ( + cacheMutex = &sync.Mutex{} + reDict = atomic.Value{} //map[string]*re.Regexp +) + +func compileRegexp(pattern string) (*re.Regexp, error) { + if cache, ok := reDict.Load().(map[string]*re.Regexp); ok { + if r := cache[pattern]; r != nil { + return r, nil + } + } + + r, err := re.Compile(pattern) + if err != nil { + return nil, err + } + cacheRegexp(r) + return r, nil +} + +func mustCompileRegexp(pattern string) *re.Regexp { + if cache, ok := reDict.Load().(map[string]*re.Regexp); ok { + if r := cache[pattern]; r != nil { + return r + } + } + + r := re.MustCompile(pattern) + cacheRegexp(r) + return r +} + +func cacheRegexp(r *re.Regexp) { + cacheMutex.Lock() + defer cacheMutex.Unlock() + + if cache, ok := reDict.Load().(map[string]*re.Regexp); !ok || cache[r.String()] == nil { + newCache := map[string]*re.Regexp{ + r.String(): r, + } + + for k, v := range cache { + newCache[k] = v + } + + reDict.Store(newCache) + } +} diff --git a/vendor/github.com/go-openapi/validate/schema.go b/vendor/github.com/go-openapi/validate/schema.go new file mode 100644 index 000000000..9bf8f2eb7 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/schema.go @@ -0,0 +1,253 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "encoding/json" + "reflect" + + "github.com/go-openapi/errors" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +var ( + specSchemaType = reflect.TypeOf(&spec.Schema{}) + specParameterType = reflect.TypeOf(&spec.Parameter{}) + specItemsType = reflect.TypeOf(&spec.Items{}) + specHeaderType = reflect.TypeOf(&spec.Header{}) +) + +// SchemaValidator validates data against a JSON schema +type SchemaValidator struct { + Path string + in string + Schema *spec.Schema + validators []valueValidator + Root interface{} + KnownFormats strfmt.Registry + Options *SchemaValidatorOptions +} + +// AgainstSchema validates the specified data against the provided schema, using a registry of supported formats. +// +// When no pre-parsed *spec.Schema structure is provided, it uses a JSON schema as default. See example. +func AgainstSchema(schema *spec.Schema, data interface{}, formats strfmt.Registry) error { + res := NewSchemaValidator(schema, nil, "", formats).Validate(data) + if res.HasErrors() { + return errors.CompositeValidationError(res.Errors...) + } + return nil +} + +// NewSchemaValidator creates a new schema validator. +// +// Panics if the provided schema is invalid. +func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...Option) *SchemaValidator { + if schema == nil { + return nil + } + + if rootSchema == nil { + rootSchema = schema + } + + if schema.ID != "" || schema.Ref.String() != "" || schema.Ref.IsRoot() { + err := spec.ExpandSchema(schema, rootSchema, nil) + if err != nil { + msg := invalidSchemaProvidedMsg(err).Error() + panic(msg) + } + } + s := SchemaValidator{Path: root, in: "body", Schema: schema, Root: rootSchema, KnownFormats: formats, Options: &SchemaValidatorOptions{}} + for _, o := range options { + o(s.Options) + } + s.validators = []valueValidator{ + s.typeValidator(), + s.schemaPropsValidator(), + s.stringValidator(), + s.formatValidator(), + s.numberValidator(), + s.sliceValidator(), + s.commonValidator(), + s.objectValidator(), + } + return &s +} + +// SetPath sets the path for this schema valdiator +func (s *SchemaValidator) SetPath(path string) { + s.Path = path +} + +// Applies returns true when this schema validator applies +func (s *SchemaValidator) Applies(source interface{}, kind reflect.Kind) bool { + _, ok := source.(*spec.Schema) + return ok +} + +// Validate validates the data against the schema +func (s *SchemaValidator) Validate(data interface{}) *Result { + result := &Result{data: data} + if s == nil { + return result + } + if s.Schema != nil { + result.addRootObjectSchemata(s.Schema) + } + + if data == nil { + result.Merge(s.validators[0].Validate(data)) // type validator + result.Merge(s.validators[6].Validate(data)) // common validator + return result + } + + tpe := reflect.TypeOf(data) + kind := tpe.Kind() + for kind == reflect.Ptr { + tpe = tpe.Elem() + kind = tpe.Kind() + } + d := data + + if kind == reflect.Struct { + // NOTE: since reflect retrieves the true nature of types + // this means that all strfmt types passed here (e.g. strfmt.Datetime, etc..) + // are converted here to strings, and structs are systematically converted + // to map[string]interface{}. + d = swag.ToDynamicJSON(data) + } + + // TODO: this part should be handed over to type validator + // Handle special case of json.Number data (number marshalled as string) + isnumber := s.Schema.Type.Contains("number") || s.Schema.Type.Contains("integer") + if num, ok := data.(json.Number); ok && isnumber { + if s.Schema.Type.Contains("integer") { // avoid lossy conversion + in, erri := num.Int64() + if erri != nil { + result.AddErrors(invalidTypeConversionMsg(s.Path, erri)) + result.Inc() + return result + } + d = in + } else { + nf, errf := num.Float64() + if errf != nil { + result.AddErrors(invalidTypeConversionMsg(s.Path, errf)) + result.Inc() + return result + } + d = nf + } + + tpe = reflect.TypeOf(d) + kind = tpe.Kind() + } + + for _, v := range s.validators { + if !v.Applies(s.Schema, kind) { + debugLog("%T does not apply for %v", v, kind) + continue + } + + err := v.Validate(d) + result.Merge(err) + result.Inc() + } + result.Inc() + + return result +} + +func (s *SchemaValidator) typeValidator() valueValidator { + return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path} +} + +func (s *SchemaValidator) commonValidator() valueValidator { + return &basicCommonValidator{ + Path: s.Path, + In: s.in, + Enum: s.Schema.Enum, + } +} + +func (s *SchemaValidator) sliceValidator() valueValidator { + return &schemaSliceValidator{ + Path: s.Path, + In: s.in, + MaxItems: s.Schema.MaxItems, + MinItems: s.Schema.MinItems, + UniqueItems: s.Schema.UniqueItems, + AdditionalItems: s.Schema.AdditionalItems, + Items: s.Schema.Items, + Root: s.Root, + KnownFormats: s.KnownFormats, + } +} + +func (s *SchemaValidator) numberValidator() valueValidator { + return &numberValidator{ + Path: s.Path, + In: s.in, + Default: s.Schema.Default, + MultipleOf: s.Schema.MultipleOf, + Maximum: s.Schema.Maximum, + ExclusiveMaximum: s.Schema.ExclusiveMaximum, + Minimum: s.Schema.Minimum, + ExclusiveMinimum: s.Schema.ExclusiveMinimum, + } +} + +func (s *SchemaValidator) stringValidator() valueValidator { + return &stringValidator{ + Path: s.Path, + In: s.in, + MaxLength: s.Schema.MaxLength, + MinLength: s.Schema.MinLength, + Pattern: s.Schema.Pattern, + } +} + +func (s *SchemaValidator) formatValidator() valueValidator { + return &formatValidator{ + Path: s.Path, + In: s.in, + Format: s.Schema.Format, + KnownFormats: s.KnownFormats, + } +} + +func (s *SchemaValidator) schemaPropsValidator() valueValidator { + sch := s.Schema + return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...) +} + +func (s *SchemaValidator) objectValidator() valueValidator { + return &objectValidator{ + Path: s.Path, + In: s.in, + MaxProperties: s.Schema.MaxProperties, + MinProperties: s.Schema.MinProperties, + Required: s.Schema.Required, + Properties: s.Schema.Properties, + AdditionalProperties: s.Schema.AdditionalProperties, + PatternProperties: s.Schema.PatternProperties, + Root: s.Root, + KnownFormats: s.KnownFormats, + Options: *s.Options, + } +} diff --git a/vendor/github.com/go-openapi/validate/schema_messages.go b/vendor/github.com/go-openapi/validate/schema_messages.go new file mode 100644 index 000000000..786e2e355 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/schema_messages.go @@ -0,0 +1,78 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "github.com/go-openapi/errors" +) + +// Error messages related to schema validation and returned as results. +const ( + // ArrayDoesNotAllowAdditionalItemsError when an additionalItems construct is not verified by the array values provided. + // + // TODO: should move to package go-openapi/errors + ArrayDoesNotAllowAdditionalItemsError = "array doesn't allow for additional items" + + // HasDependencyError indicates that a dependencies construct was not verified + HasDependencyError = "%q has a dependency on %s" + + // InvalidSchemaProvidedError indicates that the schema provided to validate a value cannot be properly compiled + InvalidSchemaProvidedError = "Invalid schema provided to SchemaValidator: %v" + + // InvalidTypeConversionError indicates that a numerical conversion for the given type could not be carried on + InvalidTypeConversionError = "invalid type conversion in %s: %v " + + // MustValidateAtLeastOneSchemaError indicates that in a AnyOf construct, none of the schema constraints specified were verified + MustValidateAtLeastOneSchemaError = "%q must validate at least one schema (anyOf)" + + // MustValidateOnlyOneSchemaError indicates that in a OneOf construct, either none of the schema constraints specified were verified, or several were + MustValidateOnlyOneSchemaError = "%q must validate one and only one schema (oneOf). %s" + + // MustValidateAllSchemasError indicates that in a AllOf construct, at least one of the schema constraints specified were not verified + // + // TODO: punctuation in message + MustValidateAllSchemasError = "%q must validate all the schemas (allOf)%s" + + // MustNotValidateSchemaError indicates that in a Not construct, the schema constraint specified was verified + MustNotValidateSchemaError = "%q must not validate the schema (not)" +) + +// Warning messages related to schema validation and returned as results +const () + +func invalidSchemaProvidedMsg(err error) errors.Error { + return errors.New(InternalErrorCode, InvalidSchemaProvidedError, err) +} +func invalidTypeConversionMsg(path string, err error) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidTypeConversionError, path, err) +} +func mustValidateOnlyOneSchemaMsg(path, additionalMsg string) errors.Error { + return errors.New(errors.CompositeErrorCode, MustValidateOnlyOneSchemaError, path, additionalMsg) +} +func mustValidateAtLeastOneSchemaMsg(path string) errors.Error { + return errors.New(errors.CompositeErrorCode, MustValidateAtLeastOneSchemaError, path) +} +func mustValidateAllSchemasMsg(path, additionalMsg string) errors.Error { + return errors.New(errors.CompositeErrorCode, MustValidateAllSchemasError, path, additionalMsg) +} +func mustNotValidatechemaMsg(path string) errors.Error { + return errors.New(errors.CompositeErrorCode, MustNotValidateSchemaError, path) +} +func hasADependencyMsg(path, depkey string) errors.Error { + return errors.New(errors.CompositeErrorCode, HasDependencyError, path, depkey) +} +func arrayDoesNotAllowAdditionalItemsMsg() errors.Error { + return errors.New(errors.CompositeErrorCode, ArrayDoesNotAllowAdditionalItemsError) +} diff --git a/vendor/github.com/go-openapi/validate/schema_option.go b/vendor/github.com/go-openapi/validate/schema_option.go new file mode 100644 index 000000000..f328b56b8 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/schema_option.go @@ -0,0 +1,33 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +type SchemaValidatorOptions struct { + DisableObjectArrayTypeCheck bool +} + +type Option func(*SchemaValidatorOptions) + +func DisableObjectArrayTypeCheck(disable bool) Option { + return func(svo *SchemaValidatorOptions) { + svo.DisableObjectArrayTypeCheck = disable + } +} + +func (svo SchemaValidatorOptions) Options() []Option { + return []Option{ + DisableObjectArrayTypeCheck(svo.DisableObjectArrayTypeCheck), + } +} diff --git a/vendor/github.com/go-openapi/validate/schema_props.go b/vendor/github.com/go-openapi/validate/schema_props.go new file mode 100644 index 000000000..5643c783c --- /dev/null +++ b/vendor/github.com/go-openapi/validate/schema_props.go @@ -0,0 +1,240 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "reflect" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +type schemaPropsValidator struct { + Path string + In string + AllOf []spec.Schema + OneOf []spec.Schema + AnyOf []spec.Schema + Not *spec.Schema + Dependencies spec.Dependencies + anyOfValidators []SchemaValidator + allOfValidators []SchemaValidator + oneOfValidators []SchemaValidator + notValidator *SchemaValidator + Root interface{} + KnownFormats strfmt.Registry + Options SchemaValidatorOptions +} + +func (s *schemaPropsValidator) SetPath(path string) { + s.Path = path +} + +func newSchemaPropsValidator(path string, in string, allOf, oneOf, anyOf []spec.Schema, not *spec.Schema, deps spec.Dependencies, root interface{}, formats strfmt.Registry, options ...Option) *schemaPropsValidator { + anyValidators := make([]SchemaValidator, 0, len(anyOf)) + for _, v := range anyOf { + v := v + anyValidators = append(anyValidators, *NewSchemaValidator(&v, root, path, formats, options...)) + } + allValidators := make([]SchemaValidator, 0, len(allOf)) + for _, v := range allOf { + v := v + allValidators = append(allValidators, *NewSchemaValidator(&v, root, path, formats, options...)) + } + oneValidators := make([]SchemaValidator, 0, len(oneOf)) + for _, v := range oneOf { + v := v + oneValidators = append(oneValidators, *NewSchemaValidator(&v, root, path, formats, options...)) + } + + var notValidator *SchemaValidator + if not != nil { + notValidator = NewSchemaValidator(not, root, path, formats, options...) + } + + schOptions := &SchemaValidatorOptions{} + for _, o := range options { + o(schOptions) + } + return &schemaPropsValidator{ + Path: path, + In: in, + AllOf: allOf, + OneOf: oneOf, + AnyOf: anyOf, + Not: not, + Dependencies: deps, + anyOfValidators: anyValidators, + allOfValidators: allValidators, + oneOfValidators: oneValidators, + notValidator: notValidator, + Root: root, + KnownFormats: formats, + Options: *schOptions, + } +} + +func (s *schemaPropsValidator) Applies(source interface{}, kind reflect.Kind) bool { + r := reflect.TypeOf(source) == specSchemaType + debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) + return r +} + +func (s *schemaPropsValidator) Validate(data interface{}) *Result { + mainResult := new(Result) + + // Intermediary error results + + // IMPORTANT! messages from underlying validators + keepResultAnyOf := new(Result) + keepResultOneOf := new(Result) + keepResultAllOf := new(Result) + + // Validates at least one in anyOf schemas + var firstSuccess *Result + if len(s.anyOfValidators) > 0 { + var bestFailures *Result + succeededOnce := false + for _, anyOfSchema := range s.anyOfValidators { + result := anyOfSchema.Validate(data) + // We keep inner IMPORTANT! errors no matter what MatchCount tells us + keepResultAnyOf.Merge(result.keepRelevantErrors()) + if result.IsValid() { + bestFailures = nil + succeededOnce = true + if firstSuccess == nil { + firstSuccess = result + } + keepResultAnyOf = new(Result) + break + } + // MatchCount is used to select errors from the schema with most positive checks + if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { + bestFailures = result + } + } + + if !succeededOnce { + mainResult.AddErrors(mustValidateAtLeastOneSchemaMsg(s.Path)) + } + if bestFailures != nil { + mainResult.Merge(bestFailures) + } else if firstSuccess != nil { + mainResult.Merge(firstSuccess) + } + } + + // Validates exactly one in oneOf schemas + if len(s.oneOfValidators) > 0 { + var bestFailures *Result + var firstSuccess *Result + validated := 0 + + for _, oneOfSchema := range s.oneOfValidators { + result := oneOfSchema.Validate(data) + // We keep inner IMPORTANT! errors no matter what MatchCount tells us + keepResultOneOf.Merge(result.keepRelevantErrors()) + if result.IsValid() { + validated++ + bestFailures = nil + if firstSuccess == nil { + firstSuccess = result + } + keepResultOneOf = new(Result) + continue + } + // MatchCount is used to select errors from the schema with most positive checks + if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { + bestFailures = result + } + } + + if validated != 1 { + additionalMsg := "" + if validated == 0 { + additionalMsg = "Found none valid" + } else { + additionalMsg = fmt.Sprintf("Found %d valid alternatives", validated) + } + + mainResult.AddErrors(mustValidateOnlyOneSchemaMsg(s.Path, additionalMsg)) + if bestFailures != nil { + mainResult.Merge(bestFailures) + } + } else if firstSuccess != nil { + mainResult.Merge(firstSuccess) + } + } + + // Validates all of allOf schemas + if len(s.allOfValidators) > 0 { + validated := 0 + + for _, allOfSchema := range s.allOfValidators { + result := allOfSchema.Validate(data) + // We keep inner IMPORTANT! errors no matter what MatchCount tells us + keepResultAllOf.Merge(result.keepRelevantErrors()) + //keepResultAllOf.Merge(result) + if result.IsValid() { + validated++ + } + mainResult.Merge(result) + } + + if validated != len(s.allOfValidators) { + additionalMsg := "" + if validated == 0 { + additionalMsg = ". None validated" + } + + mainResult.AddErrors(mustValidateAllSchemasMsg(s.Path, additionalMsg)) + } + } + + if s.notValidator != nil { + result := s.notValidator.Validate(data) + // We keep inner IMPORTANT! errors no matter what MatchCount tells us + if result.IsValid() { + mainResult.AddErrors(mustNotValidatechemaMsg(s.Path)) + } + } + + if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map { + val := data.(map[string]interface{}) + for key := range val { + if dep, ok := s.Dependencies[key]; ok { + + if dep.Schema != nil { + mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats, s.Options.Options()...).Validate(data)) + continue + } + + if len(dep.Property) > 0 { + for _, depKey := range dep.Property { + if _, ok := val[depKey]; !ok { + mainResult.AddErrors(hasADependencyMsg(s.Path, depKey)) + } + } + } + } + } + } + + mainResult.Inc() + // In the end we retain best failures for schema validation + // plus, if any, composite errors which may explain special cases (tagged as IMPORTANT!). + return mainResult.Merge(keepResultAllOf, keepResultOneOf, keepResultAnyOf) +} diff --git a/vendor/github.com/go-openapi/validate/slice_validator.go b/vendor/github.com/go-openapi/validate/slice_validator.go new file mode 100644 index 000000000..6e615946b --- /dev/null +++ b/vendor/github.com/go-openapi/validate/slice_validator.go @@ -0,0 +1,104 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "reflect" + + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +type schemaSliceValidator struct { + Path string + In string + MaxItems *int64 + MinItems *int64 + UniqueItems bool + AdditionalItems *spec.SchemaOrBool + Items *spec.SchemaOrArray + Root interface{} + KnownFormats strfmt.Registry +} + +func (s *schemaSliceValidator) SetPath(path string) { + s.Path = path +} + +func (s *schemaSliceValidator) Applies(source interface{}, kind reflect.Kind) bool { + _, ok := source.(*spec.Schema) + r := ok && kind == reflect.Slice + return r +} + +func (s *schemaSliceValidator) Validate(data interface{}) *Result { + result := new(Result) + if data == nil { + return result + } + val := reflect.ValueOf(data) + size := val.Len() + + if s.Items != nil && s.Items.Schema != nil { + validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats) + for i := 0; i < size; i++ { + validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i)) + value := val.Index(i) + result.mergeForSlice(val, i, validator.Validate(value.Interface())) + } + } + + itemsSize := 0 + if s.Items != nil && len(s.Items.Schemas) > 0 { + itemsSize = len(s.Items.Schemas) + for i := 0; i < itemsSize; i++ { + validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats) + if val.Len() <= i { + break + } + result.mergeForSlice(val, int(i), validator.Validate(val.Index(i).Interface())) + } + } + if s.AdditionalItems != nil && itemsSize < size { + if s.Items != nil && len(s.Items.Schemas) > 0 && !s.AdditionalItems.Allows { + result.AddErrors(arrayDoesNotAllowAdditionalItemsMsg()) + } + if s.AdditionalItems.Schema != nil { + for i := itemsSize; i < size-itemsSize+1; i++ { + validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats) + result.mergeForSlice(val, int(i), validator.Validate(val.Index(int(i)).Interface())) + } + } + } + + if s.MinItems != nil { + if err := MinItems(s.Path, s.In, int64(size), *s.MinItems); err != nil { + result.AddErrors(err) + } + } + if s.MaxItems != nil { + if err := MaxItems(s.Path, s.In, int64(size), *s.MaxItems); err != nil { + result.AddErrors(err) + } + } + if s.UniqueItems { + if err := UniqueItems(s.Path, s.In, val.Interface()); err != nil { + result.AddErrors(err) + } + } + result.Inc() + return result +} diff --git a/vendor/github.com/go-openapi/validate/spec.go b/vendor/github.com/go-openapi/validate/spec.go new file mode 100644 index 000000000..08ccd22fe --- /dev/null +++ b/vendor/github.com/go-openapi/validate/spec.go @@ -0,0 +1,777 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/go-openapi/analysis" + "github.com/go-openapi/errors" + "github.com/go-openapi/jsonpointer" + "github.com/go-openapi/loads" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// Spec validates an OpenAPI 2.0 specification document. +// +// Returns an error flattening in a single standard error, all validation messages. +// +// - TODO: $ref should not have siblings +// - TODO: make sure documentation reflects all checks and warnings +// - TODO: check on discriminators +// - TODO: explicit message on unsupported keywords (better than "forbidden property"...) +// - TODO: full list of unresolved refs +// - TODO: validate numeric constraints (issue#581): this should be handled like defaults and examples +// - TODO: option to determine if we validate for go-swagger or in a more general context +// - TODO: check on required properties to support anyOf, allOf, oneOf +// +// NOTE: SecurityScopes are maps: no need to check uniqueness +// +func Spec(doc *loads.Document, formats strfmt.Registry) error { + errs, _ /*warns*/ := NewSpecValidator(doc.Schema(), formats).Validate(doc) + if errs.HasErrors() { + return errors.CompositeValidationError(errs.Errors...) + } + return nil +} + +// SpecValidator validates a swagger 2.0 spec +type SpecValidator struct { + schema *spec.Schema // swagger 2.0 schema + spec *loads.Document + analyzer *analysis.Spec + expanded *loads.Document + KnownFormats strfmt.Registry + Options Opts // validation options +} + +// NewSpecValidator creates a new swagger spec validator instance +func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator { + return &SpecValidator{ + schema: schema, + KnownFormats: formats, + Options: defaultOpts, + } +} + +// Validate validates the swagger spec +func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) { + var sd *loads.Document + errs = new(Result) + + switch v := data.(type) { + case *loads.Document: + sd = v + } + if sd == nil { + errs.AddErrors(invalidDocumentMsg()) + return + } + s.spec = sd + s.analyzer = analysis.New(sd.Spec()) + + warnings = new(Result) + + // Swagger schema validator + schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats) + var obj interface{} + + // Raw spec unmarshalling errors + if err := json.Unmarshal(sd.Raw(), &obj); err != nil { + // NOTE: under normal conditions, the *load.Document has been already unmarshalled + // So this one is just a paranoid check on the behavior of the spec package + panic(InvalidDocumentError) + } + + defer func() { + // errs holds all errors and warnings, + // warnings only warnings + errs.MergeAsWarnings(warnings) + warnings.AddErrors(errs.Warnings...) + }() + + errs.Merge(schv.Validate(obj)) // error - + // There may be a point in continuing to try and determine more accurate errors + if !s.Options.ContinueOnErrors && errs.HasErrors() { + return // no point in continuing + } + + errs.Merge(s.validateReferencesValid()) // error - + // There may be a point in continuing to try and determine more accurate errors + if !s.Options.ContinueOnErrors && errs.HasErrors() { + return // no point in continuing + } + + errs.Merge(s.validateDuplicateOperationIDs()) + errs.Merge(s.validateDuplicatePropertyNames()) // error - + errs.Merge(s.validateParameters()) // error - + errs.Merge(s.validateItems()) // error - + + // Properties in required definition MUST validate their schema + // Properties SHOULD NOT be declared as both required and readOnly (warning) + errs.Merge(s.validateRequiredDefinitions()) // error and warning + + // There may be a point in continuing to try and determine more accurate errors + if !s.Options.ContinueOnErrors && errs.HasErrors() { + return // no point in continuing + } + + // Values provided as default MUST validate their schema + df := &defaultValidator{SpecValidator: s} + errs.Merge(df.Validate()) + + // Values provided as examples MUST validate their schema + // Value provided as examples in a response without schema generate a warning + // Known limitations: examples in responses for mime type not application/json are ignored (warning) + ex := &exampleValidator{SpecValidator: s} + errs.Merge(ex.Validate()) + + errs.Merge(s.validateNonEmptyPathParamNames()) + + //errs.Merge(s.validateRefNoSibling()) // warning only + errs.Merge(s.validateReferenced()) // warning only + + return +} + +func (s *SpecValidator) validateNonEmptyPathParamNames() *Result { + res := new(Result) + if s.spec.Spec().Paths == nil { + // There is no Paths object: error + res.AddErrors(noValidPathMsg()) + } else { + if s.spec.Spec().Paths.Paths == nil { + // Paths may be empty: warning + res.AddWarnings(noValidPathMsg()) + } else { + for k := range s.spec.Spec().Paths.Paths { + if strings.Contains(k, "{}") { + res.AddErrors(emptyPathParameterMsg(k)) + } + } + } + } + return res +} + +func (s *SpecValidator) validateDuplicateOperationIDs() *Result { + // OperationID, if specified, must be unique across the board + res := new(Result) + known := make(map[string]int) + for _, v := range s.analyzer.OperationIDs() { + if v != "" { + known[v]++ + } + } + for k, v := range known { + if v > 1 { + res.AddErrors(nonUniqueOperationIDMsg(k, v)) + } + } + return res +} + +type dupProp struct { + Name string + Definition string +} + +func (s *SpecValidator) validateDuplicatePropertyNames() *Result { + // definition can't declare a property that's already defined by one of its ancestors + res := new(Result) + for k, sch := range s.spec.Spec().Definitions { + if len(sch.AllOf) == 0 { + continue + } + + knownanc := map[string]struct{}{ + "#/definitions/" + k: {}, + } + + ancs, rec := s.validateCircularAncestry(k, sch, knownanc) + if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) { + res.Merge(rec) + } + if len(ancs) > 0 { + res.AddErrors(circularAncestryDefinitionMsg(k, ancs)) + return res + } + + knowns := make(map[string]struct{}) + dups, rep := s.validateSchemaPropertyNames(k, sch, knowns) + if rep != nil && (rep.HasErrors() || rep.HasWarnings()) { + res.Merge(rep) + } + if len(dups) > 0 { + var pns []string + for _, v := range dups { + pns = append(pns, v.Definition+"."+v.Name) + } + res.AddErrors(duplicatePropertiesMsg(k, pns)) + } + + } + return res +} + +func (s *SpecValidator) resolveRef(ref *spec.Ref) (*spec.Schema, error) { + if s.spec.SpecFilePath() != "" { + return spec.ResolveRefWithBase(s.spec.Spec(), ref, &spec.ExpandOptions{RelativeBase: s.spec.SpecFilePath()}) + } + // NOTE: it looks like with the new spec resolver, this code is now unrecheable + return spec.ResolveRef(s.spec.Spec(), ref) +} + +func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema, knowns map[string]struct{}) ([]dupProp, *Result) { + var dups []dupProp + + schn := nm + schc := &sch + res := new(Result) + + for schc.Ref.String() != "" { + // gather property names + reso, err := s.resolveRef(&schc.Ref) + if err != nil { + errorHelp.addPointerError(res, err, schc.Ref.String(), nm) + return dups, res + } + schc = reso + schn = sch.Ref.String() + } + + if len(schc.AllOf) > 0 { + for _, chld := range schc.AllOf { + dup, rep := s.validateSchemaPropertyNames(schn, chld, knowns) + if rep != nil && (rep.HasErrors() || rep.HasWarnings()) { + res.Merge(rep) + } + dups = append(dups, dup...) + } + return dups, res + } + + for k := range schc.Properties { + _, ok := knowns[k] + if ok { + dups = append(dups, dupProp{Name: k, Definition: schn}) + } else { + knowns[k] = struct{}{} + } + } + + return dups, res +} + +func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, knowns map[string]struct{}) ([]string, *Result) { + res := new(Result) + + if sch.Ref.String() == "" && len(sch.AllOf) == 0 { // Safeguard. We should not be able to actually get there + return nil, res + } + var ancs []string + + schn := nm + schc := &sch + + for schc.Ref.String() != "" { + reso, err := s.resolveRef(&schc.Ref) + if err != nil { + errorHelp.addPointerError(res, err, schc.Ref.String(), nm) + return ancs, res + } + schc = reso + schn = sch.Ref.String() + } + + if schn != nm && schn != "" { + if _, ok := knowns[schn]; ok { + ancs = append(ancs, schn) + } + knowns[schn] = struct{}{} + + if len(ancs) > 0 { + return ancs, res + } + } + + if len(schc.AllOf) > 0 { + for _, chld := range schc.AllOf { + if chld.Ref.String() != "" || len(chld.AllOf) > 0 { + anc, rec := s.validateCircularAncestry(schn, chld, knowns) + if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) { + res.Merge(rec) + } + ancs = append(ancs, anc...) + if len(ancs) > 0 { + return ancs, res + } + } + } + } + return ancs, res +} + +func (s *SpecValidator) validateItems() *Result { + // validate parameter, items, schema and response objects for presence of item if type is array + res := new(Result) + + for method, pi := range s.analyzer.Operations() { + for path, op := range pi { + for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { + + if param.TypeName() == "array" && param.ItemsTypeName() == "" { + res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) + continue + } + if param.In != "body" { + if param.Items != nil { + items := param.Items + for items.TypeName() == "array" { + if items.ItemsTypeName() == "" { + res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) + break + } + items = items.Items + } + } + } else { + // In: body + if param.Schema != nil { + res.Merge(s.validateSchemaItems(*param.Schema, fmt.Sprintf("body param %q", param.Name), op.ID)) + } + } + } + + var responses []spec.Response + if op.Responses != nil { + if op.Responses.Default != nil { + responses = append(responses, *op.Responses.Default) + } + if op.Responses.StatusCodeResponses != nil { + for _, v := range op.Responses.StatusCodeResponses { + responses = append(responses, v) + } + } + } + + for _, resp := range responses { + // Response headers with array + for hn, hv := range resp.Headers { + if hv.TypeName() == "array" && hv.ItemsTypeName() == "" { + res.AddErrors(arrayInHeaderRequiresItemsMsg(hn, op.ID)) + } + } + if resp.Schema != nil { + res.Merge(s.validateSchemaItems(*resp.Schema, "response body", op.ID)) + } + } + } + } + return res +} + +// Verifies constraints on array type +func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result { + res := new(Result) + if !schema.Type.Contains("array") { + return res + } + + if schema.Items == nil || schema.Items.Len() == 0 { + res.AddErrors(arrayRequiresItemsMsg(prefix, opID)) + return res + } + + if schema.Items.Schema != nil { + schema = *schema.Items.Schema + if _, err := compileRegexp(schema.Pattern); err != nil { + res.AddErrors(invalidItemsPatternMsg(prefix, opID, schema.Pattern)) + } + + res.Merge(s.validateSchemaItems(schema, prefix, opID)) + } + return res +} + +func (s *SpecValidator) validatePathParamPresence(path string, fromPath, fromOperation []string) *Result { + // Each defined operation path parameters must correspond to a named element in the API's path pattern. + // (For example, you cannot have a path parameter named id for the following path /pets/{petId} but you must have a path parameter named petId.) + res := new(Result) + for _, l := range fromPath { + var matched bool + for _, r := range fromOperation { + if l == "{"+r+"}" { + matched = true + break + } + } + if !matched { + res.AddErrors(noParameterInPathMsg(l)) + } + } + + for _, p := range fromOperation { + var matched bool + for _, r := range fromPath { + if "{"+p+"}" == r { + matched = true + break + } + } + if !matched { + res.AddErrors(pathParamNotInPathMsg(path, p)) + } + } + + return res +} + +func (s *SpecValidator) validateReferenced() *Result { + var res Result + res.MergeAsWarnings(s.validateReferencedParameters()) + res.MergeAsWarnings(s.validateReferencedResponses()) + res.MergeAsWarnings(s.validateReferencedDefinitions()) + return &res +} + +func (s *SpecValidator) validateReferencedParameters() *Result { + // Each referenceable definition should have references. + params := s.spec.Spec().Parameters + if len(params) == 0 { + return nil + } + + expected := make(map[string]struct{}) + for k := range params { + expected["#/parameters/"+jsonpointer.Escape(k)] = struct{}{} + } + for _, k := range s.analyzer.AllParameterReferences() { + if _, ok := expected[k]; ok { + delete(expected, k) + } + } + + if len(expected) == 0 { + return nil + } + result := new(Result) + for k := range expected { + result.AddWarnings(unusedParamMsg(k)) + } + return result +} + +func (s *SpecValidator) validateReferencedResponses() *Result { + // Each referenceable definition should have references. + responses := s.spec.Spec().Responses + if len(responses) == 0 { + return nil + } + + expected := make(map[string]struct{}) + for k := range responses { + expected["#/responses/"+jsonpointer.Escape(k)] = struct{}{} + } + for _, k := range s.analyzer.AllResponseReferences() { + if _, ok := expected[k]; ok { + delete(expected, k) + } + } + + if len(expected) == 0 { + return nil + } + result := new(Result) + for k := range expected { + result.AddWarnings(unusedResponseMsg(k)) + } + return result +} + +func (s *SpecValidator) validateReferencedDefinitions() *Result { + // Each referenceable definition must have references. + defs := s.spec.Spec().Definitions + if len(defs) == 0 { + return nil + } + + expected := make(map[string]struct{}) + for k := range defs { + expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{} + } + for _, k := range s.analyzer.AllDefinitionReferences() { + if _, ok := expected[k]; ok { + delete(expected, k) + } + } + + if len(expected) == 0 { + return nil + } + + result := new(Result) + for k := range expected { + result.AddWarnings(unusedDefinitionMsg(k)) + } + return result +} + +func (s *SpecValidator) validateRequiredDefinitions() *Result { + // Each property listed in the required array must be defined in the properties of the model + res := new(Result) + +DEFINITIONS: + for d, schema := range s.spec.Spec().Definitions { + if schema.Required != nil { // Safeguard + for _, pn := range schema.Required { + red := s.validateRequiredProperties(pn, d, &schema) + res.Merge(red) + if !red.IsValid() && !s.Options.ContinueOnErrors { + break DEFINITIONS // there is an error, let's stop that bleeding + } + } + } + } + return res +} + +func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Schema) *Result { + // Takes care of recursive property definitions, which may be nested in additionalProperties schemas + res := new(Result) + propertyMatch := false + patternMatch := false + additionalPropertiesMatch := false + isReadOnly := false + + // Regular properties + if _, ok := v.Properties[path]; ok { + propertyMatch = true + isReadOnly = v.Properties[path].ReadOnly + } + + // NOTE: patternProperties are not supported in swagger. Even though, we continue validation here + // We check all defined patterns: if one regexp is invalid, croaks an error + for pp, pv := range v.PatternProperties { + re, err := compileRegexp(pp) + if err != nil { + res.AddErrors(invalidPatternMsg(pp, in)) + } else if re.MatchString(path) { + patternMatch = true + if !propertyMatch { + isReadOnly = pv.ReadOnly + } + } + } + + if !(propertyMatch || patternMatch) { + if v.AdditionalProperties != nil { + if v.AdditionalProperties.Allows && v.AdditionalProperties.Schema == nil { + additionalPropertiesMatch = true + } else if v.AdditionalProperties.Schema != nil { + // additionalProperties as schema are upported in swagger + // recursively validates additionalProperties schema + // TODO : anyOf, allOf, oneOf like in schemaPropsValidator + red := s.validateRequiredProperties(path, in, v.AdditionalProperties.Schema) + if red.IsValid() { + additionalPropertiesMatch = true + if !propertyMatch && !patternMatch { + isReadOnly = v.AdditionalProperties.Schema.ReadOnly + } + } + res.Merge(red) + } + } + } + + if !(propertyMatch || patternMatch || additionalPropertiesMatch) { + res.AddErrors(requiredButNotDefinedMsg(path, in)) + } + + if isReadOnly { + res.AddWarnings(readOnlyAndRequiredMsg(in, path)) + } + return res +} + +func (s *SpecValidator) validateParameters() *Result { + // - for each method, path is unique, regardless of path parameters + // e.g. GET:/petstore/{id}, GET:/petstore/{pet}, GET:/petstore are + // considered duplicate paths + // - each parameter should have a unique `name` and `type` combination + // - each operation should have only 1 parameter of type body + // - there must be at most 1 parameter in body + // - parameters with pattern property must specify valid patterns + // - $ref in parameters must resolve + // - path param must be required + res := new(Result) + rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`) + for method, pi := range s.analyzer.Operations() { + methodPaths := make(map[string]map[string]string) + if pi != nil { // Safeguard + for path, op := range pi { + pathToAdd := pathHelp.stripParametersInPath(path) + + // Warn on garbled path afer param stripping + if rexGarbledPathSegment.MatchString(pathToAdd) { + res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd)) + } + + // Check uniqueness of stripped paths + if _, found := methodPaths[method][pathToAdd]; found { + + // Sort names for stable, testable output + if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 { + res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd])) + } else { + res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path)) + } + } else { + if _, found := methodPaths[method]; !found { + methodPaths[method] = map[string]string{} + } + methodPaths[method][pathToAdd] = path //Original non stripped path + + } + + var bodyParams []string + var paramNames []string + var hasForm, hasBody bool + + // Check parameters names uniqueness for operation + // TODO: should be done after param expansion + res.Merge(s.checkUniqueParams(path, method, op)) + + for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { + // Validate pattern regexp for parameters with a Pattern property + if _, err := compileRegexp(pr.Pattern); err != nil { + res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern)) + } + + // There must be at most one parameter in body: list them all + if pr.In == "body" { + bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name)) + hasBody = true + } + + if pr.In == "path" { + paramNames = append(paramNames, pr.Name) + // Path declared in path must have the required: true property + if !pr.Required { + res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name)) + } + } + + if pr.In == "formData" { + hasForm = true + } + } + + // In:formData and In:body are mutually exclusive + if hasBody && hasForm { + res.AddErrors(bothFormDataAndBodyMsg(op.ID)) + } + // There must be at most one body param + // Accurately report situations when more than 1 body param is declared (possibly unnamed) + if len(bodyParams) > 1 { + sort.Strings(bodyParams) + res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams)) + } + + // Check uniqueness of parameters in path + paramsInPath := pathHelp.extractPathParams(path) + for i, p := range paramsInPath { + for j, q := range paramsInPath { + if p == q && i > j { + res.AddErrors(pathParamNotUniqueMsg(path, p, q)) + break + } + } + } + + // Warns about possible malformed params in path + rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`) + for _, p := range paramsInPath { + if rexGarbledParam.MatchString(p) { + res.AddWarnings(pathParamGarbledMsg(path, p)) + } + } + + // Match params from path vs params from params section + res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames)) + } + } + } + return res +} + +func (s *SpecValidator) validateReferencesValid() *Result { + // each reference must point to a valid object + res := new(Result) + for _, r := range s.analyzer.AllRefs() { + if !r.IsValidURI(s.spec.SpecFilePath()) { // Safeguard - spec should always yield a valid URI + res.AddErrors(invalidRefMsg(r.String())) + } + } + if !res.HasErrors() { + // NOTE: with default settings, loads.Document.Expanded() + // stops on first error. Anyhow, the expand option to continue + // on errors fails to report errors at all. + exp, err := s.spec.Expanded() + if err != nil { + res.AddErrors(unresolvedReferencesMsg(err)) + } + s.expanded = exp + } + return res +} + +func (s *SpecValidator) checkUniqueParams(path, method string, op *spec.Operation) *Result { + // Check for duplicate parameters declaration in param section. + // Each parameter should have a unique `name` and `type` combination + // NOTE: this could be factorized in analysis (when constructing the params map) + // However, there are some issues with such a factorization: + // - analysis does not seem to fully expand params + // - param keys may be altered by x-go-name + res := new(Result) + pnames := make(map[string]struct{}) + + if op.Parameters != nil { // Safeguard + for _, ppr := range op.Parameters { + var ok bool + pr, red := paramHelp.resolveParam(path, method, op.ID, &ppr, s) + res.Merge(red) + + if pr != nil && pr.Name != "" { // params with empty name does no participate the check + key := fmt.Sprintf("%s#%s", pr.In, pr.Name) + + if _, ok = pnames[key]; ok { + res.AddErrors(duplicateParamNameMsg(pr.In, pr.Name, op.ID)) + } + pnames[key] = struct{}{} + } + } + } + return res +} + +// SetContinueOnErrors sets the ContinueOnErrors option for this validator. +func (s *SpecValidator) SetContinueOnErrors(c bool) { + s.Options.ContinueOnErrors = c +} diff --git a/vendor/github.com/go-openapi/validate/spec_messages.go b/vendor/github.com/go-openapi/validate/spec_messages.go new file mode 100644 index 000000000..441bb5197 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/spec_messages.go @@ -0,0 +1,354 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "net/http" + + "github.com/go-openapi/errors" +) + +// Error messages related to spec validation and returned as results. +const ( + // ArrayRequiresItemsError ... + ArrayRequiresItemsError = "%s for %q is a collection without an element type (array requires items definition)" + + // ArrayInParamRequiresItemsError ... + ArrayInParamRequiresItemsError = "param %q for %q is a collection without an element type (array requires item definition)" + + // ArrayInHeaderRequiresItemsError ... + ArrayInHeaderRequiresItemsError = "header %q for %q is a collection without an element type (array requires items definition)" + + // BothFormDataAndBodyError indicates that an operation specifies both a body and a formData parameter, which is forbidden + BothFormDataAndBodyError = "operation %q has both formData and body parameters. Only one such In: type may be used for a given operation" + + // CannotResolveRefError when a $ref could not be resolved + CannotResolveReferenceError = "could not resolve reference in %s to $ref %s: %v" + + // CircularAncestryDefinitionError ... + CircularAncestryDefinitionError = "definition %q has circular ancestry: %v" + + // DefaultValueDoesNotValidateError results from an invalid default value provided + DefaultValueDoesNotValidateError = "default value for %s in %s does not validate its schema" + + // DefaultValueItemsDoesNotValidateError results from an invalid default value provided for Items + DefaultValueItemsDoesNotValidateError = "default value for %s.items in %s does not validate its schema" + + // DefaultValueHeaderDoesNotValidateError results from an invalid default value provided in header + DefaultValueHeaderDoesNotValidateError = "in operation %q, default value in header %s for %s does not validate its schema" + + // DefaultValueHeaderItemsDoesNotValidateError results from an invalid default value provided in header.items + DefaultValueHeaderItemsDoesNotValidateError = "in operation %q, default value in header.items %s for %s does not validate its schema" + + // DefaultValueInDoesNotValidateError ... + DefaultValueInDoesNotValidateError = "in operation %q, default value in %s does not validate its schema" + + // DuplicateParamNameError ... + DuplicateParamNameError = "duplicate parameter name %q for %q in operation %q" + + // DuplicatePropertiesError ... + DuplicatePropertiesError = "definition %q contains duplicate properties: %v" + + // ExampleValueDoesNotValidateError results from an invalid example value provided + ExampleValueDoesNotValidateError = "example value for %s in %s does not validate its schema" + + // ExampleValueItemsDoesNotValidateError results from an invalid example value provided for Items + ExampleValueItemsDoesNotValidateError = "example value for %s.items in %s does not validate its schema" + + // ExampleValueHeaderDoesNotValidateError results from an invalid example value provided in header + ExampleValueHeaderDoesNotValidateError = "in operation %q, example value in header %s for %s does not validate its schema" + + // ExampleValueHeaderItemsDoesNotValidateError results from an invalid example value provided in header.items + ExampleValueHeaderItemsDoesNotValidateError = "in operation %q, example value in header.items %s for %s does not validate its schema" + + // ExampleValueInDoesNotValidateError ... + ExampleValueInDoesNotValidateError = "in operation %q, example value in %s does not validate its schema" + + // EmptyPathParameterError means that a path parameter was found empty (e.g. "{}") + EmptyPathParameterError = "%q contains an empty path parameter" + + // InvalidDocumentError states that spec validation only processes spec.Document objects + InvalidDocumentError = "spec validator can only validate spec.Document objects" + + // InvalidItemsPatternError indicates an Items definition with invalid pattern + InvalidItemsPatternError = "%s for %q has invalid items pattern: %q" + + // InvalidParameterDefinitionError indicates an error detected on a parameter definition + InvalidParameterDefinitionError = "invalid definition for parameter %s in %s in operation %q" + + // InvalidParameterDefinitionAsSchemaError indicates an error detected on a parameter definition, which was mistaken with a schema definition. + // Most likely, this situation is encountered whenever a $ref has been added as a sibling of the parameter definition. + InvalidParameterDefinitionAsSchemaError = "invalid definition as Schema for parameter %s in %s in operation %q" + + // InvalidPatternError ... + InvalidPatternError = "pattern %q is invalid in %s" + + // InvalidPatternInError indicates an invalid pattern in a schema or items definition + InvalidPatternInError = "%s in %s has invalid pattern: %q" + + // InvalidPatternInHeaderError indicates a header definition with an invalid pattern + InvalidPatternInHeaderError = "in operation %q, header %s for %s has invalid pattern %q: %v" + + // InvalidPatternInParamError ... + InvalidPatternInParamError = "operation %q has invalid pattern in param %q: %q" + + // InvalidReferenceError indicates that a $ref property could not be resolved + InvalidReferenceError = "invalid ref %q" + + // InvalidResponseDefinitionAsSchemaError indicates an error detected on a response definition, which was mistaken with a schema definition. + // Most likely, this situation is encountered whenever a $ref has been added as a sibling of the response definition. + InvalidResponseDefinitionAsSchemaError = "invalid definition as Schema for response %s in %s" + + // MultipleBodyParamError indicates that an operation specifies multiple parameter with in: body + MultipleBodyParamError = "operation %q has more than 1 body param: %v" + + // NonUniqueOperationIDError indicates that the same operationId has been specified several times + NonUniqueOperationIDError = "%q is defined %d times" + + // NoParameterInPathError indicates that a path was found without any parameter + NoParameterInPathError = "path param %q has no parameter definition" + + // NoValidPathErrorOrWarning indicates that no single path could be validated. If Paths is empty, this message is only a warning. + NoValidPathErrorOrWarning = "spec has no valid path defined" + + // NoValidResponseError indicates that no valid response description could be found for an operation + NoValidResponseError = "operation %q has no valid response" + + // PathOverlapError ... + PathOverlapError = "path %s overlaps with %s" + + // PathParamNotInPathError indicates that a parameter specified with in: path was not found in the path specification + PathParamNotInPathError = "path param %q is not present in path %q" + + // PathParamNotUniqueError ... + PathParamNotUniqueError = "params in path %q must be unique: %q conflicts with %q" + + // PathParamNotRequiredError ... + PathParamRequiredError = "in operation %q,path param %q must be declared as required" + + // RefNotAllowedInHeaderError indicates a $ref was found in a header definition, which is not allowed by Swagger + RefNotAllowedInHeaderError = "IMPORTANT!in %q: $ref are not allowed in headers. In context for header %q%s" + + // RequiredButNotDefinedError ... + RequiredButNotDefinedError = "%q is present in required but not defined as property in definition %q" + + // SomeParametersBrokenError indicates that some parameters could not be resolved, which might result in partial checks to be carried on + SomeParametersBrokenError = "some parameters definitions are broken in %q.%s. Cannot carry on full checks on parameters for operation %s" + + // UnresolvedReferencesError indicates that at least one $ref could not be resolved + UnresolvedReferencesError = "some references could not be resolved in spec. First found: %v" +) + +// Warning messages related to spec validation and returned as results +const ( + // ExamplesWithoutSchemaWarning indicates that examples are provided for a response,but not schema to validate the example against + ExamplesWithoutSchemaWarning = "Examples provided without schema in operation %q, %s" + + // ExamplesMimeNotSupportedWarning indicates that examples are provided with a mime type different than application/json, which + // the validator dos not support yetl + ExamplesMimeNotSupportedWarning = "No validation attempt for examples for media types other than application/json, in operation %q, %s" + + // PathParamGarbledWarning ... + PathParamGarbledWarning = "in path %q, param %q contains {,} or white space. Albeit not stricly illegal, this is probably no what you want" + + // PathStrippedParamGarbledWarning ... + PathStrippedParamGarbledWarning = "path stripped from path parameters %s contains {,} or white space. This is probably no what you want." + + // ReadOnlyAndRequiredWarning ... + ReadOnlyAndRequiredWarning = "Required property %s in %q should not be marked as both required and readOnly" + + // RefShouldNotHaveSiblingsWarning indicates that a $ref was found with a sibling definition. This results in the $ref taking over its siblings, + // which is most likely not wanted. + RefShouldNotHaveSiblingsWarning = "$ref property should have no sibling in %q.%s" + + // RequiredHasDefaultWarning indicates that a required parameter property should not have a default + RequiredHasDefaultWarning = "%s in %s has a default value and is required as parameter" + + // UnusedDefinitionWarning ... + UnusedDefinitionWarning = "definition %q is not used anywhere" + + // UnusedParamWarning ... + UnusedParamWarning = "parameter %q is not used anywhere" + + // UnusedResponseWarning ... + UnusedResponseWarning = "response %q is not used anywhere" +) + +// Additional error codes +const ( + // InternalErrorCode reports an internal technical error + InternalErrorCode = http.StatusInternalServerError + // NotFoundErrorCode indicates that a resource (e.g. a $ref) could not be found + NotFoundErrorCode = http.StatusNotFound +) + +func invalidDocumentMsg() errors.Error { + return errors.New(InternalErrorCode, InvalidDocumentError) +} +func invalidRefMsg(path string) errors.Error { + return errors.New(NotFoundErrorCode, InvalidReferenceError, path) +} +func unresolvedReferencesMsg(err error) errors.Error { + return errors.New(errors.CompositeErrorCode, UnresolvedReferencesError, err) +} +func noValidPathMsg() errors.Error { + return errors.New(errors.CompositeErrorCode, NoValidPathErrorOrWarning) +} +func emptyPathParameterMsg(path string) errors.Error { + return errors.New(errors.CompositeErrorCode, EmptyPathParameterError, path) +} +func nonUniqueOperationIDMsg(path string, i int) errors.Error { + return errors.New(errors.CompositeErrorCode, NonUniqueOperationIDError, path, i) +} +func circularAncestryDefinitionMsg(path string, args interface{}) errors.Error { + return errors.New(errors.CompositeErrorCode, CircularAncestryDefinitionError, path, args) +} +func duplicatePropertiesMsg(path string, args interface{}) errors.Error { + return errors.New(errors.CompositeErrorCode, DuplicatePropertiesError, path, args) +} +func pathParamNotInPathMsg(path, param string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathParamNotInPathError, param, path) +} +func arrayRequiresItemsMsg(path, operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, ArrayRequiresItemsError, path, operation) +} +func arrayInParamRequiresItemsMsg(path, operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, ArrayInParamRequiresItemsError, path, operation) +} +func arrayInHeaderRequiresItemsMsg(path, operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, ArrayInHeaderRequiresItemsError, path, operation) +} +func invalidItemsPatternMsg(path, operation, pattern string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidItemsPatternError, path, operation, pattern) +} +func invalidPatternMsg(pattern, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidPatternError, pattern, path) +} +func requiredButNotDefinedMsg(path, definition string) errors.Error { + return errors.New(errors.CompositeErrorCode, RequiredButNotDefinedError, path, definition) +} +func pathParamGarbledMsg(path, param string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathParamGarbledWarning, path, param) +} +func pathStrippedParamGarbledMsg(path string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathStrippedParamGarbledWarning, path) +} +func pathOverlapMsg(path, arg string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathOverlapError, path, arg) +} +func invalidPatternInParamMsg(operation, param, pattern string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidPatternInParamError, operation, param, pattern) +} +func pathParamRequiredMsg(operation, param string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathParamRequiredError, operation, param) +} +func bothFormDataAndBodyMsg(operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, BothFormDataAndBodyError, operation) +} +func multipleBodyParamMsg(operation string, args interface{}) errors.Error { + return errors.New(errors.CompositeErrorCode, MultipleBodyParamError, operation, args) +} +func pathParamNotUniqueMsg(path, param, arg string) errors.Error { + return errors.New(errors.CompositeErrorCode, PathParamNotUniqueError, path, param, arg) +} +func duplicateParamNameMsg(path, param, operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, DuplicateParamNameError, param, path, operation) +} +func unusedParamMsg(arg string) errors.Error { + return errors.New(errors.CompositeErrorCode, UnusedParamWarning, arg) +} +func unusedDefinitionMsg(arg string) errors.Error { + return errors.New(errors.CompositeErrorCode, UnusedDefinitionWarning, arg) +} +func unusedResponseMsg(arg string) errors.Error { + return errors.New(errors.CompositeErrorCode, UnusedResponseWarning, arg) +} +func readOnlyAndRequiredMsg(path, param string) errors.Error { + return errors.New(errors.CompositeErrorCode, ReadOnlyAndRequiredWarning, param, path) +} +func noParameterInPathMsg(param string) errors.Error { + return errors.New(errors.CompositeErrorCode, NoParameterInPathError, param) +} +func requiredHasDefaultMsg(param, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, RequiredHasDefaultWarning, param, path) +} +func defaultValueDoesNotValidateMsg(param, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, DefaultValueDoesNotValidateError, param, path) +} +func defaultValueItemsDoesNotValidateMsg(param, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, DefaultValueItemsDoesNotValidateError, param, path) +} +func noValidResponseMsg(operation string) errors.Error { + return errors.New(errors.CompositeErrorCode, NoValidResponseError, operation) +} +func defaultValueHeaderDoesNotValidateMsg(operation, header, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, DefaultValueHeaderDoesNotValidateError, operation, header, path) +} +func defaultValueHeaderItemsDoesNotValidateMsg(operation, header, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, DefaultValueHeaderItemsDoesNotValidateError, operation, header, path) +} +func invalidPatternInHeaderMsg(operation, header, path, pattern string, args interface{}) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidPatternInHeaderError, operation, header, path, pattern, args) +} +func invalidPatternInMsg(path, in, pattern string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidPatternInError, path, in, pattern) +} +func defaultValueInDoesNotValidateMsg(operation, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, DefaultValueInDoesNotValidateError, operation, path) +} +func exampleValueDoesNotValidateMsg(param, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExampleValueDoesNotValidateError, param, path) +} +func exampleValueItemsDoesNotValidateMsg(param, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExampleValueItemsDoesNotValidateError, param, path) +} +func exampleValueHeaderDoesNotValidateMsg(operation, header, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExampleValueHeaderDoesNotValidateError, operation, header, path) +} +func exampleValueHeaderItemsDoesNotValidateMsg(operation, header, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExampleValueHeaderItemsDoesNotValidateError, operation, header, path) +} +func exampleValueInDoesNotValidateMsg(operation, path string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExampleValueInDoesNotValidateError, operation, path) +} +func examplesWithoutSchemaMsg(operation, response string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExamplesWithoutSchemaWarning, operation, response) +} +func examplesMimeNotSupportedMsg(operation, response string) errors.Error { + return errors.New(errors.CompositeErrorCode, ExamplesMimeNotSupportedWarning, operation, response) +} +func refNotAllowedInHeaderMsg(path, header, ref string) errors.Error { + return errors.New(errors.CompositeErrorCode, RefNotAllowedInHeaderError, path, header, ref) +} +func cannotResolveRefMsg(path, ref string, err error) errors.Error { + return errors.New(errors.CompositeErrorCode, CannotResolveReferenceError, path, ref, err) +} +func invalidParameterDefinitionMsg(path, method, operationID string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidParameterDefinitionError, path, method, operationID) +} +func invalidParameterDefinitionAsSchemaMsg(path, method, operationID string) errors.Error { + return errors.New(errors.CompositeErrorCode, InvalidParameterDefinitionAsSchemaError, path, method, operationID) +} + +// disabled +//func invalidResponseDefinitionAsSchemaMsg(path, method string) errors.Error { +// return errors.New(errors.CompositeErrorCode, InvalidResponseDefinitionAsSchemaError, path, method) +//} +func someParametersBrokenMsg(path, method, operationID string) errors.Error { + return errors.New(errors.CompositeErrorCode, SomeParametersBrokenError, path, method, operationID) +} +func refShouldNotHaveSiblingsMsg(path, operationID string) errors.Error { + return errors.New(errors.CompositeErrorCode, RefShouldNotHaveSiblingsWarning, operationID, path) +} diff --git a/vendor/github.com/go-openapi/validate/type.go b/vendor/github.com/go-openapi/validate/type.go new file mode 100644 index 000000000..ba0e956d6 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/type.go @@ -0,0 +1,178 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "reflect" + "strings" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +type typeValidator struct { + Type spec.StringOrArray + Nullable bool + Format string + In string + Path string +} + +func (t *typeValidator) schemaInfoForType(data interface{}) (string, string) { + // internal type to JSON type with swagger 2.0 format (with go-openapi/strfmt extensions), + // see https://github.com/go-openapi/strfmt/blob/master/README.md + // TODO: this switch really is some sort of reverse lookup for formats. It should be provided by strfmt. + switch data.(type) { + case []byte, strfmt.Base64, *strfmt.Base64: + return "string", "byte" + case strfmt.CreditCard, *strfmt.CreditCard: + return "string", "creditcard" + case strfmt.Date, *strfmt.Date: + return "string", "date" + case strfmt.DateTime, *strfmt.DateTime: + return "string", "date-time" + case strfmt.Duration, *strfmt.Duration: + return "string", "duration" + case runtime.File, *runtime.File: + return "file", "" + case strfmt.Email, *strfmt.Email: + return "string", "email" + case strfmt.HexColor, *strfmt.HexColor: + return "string", "hexcolor" + case strfmt.Hostname, *strfmt.Hostname: + return "string", "hostname" + case strfmt.IPv4, *strfmt.IPv4: + return "string", "ipv4" + case strfmt.IPv6, *strfmt.IPv6: + return "string", "ipv6" + case strfmt.ISBN, *strfmt.ISBN: + return "string", "isbn" + case strfmt.ISBN10, *strfmt.ISBN10: + return "string", "isbn10" + case strfmt.ISBN13, *strfmt.ISBN13: + return "string", "isbn13" + case strfmt.MAC, *strfmt.MAC: + return "string", "mac" + case strfmt.ObjectId, *strfmt.ObjectId: + return "string", "bsonobjectid" + case strfmt.Password, *strfmt.Password: + return "string", "password" + case strfmt.RGBColor, *strfmt.RGBColor: + return "string", "rgbcolor" + case strfmt.SSN, *strfmt.SSN: + return "string", "ssn" + case strfmt.URI, *strfmt.URI: + return "string", "uri" + case strfmt.UUID, *strfmt.UUID: + return "string", "uuid" + case strfmt.UUID3, *strfmt.UUID3: + return "string", "uuid3" + case strfmt.UUID4, *strfmt.UUID4: + return "string", "uuid4" + case strfmt.UUID5, *strfmt.UUID5: + return "string", "uuid5" + // TODO: missing binary (io.ReadCloser) + // TODO: missing json.Number + default: + val := reflect.ValueOf(data) + tpe := val.Type() + switch tpe.Kind() { + case reflect.Bool: + return "boolean", "" + case reflect.String: + return "string", "" + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32: + // NOTE: that is the spec. With go-openapi, is that not uint32 for unsigned integers? + return "integer", "int32" + case reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64: + return "integer", "int64" + case reflect.Float32: + // NOTE: is that not "float"? + return "number", "float32" + case reflect.Float64: + // NOTE: is that not "double"? + return "number", "float64" + // NOTE: go arrays (reflect.Array) are not supported (fixed length) + case reflect.Slice: + return "array", "" + case reflect.Map, reflect.Struct: + return "object", "" + case reflect.Interface: + // What to do here? + panic("dunno what to do here") + case reflect.Ptr: + return t.schemaInfoForType(reflect.Indirect(val).Interface()) + } + } + return "", "" +} + +func (t *typeValidator) SetPath(path string) { + t.Path = path +} + +func (t *typeValidator) Applies(source interface{}, kind reflect.Kind) bool { + // typeValidator applies to Schema, Parameter and Header objects + stpe := reflect.TypeOf(source) + r := (len(t.Type) > 0 || t.Format != "") && (stpe == specSchemaType || stpe == specParameterType || stpe == specHeaderType) + debugLog("type validator for %q applies %t for %T (kind: %v)\n", t.Path, r, source, kind) + return r +} + +func (t *typeValidator) Validate(data interface{}) *Result { + result := new(Result) + result.Inc() + if data == nil || reflect.DeepEqual(reflect.Zero(reflect.TypeOf(data)), reflect.ValueOf(data)) { + // nil or zero value for the passed structure require Type: null + if len(t.Type) > 0 && !t.Type.Contains("null") && !t.Nullable { // TODO: if a property is not required it also passes this + return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), "null")) + } + return result + } + + // check if the type matches, should be used in every validator chain as first item + val := reflect.Indirect(reflect.ValueOf(data)) + kind := val.Kind() + + // infer schema type (JSON) and format from passed data type + schType, format := t.schemaInfoForType(data) + + debugLog("path: %s, schType: %s, format: %s, expType: %s, expFmt: %s, kind: %s", t.Path, schType, format, t.Type, t.Format, val.Kind().String()) + + // check numerical types + // TODO: check unsigned ints + // TODO: check json.Number (see schema.go) + isLowerInt := t.Format == "int64" && format == "int32" + isLowerFloat := t.Format == "float64" && format == "float32" + isFloatInt := schType == "number" && swag.IsFloat64AJSONInteger(val.Float()) && t.Type.Contains("integer") + isIntFloat := schType == "integer" && t.Type.Contains("number") + + if kind != reflect.String && kind != reflect.Slice && t.Format != "" && !(t.Type.Contains(schType) || format == t.Format || isFloatInt || isIntFloat || isLowerInt || isLowerFloat) { + // TODO: test case + return errorHelp.sErr(errors.InvalidType(t.Path, t.In, t.Format, format)) + } + + if !(t.Type.Contains("number") || t.Type.Contains("integer")) && t.Format != "" && (kind == reflect.String || kind == reflect.Slice) { + return result + } + + if !(t.Type.Contains(schType) || isFloatInt || isIntFloat) { + return errorHelp.sErr(errors.InvalidType(t.Path, t.In, strings.Join(t.Type, ","), schType)) + } + return result +} diff --git a/vendor/github.com/go-openapi/validate/update-fixtures.sh b/vendor/github.com/go-openapi/validate/update-fixtures.sh new file mode 100644 index 000000000..21b06e2b0 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/update-fixtures.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -eu -o pipefail +dir=$(git rev-parse --show-toplevel) +scratch=$(mktemp -d -t tmp.XXXXXXXXXX) + +function finish { + rm -rf "$scratch" +} +trap finish EXIT SIGHUP SIGINT SIGTERM + +cd "$scratch" +git clone https://github.com/json-schema-org/JSON-Schema-Test-Suite Suite +cp -r Suite/tests/draft4/* "$dir/fixtures/jsonschema_suite" +cp -a Suite/remotes "$dir/fixtures/jsonschema_suite" diff --git a/vendor/github.com/go-openapi/validate/validator.go b/vendor/github.com/go-openapi/validate/validator.go new file mode 100644 index 000000000..df700d3cd --- /dev/null +++ b/vendor/github.com/go-openapi/validate/validator.go @@ -0,0 +1,641 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "reflect" + + "github.com/go-openapi/errors" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" +) + +// An EntityValidator is an interface for things that can validate entities +type EntityValidator interface { + Validate(interface{}) *Result +} + +type valueValidator interface { + SetPath(path string) + Applies(interface{}, reflect.Kind) bool + Validate(interface{}) *Result +} + +type itemsValidator struct { + items *spec.Items + root interface{} + path string + in string + validators []valueValidator + KnownFormats strfmt.Registry +} + +func newItemsValidator(path, in string, items *spec.Items, root interface{}, formats strfmt.Registry) *itemsValidator { + iv := &itemsValidator{path: path, in: in, items: items, root: root, KnownFormats: formats} + iv.validators = []valueValidator{ + &typeValidator{ + Type: spec.StringOrArray([]string{items.Type}), + Nullable: items.Nullable, + Format: items.Format, + In: in, + Path: path, + }, + iv.stringValidator(), + iv.formatValidator(), + iv.numberValidator(), + iv.sliceValidator(), + iv.commonValidator(), + } + return iv +} + +func (i *itemsValidator) Validate(index int, data interface{}) *Result { + tpe := reflect.TypeOf(data) + kind := tpe.Kind() + mainResult := new(Result) + path := fmt.Sprintf("%s.%d", i.path, index) + + for _, validator := range i.validators { + validator.SetPath(path) + if validator.Applies(i.root, kind) { + result := validator.Validate(data) + mainResult.Merge(result) + mainResult.Inc() + if result != nil && result.HasErrors() { + return mainResult + } + } + } + return mainResult +} + +func (i *itemsValidator) commonValidator() valueValidator { + return &basicCommonValidator{ + In: i.in, + Default: i.items.Default, + Enum: i.items.Enum, + } +} + +func (i *itemsValidator) sliceValidator() valueValidator { + return &basicSliceValidator{ + In: i.in, + Default: i.items.Default, + MaxItems: i.items.MaxItems, + MinItems: i.items.MinItems, + UniqueItems: i.items.UniqueItems, + Source: i.root, + Items: i.items.Items, + KnownFormats: i.KnownFormats, + } +} + +func (i *itemsValidator) numberValidator() valueValidator { + return &numberValidator{ + In: i.in, + Default: i.items.Default, + MultipleOf: i.items.MultipleOf, + Maximum: i.items.Maximum, + ExclusiveMaximum: i.items.ExclusiveMaximum, + Minimum: i.items.Minimum, + ExclusiveMinimum: i.items.ExclusiveMinimum, + Type: i.items.Type, + Format: i.items.Format, + } +} + +func (i *itemsValidator) stringValidator() valueValidator { + return &stringValidator{ + In: i.in, + Default: i.items.Default, + MaxLength: i.items.MaxLength, + MinLength: i.items.MinLength, + Pattern: i.items.Pattern, + AllowEmptyValue: false, + } +} + +func (i *itemsValidator) formatValidator() valueValidator { + return &formatValidator{ + In: i.in, + //Default: i.items.Default, + Format: i.items.Format, + KnownFormats: i.KnownFormats, + } +} + +type basicCommonValidator struct { + Path string + In string + Default interface{} + Enum []interface{} +} + +func (b *basicCommonValidator) SetPath(path string) { + b.Path = path +} + +func (b *basicCommonValidator) Applies(source interface{}, kind reflect.Kind) bool { + switch source.(type) { + case *spec.Parameter, *spec.Schema, *spec.Header: + return true + } + return false +} + +func (b *basicCommonValidator) Validate(data interface{}) (res *Result) { + if len(b.Enum) > 0 { + for _, enumValue := range b.Enum { + actualType := reflect.TypeOf(enumValue) + if actualType != nil { // Safeguard + expectedValue := reflect.ValueOf(data) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) { + return nil + } + } + } + } + return errorHelp.sErr(errors.EnumFail(b.Path, b.In, data, b.Enum)) + } + return nil +} + +// A HeaderValidator has very limited subset of validations to apply +type HeaderValidator struct { + name string + header *spec.Header + validators []valueValidator + KnownFormats strfmt.Registry +} + +// NewHeaderValidator creates a new header validator object +func NewHeaderValidator(name string, header *spec.Header, formats strfmt.Registry) *HeaderValidator { + p := &HeaderValidator{name: name, header: header, KnownFormats: formats} + p.validators = []valueValidator{ + &typeValidator{ + Type: spec.StringOrArray([]string{header.Type}), + Nullable: header.Nullable, + Format: header.Format, + In: "header", + Path: name, + }, + p.stringValidator(), + p.formatValidator(), + p.numberValidator(), + p.sliceValidator(), + p.commonValidator(), + } + return p +} + +// Validate the value of the header against its schema +func (p *HeaderValidator) Validate(data interface{}) *Result { + result := new(Result) + tpe := reflect.TypeOf(data) + kind := tpe.Kind() + + for _, validator := range p.validators { + if validator.Applies(p.header, kind) { + if err := validator.Validate(data); err != nil { + result.Merge(err) + if err.HasErrors() { + return result + } + } + } + } + return nil +} + +func (p *HeaderValidator) commonValidator() valueValidator { + return &basicCommonValidator{ + Path: p.name, + In: "response", + Default: p.header.Default, + Enum: p.header.Enum, + } +} + +func (p *HeaderValidator) sliceValidator() valueValidator { + return &basicSliceValidator{ + Path: p.name, + In: "response", + Default: p.header.Default, + MaxItems: p.header.MaxItems, + MinItems: p.header.MinItems, + UniqueItems: p.header.UniqueItems, + Items: p.header.Items, + Source: p.header, + KnownFormats: p.KnownFormats, + } +} + +func (p *HeaderValidator) numberValidator() valueValidator { + return &numberValidator{ + Path: p.name, + In: "response", + Default: p.header.Default, + MultipleOf: p.header.MultipleOf, + Maximum: p.header.Maximum, + ExclusiveMaximum: p.header.ExclusiveMaximum, + Minimum: p.header.Minimum, + ExclusiveMinimum: p.header.ExclusiveMinimum, + Type: p.header.Type, + Format: p.header.Format, + } +} + +func (p *HeaderValidator) stringValidator() valueValidator { + return &stringValidator{ + Path: p.name, + In: "response", + Default: p.header.Default, + Required: true, + MaxLength: p.header.MaxLength, + MinLength: p.header.MinLength, + Pattern: p.header.Pattern, + AllowEmptyValue: false, + } +} + +func (p *HeaderValidator) formatValidator() valueValidator { + return &formatValidator{ + Path: p.name, + In: "response", + //Default: p.header.Default, + Format: p.header.Format, + KnownFormats: p.KnownFormats, + } +} + +// A ParamValidator has very limited subset of validations to apply +type ParamValidator struct { + param *spec.Parameter + validators []valueValidator + KnownFormats strfmt.Registry +} + +// NewParamValidator creates a new param validator object +func NewParamValidator(param *spec.Parameter, formats strfmt.Registry) *ParamValidator { + p := &ParamValidator{param: param, KnownFormats: formats} + p.validators = []valueValidator{ + &typeValidator{ + Type: spec.StringOrArray([]string{param.Type}), + Nullable: param.Nullable, + Format: param.Format, + In: param.In, + Path: param.Name, + }, + p.stringValidator(), + p.formatValidator(), + p.numberValidator(), + p.sliceValidator(), + p.commonValidator(), + } + return p +} + +// Validate the data against the description of the parameter +func (p *ParamValidator) Validate(data interface{}) *Result { + result := new(Result) + tpe := reflect.TypeOf(data) + kind := tpe.Kind() + + // TODO: validate type + for _, validator := range p.validators { + if validator.Applies(p.param, kind) { + if err := validator.Validate(data); err != nil { + result.Merge(err) + if err.HasErrors() { + return result + } + } + } + } + return nil +} + +func (p *ParamValidator) commonValidator() valueValidator { + return &basicCommonValidator{ + Path: p.param.Name, + In: p.param.In, + Default: p.param.Default, + Enum: p.param.Enum, + } +} + +func (p *ParamValidator) sliceValidator() valueValidator { + return &basicSliceValidator{ + Path: p.param.Name, + In: p.param.In, + Default: p.param.Default, + MaxItems: p.param.MaxItems, + MinItems: p.param.MinItems, + UniqueItems: p.param.UniqueItems, + Items: p.param.Items, + Source: p.param, + KnownFormats: p.KnownFormats, + } +} + +func (p *ParamValidator) numberValidator() valueValidator { + return &numberValidator{ + Path: p.param.Name, + In: p.param.In, + Default: p.param.Default, + MultipleOf: p.param.MultipleOf, + Maximum: p.param.Maximum, + ExclusiveMaximum: p.param.ExclusiveMaximum, + Minimum: p.param.Minimum, + ExclusiveMinimum: p.param.ExclusiveMinimum, + Type: p.param.Type, + Format: p.param.Format, + } +} + +func (p *ParamValidator) stringValidator() valueValidator { + return &stringValidator{ + Path: p.param.Name, + In: p.param.In, + Default: p.param.Default, + AllowEmptyValue: p.param.AllowEmptyValue, + Required: p.param.Required, + MaxLength: p.param.MaxLength, + MinLength: p.param.MinLength, + Pattern: p.param.Pattern, + } +} + +func (p *ParamValidator) formatValidator() valueValidator { + return &formatValidator{ + Path: p.param.Name, + In: p.param.In, + //Default: p.param.Default, + Format: p.param.Format, + KnownFormats: p.KnownFormats, + } +} + +type basicSliceValidator struct { + Path string + In string + Default interface{} + MaxItems *int64 + MinItems *int64 + UniqueItems bool + Items *spec.Items + Source interface{} + itemsValidator *itemsValidator + KnownFormats strfmt.Registry +} + +func (s *basicSliceValidator) SetPath(path string) { + s.Path = path +} + +func (s *basicSliceValidator) Applies(source interface{}, kind reflect.Kind) bool { + switch source.(type) { + case *spec.Parameter, *spec.Items, *spec.Header: + return kind == reflect.Slice + } + return false +} + +func (s *basicSliceValidator) Validate(data interface{}) *Result { + val := reflect.ValueOf(data) + + size := int64(val.Len()) + if s.MinItems != nil { + if err := MinItems(s.Path, s.In, size, *s.MinItems); err != nil { + return errorHelp.sErr(err) + } + } + + if s.MaxItems != nil { + if err := MaxItems(s.Path, s.In, size, *s.MaxItems); err != nil { + return errorHelp.sErr(err) + } + } + + if s.UniqueItems { + if err := UniqueItems(s.Path, s.In, data); err != nil { + return errorHelp.sErr(err) + } + } + + if s.itemsValidator == nil && s.Items != nil { + s.itemsValidator = newItemsValidator(s.Path, s.In, s.Items, s.Source, s.KnownFormats) + } + + if s.itemsValidator != nil { + for i := 0; i < int(size); i++ { + ele := val.Index(i) + if err := s.itemsValidator.Validate(i, ele.Interface()); err != nil && err.HasErrors() { + return err + } + } + } + return nil +} + +func (s *basicSliceValidator) hasDuplicates(value reflect.Value, size int) bool { + dict := make(map[interface{}]struct{}) + for i := 0; i < size; i++ { + ele := value.Index(i) + if _, ok := dict[ele.Interface()]; ok { + return true + } + dict[ele.Interface()] = struct{}{} + } + return false +} + +type numberValidator struct { + Path string + In string + Default interface{} + MultipleOf *float64 + Maximum *float64 + ExclusiveMaximum bool + Minimum *float64 + ExclusiveMinimum bool + // Allows for more accurate behavior regarding integers + Type string + Format string +} + +func (n *numberValidator) SetPath(path string) { + n.Path = path +} + +func (n *numberValidator) Applies(source interface{}, kind reflect.Kind) bool { + switch source.(type) { + case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header: + isInt := kind >= reflect.Int && kind <= reflect.Uint64 + isFloat := kind == reflect.Float32 || kind == reflect.Float64 + r := isInt || isFloat + debugLog("schema props validator for %q applies %t for %T (kind: %v) isInt=%t, isFloat=%t\n", n.Path, r, source, kind, isInt, isFloat) + return r + } + debugLog("schema props validator for %q applies %t for %T (kind: %v)\n", n.Path, false, source, kind) + return false +} + +// Validate provides a validator for generic JSON numbers, +// +// By default, numbers are internally represented as float64. +// Formats float, or float32 may alter this behavior by mapping to float32. +// A special validation process is followed for integers, with optional "format": +// this is an attempt to provide a validation with native types. +// +// NOTE: since the constraint specified (boundary, multipleOf) is unmarshalled +// as float64, loss of information remains possible (e.g. on very large integers). +// +// Since this value directly comes from the unmarshalling, it is not possible +// at this stage of processing to check further and guarantee the correctness of such values. +// +// Normally, the JSON Number.MAX_SAFE_INTEGER (resp. Number.MIN_SAFE_INTEGER) +// would check we do not get such a loss. +// +// If this is the case, replace AddErrors() by AddWarnings() and IsValid() by !HasWarnings(). +// +// TODO: consider replacing boundary check errors by simple warnings. +// +// TODO: default boundaries with MAX_SAFE_INTEGER are not checked (specific to json.Number?) +func (n *numberValidator) Validate(val interface{}) *Result { + res := new(Result) + + resMultiple := new(Result) + resMinimum := new(Result) + resMaximum := new(Result) + + // Used only to attempt to validate constraint on value, + // even though value or constraint specified do not match type and format + data := valueHelp.asFloat64(val) + + // Is the provided value within the range of the specified numeric type and format? + res.AddErrors(IsValueValidAgainstRange(val, n.Type, n.Format, "Checked", n.Path)) + + if n.MultipleOf != nil { + // Is the constraint specifier within the range of the specific numeric type and format? + resMultiple.AddErrors(IsValueValidAgainstRange(*n.MultipleOf, n.Type, n.Format, "MultipleOf", n.Path)) + if resMultiple.IsValid() { + // Constraint validated with compatible types + if err := MultipleOfNativeType(n.Path, n.In, val, *n.MultipleOf); err != nil { + resMultiple.Merge(errorHelp.sErr(err)) + } + } else { + // Constraint nevertheless validated, converted as general number + if err := MultipleOf(n.Path, n.In, data, *n.MultipleOf); err != nil { + resMultiple.Merge(errorHelp.sErr(err)) + } + } + } + + if n.Maximum != nil { + // Is the constraint specifier within the range of the specific numeric type and format? + resMaximum.AddErrors(IsValueValidAgainstRange(*n.Maximum, n.Type, n.Format, "Maximum boundary", n.Path)) + if resMaximum.IsValid() { + // Constraint validated with compatible types + if err := MaximumNativeType(n.Path, n.In, val, *n.Maximum, n.ExclusiveMaximum); err != nil { + resMaximum.Merge(errorHelp.sErr(err)) + } + } else { + // Constraint nevertheless validated, converted as general number + if err := Maximum(n.Path, n.In, data, *n.Maximum, n.ExclusiveMaximum); err != nil { + resMaximum.Merge(errorHelp.sErr(err)) + } + } + } + + if n.Minimum != nil { + // Is the constraint specifier within the range of the specific numeric type and format? + resMinimum.AddErrors(IsValueValidAgainstRange(*n.Minimum, n.Type, n.Format, "Minimum boundary", n.Path)) + if resMinimum.IsValid() { + // Constraint validated with compatible types + if err := MinimumNativeType(n.Path, n.In, val, *n.Minimum, n.ExclusiveMinimum); err != nil { + resMinimum.Merge(errorHelp.sErr(err)) + } + } else { + // Constraint nevertheless validated, converted as general number + if err := Minimum(n.Path, n.In, data, *n.Minimum, n.ExclusiveMinimum); err != nil { + resMinimum.Merge(errorHelp.sErr(err)) + } + } + } + res.Merge(resMultiple, resMinimum, resMaximum) + res.Inc() + return res +} + +type stringValidator struct { + Default interface{} + Required bool + AllowEmptyValue bool + MaxLength *int64 + MinLength *int64 + Pattern string + Path string + In string +} + +func (s *stringValidator) SetPath(path string) { + s.Path = path +} + +func (s *stringValidator) Applies(source interface{}, kind reflect.Kind) bool { + switch source.(type) { + case *spec.Parameter, *spec.Schema, *spec.Items, *spec.Header: + r := kind == reflect.String + debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, r, source, kind) + return r + } + debugLog("string validator for %q applies %t for %T (kind: %v)\n", s.Path, false, source, kind) + return false +} + +func (s *stringValidator) Validate(val interface{}) *Result { + data, ok := val.(string) + if !ok { + return errorHelp.sErr(errors.InvalidType(s.Path, s.In, "string", val)) + } + + if s.Required && !s.AllowEmptyValue && (s.Default == nil || s.Default == "") { + if err := RequiredString(s.Path, s.In, data); err != nil { + return errorHelp.sErr(err) + } + } + + if s.MaxLength != nil { + if err := MaxLength(s.Path, s.In, data, *s.MaxLength); err != nil { + return errorHelp.sErr(err) + } + } + + if s.MinLength != nil { + if err := MinLength(s.Path, s.In, data, *s.MinLength); err != nil { + return errorHelp.sErr(err) + } + } + + if s.Pattern != "" { + if err := Pattern(s.Path, s.In, data, s.Pattern); err != nil { + return errorHelp.sErr(err) + } + } + return nil +} diff --git a/vendor/github.com/go-openapi/validate/values.go b/vendor/github.com/go-openapi/validate/values.go new file mode 100644 index 000000000..24606da8d --- /dev/null +++ b/vendor/github.com/go-openapi/validate/values.go @@ -0,0 +1,398 @@ +// Copyright 2015 go-swagger maintainers +// +// 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 validate + +import ( + "fmt" + "reflect" + "unicode/utf8" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// Enum validates if the data is a member of the enum +func Enum(path, in string, data interface{}, enum interface{}) *errors.Validation { + val := reflect.ValueOf(enum) + if val.Kind() != reflect.Slice { + return nil + } + + var values []interface{} + for i := 0; i < val.Len(); i++ { + ele := val.Index(i) + enumValue := ele.Interface() + if data != nil { + if reflect.DeepEqual(data, enumValue) { + return nil + } + actualType := reflect.TypeOf(enumValue) + if actualType == nil { // Safeguard. Frankly, I don't know how we may get a nil + continue + } + expectedValue := reflect.ValueOf(data) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), enumValue) { + return nil + } + } + } + values = append(values, enumValue) + } + return errors.EnumFail(path, in, data, values) +} + +// MinItems validates that there are at least n items in a slice +func MinItems(path, in string, size, min int64) *errors.Validation { + if size < min { + return errors.TooFewItems(path, in, min) + } + return nil +} + +// MaxItems validates that there are at most n items in a slice +func MaxItems(path, in string, size, max int64) *errors.Validation { + if size > max { + return errors.TooManyItems(path, in, max) + } + return nil +} + +// UniqueItems validates that the provided slice has unique elements +func UniqueItems(path, in string, data interface{}) *errors.Validation { + val := reflect.ValueOf(data) + if val.Kind() != reflect.Slice { + return nil + } + var unique []interface{} + for i := 0; i < val.Len(); i++ { + v := val.Index(i).Interface() + for _, u := range unique { + if reflect.DeepEqual(v, u) { + return errors.DuplicateItems(path, in) + } + } + unique = append(unique, v) + } + return nil +} + +// MinLength validates a string for minimum length +func MinLength(path, in, data string, minLength int64) *errors.Validation { + strLen := int64(utf8.RuneCount([]byte(data))) + if strLen < minLength { + return errors.TooShort(path, in, minLength) + } + return nil +} + +// MaxLength validates a string for maximum length +func MaxLength(path, in, data string, maxLength int64) *errors.Validation { + strLen := int64(utf8.RuneCount([]byte(data))) + if strLen > maxLength { + return errors.TooLong(path, in, maxLength) + } + return nil +} + +// Required validates an interface for requiredness +func Required(path, in string, data interface{}) *errors.Validation { + val := reflect.ValueOf(data) + if val.IsValid() { + if reflect.DeepEqual(reflect.Zero(val.Type()).Interface(), val.Interface()) { + return errors.Required(path, in) + } + return nil + } + return errors.Required(path, in) +} + +// RequiredString validates a string for requiredness +func RequiredString(path, in, data string) *errors.Validation { + if data == "" { + return errors.Required(path, in) + } + return nil +} + +// RequiredNumber validates a number for requiredness +func RequiredNumber(path, in string, data float64) *errors.Validation { + if data == 0 { + return errors.Required(path, in) + } + return nil +} + +// Pattern validates a string against a regular expression +func Pattern(path, in, data, pattern string) *errors.Validation { + re, err := compileRegexp(pattern) + if err != nil { + return errors.FailedPattern(path, in, fmt.Sprintf("%s, but pattern is invalid: %s", pattern, err.Error())) + } + if !re.MatchString(data) { + return errors.FailedPattern(path, in, pattern) + } + return nil +} + +// MaximumInt validates if a number is smaller than a given maximum +func MaximumInt(path, in string, data, max int64, exclusive bool) *errors.Validation { + if (!exclusive && data > max) || (exclusive && data >= max) { + return errors.ExceedsMaximumInt(path, in, max, exclusive) + } + return nil +} + +// MaximumUint validates if a number is smaller than a given maximum +func MaximumUint(path, in string, data, max uint64, exclusive bool) *errors.Validation { + if (!exclusive && data > max) || (exclusive && data >= max) { + return errors.ExceedsMaximumUint(path, in, max, exclusive) + } + return nil +} + +// Maximum validates if a number is smaller than a given maximum +func Maximum(path, in string, data, max float64, exclusive bool) *errors.Validation { + if (!exclusive && data > max) || (exclusive && data >= max) { + return errors.ExceedsMaximum(path, in, max, exclusive) + } + return nil +} + +// Minimum validates if a number is smaller than a given minimum +func Minimum(path, in string, data, min float64, exclusive bool) *errors.Validation { + if (!exclusive && data < min) || (exclusive && data <= min) { + return errors.ExceedsMinimum(path, in, min, exclusive) + } + return nil +} + +// MinimumInt validates if a number is smaller than a given minimum +func MinimumInt(path, in string, data, min int64, exclusive bool) *errors.Validation { + if (!exclusive && data < min) || (exclusive && data <= min) { + return errors.ExceedsMinimumInt(path, in, min, exclusive) + } + return nil +} + +// MinimumUint validates if a number is smaller than a given minimum +func MinimumUint(path, in string, data, min uint64, exclusive bool) *errors.Validation { + if (!exclusive && data < min) || (exclusive && data <= min) { + return errors.ExceedsMinimumUint(path, in, min, exclusive) + } + return nil +} + +// MultipleOf validates if the provided number is a multiple of the factor +func MultipleOf(path, in string, data, factor float64) *errors.Validation { + // multipleOf factor must be positive + if factor < 0 { + return errors.MultipleOfMustBePositive(path, in, factor) + } + var mult float64 + if factor < 1 { + mult = 1 / factor * data + } else { + mult = data / factor + } + if !swag.IsFloat64AJSONInteger(mult) { + return errors.NotMultipleOf(path, in, factor) + } + return nil +} + +// MultipleOfInt validates if the provided integer is a multiple of the factor +func MultipleOfInt(path, in string, data int64, factor int64) *errors.Validation { + // multipleOf factor must be positive + if factor < 0 { + return errors.MultipleOfMustBePositive(path, in, factor) + } + mult := data / factor + if mult*factor != data { + return errors.NotMultipleOf(path, in, factor) + } + return nil +} + +// MultipleOfUint validates if the provided unsigned integer is a multiple of the factor +func MultipleOfUint(path, in string, data, factor uint64) *errors.Validation { + mult := data / factor + if mult*factor != data { + return errors.NotMultipleOf(path, in, factor) + } + return nil +} + +// FormatOf validates if a string matches a format in the format registry +func FormatOf(path, in, format, data string, registry strfmt.Registry) *errors.Validation { + if registry == nil { + registry = strfmt.Default + } + if ok := registry.ContainsName(format); !ok { + return errors.InvalidTypeName(format) + } + if ok := registry.Validates(format, data); !ok { + return errors.InvalidType(path, in, format, data) + } + return nil +} + +// MaximumNativeType provides native type constraint validation as a facade +// to various numeric types versions of Maximum constraint check. +// +// Assumes that any possible loss conversion during conversion has been +// checked beforehand. +// +// NOTE: currently, the max value is marshalled as a float64, no matter what, +// which means there may be a loss during conversions (e.g. for very large integers) +// +// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free +func MaximumNativeType(path, in string, val interface{}, max float64, exclusive bool) *errors.Validation { + kind := reflect.ValueOf(val).Type().Kind() + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + value := valueHelp.asInt64(val) + return MaximumInt(path, in, value, int64(max), exclusive) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + value := valueHelp.asUint64(val) + if max < 0 { + return errors.ExceedsMaximum(path, in, max, exclusive) + } + return MaximumUint(path, in, value, uint64(max), exclusive) + case reflect.Float32, reflect.Float64: + fallthrough + default: + value := valueHelp.asFloat64(val) + return Maximum(path, in, value, max, exclusive) + } +} + +// MinimumNativeType provides native type constraint validation as a facade +// to various numeric types versions of Minimum constraint check. +// +// Assumes that any possible loss conversion during conversion has been +// checked beforehand. +// +// NOTE: currently, the min value is marshalled as a float64, no matter what, +// which means there may be a loss during conversions (e.g. for very large integers) +// +// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free +func MinimumNativeType(path, in string, val interface{}, min float64, exclusive bool) *errors.Validation { + kind := reflect.ValueOf(val).Type().Kind() + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + value := valueHelp.asInt64(val) + return MinimumInt(path, in, value, int64(min), exclusive) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + value := valueHelp.asUint64(val) + if min < 0 { + return nil + } + return MinimumUint(path, in, value, uint64(min), exclusive) + case reflect.Float32, reflect.Float64: + fallthrough + default: + value := valueHelp.asFloat64(val) + return Minimum(path, in, value, min, exclusive) + } +} + +// MultipleOfNativeType provides native type constraint validation as a facade +// to various numeric types version of MultipleOf constraint check. +// +// Assumes that any possible loss conversion during conversion has been +// checked beforehand. +// +// NOTE: currently, the multipleOf factor is marshalled as a float64, no matter what, +// which means there may be a loss during conversions (e.g. for very large integers) +// +// TODO: Normally, a JSON MAX_SAFE_INTEGER check would ensure conversion remains loss-free +func MultipleOfNativeType(path, in string, val interface{}, multipleOf float64) *errors.Validation { + kind := reflect.ValueOf(val).Type().Kind() + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + value := valueHelp.asInt64(val) + return MultipleOfInt(path, in, value, int64(multipleOf)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + value := valueHelp.asUint64(val) + return MultipleOfUint(path, in, value, uint64(multipleOf)) + case reflect.Float32, reflect.Float64: + fallthrough + default: + value := valueHelp.asFloat64(val) + return MultipleOf(path, in, value, multipleOf) + } +} + +// IsValueValidAgainstRange checks that a numeric value is compatible with +// the range defined by Type and Format, that is, may be converted without loss. +// +// NOTE: this check is about type capacity and not formal verification such as: 1.0 != 1L +func IsValueValidAgainstRange(val interface{}, typeName, format, prefix, path string) error { + kind := reflect.ValueOf(val).Type().Kind() + + // What is the string representation of val + stringRep := "" + switch kind { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + stringRep = swag.FormatUint64(valueHelp.asUint64(val)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + stringRep = swag.FormatInt64(valueHelp.asInt64(val)) + case reflect.Float32, reflect.Float64: + stringRep = swag.FormatFloat64(valueHelp.asFloat64(val)) + default: + return fmt.Errorf("%s value number range checking called with invalid (non numeric) val type in %s", prefix, path) + } + + var errVal error + + switch typeName { + case "integer": + switch format { + case "int32": + _, errVal = swag.ConvertInt32(stringRep) + case "uint32": + _, errVal = swag.ConvertUint32(stringRep) + case "uint64": + _, errVal = swag.ConvertUint64(stringRep) + case "int64": + fallthrough + default: + _, errVal = swag.ConvertInt64(stringRep) + } + case "number": + fallthrough + default: + switch format { + case "float", "float32": + _, errVal = swag.ConvertFloat32(stringRep) + case "double", "float64": + fallthrough + default: + // No check can be performed here since + // no number beyond float64 is supported + } + } + if errVal != nil { // We don't report the actual errVal from strconv + if format != "" { + errVal = fmt.Errorf("%s value must be of type %s with format %s in %s", prefix, typeName, format, path) + } else { + errVal = fmt.Errorf("%s value must be of type %s (default format) in %s", prefix, typeName, path) + } + } + return errVal +} diff --git a/vendor/github.com/go-playground/locales/.gitignore b/vendor/github.com/go-playground/locales/.gitignore deleted file mode 100644 index daf913b1b..000000000 --- a/vendor/github.com/go-playground/locales/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/go-playground/locales/LICENSE b/vendor/github.com/go-playground/locales/LICENSE deleted file mode 100644 index 75854ac4f..000000000 --- a/vendor/github.com/go-playground/locales/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Go Playground - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/go-playground/locales/README.md b/vendor/github.com/go-playground/locales/README.md deleted file mode 100644 index 43329f8d3..000000000 --- a/vendor/github.com/go-playground/locales/README.md +++ /dev/null @@ -1,172 +0,0 @@ -## locales -![Project status](https://img.shields.io/badge/version-0.12.1-green.svg) -[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/locales/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/locales) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/locales)](https://goreportcard.com/report/github.com/go-playground/locales) -[![GoDoc](https://godoc.org/github.com/go-playground/locales?status.svg)](https://godoc.org/github.com/go-playground/locales) -![License](https://img.shields.io/dub/l/vibe-d.svg) -[![Gitter](https://badges.gitter.im/go-playground/locales.svg)](https://gitter.im/go-playground/locales?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -Locales is a set of locales generated from the [Unicode CLDR Project](http://cldr.unicode.org/) which can be used independently or within -an i18n package; these were built for use with, but not exclusive to, [Universal Translator](https://github.com/go-playground/universal-translator). - -Features --------- -- [x] Rules generated from the latest [CLDR](http://cldr.unicode.org/index/downloads) data, v31.0.1 -- [x] Contains Cardinal, Ordinal and Range Plural Rules -- [x] Contains Month, Weekday and Timezone translations built in -- [x] Contains Date & Time formatting functions -- [x] Contains Number, Currency, Accounting and Percent formatting functions -- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) - -Full Tests --------------------- -I could sure use your help adding tests for every locale, it is a huge undertaking and I just don't have the free time to do it all at the moment; -any help would be **greatly appreciated!!!!** please see [issue](https://github.com/go-playground/locales/issues/1) for details. - -Installation ------------ - -Use go get - -```shell -go get github.com/go-playground/locales -``` - -NOTES --------- -You'll notice most return types are []byte, this is because most of the time the results will be concatenated with a larger body -of text and can avoid some allocations if already appending to a byte array, otherwise just cast as string. - -Usage -------- -```go -package main - -import ( - "fmt" - "time" - - "github.com/go-playground/locales/currency" - "github.com/go-playground/locales/en_CA" -) - -func main() { - - loc, _ := time.LoadLocation("America/Toronto") - datetime := time.Date(2016, 02, 03, 9, 0, 1, 0, loc) - - l := en_CA.New() - - // Dates - fmt.Println(l.FmtDateFull(datetime)) - fmt.Println(l.FmtDateLong(datetime)) - fmt.Println(l.FmtDateMedium(datetime)) - fmt.Println(l.FmtDateShort(datetime)) - - // Times - fmt.Println(l.FmtTimeFull(datetime)) - fmt.Println(l.FmtTimeLong(datetime)) - fmt.Println(l.FmtTimeMedium(datetime)) - fmt.Println(l.FmtTimeShort(datetime)) - - // Months Wide - fmt.Println(l.MonthWide(time.January)) - fmt.Println(l.MonthWide(time.February)) - fmt.Println(l.MonthWide(time.March)) - // ... - - // Months Abbreviated - fmt.Println(l.MonthAbbreviated(time.January)) - fmt.Println(l.MonthAbbreviated(time.February)) - fmt.Println(l.MonthAbbreviated(time.March)) - // ... - - // Months Narrow - fmt.Println(l.MonthNarrow(time.January)) - fmt.Println(l.MonthNarrow(time.February)) - fmt.Println(l.MonthNarrow(time.March)) - // ... - - // Weekdays Wide - fmt.Println(l.WeekdayWide(time.Sunday)) - fmt.Println(l.WeekdayWide(time.Monday)) - fmt.Println(l.WeekdayWide(time.Tuesday)) - // ... - - // Weekdays Abbreviated - fmt.Println(l.WeekdayAbbreviated(time.Sunday)) - fmt.Println(l.WeekdayAbbreviated(time.Monday)) - fmt.Println(l.WeekdayAbbreviated(time.Tuesday)) - // ... - - // Weekdays Short - fmt.Println(l.WeekdayShort(time.Sunday)) - fmt.Println(l.WeekdayShort(time.Monday)) - fmt.Println(l.WeekdayShort(time.Tuesday)) - // ... - - // Weekdays Narrow - fmt.Println(l.WeekdayNarrow(time.Sunday)) - fmt.Println(l.WeekdayNarrow(time.Monday)) - fmt.Println(l.WeekdayNarrow(time.Tuesday)) - // ... - - var f64 float64 - - f64 = -10356.4523 - - // Number - fmt.Println(l.FmtNumber(f64, 2)) - - // Currency - fmt.Println(l.FmtCurrency(f64, 2, currency.CAD)) - fmt.Println(l.FmtCurrency(f64, 2, currency.USD)) - - // Accounting - fmt.Println(l.FmtAccounting(f64, 2, currency.CAD)) - fmt.Println(l.FmtAccounting(f64, 2, currency.USD)) - - f64 = 78.12 - - // Percent - fmt.Println(l.FmtPercent(f64, 0)) - - // Plural Rules for locale, so you know what rules you must cover - fmt.Println(l.PluralsCardinal()) - fmt.Println(l.PluralsOrdinal()) - - // Cardinal Plural Rules - fmt.Println(l.CardinalPluralRule(1, 0)) - fmt.Println(l.CardinalPluralRule(1.0, 0)) - fmt.Println(l.CardinalPluralRule(1.0, 1)) - fmt.Println(l.CardinalPluralRule(3, 0)) - - // Ordinal Plural Rules - fmt.Println(l.OrdinalPluralRule(21, 0)) // 21st - fmt.Println(l.OrdinalPluralRule(22, 0)) // 22nd - fmt.Println(l.OrdinalPluralRule(33, 0)) // 33rd - fmt.Println(l.OrdinalPluralRule(34, 0)) // 34th - - // Range Plural Rules - fmt.Println(l.RangePluralRule(1, 0, 1, 0)) // 1-1 - fmt.Println(l.RangePluralRule(1, 0, 2, 0)) // 1-2 - fmt.Println(l.RangePluralRule(5, 0, 8, 0)) // 5-8 -} -``` - -NOTES: -------- -These rules were generated from the [Unicode CLDR Project](http://cldr.unicode.org/), if you encounter any issues -I strongly encourage contributing to the CLDR project to get the locale information corrected and the next time -these locales are regenerated the fix will come with. - -I do however realize that time constraints are often important and so there are two options: - -1. Create your own locale, copy, paste and modify, and ensure it complies with the `Translator` interface. -2. Add an exception in the locale generation code directly and once regenerated, fix will be in place. - -Please to not make fixes inside the locale files, they WILL get overwritten when the locales are regenerated. - -License ------- -Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/locales/currency/currency.go b/vendor/github.com/go-playground/locales/currency/currency.go deleted file mode 100644 index cdaba596b..000000000 --- a/vendor/github.com/go-playground/locales/currency/currency.go +++ /dev/null @@ -1,308 +0,0 @@ -package currency - -// Type is the currency type associated with the locales currency enum -type Type int - -// locale currencies -const ( - ADP Type = iota - AED - AFA - AFN - ALK - ALL - AMD - ANG - AOA - AOK - AON - AOR - ARA - ARL - ARM - ARP - ARS - ATS - AUD - AWG - AZM - AZN - BAD - BAM - BAN - BBD - BDT - BEC - BEF - BEL - BGL - BGM - BGN - BGO - BHD - BIF - BMD - BND - BOB - BOL - BOP - BOV - BRB - BRC - BRE - BRL - BRN - BRR - BRZ - BSD - BTN - BUK - BWP - BYB - BYN - BYR - BZD - CAD - CDF - CHE - CHF - CHW - CLE - CLF - CLP - CNH - CNX - CNY - COP - COU - CRC - CSD - CSK - CUC - CUP - CVE - CYP - CZK - DDM - DEM - DJF - DKK - DOP - DZD - ECS - ECV - EEK - EGP - ERN - ESA - ESB - ESP - ETB - EUR - FIM - FJD - FKP - FRF - GBP - GEK - GEL - GHC - GHS - GIP - GMD - GNF - GNS - GQE - GRD - GTQ - GWE - GWP - GYD - HKD - HNL - HRD - HRK - HTG - HUF - IDR - IEP - ILP - ILR - ILS - INR - IQD - IRR - ISJ - ISK - ITL - JMD - JOD - JPY - KES - KGS - KHR - KMF - KPW - KRH - KRO - KRW - KWD - KYD - KZT - LAK - LBP - LKR - LRD - LSL - LTL - LTT - LUC - LUF - LUL - LVL - LVR - LYD - MAD - MAF - MCF - MDC - MDL - MGA - MGF - MKD - MKN - MLF - MMK - MNT - MOP - MRO - MTL - MTP - MUR - MVP - MVR - MWK - MXN - MXP - MXV - MYR - MZE - MZM - MZN - NAD - NGN - NIC - NIO - NLG - NOK - NPR - NZD - OMR - PAB - PEI - PEN - PES - PGK - PHP - PKR - PLN - PLZ - PTE - PYG - QAR - RHD - ROL - RON - RSD - RUB - RUR - RWF - SAR - SBD - SCR - SDD - SDG - SDP - SEK - SGD - SHP - SIT - SKK - SLL - SOS - SRD - SRG - SSP - STD - STN - SUR - SVC - SYP - SZL - THB - TJR - TJS - TMM - TMT - TND - TOP - TPE - TRL - TRY - TTD - TWD - TZS - UAH - UAK - UGS - UGX - USD - USN - USS - UYI - UYP - UYU - UZS - VEB - VEF - VND - VNN - VUV - WST - XAF - XAG - XAU - XBA - XBB - XBC - XBD - XCD - XDR - XEU - XFO - XFU - XOF - XPD - XPF - XPT - XRE - XSU - XTS - XUA - XXX - YDD - YER - YUD - YUM - YUN - YUR - ZAL - ZAR - ZMK - ZMW - ZRN - ZRZ - ZWD - ZWL - ZWR -) diff --git a/vendor/github.com/go-playground/locales/logo.png b/vendor/github.com/go-playground/locales/logo.png deleted file mode 100644 index 3038276e6..000000000 Binary files a/vendor/github.com/go-playground/locales/logo.png and /dev/null differ diff --git a/vendor/github.com/go-playground/locales/rules.go b/vendor/github.com/go-playground/locales/rules.go deleted file mode 100644 index 920290014..000000000 --- a/vendor/github.com/go-playground/locales/rules.go +++ /dev/null @@ -1,293 +0,0 @@ -package locales - -import ( - "strconv" - "time" - - "github.com/go-playground/locales/currency" -) - -// // ErrBadNumberValue is returned when the number passed for -// // plural rule determination cannot be parsed -// type ErrBadNumberValue struct { -// NumberValue string -// InnerError error -// } - -// // Error returns ErrBadNumberValue error string -// func (e *ErrBadNumberValue) Error() string { -// return fmt.Sprintf("Invalid Number Value '%s' %s", e.NumberValue, e.InnerError) -// } - -// var _ error = new(ErrBadNumberValue) - -// PluralRule denotes the type of plural rules -type PluralRule int - -// PluralRule's -const ( - PluralRuleUnknown PluralRule = iota - PluralRuleZero // zero - PluralRuleOne // one - singular - PluralRuleTwo // two - dual - PluralRuleFew // few - paucal - PluralRuleMany // many - also used for fractions if they have a separate class - PluralRuleOther // other - required—general plural form—also used if the language only has a single form -) - -const ( - pluralsString = "UnknownZeroOneTwoFewManyOther" -) - -// Translator encapsulates an instance of a locale -// NOTE: some values are returned as a []byte just in case the caller -// wishes to add more and can help avoid allocations; otherwise just cast as string -type Translator interface { - - // The following Functions are for overriding, debugging or developing - // with a Translator Locale - - // Locale returns the string value of the translator - Locale() string - - // returns an array of cardinal plural rules associated - // with this translator - PluralsCardinal() []PluralRule - - // returns an array of ordinal plural rules associated - // with this translator - PluralsOrdinal() []PluralRule - - // returns an array of range plural rules associated - // with this translator - PluralsRange() []PluralRule - - // returns the cardinal PluralRule given 'num' and digits/precision of 'v' for locale - CardinalPluralRule(num float64, v uint64) PluralRule - - // returns the ordinal PluralRule given 'num' and digits/precision of 'v' for locale - OrdinalPluralRule(num float64, v uint64) PluralRule - - // returns the ordinal PluralRule given 'num1', 'num2' and digits/precision of 'v1' and 'v2' for locale - RangePluralRule(num1 float64, v1 uint64, num2 float64, v2 uint64) PluralRule - - // returns the locales abbreviated month given the 'month' provided - MonthAbbreviated(month time.Month) string - - // returns the locales abbreviated months - MonthsAbbreviated() []string - - // returns the locales narrow month given the 'month' provided - MonthNarrow(month time.Month) string - - // returns the locales narrow months - MonthsNarrow() []string - - // returns the locales wide month given the 'month' provided - MonthWide(month time.Month) string - - // returns the locales wide months - MonthsWide() []string - - // returns the locales abbreviated weekday given the 'weekday' provided - WeekdayAbbreviated(weekday time.Weekday) string - - // returns the locales abbreviated weekdays - WeekdaysAbbreviated() []string - - // returns the locales narrow weekday given the 'weekday' provided - WeekdayNarrow(weekday time.Weekday) string - - // WeekdaysNarrowreturns the locales narrow weekdays - WeekdaysNarrow() []string - - // returns the locales short weekday given the 'weekday' provided - WeekdayShort(weekday time.Weekday) string - - // returns the locales short weekdays - WeekdaysShort() []string - - // returns the locales wide weekday given the 'weekday' provided - WeekdayWide(weekday time.Weekday) string - - // returns the locales wide weekdays - WeekdaysWide() []string - - // The following Functions are common Formatting functionsfor the Translator's Locale - - // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' - FmtNumber(num float64, v uint64) string - - // returns 'num' with digits/precision of 'v' for locale and handles both Whole and Real numbers based on 'v' - // NOTE: 'num' passed into FmtPercent is assumed to be in percent already - FmtPercent(num float64, v uint64) string - - // returns the currency representation of 'num' with digits/precision of 'v' for locale - FmtCurrency(num float64, v uint64, currency currency.Type) string - - // returns the currency representation of 'num' with digits/precision of 'v' for locale - // in accounting notation. - FmtAccounting(num float64, v uint64, currency currency.Type) string - - // returns the short date representation of 't' for locale - FmtDateShort(t time.Time) string - - // returns the medium date representation of 't' for locale - FmtDateMedium(t time.Time) string - - // returns the long date representation of 't' for locale - FmtDateLong(t time.Time) string - - // returns the full date representation of 't' for locale - FmtDateFull(t time.Time) string - - // returns the short time representation of 't' for locale - FmtTimeShort(t time.Time) string - - // returns the medium time representation of 't' for locale - FmtTimeMedium(t time.Time) string - - // returns the long time representation of 't' for locale - FmtTimeLong(t time.Time) string - - // returns the full time representation of 't' for locale - FmtTimeFull(t time.Time) string -} - -// String returns the string value of PluralRule -func (p PluralRule) String() string { - - switch p { - case PluralRuleZero: - return pluralsString[7:11] - case PluralRuleOne: - return pluralsString[11:14] - case PluralRuleTwo: - return pluralsString[14:17] - case PluralRuleFew: - return pluralsString[17:20] - case PluralRuleMany: - return pluralsString[20:24] - case PluralRuleOther: - return pluralsString[24:] - default: - return pluralsString[:7] - } -} - -// -// Precision Notes: -// -// must specify a precision >= 0, and here is why https://play.golang.org/p/LyL90U0Vyh -// -// v := float64(3.141) -// i := float64(int64(v)) -// -// fmt.Println(v - i) -// -// or -// -// s := strconv.FormatFloat(v-i, 'f', -1, 64) -// fmt.Println(s) -// -// these will not print what you'd expect: 0.14100000000000001 -// and so this library requires a precision to be specified, or -// inaccurate plural rules could be applied. -// -// -// -// n - absolute value of the source number (integer and decimals). -// i - integer digits of n. -// v - number of visible fraction digits in n, with trailing zeros. -// w - number of visible fraction digits in n, without trailing zeros. -// f - visible fractional digits in n, with trailing zeros. -// t - visible fractional digits in n, without trailing zeros. -// -// -// Func(num float64, v uint64) // v = digits/precision and prevents -1 as a special case as this can lead to very unexpected behaviour, see precision note's above. -// -// n := math.Abs(num) -// i := int64(n) -// v := v -// -// -// w := strconv.FormatFloat(num-float64(i), 'f', int(v), 64) // then parse backwards on string until no more zero's.... -// f := strconv.FormatFloat(n, 'f', int(v), 64) // then turn everything after decimal into an int64 -// t := strconv.FormatFloat(n, 'f', int(v), 64) // then parse backwards on string until no more zero's.... -// -// -// -// General Inclusion Rules -// - v will always be available inherently -// - all require n -// - w requires i -// - -// W returns the number of visible fraction digits in N, without trailing zeros. -func W(n float64, v uint64) (w int64) { - - s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) - - // with either be '0' or '0.xxxx', so if 1 then w will be zero - // otherwise need to parse - if len(s) != 1 { - - s = s[2:] - end := len(s) + 1 - - for i := end; i >= 0; i-- { - if s[i] != '0' { - end = i + 1 - break - } - } - - w = int64(len(s[:end])) - } - - return -} - -// F returns the visible fractional digits in N, with trailing zeros. -func F(n float64, v uint64) (f int64) { - - s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) - - // with either be '0' or '0.xxxx', so if 1 then f will be zero - // otherwise need to parse - if len(s) != 1 { - - // ignoring error, because it can't fail as we generated - // the string internally from a real number - f, _ = strconv.ParseInt(s[2:], 10, 64) - } - - return -} - -// T returns the visible fractional digits in N, without trailing zeros. -func T(n float64, v uint64) (t int64) { - - s := strconv.FormatFloat(n-float64(int64(n)), 'f', int(v), 64) - - // with either be '0' or '0.xxxx', so if 1 then t will be zero - // otherwise need to parse - if len(s) != 1 { - - s = s[2:] - end := len(s) + 1 - - for i := end; i >= 0; i-- { - if s[i] != '0' { - end = i + 1 - break - } - } - - // ignoring error, because it can't fail as we generated - // the string internally from a real number - t, _ = strconv.ParseInt(s[:end], 10, 64) - } - - return -} diff --git a/vendor/github.com/go-playground/universal-translator/.gitignore b/vendor/github.com/go-playground/universal-translator/.gitignore deleted file mode 100644 index 26617857e..000000000 --- a/vendor/github.com/go-playground/universal-translator/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof \ No newline at end of file diff --git a/vendor/github.com/go-playground/universal-translator/LICENSE b/vendor/github.com/go-playground/universal-translator/LICENSE deleted file mode 100644 index 8d8aba15b..000000000 --- a/vendor/github.com/go-playground/universal-translator/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Go Playground - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/go-playground/universal-translator/README.md b/vendor/github.com/go-playground/universal-translator/README.md deleted file mode 100644 index 24aef1585..000000000 --- a/vendor/github.com/go-playground/universal-translator/README.md +++ /dev/null @@ -1,90 +0,0 @@ -## universal-translator - -![Project status](https://img.shields.io/badge/version-0.16.0-green.svg) -[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/universal-translator/branches/master/badge.svg)](https://semaphoreci.com/joeybloggs/universal-translator) -[![Coverage Status](https://coveralls.io/repos/github/go-playground/universal-translator/badge.svg)](https://coveralls.io/github/go-playground/universal-translator) -[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/universal-translator)](https://goreportcard.com/report/github.com/go-playground/universal-translator) -[![GoDoc](https://godoc.org/github.com/go-playground/universal-translator?status.svg)](https://godoc.org/github.com/go-playground/universal-translator) -![License](https://img.shields.io/dub/l/vibe-d.svg) -[![Gitter](https://badges.gitter.im/go-playground/universal-translator.svg)](https://gitter.im/go-playground/universal-translator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) - -Universal Translator is an i18n Translator for Go/Golang using CLDR data + pluralization rules - -Why another i18n library? --------------------------- -Because none of the plural rules seem to be correct out there, including the previous implementation of this package, -so I took it upon myself to create [locales](https://github.com/go-playground/locales) for everyone to use; this package -is a thin wrapper around [locales](https://github.com/go-playground/locales) in order to store and translate text for -use in your applications. - -Features --------- -- [x] Rules generated from the [CLDR](http://cldr.unicode.org/index/downloads) data, v30.0.3 -- [x] Contains Cardinal, Ordinal and Range Plural Rules -- [x] Contains Month, Weekday and Timezone translations built in -- [x] Contains Date & Time formatting functions -- [x] Contains Number, Currency, Accounting and Percent formatting functions -- [x] Supports the "Gregorian" calendar only ( my time isn't unlimited, had to draw the line somewhere ) -- [x] Support loading translations from files -- [x] Exporting translations to file(s), mainly for getting them professionally translated -- [ ] Code Generation for translation files -> Go code.. i.e. after it has been professionally translated -- [ ] Tests for all languages, I need help with this, please see [here](https://github.com/go-playground/locales/issues/1) - -Installation ------------ - -Use go get - -```shell -go get github.com/go-playground/universal-translator -``` - -Usage & Documentation -------- - -Please see https://godoc.org/github.com/go-playground/universal-translator for usage docs - -##### Examples: - -- [Basic](https://github.com/go-playground/universal-translator/tree/master/examples/basic) -- [Full - no files](https://github.com/go-playground/universal-translator/tree/master/examples/full-no-files) -- [Full - with files](https://github.com/go-playground/universal-translator/tree/master/examples/full-with-files) - -File formatting --------------- -All types, Plain substitution, Cardinal, Ordinal and Range translations can all be contained withing the same file(s); -they are only separated for easy viewing. - -##### Examples: - -- [Formats](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats) - -##### Basic Makeup -NOTE: not all fields are needed for all translation types, see [examples](https://github.com/go-playground/universal-translator/tree/master/examples/file-formats) -```json -{ - "locale": "en", - "key": "days-left", - "trans": "You have {0} day left.", - "type": "Cardinal", - "rule": "One", - "override": false -} -``` -|Field|Description| -|---|---| -|locale|The locale for which the translation is for.| -|key|The translation key that will be used to store and lookup each translation; normally it is a string or integer.| -|trans|The actual translation text.| -|type|The type of translation Cardinal, Ordinal, Range or "" for a plain substitution(not required to be defined if plain used)| -|rule|The plural rule for which the translation is for eg. One, Two, Few, Many or Other.(not required to be defined if plain used)| -|override|If you wish to override an existing translation that has already been registered, set this to 'true'. 99% of the time there is no need to define it.| - -Help With Tests ---------------- -To anyone interesting in helping or contributing, I sure could use some help creating tests for each language. -Please see issue [here](https://github.com/go-playground/locales/issues/1) for details. - -License ------- -Distributed under MIT License, please see license file in code for more details. diff --git a/vendor/github.com/go-playground/universal-translator/errors.go b/vendor/github.com/go-playground/universal-translator/errors.go deleted file mode 100644 index 38b163b62..000000000 --- a/vendor/github.com/go-playground/universal-translator/errors.go +++ /dev/null @@ -1,148 +0,0 @@ -package ut - -import ( - "errors" - "fmt" - - "github.com/go-playground/locales" -) - -var ( - // ErrUnknowTranslation indicates the translation could not be found - ErrUnknowTranslation = errors.New("Unknown Translation") -) - -var _ error = new(ErrConflictingTranslation) -var _ error = new(ErrRangeTranslation) -var _ error = new(ErrOrdinalTranslation) -var _ error = new(ErrCardinalTranslation) -var _ error = new(ErrMissingPluralTranslation) -var _ error = new(ErrExistingTranslator) - -// ErrExistingTranslator is the error representing a conflicting translator -type ErrExistingTranslator struct { - locale string -} - -// Error returns ErrExistingTranslator's internal error text -func (e *ErrExistingTranslator) Error() string { - return fmt.Sprintf("error: conflicting translator for locale '%s'", e.locale) -} - -// ErrConflictingTranslation is the error representing a conflicting translation -type ErrConflictingTranslation struct { - locale string - key interface{} - rule locales.PluralRule - text string -} - -// Error returns ErrConflictingTranslation's internal error text -func (e *ErrConflictingTranslation) Error() string { - - if _, ok := e.key.(string); !ok { - return fmt.Sprintf("error: conflicting key '%#v' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) - } - - return fmt.Sprintf("error: conflicting key '%s' rule '%s' with text '%s' for locale '%s', value being ignored", e.key, e.rule, e.text, e.locale) -} - -// ErrRangeTranslation is the error representing a range translation error -type ErrRangeTranslation struct { - text string -} - -// Error returns ErrRangeTranslation's internal error text -func (e *ErrRangeTranslation) Error() string { - return e.text -} - -// ErrOrdinalTranslation is the error representing an ordinal translation error -type ErrOrdinalTranslation struct { - text string -} - -// Error returns ErrOrdinalTranslation's internal error text -func (e *ErrOrdinalTranslation) Error() string { - return e.text -} - -// ErrCardinalTranslation is the error representing a cardinal translation error -type ErrCardinalTranslation struct { - text string -} - -// Error returns ErrCardinalTranslation's internal error text -func (e *ErrCardinalTranslation) Error() string { - return e.text -} - -// ErrMissingPluralTranslation is the error signifying a missing translation given -// the locales plural rules. -type ErrMissingPluralTranslation struct { - locale string - key interface{} - rule locales.PluralRule - translationType string -} - -// Error returns ErrMissingPluralTranslation's internal error text -func (e *ErrMissingPluralTranslation) Error() string { - - if _, ok := e.key.(string); !ok { - return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%#v' and locale '%s'", e.translationType, e.rule, e.key, e.locale) - } - - return fmt.Sprintf("error: missing '%s' plural rule '%s' for translation with key '%s' and locale '%s'", e.translationType, e.rule, e.key, e.locale) -} - -// ErrMissingBracket is the error representing a missing bracket in a translation -// eg. This is a {0 <-- missing ending '}' -type ErrMissingBracket struct { - locale string - key interface{} - text string -} - -// Error returns ErrMissingBracket error message -func (e *ErrMissingBracket) Error() string { - return fmt.Sprintf("error: missing bracket '{}', in translation. locale: '%s' key: '%v' text: '%s'", e.locale, e.key, e.text) -} - -// ErrBadParamSyntax is the error representing a bad parameter definition in a translation -// eg. This is a {must-be-int} -type ErrBadParamSyntax struct { - locale string - param string - key interface{} - text string -} - -// Error returns ErrBadParamSyntax error message -func (e *ErrBadParamSyntax) Error() string { - return fmt.Sprintf("error: bad parameter syntax, missing parameter '%s' in translation. locale: '%s' key: '%v' text: '%s'", e.param, e.locale, e.key, e.text) -} - -// import/export errors - -// ErrMissingLocale is the error representing an expected locale that could -// not be found aka locale not registered with the UniversalTranslator Instance -type ErrMissingLocale struct { - locale string -} - -// Error returns ErrMissingLocale's internal error text -func (e *ErrMissingLocale) Error() string { - return fmt.Sprintf("error: locale '%s' not registered.", e.locale) -} - -// ErrBadPluralDefinition is the error representing an incorrect plural definition -// usually found within translations defined within files during the import process. -type ErrBadPluralDefinition struct { - tl translation -} - -// Error returns ErrBadPluralDefinition's internal error text -func (e *ErrBadPluralDefinition) Error() string { - return fmt.Sprintf("error: bad plural definition '%#v'", e.tl) -} diff --git a/vendor/github.com/go-playground/universal-translator/import_export.go b/vendor/github.com/go-playground/universal-translator/import_export.go deleted file mode 100644 index 7bd76f26b..000000000 --- a/vendor/github.com/go-playground/universal-translator/import_export.go +++ /dev/null @@ -1,274 +0,0 @@ -package ut - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "io" - - "github.com/go-playground/locales" -) - -type translation struct { - Locale string `json:"locale"` - Key interface{} `json:"key"` // either string or integer - Translation string `json:"trans"` - PluralType string `json:"type,omitempty"` - PluralRule string `json:"rule,omitempty"` - OverrideExisting bool `json:"override,omitempty"` -} - -const ( - cardinalType = "Cardinal" - ordinalType = "Ordinal" - rangeType = "Range" -) - -// ImportExportFormat is the format of the file import or export -type ImportExportFormat uint8 - -// supported Export Formats -const ( - FormatJSON ImportExportFormat = iota -) - -// Export writes the translations out to a file on disk. -// -// NOTE: this currently only works with string or int translations keys. -func (t *UniversalTranslator) Export(format ImportExportFormat, dirname string) error { - - _, err := os.Stat(dirname) - fmt.Println(dirname, err, os.IsNotExist(err)) - if err != nil { - - if !os.IsNotExist(err) { - return err - } - - if err = os.MkdirAll(dirname, 0744); err != nil { - return err - } - } - - // build up translations - var trans []translation - var b []byte - var ext string - - for _, locale := range t.translators { - - for k, v := range locale.(*translator).translations { - trans = append(trans, translation{ - Locale: locale.Locale(), - Key: k, - Translation: v.text, - }) - } - - for k, pluralTrans := range locale.(*translator).cardinalTanslations { - - for i, plural := range pluralTrans { - - // leave enough for all plural rules - // but not all are set for all languages. - if plural == nil { - continue - } - - trans = append(trans, translation{ - Locale: locale.Locale(), - Key: k.(string), - Translation: plural.text, - PluralType: cardinalType, - PluralRule: locales.PluralRule(i).String(), - }) - } - } - - for k, pluralTrans := range locale.(*translator).ordinalTanslations { - - for i, plural := range pluralTrans { - - // leave enough for all plural rules - // but not all are set for all languages. - if plural == nil { - continue - } - - trans = append(trans, translation{ - Locale: locale.Locale(), - Key: k.(string), - Translation: plural.text, - PluralType: ordinalType, - PluralRule: locales.PluralRule(i).String(), - }) - } - } - - for k, pluralTrans := range locale.(*translator).rangeTanslations { - - for i, plural := range pluralTrans { - - // leave enough for all plural rules - // but not all are set for all languages. - if plural == nil { - continue - } - - trans = append(trans, translation{ - Locale: locale.Locale(), - Key: k.(string), - Translation: plural.text, - PluralType: rangeType, - PluralRule: locales.PluralRule(i).String(), - }) - } - } - - switch format { - case FormatJSON: - b, err = json.MarshalIndent(trans, "", " ") - ext = ".json" - } - - if err != nil { - return err - } - - err = ioutil.WriteFile(filepath.Join(dirname, fmt.Sprintf("%s%s", locale.Locale(), ext)), b, 0644) - if err != nil { - return err - } - - trans = trans[0:0] - } - - return nil -} - -// Import reads the translations out of a file or directory on disk. -// -// NOTE: this currently only works with string or int translations keys. -func (t *UniversalTranslator) Import(format ImportExportFormat, dirnameOrFilename string) error { - - fi, err := os.Stat(dirnameOrFilename) - if err != nil { - return err - } - - processFn := func(filename string) error { - - f, err := os.Open(filename) - if err != nil { - return err - } - defer f.Close() - - return t.ImportByReader(format, f) - } - - if !fi.IsDir() { - return processFn(dirnameOrFilename) - } - - // recursively go through directory - walker := func(path string, info os.FileInfo, err error) error { - - if info.IsDir() { - return nil - } - - switch format { - case FormatJSON: - // skip non JSON files - if filepath.Ext(info.Name()) != ".json" { - return nil - } - } - - return processFn(path) - } - - return filepath.Walk(dirnameOrFilename, walker) -} - -// ImportByReader imports the the translations found within the contents read from the supplied reader. -// -// NOTE: generally used when assets have been embedded into the binary and are already in memory. -func (t *UniversalTranslator) ImportByReader(format ImportExportFormat, reader io.Reader) error { - - b, err := ioutil.ReadAll(reader) - if err != nil { - return err - } - - var trans []translation - - switch format { - case FormatJSON: - err = json.Unmarshal(b, &trans) - } - - if err != nil { - return err - } - - for _, tl := range trans { - - locale, found := t.FindTranslator(tl.Locale) - if !found { - return &ErrMissingLocale{locale: tl.Locale} - } - - pr := stringToPR(tl.PluralRule) - - if pr == locales.PluralRuleUnknown { - - err = locale.Add(tl.Key, tl.Translation, tl.OverrideExisting) - if err != nil { - return err - } - - continue - } - - switch tl.PluralType { - case cardinalType: - err = locale.AddCardinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) - case ordinalType: - err = locale.AddOrdinal(tl.Key, tl.Translation, pr, tl.OverrideExisting) - case rangeType: - err = locale.AddRange(tl.Key, tl.Translation, pr, tl.OverrideExisting) - default: - return &ErrBadPluralDefinition{tl: tl} - } - - if err != nil { - return err - } - } - - return nil -} - -func stringToPR(s string) locales.PluralRule { - - switch s { - case "One": - return locales.PluralRuleOne - case "Two": - return locales.PluralRuleTwo - case "Few": - return locales.PluralRuleFew - case "Many": - return locales.PluralRuleMany - case "Other": - return locales.PluralRuleOther - default: - return locales.PluralRuleUnknown - } - -} diff --git a/vendor/github.com/go-playground/universal-translator/logo.png b/vendor/github.com/go-playground/universal-translator/logo.png deleted file mode 100644 index a37aa8c0c..000000000 Binary files a/vendor/github.com/go-playground/universal-translator/logo.png and /dev/null differ diff --git a/vendor/github.com/go-playground/universal-translator/translator.go b/vendor/github.com/go-playground/universal-translator/translator.go deleted file mode 100644 index cfafce8a0..000000000 --- a/vendor/github.com/go-playground/universal-translator/translator.go +++ /dev/null @@ -1,420 +0,0 @@ -package ut - -import ( - "fmt" - "strconv" - "strings" - - "github.com/go-playground/locales" -) - -const ( - paramZero = "{0}" - paramOne = "{1}" - unknownTranslation = "" -) - -// Translator is universal translators -// translator instance which is a thin wrapper -// around locales.Translator instance providing -// some extra functionality -type Translator interface { - locales.Translator - - // adds a normal translation for a particular language/locale - // {#} is the only replacement type accepted and are ad infinitum - // eg. one: '{0} day left' other: '{0} days left' - Add(key interface{}, text string, override bool) error - - // adds a cardinal plural translation for a particular language/locale - // {0} is the only replacement type accepted and only one variable is accepted as - // multiple cannot be used for a plural rule determination, unless it is a range; - // see AddRange below. - // eg. in locale 'en' one: '{0} day left' other: '{0} days left' - AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error - - // adds an ordinal plural translation for a particular language/locale - // {0} is the only replacement type accepted and only one variable is accepted as - // multiple cannot be used for a plural rule determination, unless it is a range; - // see AddRange below. - // eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - // - 1st, 2nd, 3rd... - AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error - - // adds a range plural translation for a particular language/locale - // {0} and {1} are the only replacement types accepted and only these are accepted. - // eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left' - AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error - - // creates the translation for the locale given the 'key' and params passed in - T(key interface{}, params ...string) (string, error) - - // creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments - // and param passed in - C(key interface{}, num float64, digits uint64, param string) (string, error) - - // creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments - // and param passed in - O(key interface{}, num float64, digits uint64, param string) (string, error) - - // creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and - // 'digit2' arguments and 'param1' and 'param2' passed in - R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) - - // VerifyTranslations checks to ensures that no plural rules have been - // missed within the translations. - VerifyTranslations() error -} - -var _ Translator = new(translator) -var _ locales.Translator = new(translator) - -type translator struct { - locales.Translator - translations map[interface{}]*transText - cardinalTanslations map[interface{}][]*transText // array index is mapped to locales.PluralRule index + the locales.PluralRuleUnknown - ordinalTanslations map[interface{}][]*transText - rangeTanslations map[interface{}][]*transText -} - -type transText struct { - text string - indexes []int -} - -func newTranslator(trans locales.Translator) Translator { - return &translator{ - Translator: trans, - translations: make(map[interface{}]*transText), // translation text broken up by byte index - cardinalTanslations: make(map[interface{}][]*transText), - ordinalTanslations: make(map[interface{}][]*transText), - rangeTanslations: make(map[interface{}][]*transText), - } -} - -// Add adds a normal translation for a particular language/locale -// {#} is the only replacement type accepted and are ad infinitum -// eg. one: '{0} day left' other: '{0} days left' -func (t *translator) Add(key interface{}, text string, override bool) error { - - if _, ok := t.translations[key]; ok && !override { - return &ErrConflictingTranslation{locale: t.Locale(), key: key, text: text} - } - - lb := strings.Count(text, "{") - rb := strings.Count(text, "}") - - if lb != rb { - return &ErrMissingBracket{locale: t.Locale(), key: key, text: text} - } - - trans := &transText{ - text: text, - } - - var idx int - - for i := 0; i < lb; i++ { - s := "{" + strconv.Itoa(i) + "}" - idx = strings.Index(text, s) - if idx == -1 { - return &ErrBadParamSyntax{locale: t.Locale(), param: s, key: key, text: text} - } - - trans.indexes = append(trans.indexes, idx) - trans.indexes = append(trans.indexes, idx+len(s)) - } - - t.translations[key] = trans - - return nil -} - -// AddCardinal adds a cardinal plural translation for a particular language/locale -// {0} is the only replacement type accepted and only one variable is accepted as -// multiple cannot be used for a plural rule determination, unless it is a range; -// see AddRange below. -// eg. in locale 'en' one: '{0} day left' other: '{0} days left' -func (t *translator) AddCardinal(key interface{}, text string, rule locales.PluralRule, override bool) error { - - var verified bool - - // verify plural rule exists for locale - for _, pr := range t.PluralsCardinal() { - if pr == rule { - verified = true - break - } - } - - if !verified { - return &ErrCardinalTranslation{text: fmt.Sprintf("error: cardinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} - } - - tarr, ok := t.cardinalTanslations[key] - if ok { - // verify not adding a conflicting record - if len(tarr) > 0 && tarr[rule] != nil && !override { - return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} - } - - } else { - tarr = make([]*transText, 7, 7) - t.cardinalTanslations[key] = tarr - } - - trans := &transText{ - text: text, - indexes: make([]int, 2, 2), - } - - tarr[rule] = trans - - idx := strings.Index(text, paramZero) - if idx == -1 { - tarr[rule] = nil - return &ErrCardinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddCardinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} - } - - trans.indexes[0] = idx - trans.indexes[1] = idx + len(paramZero) - - return nil -} - -// AddOrdinal adds an ordinal plural translation for a particular language/locale -// {0} is the only replacement type accepted and only one variable is accepted as -// multiple cannot be used for a plural rule determination, unless it is a range; -// see AddRange below. -// eg. in locale 'en' one: '{0}st day of spring' other: '{0}nd day of spring' - 1st, 2nd, 3rd... -func (t *translator) AddOrdinal(key interface{}, text string, rule locales.PluralRule, override bool) error { - - var verified bool - - // verify plural rule exists for locale - for _, pr := range t.PluralsOrdinal() { - if pr == rule { - verified = true - break - } - } - - if !verified { - return &ErrOrdinalTranslation{text: fmt.Sprintf("error: ordinal plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} - } - - tarr, ok := t.ordinalTanslations[key] - if ok { - // verify not adding a conflicting record - if len(tarr) > 0 && tarr[rule] != nil && !override { - return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} - } - - } else { - tarr = make([]*transText, 7, 7) - t.ordinalTanslations[key] = tarr - } - - trans := &transText{ - text: text, - indexes: make([]int, 2, 2), - } - - tarr[rule] = trans - - idx := strings.Index(text, paramZero) - if idx == -1 { - tarr[rule] = nil - return &ErrOrdinalTranslation{text: fmt.Sprintf("error: parameter '%s' not found, may want to use 'Add' instead of 'AddOrdinal'. locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} - } - - trans.indexes[0] = idx - trans.indexes[1] = idx + len(paramZero) - - return nil -} - -// AddRange adds a range plural translation for a particular language/locale -// {0} and {1} are the only replacement types accepted and only these are accepted. -// eg. in locale 'nl' one: '{0}-{1} day left' other: '{0}-{1} days left' -func (t *translator) AddRange(key interface{}, text string, rule locales.PluralRule, override bool) error { - - var verified bool - - // verify plural rule exists for locale - for _, pr := range t.PluralsRange() { - if pr == rule { - verified = true - break - } - } - - if !verified { - return &ErrRangeTranslation{text: fmt.Sprintf("error: range plural rule '%s' does not exist for locale '%s' key: '%v' text: '%s'", rule, t.Locale(), key, text)} - } - - tarr, ok := t.rangeTanslations[key] - if ok { - // verify not adding a conflicting record - if len(tarr) > 0 && tarr[rule] != nil && !override { - return &ErrConflictingTranslation{locale: t.Locale(), key: key, rule: rule, text: text} - } - - } else { - tarr = make([]*transText, 7, 7) - t.rangeTanslations[key] = tarr - } - - trans := &transText{ - text: text, - indexes: make([]int, 4, 4), - } - - tarr[rule] = trans - - idx := strings.Index(text, paramZero) - if idx == -1 { - tarr[rule] = nil - return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, are you sure you're adding a Range Translation? locale: '%s' key: '%v' text: '%s'", paramZero, t.Locale(), key, text)} - } - - trans.indexes[0] = idx - trans.indexes[1] = idx + len(paramZero) - - idx = strings.Index(text, paramOne) - if idx == -1 { - tarr[rule] = nil - return &ErrRangeTranslation{text: fmt.Sprintf("error: parameter '%s' not found, a Range Translation requires two parameters. locale: '%s' key: '%v' text: '%s'", paramOne, t.Locale(), key, text)} - } - - trans.indexes[2] = idx - trans.indexes[3] = idx + len(paramOne) - - return nil -} - -// T creates the translation for the locale given the 'key' and params passed in -func (t *translator) T(key interface{}, params ...string) (string, error) { - - trans, ok := t.translations[key] - if !ok { - return unknownTranslation, ErrUnknowTranslation - } - - b := make([]byte, 0, 64) - - var start, end, count int - - for i := 0; i < len(trans.indexes); i++ { - end = trans.indexes[i] - b = append(b, trans.text[start:end]...) - b = append(b, params[count]...) - i++ - start = trans.indexes[i] - count++ - } - - b = append(b, trans.text[start:]...) - - return string(b), nil -} - -// C creates the cardinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in -func (t *translator) C(key interface{}, num float64, digits uint64, param string) (string, error) { - - tarr, ok := t.cardinalTanslations[key] - if !ok { - return unknownTranslation, ErrUnknowTranslation - } - - rule := t.CardinalPluralRule(num, digits) - - trans := tarr[rule] - - b := make([]byte, 0, 64) - b = append(b, trans.text[:trans.indexes[0]]...) - b = append(b, param...) - b = append(b, trans.text[trans.indexes[1]:]...) - - return string(b), nil -} - -// O creates the ordinal translation for the locale given the 'key', 'num' and 'digit' arguments and param passed in -func (t *translator) O(key interface{}, num float64, digits uint64, param string) (string, error) { - - tarr, ok := t.ordinalTanslations[key] - if !ok { - return unknownTranslation, ErrUnknowTranslation - } - - rule := t.OrdinalPluralRule(num, digits) - - trans := tarr[rule] - - b := make([]byte, 0, 64) - b = append(b, trans.text[:trans.indexes[0]]...) - b = append(b, param...) - b = append(b, trans.text[trans.indexes[1]:]...) - - return string(b), nil -} - -// R creates the range translation for the locale given the 'key', 'num1', 'digit1', 'num2' and 'digit2' arguments -// and 'param1' and 'param2' passed in -func (t *translator) R(key interface{}, num1 float64, digits1 uint64, num2 float64, digits2 uint64, param1, param2 string) (string, error) { - - tarr, ok := t.rangeTanslations[key] - if !ok { - return unknownTranslation, ErrUnknowTranslation - } - - rule := t.RangePluralRule(num1, digits1, num2, digits2) - - trans := tarr[rule] - - b := make([]byte, 0, 64) - b = append(b, trans.text[:trans.indexes[0]]...) - b = append(b, param1...) - b = append(b, trans.text[trans.indexes[1]:trans.indexes[2]]...) - b = append(b, param2...) - b = append(b, trans.text[trans.indexes[3]:]...) - - return string(b), nil -} - -// VerifyTranslations checks to ensures that no plural rules have been -// missed within the translations. -func (t *translator) VerifyTranslations() error { - - for k, v := range t.cardinalTanslations { - - for _, rule := range t.PluralsCardinal() { - - if v[rule] == nil { - return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "plural", rule: rule, key: k} - } - } - } - - for k, v := range t.ordinalTanslations { - - for _, rule := range t.PluralsOrdinal() { - - if v[rule] == nil { - return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "ordinal", rule: rule, key: k} - } - } - } - - for k, v := range t.rangeTanslations { - - for _, rule := range t.PluralsRange() { - - if v[rule] == nil { - return &ErrMissingPluralTranslation{locale: t.Locale(), translationType: "range", rule: rule, key: k} - } - } - } - - return nil -} diff --git a/vendor/github.com/go-playground/universal-translator/universal_translator.go b/vendor/github.com/go-playground/universal-translator/universal_translator.go deleted file mode 100644 index dbf707f5c..000000000 --- a/vendor/github.com/go-playground/universal-translator/universal_translator.go +++ /dev/null @@ -1,113 +0,0 @@ -package ut - -import ( - "strings" - - "github.com/go-playground/locales" -) - -// UniversalTranslator holds all locale & translation data -type UniversalTranslator struct { - translators map[string]Translator - fallback Translator -} - -// New returns a new UniversalTranslator instance set with -// the fallback locale and locales it should support -func New(fallback locales.Translator, supportedLocales ...locales.Translator) *UniversalTranslator { - - t := &UniversalTranslator{ - translators: make(map[string]Translator), - } - - for _, v := range supportedLocales { - - trans := newTranslator(v) - t.translators[strings.ToLower(trans.Locale())] = trans - - if fallback.Locale() == v.Locale() { - t.fallback = trans - } - } - - if t.fallback == nil && fallback != nil { - t.fallback = newTranslator(fallback) - } - - return t -} - -// FindTranslator trys to find a Translator based on an array of locales -// and returns the first one it can find, otherwise returns the -// fallback translator. -func (t *UniversalTranslator) FindTranslator(locales ...string) (trans Translator, found bool) { - - for _, locale := range locales { - - if trans, found = t.translators[strings.ToLower(locale)]; found { - return - } - } - - return t.fallback, false -} - -// GetTranslator returns the specified translator for the given locale, -// or fallback if not found -func (t *UniversalTranslator) GetTranslator(locale string) (trans Translator, found bool) { - - if trans, found = t.translators[strings.ToLower(locale)]; found { - return - } - - return t.fallback, false -} - -// GetFallback returns the fallback locale -func (t *UniversalTranslator) GetFallback() Translator { - return t.fallback -} - -// AddTranslator adds the supplied translator, if it already exists the override param -// will be checked and if false an error will be returned, otherwise the translator will be -// overridden; if the fallback matches the supplied translator it will be overridden as well -// NOTE: this is normally only used when translator is embedded within a library -func (t *UniversalTranslator) AddTranslator(translator locales.Translator, override bool) error { - - lc := strings.ToLower(translator.Locale()) - _, ok := t.translators[lc] - if ok && !override { - return &ErrExistingTranslator{locale: translator.Locale()} - } - - trans := newTranslator(translator) - - if t.fallback.Locale() == translator.Locale() { - - // because it's optional to have a fallback, I don't impose that limitation - // don't know why you wouldn't but... - if !override { - return &ErrExistingTranslator{locale: translator.Locale()} - } - - t.fallback = trans - } - - t.translators[lc] = trans - - return nil -} - -// VerifyTranslations runs through all locales and identifies any issues -// eg. missing plural rules for a locale -func (t *UniversalTranslator) VerifyTranslations() (err error) { - - for _, trans := range t.translators { - err = trans.VerifyTranslations() - if err != nil { - return - } - } - - return -} diff --git a/vendor/github.com/gobwas/glob/.gitignore b/vendor/github.com/gobwas/glob/.gitignore new file mode 100644 index 000000000..b4ae623be --- /dev/null +++ b/vendor/github.com/gobwas/glob/.gitignore @@ -0,0 +1,8 @@ +glob.iml +.idea +*.cpu +*.mem +*.test +*.dot +*.png +*.svg diff --git a/vendor/github.com/gobwas/glob/.travis.yml b/vendor/github.com/gobwas/glob/.travis.yml new file mode 100644 index 000000000..e8a276826 --- /dev/null +++ b/vendor/github.com/gobwas/glob/.travis.yml @@ -0,0 +1,9 @@ +sudo: false + +language: go + +go: + - 1.5.3 + +script: + - go test -v ./... diff --git a/vendor/github.com/gobwas/glob/LICENSE b/vendor/github.com/gobwas/glob/LICENSE new file mode 100644 index 000000000..9d4735cad --- /dev/null +++ b/vendor/github.com/gobwas/glob/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Sergey Kamardin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/gobwas/glob/bench.sh b/vendor/github.com/gobwas/glob/bench.sh new file mode 100644 index 000000000..804cf22e6 --- /dev/null +++ b/vendor/github.com/gobwas/glob/bench.sh @@ -0,0 +1,26 @@ +#! /bin/bash + +bench() { + filename="/tmp/$1-$2.bench" + if test -e "${filename}"; + then + echo "Already exists ${filename}" + else + backup=`git rev-parse --abbrev-ref HEAD` + git checkout $1 + echo -n "Creating ${filename}... " + go test ./... -run=NONE -bench=$2 > "${filename}" -benchmem + echo "OK" + git checkout ${backup} + sleep 5 + fi +} + + +to=$1 +current=`git rev-parse --abbrev-ref HEAD` + +bench ${to} $2 +bench ${current} $2 + +benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench" diff --git a/vendor/github.com/gobwas/glob/compiler/compiler.go b/vendor/github.com/gobwas/glob/compiler/compiler.go new file mode 100644 index 000000000..02e7de80a --- /dev/null +++ b/vendor/github.com/gobwas/glob/compiler/compiler.go @@ -0,0 +1,525 @@ +package compiler + +// TODO use constructor with all matchers, and to their structs private +// TODO glue multiple Text nodes (like after QuoteMeta) + +import ( + "fmt" + "reflect" + + "github.com/gobwas/glob/match" + "github.com/gobwas/glob/syntax/ast" + "github.com/gobwas/glob/util/runes" +) + +func optimizeMatcher(matcher match.Matcher) match.Matcher { + switch m := matcher.(type) { + + case match.Any: + if len(m.Separators) == 0 { + return match.NewSuper() + } + + case match.AnyOf: + if len(m.Matchers) == 1 { + return m.Matchers[0] + } + + return m + + case match.List: + if m.Not == false && len(m.List) == 1 { + return match.NewText(string(m.List)) + } + + return m + + case match.BTree: + m.Left = optimizeMatcher(m.Left) + m.Right = optimizeMatcher(m.Right) + + r, ok := m.Value.(match.Text) + if !ok { + return m + } + + var ( + leftNil = m.Left == nil + rightNil = m.Right == nil + ) + if leftNil && rightNil { + return match.NewText(r.Str) + } + + _, leftSuper := m.Left.(match.Super) + lp, leftPrefix := m.Left.(match.Prefix) + la, leftAny := m.Left.(match.Any) + + _, rightSuper := m.Right.(match.Super) + rs, rightSuffix := m.Right.(match.Suffix) + ra, rightAny := m.Right.(match.Any) + + switch { + case leftSuper && rightSuper: + return match.NewContains(r.Str, false) + + case leftSuper && rightNil: + return match.NewSuffix(r.Str) + + case rightSuper && leftNil: + return match.NewPrefix(r.Str) + + case leftNil && rightSuffix: + return match.NewPrefixSuffix(r.Str, rs.Suffix) + + case rightNil && leftPrefix: + return match.NewPrefixSuffix(lp.Prefix, r.Str) + + case rightNil && leftAny: + return match.NewSuffixAny(r.Str, la.Separators) + + case leftNil && rightAny: + return match.NewPrefixAny(r.Str, ra.Separators) + } + + return m + } + + return matcher +} + +func compileMatchers(matchers []match.Matcher) (match.Matcher, error) { + if len(matchers) == 0 { + return nil, fmt.Errorf("compile error: need at least one matcher") + } + if len(matchers) == 1 { + return matchers[0], nil + } + if m := glueMatchers(matchers); m != nil { + return m, nil + } + + idx := -1 + maxLen := -1 + var val match.Matcher + for i, matcher := range matchers { + if l := matcher.Len(); l != -1 && l >= maxLen { + maxLen = l + idx = i + val = matcher + } + } + + if val == nil { // not found matcher with static length + r, err := compileMatchers(matchers[1:]) + if err != nil { + return nil, err + } + return match.NewBTree(matchers[0], nil, r), nil + } + + left := matchers[:idx] + var right []match.Matcher + if len(matchers) > idx+1 { + right = matchers[idx+1:] + } + + var l, r match.Matcher + var err error + if len(left) > 0 { + l, err = compileMatchers(left) + if err != nil { + return nil, err + } + } + + if len(right) > 0 { + r, err = compileMatchers(right) + if err != nil { + return nil, err + } + } + + return match.NewBTree(val, l, r), nil +} + +func glueMatchers(matchers []match.Matcher) match.Matcher { + if m := glueMatchersAsEvery(matchers); m != nil { + return m + } + if m := glueMatchersAsRow(matchers); m != nil { + return m + } + return nil +} + +func glueMatchersAsRow(matchers []match.Matcher) match.Matcher { + if len(matchers) <= 1 { + return nil + } + + var ( + c []match.Matcher + l int + ) + for _, matcher := range matchers { + if ml := matcher.Len(); ml == -1 { + return nil + } else { + c = append(c, matcher) + l += ml + } + } + return match.NewRow(l, c...) +} + +func glueMatchersAsEvery(matchers []match.Matcher) match.Matcher { + if len(matchers) <= 1 { + return nil + } + + var ( + hasAny bool + hasSuper bool + hasSingle bool + min int + separator []rune + ) + + for i, matcher := range matchers { + var sep []rune + + switch m := matcher.(type) { + case match.Super: + sep = []rune{} + hasSuper = true + + case match.Any: + sep = m.Separators + hasAny = true + + case match.Single: + sep = m.Separators + hasSingle = true + min++ + + case match.List: + if !m.Not { + return nil + } + sep = m.List + hasSingle = true + min++ + + default: + return nil + } + + // initialize + if i == 0 { + separator = sep + } + + if runes.Equal(sep, separator) { + continue + } + + return nil + } + + if hasSuper && !hasAny && !hasSingle { + return match.NewSuper() + } + + if hasAny && !hasSuper && !hasSingle { + return match.NewAny(separator) + } + + if (hasAny || hasSuper) && min > 0 && len(separator) == 0 { + return match.NewMin(min) + } + + every := match.NewEveryOf() + + if min > 0 { + every.Add(match.NewMin(min)) + + if !hasAny && !hasSuper { + every.Add(match.NewMax(min)) + } + } + + if len(separator) > 0 { + every.Add(match.NewContains(string(separator), true)) + } + + return every +} + +func minimizeMatchers(matchers []match.Matcher) []match.Matcher { + var done match.Matcher + var left, right, count int + + for l := 0; l < len(matchers); l++ { + for r := len(matchers); r > l; r-- { + if glued := glueMatchers(matchers[l:r]); glued != nil { + var swap bool + + if done == nil { + swap = true + } else { + cl, gl := done.Len(), glued.Len() + swap = cl > -1 && gl > -1 && gl > cl + swap = swap || count < r-l + } + + if swap { + done = glued + left = l + right = r + count = r - l + } + } + } + } + + if done == nil { + return matchers + } + + next := append(append([]match.Matcher{}, matchers[:left]...), done) + if right < len(matchers) { + next = append(next, matchers[right:]...) + } + + if len(next) == len(matchers) { + return next + } + + return minimizeMatchers(next) +} + +// minimizeAnyOf tries to apply some heuristics to minimize number of nodes in given tree +func minimizeTree(tree *ast.Node) *ast.Node { + switch tree.Kind { + case ast.KindAnyOf: + return minimizeTreeAnyOf(tree) + default: + return nil + } +} + +// minimizeAnyOf tries to find common children of given node of AnyOf pattern +// it searches for common children from left and from right +// if any common children are found – then it returns new optimized ast tree +// else it returns nil +func minimizeTreeAnyOf(tree *ast.Node) *ast.Node { + if !areOfSameKind(tree.Children, ast.KindPattern) { + return nil + } + + commonLeft, commonRight := commonChildren(tree.Children) + commonLeftCount, commonRightCount := len(commonLeft), len(commonRight) + if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts + return nil + } + + var result []*ast.Node + if commonLeftCount > 0 { + result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...)) + } + + var anyOf []*ast.Node + for _, child := range tree.Children { + reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount] + var node *ast.Node + if len(reuse) == 0 { + // this pattern is completely reduced by commonLeft and commonRight patterns + // so it become nothing + node = ast.NewNode(ast.KindNothing, nil) + } else { + node = ast.NewNode(ast.KindPattern, nil, reuse...) + } + anyOf = appendIfUnique(anyOf, node) + } + switch { + case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing: + result = append(result, anyOf[0]) + case len(anyOf) > 1: + result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...)) + } + + if commonRightCount > 0 { + result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...)) + } + + return ast.NewNode(ast.KindPattern, nil, result...) +} + +func commonChildren(nodes []*ast.Node) (commonLeft, commonRight []*ast.Node) { + if len(nodes) <= 1 { + return + } + + // find node that has least number of children + idx := leastChildren(nodes) + if idx == -1 { + return + } + tree := nodes[idx] + treeLength := len(tree.Children) + + // allocate max able size for rightCommon slice + // to get ability insert elements in reverse order (from end to start) + // without sorting + commonRight = make([]*ast.Node, treeLength) + lastRight := treeLength // will use this to get results as commonRight[lastRight:] + + var ( + breakLeft bool + breakRight bool + commonTotal int + ) + for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 { + treeLeft := tree.Children[i] + treeRight := tree.Children[j] + + for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ { + // skip least children node + if k == idx { + continue + } + + restLeft := nodes[k].Children[i] + restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength] + + breakLeft = breakLeft || !treeLeft.Equal(restLeft) + + // disable searching for right common parts, if left part is already overlapping + breakRight = breakRight || (!breakLeft && j <= i) + breakRight = breakRight || !treeRight.Equal(restRight) + } + + if !breakLeft { + commonTotal++ + commonLeft = append(commonLeft, treeLeft) + } + if !breakRight { + commonTotal++ + lastRight = j + commonRight[j] = treeRight + } + } + + commonRight = commonRight[lastRight:] + + return +} + +func appendIfUnique(target []*ast.Node, val *ast.Node) []*ast.Node { + for _, n := range target { + if reflect.DeepEqual(n, val) { + return target + } + } + return append(target, val) +} + +func areOfSameKind(nodes []*ast.Node, kind ast.Kind) bool { + for _, n := range nodes { + if n.Kind != kind { + return false + } + } + return true +} + +func leastChildren(nodes []*ast.Node) int { + min := -1 + idx := -1 + for i, n := range nodes { + if idx == -1 || (len(n.Children) < min) { + min = len(n.Children) + idx = i + } + } + return idx +} + +func compileTreeChildren(tree *ast.Node, sep []rune) ([]match.Matcher, error) { + var matchers []match.Matcher + for _, desc := range tree.Children { + m, err := compile(desc, sep) + if err != nil { + return nil, err + } + matchers = append(matchers, optimizeMatcher(m)) + } + return matchers, nil +} + +func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) { + switch tree.Kind { + case ast.KindAnyOf: + // todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go) + if n := minimizeTree(tree); n != nil { + return compile(n, sep) + } + matchers, err := compileTreeChildren(tree, sep) + if err != nil { + return nil, err + } + return match.NewAnyOf(matchers...), nil + + case ast.KindPattern: + if len(tree.Children) == 0 { + return match.NewNothing(), nil + } + matchers, err := compileTreeChildren(tree, sep) + if err != nil { + return nil, err + } + m, err = compileMatchers(minimizeMatchers(matchers)) + if err != nil { + return nil, err + } + + case ast.KindAny: + m = match.NewAny(sep) + + case ast.KindSuper: + m = match.NewSuper() + + case ast.KindSingle: + m = match.NewSingle(sep) + + case ast.KindNothing: + m = match.NewNothing() + + case ast.KindList: + l := tree.Value.(ast.List) + m = match.NewList([]rune(l.Chars), l.Not) + + case ast.KindRange: + r := tree.Value.(ast.Range) + m = match.NewRange(r.Lo, r.Hi, r.Not) + + case ast.KindText: + t := tree.Value.(ast.Text) + m = match.NewText(t.Text) + + default: + return nil, fmt.Errorf("could not compile tree: unknown node type") + } + + return optimizeMatcher(m), nil +} + +func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) { + m, err := compile(tree, sep) + if err != nil { + return nil, err + } + + return m, nil +} diff --git a/vendor/github.com/gobwas/glob/glob.go b/vendor/github.com/gobwas/glob/glob.go new file mode 100644 index 000000000..2afde343a --- /dev/null +++ b/vendor/github.com/gobwas/glob/glob.go @@ -0,0 +1,80 @@ +package glob + +import ( + "github.com/gobwas/glob/compiler" + "github.com/gobwas/glob/syntax" +) + +// Glob represents compiled glob pattern. +type Glob interface { + Match(string) bool +} + +// Compile creates Glob for given pattern and strings (if any present after pattern) as separators. +// The pattern syntax is: +// +// pattern: +// { term } +// +// term: +// `*` matches any sequence of non-separator characters +// `**` matches any sequence of characters +// `?` matches any single non-separator character +// `[` [ `!` ] { character-range } `]` +// character class (must be non-empty) +// `{` pattern-list `}` +// pattern alternatives +// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`) +// `\` c matches character c +// +// character-range: +// c matches character c (c != `\\`, `-`, `]`) +// `\` c matches character c +// lo `-` hi matches character c for lo <= c <= hi +// +// pattern-list: +// pattern { `,` pattern } +// comma-separated (without spaces) patterns +// +func Compile(pattern string, separators ...rune) (Glob, error) { + ast, err := syntax.Parse(pattern) + if err != nil { + return nil, err + } + + matcher, err := compiler.Compile(ast, separators) + if err != nil { + return nil, err + } + + return matcher, nil +} + +// MustCompile is the same as Compile, except that if Compile returns error, this will panic +func MustCompile(pattern string, separators ...rune) Glob { + g, err := Compile(pattern, separators...) + if err != nil { + panic(err) + } + + return g +} + +// QuoteMeta returns a string that quotes all glob pattern meta characters +// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`. +func QuoteMeta(s string) string { + b := make([]byte, 2*len(s)) + + // a byte loop is correct because all meta characters are ASCII + j := 0 + for i := 0; i < len(s); i++ { + if syntax.Special(s[i]) { + b[j] = '\\' + j++ + } + b[j] = s[i] + j++ + } + + return string(b[0:j]) +} diff --git a/vendor/github.com/gobwas/glob/match/any.go b/vendor/github.com/gobwas/glob/match/any.go new file mode 100644 index 000000000..514a9a5c4 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/any.go @@ -0,0 +1,45 @@ +package match + +import ( + "fmt" + "github.com/gobwas/glob/util/strings" +) + +type Any struct { + Separators []rune +} + +func NewAny(s []rune) Any { + return Any{s} +} + +func (self Any) Match(s string) bool { + return strings.IndexAnyRunes(s, self.Separators) == -1 +} + +func (self Any) Index(s string) (int, []int) { + found := strings.IndexAnyRunes(s, self.Separators) + switch found { + case -1: + case 0: + return 0, segments0 + default: + s = s[:found] + } + + segments := acquireSegments(len(s)) + for i := range s { + segments = append(segments, i) + } + segments = append(segments, len(s)) + + return 0, segments +} + +func (self Any) Len() int { + return lenNo +} + +func (self Any) String() string { + return fmt.Sprintf("", string(self.Separators)) +} diff --git a/vendor/github.com/gobwas/glob/match/any_of.go b/vendor/github.com/gobwas/glob/match/any_of.go new file mode 100644 index 000000000..8e65356cd --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/any_of.go @@ -0,0 +1,82 @@ +package match + +import "fmt" + +type AnyOf struct { + Matchers Matchers +} + +func NewAnyOf(m ...Matcher) AnyOf { + return AnyOf{Matchers(m)} +} + +func (self *AnyOf) Add(m Matcher) error { + self.Matchers = append(self.Matchers, m) + return nil +} + +func (self AnyOf) Match(s string) bool { + for _, m := range self.Matchers { + if m.Match(s) { + return true + } + } + + return false +} + +func (self AnyOf) Index(s string) (int, []int) { + index := -1 + + segments := acquireSegments(len(s)) + for _, m := range self.Matchers { + idx, seg := m.Index(s) + if idx == -1 { + continue + } + + if index == -1 || idx < index { + index = idx + segments = append(segments[:0], seg...) + continue + } + + if idx > index { + continue + } + + // here idx == index + segments = appendMerge(segments, seg) + } + + if index == -1 { + releaseSegments(segments) + return -1, nil + } + + return index, segments +} + +func (self AnyOf) Len() (l int) { + l = -1 + for _, m := range self.Matchers { + ml := m.Len() + switch { + case l == -1: + l = ml + continue + + case ml == -1: + return -1 + + case l != ml: + return -1 + } + } + + return +} + +func (self AnyOf) String() string { + return fmt.Sprintf("", self.Matchers) +} diff --git a/vendor/github.com/gobwas/glob/match/btree.go b/vendor/github.com/gobwas/glob/match/btree.go new file mode 100644 index 000000000..a8130e93e --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/btree.go @@ -0,0 +1,146 @@ +package match + +import ( + "fmt" + "unicode/utf8" +) + +type BTree struct { + Value Matcher + Left Matcher + Right Matcher + ValueLengthRunes int + LeftLengthRunes int + RightLengthRunes int + LengthRunes int +} + +func NewBTree(Value, Left, Right Matcher) (tree BTree) { + tree.Value = Value + tree.Left = Left + tree.Right = Right + + lenOk := true + if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 { + lenOk = false + } + + if Left != nil { + if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 { + lenOk = false + } + } + + if Right != nil { + if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 { + lenOk = false + } + } + + if lenOk { + tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes + } else { + tree.LengthRunes = -1 + } + + return tree +} + +func (self BTree) Len() int { + return self.LengthRunes +} + +// todo? +func (self BTree) Index(s string) (int, []int) { + return -1, nil +} + +func (self BTree) Match(s string) bool { + inputLen := len(s) + + // self.Length, self.RLen and self.LLen are values meaning the length of runes for each part + // here we manipulating byte length for better optimizations + // but these checks still works, cause minLen of 1-rune string is 1 byte. + if self.LengthRunes != -1 && self.LengthRunes > inputLen { + return false + } + + // try to cut unnecessary parts + // by knowledge of length of right and left part + var offset, limit int + if self.LeftLengthRunes >= 0 { + offset = self.LeftLengthRunes + } + if self.RightLengthRunes >= 0 { + limit = inputLen - self.RightLengthRunes + } else { + limit = inputLen + } + + for offset < limit { + // search for matching part in substring + index, segments := self.Value.Index(s[offset:limit]) + if index == -1 { + releaseSegments(segments) + return false + } + + l := s[:offset+index] + var left bool + if self.Left != nil { + left = self.Left.Match(l) + } else { + left = l == "" + } + + if left { + for i := len(segments) - 1; i >= 0; i-- { + length := segments[i] + + var right bool + var r string + // if there is no string for the right branch + if inputLen <= offset+index+length { + r = "" + } else { + r = s[offset+index+length:] + } + + if self.Right != nil { + right = self.Right.Match(r) + } else { + right = r == "" + } + + if right { + releaseSegments(segments) + return true + } + } + } + + _, step := utf8.DecodeRuneInString(s[offset+index:]) + offset += index + step + + releaseSegments(segments) + } + + return false +} + +func (self BTree) String() string { + const n string = "" + var l, r string + if self.Left == nil { + l = n + } else { + l = self.Left.String() + } + if self.Right == nil { + r = n + } else { + r = self.Right.String() + } + + return fmt.Sprintf("%s]>", l, self.Value, r) +} diff --git a/vendor/github.com/gobwas/glob/match/contains.go b/vendor/github.com/gobwas/glob/match/contains.go new file mode 100644 index 000000000..0998e95b0 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/contains.go @@ -0,0 +1,58 @@ +package match + +import ( + "fmt" + "strings" +) + +type Contains struct { + Needle string + Not bool +} + +func NewContains(needle string, not bool) Contains { + return Contains{needle, not} +} + +func (self Contains) Match(s string) bool { + return strings.Contains(s, self.Needle) != self.Not +} + +func (self Contains) Index(s string) (int, []int) { + var offset int + + idx := strings.Index(s, self.Needle) + + if !self.Not { + if idx == -1 { + return -1, nil + } + + offset = idx + len(self.Needle) + if len(s) <= offset { + return 0, []int{offset} + } + s = s[offset:] + } else if idx != -1 { + s = s[:idx] + } + + segments := acquireSegments(len(s) + 1) + for i := range s { + segments = append(segments, offset+i) + } + + return 0, append(segments, offset+len(s)) +} + +func (self Contains) Len() int { + return lenNo +} + +func (self Contains) String() string { + var not string + if self.Not { + not = "!" + } + return fmt.Sprintf("", not, self.Needle) +} diff --git a/vendor/github.com/gobwas/glob/match/every_of.go b/vendor/github.com/gobwas/glob/match/every_of.go new file mode 100644 index 000000000..7c968ee36 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/every_of.go @@ -0,0 +1,99 @@ +package match + +import ( + "fmt" +) + +type EveryOf struct { + Matchers Matchers +} + +func NewEveryOf(m ...Matcher) EveryOf { + return EveryOf{Matchers(m)} +} + +func (self *EveryOf) Add(m Matcher) error { + self.Matchers = append(self.Matchers, m) + return nil +} + +func (self EveryOf) Len() (l int) { + for _, m := range self.Matchers { + if ml := m.Len(); l > 0 { + l += ml + } else { + return -1 + } + } + + return +} + +func (self EveryOf) Index(s string) (int, []int) { + var index int + var offset int + + // make `in` with cap as len(s), + // cause it is the maximum size of output segments values + next := acquireSegments(len(s)) + current := acquireSegments(len(s)) + + sub := s + for i, m := range self.Matchers { + idx, seg := m.Index(sub) + if idx == -1 { + releaseSegments(next) + releaseSegments(current) + return -1, nil + } + + if i == 0 { + // we use copy here instead of `current = seg` + // cause seg is a slice from reusable buffer `in` + // and it could be overwritten in next iteration + current = append(current, seg...) + } else { + // clear the next + next = next[:0] + + delta := index - (idx + offset) + for _, ex := range current { + for _, n := range seg { + if ex+delta == n { + next = append(next, n) + } + } + } + + if len(next) == 0 { + releaseSegments(next) + releaseSegments(current) + return -1, nil + } + + current = append(current[:0], next...) + } + + index = idx + offset + sub = s[index:] + offset += idx + } + + releaseSegments(next) + + return index, current +} + +func (self EveryOf) Match(s string) bool { + for _, m := range self.Matchers { + if !m.Match(s) { + return false + } + } + + return true +} + +func (self EveryOf) String() string { + return fmt.Sprintf("", self.Matchers) +} diff --git a/vendor/github.com/gobwas/glob/match/list.go b/vendor/github.com/gobwas/glob/match/list.go new file mode 100644 index 000000000..7fd763ecd --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/list.go @@ -0,0 +1,49 @@ +package match + +import ( + "fmt" + "github.com/gobwas/glob/util/runes" + "unicode/utf8" +) + +type List struct { + List []rune + Not bool +} + +func NewList(list []rune, not bool) List { + return List{list, not} +} + +func (self List) Match(s string) bool { + r, w := utf8.DecodeRuneInString(s) + if len(s) > w { + return false + } + + inList := runes.IndexRune(self.List, r) != -1 + return inList == !self.Not +} + +func (self List) Len() int { + return lenOne +} + +func (self List) Index(s string) (int, []int) { + for i, r := range s { + if self.Not == (runes.IndexRune(self.List, r) == -1) { + return i, segmentsByRuneLength[utf8.RuneLen(r)] + } + } + + return -1, nil +} + +func (self List) String() string { + var not string + if self.Not { + not = "!" + } + + return fmt.Sprintf("", not, string(self.List)) +} diff --git a/vendor/github.com/gobwas/glob/match/match.go b/vendor/github.com/gobwas/glob/match/match.go new file mode 100644 index 000000000..f80e007fb --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/match.go @@ -0,0 +1,81 @@ +package match + +// todo common table of rune's length + +import ( + "fmt" + "strings" +) + +const lenOne = 1 +const lenZero = 0 +const lenNo = -1 + +type Matcher interface { + Match(string) bool + Index(string) (int, []int) + Len() int + String() string +} + +type Matchers []Matcher + +func (m Matchers) String() string { + var s []string + for _, matcher := range m { + s = append(s, fmt.Sprint(matcher)) + } + + return fmt.Sprintf("%s", strings.Join(s, ",")) +} + +// appendMerge merges and sorts given already SORTED and UNIQUE segments. +func appendMerge(target, sub []int) []int { + lt, ls := len(target), len(sub) + out := make([]int, 0, lt+ls) + + for x, y := 0, 0; x < lt || y < ls; { + if x >= lt { + out = append(out, sub[y:]...) + break + } + + if y >= ls { + out = append(out, target[x:]...) + break + } + + xValue := target[x] + yValue := sub[y] + + switch { + + case xValue == yValue: + out = append(out, xValue) + x++ + y++ + + case xValue < yValue: + out = append(out, xValue) + x++ + + case yValue < xValue: + out = append(out, yValue) + y++ + + } + } + + target = append(target[:0], out...) + + return target +} + +func reverseSegments(input []int) { + l := len(input) + m := l / 2 + + for i := 0; i < m; i++ { + input[i], input[l-i-1] = input[l-i-1], input[i] + } +} diff --git a/vendor/github.com/gobwas/glob/match/max.go b/vendor/github.com/gobwas/glob/match/max.go new file mode 100644 index 000000000..d72f69eff --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/max.go @@ -0,0 +1,49 @@ +package match + +import ( + "fmt" + "unicode/utf8" +) + +type Max struct { + Limit int +} + +func NewMax(l int) Max { + return Max{l} +} + +func (self Max) Match(s string) bool { + var l int + for range s { + l += 1 + if l > self.Limit { + return false + } + } + + return true +} + +func (self Max) Index(s string) (int, []int) { + segments := acquireSegments(self.Limit + 1) + segments = append(segments, 0) + var count int + for i, r := range s { + count++ + if count > self.Limit { + break + } + segments = append(segments, i+utf8.RuneLen(r)) + } + + return 0, segments +} + +func (self Max) Len() int { + return lenNo +} + +func (self Max) String() string { + return fmt.Sprintf("", self.Limit) +} diff --git a/vendor/github.com/gobwas/glob/match/min.go b/vendor/github.com/gobwas/glob/match/min.go new file mode 100644 index 000000000..db57ac8eb --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/min.go @@ -0,0 +1,57 @@ +package match + +import ( + "fmt" + "unicode/utf8" +) + +type Min struct { + Limit int +} + +func NewMin(l int) Min { + return Min{l} +} + +func (self Min) Match(s string) bool { + var l int + for range s { + l += 1 + if l >= self.Limit { + return true + } + } + + return false +} + +func (self Min) Index(s string) (int, []int) { + var count int + + c := len(s) - self.Limit + 1 + if c <= 0 { + return -1, nil + } + + segments := acquireSegments(c) + for i, r := range s { + count++ + if count >= self.Limit { + segments = append(segments, i+utf8.RuneLen(r)) + } + } + + if len(segments) == 0 { + return -1, nil + } + + return 0, segments +} + +func (self Min) Len() int { + return lenNo +} + +func (self Min) String() string { + return fmt.Sprintf("", self.Limit) +} diff --git a/vendor/github.com/gobwas/glob/match/nothing.go b/vendor/github.com/gobwas/glob/match/nothing.go new file mode 100644 index 000000000..0d4ecd36b --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/nothing.go @@ -0,0 +1,27 @@ +package match + +import ( + "fmt" +) + +type Nothing struct{} + +func NewNothing() Nothing { + return Nothing{} +} + +func (self Nothing) Match(s string) bool { + return len(s) == 0 +} + +func (self Nothing) Index(s string) (int, []int) { + return 0, segments0 +} + +func (self Nothing) Len() int { + return lenZero +} + +func (self Nothing) String() string { + return fmt.Sprintf("") +} diff --git a/vendor/github.com/gobwas/glob/match/prefix.go b/vendor/github.com/gobwas/glob/match/prefix.go new file mode 100644 index 000000000..a7347250e --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/prefix.go @@ -0,0 +1,50 @@ +package match + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +type Prefix struct { + Prefix string +} + +func NewPrefix(p string) Prefix { + return Prefix{p} +} + +func (self Prefix) Index(s string) (int, []int) { + idx := strings.Index(s, self.Prefix) + if idx == -1 { + return -1, nil + } + + length := len(self.Prefix) + var sub string + if len(s) > idx+length { + sub = s[idx+length:] + } else { + sub = "" + } + + segments := acquireSegments(len(sub) + 1) + segments = append(segments, length) + for i, r := range sub { + segments = append(segments, length+i+utf8.RuneLen(r)) + } + + return idx, segments +} + +func (self Prefix) Len() int { + return lenNo +} + +func (self Prefix) Match(s string) bool { + return strings.HasPrefix(s, self.Prefix) +} + +func (self Prefix) String() string { + return fmt.Sprintf("", self.Prefix) +} diff --git a/vendor/github.com/gobwas/glob/match/prefix_any.go b/vendor/github.com/gobwas/glob/match/prefix_any.go new file mode 100644 index 000000000..8ee58fe1b --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/prefix_any.go @@ -0,0 +1,55 @@ +package match + +import ( + "fmt" + "strings" + "unicode/utf8" + + sutil "github.com/gobwas/glob/util/strings" +) + +type PrefixAny struct { + Prefix string + Separators []rune +} + +func NewPrefixAny(s string, sep []rune) PrefixAny { + return PrefixAny{s, sep} +} + +func (self PrefixAny) Index(s string) (int, []int) { + idx := strings.Index(s, self.Prefix) + if idx == -1 { + return -1, nil + } + + n := len(self.Prefix) + sub := s[idx+n:] + i := sutil.IndexAnyRunes(sub, self.Separators) + if i > -1 { + sub = sub[:i] + } + + seg := acquireSegments(len(sub) + 1) + seg = append(seg, n) + for i, r := range sub { + seg = append(seg, n+i+utf8.RuneLen(r)) + } + + return idx, seg +} + +func (self PrefixAny) Len() int { + return lenNo +} + +func (self PrefixAny) Match(s string) bool { + if !strings.HasPrefix(s, self.Prefix) { + return false + } + return sutil.IndexAnyRunes(s[len(self.Prefix):], self.Separators) == -1 +} + +func (self PrefixAny) String() string { + return fmt.Sprintf("", self.Prefix, string(self.Separators)) +} diff --git a/vendor/github.com/gobwas/glob/match/prefix_suffix.go b/vendor/github.com/gobwas/glob/match/prefix_suffix.go new file mode 100644 index 000000000..8208085a1 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/prefix_suffix.go @@ -0,0 +1,62 @@ +package match + +import ( + "fmt" + "strings" +) + +type PrefixSuffix struct { + Prefix, Suffix string +} + +func NewPrefixSuffix(p, s string) PrefixSuffix { + return PrefixSuffix{p, s} +} + +func (self PrefixSuffix) Index(s string) (int, []int) { + prefixIdx := strings.Index(s, self.Prefix) + if prefixIdx == -1 { + return -1, nil + } + + suffixLen := len(self.Suffix) + if suffixLen <= 0 { + return prefixIdx, []int{len(s) - prefixIdx} + } + + if (len(s) - prefixIdx) <= 0 { + return -1, nil + } + + segments := acquireSegments(len(s) - prefixIdx) + for sub := s[prefixIdx:]; ; { + suffixIdx := strings.LastIndex(sub, self.Suffix) + if suffixIdx == -1 { + break + } + + segments = append(segments, suffixIdx+suffixLen) + sub = sub[:suffixIdx] + } + + if len(segments) == 0 { + releaseSegments(segments) + return -1, nil + } + + reverseSegments(segments) + + return prefixIdx, segments +} + +func (self PrefixSuffix) Len() int { + return lenNo +} + +func (self PrefixSuffix) Match(s string) bool { + return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix) +} + +func (self PrefixSuffix) String() string { + return fmt.Sprintf("", self.Prefix, self.Suffix) +} diff --git a/vendor/github.com/gobwas/glob/match/range.go b/vendor/github.com/gobwas/glob/match/range.go new file mode 100644 index 000000000..ce30245a4 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/range.go @@ -0,0 +1,48 @@ +package match + +import ( + "fmt" + "unicode/utf8" +) + +type Range struct { + Lo, Hi rune + Not bool +} + +func NewRange(lo, hi rune, not bool) Range { + return Range{lo, hi, not} +} + +func (self Range) Len() int { + return lenOne +} + +func (self Range) Match(s string) bool { + r, w := utf8.DecodeRuneInString(s) + if len(s) > w { + return false + } + + inRange := r >= self.Lo && r <= self.Hi + + return inRange == !self.Not +} + +func (self Range) Index(s string) (int, []int) { + for i, r := range s { + if self.Not != (r >= self.Lo && r <= self.Hi) { + return i, segmentsByRuneLength[utf8.RuneLen(r)] + } + } + + return -1, nil +} + +func (self Range) String() string { + var not string + if self.Not { + not = "!" + } + return fmt.Sprintf("", not, string(self.Lo), string(self.Hi)) +} diff --git a/vendor/github.com/gobwas/glob/match/row.go b/vendor/github.com/gobwas/glob/match/row.go new file mode 100644 index 000000000..4379042e4 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/row.go @@ -0,0 +1,77 @@ +package match + +import ( + "fmt" +) + +type Row struct { + Matchers Matchers + RunesLength int + Segments []int +} + +func NewRow(len int, m ...Matcher) Row { + return Row{ + Matchers: Matchers(m), + RunesLength: len, + Segments: []int{len}, + } +} + +func (self Row) matchAll(s string) bool { + var idx int + for _, m := range self.Matchers { + length := m.Len() + + var next, i int + for next = range s[idx:] { + i++ + if i == length { + break + } + } + + if i < length || !m.Match(s[idx:idx+next+1]) { + return false + } + + idx += next + 1 + } + + return true +} + +func (self Row) lenOk(s string) bool { + var i int + for range s { + i++ + if i > self.RunesLength { + return false + } + } + return self.RunesLength == i +} + +func (self Row) Match(s string) bool { + return self.lenOk(s) && self.matchAll(s) +} + +func (self Row) Len() (l int) { + return self.RunesLength +} + +func (self Row) Index(s string) (int, []int) { + for i := range s { + if len(s[i:]) < self.RunesLength { + break + } + if self.matchAll(s[i:]) { + return i, self.Segments + } + } + return -1, nil +} + +func (self Row) String() string { + return fmt.Sprintf("", self.RunesLength, self.Matchers) +} diff --git a/vendor/github.com/gobwas/glob/match/segments.go b/vendor/github.com/gobwas/glob/match/segments.go new file mode 100644 index 000000000..9ea6f3094 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/segments.go @@ -0,0 +1,91 @@ +package match + +import ( + "sync" +) + +type SomePool interface { + Get() []int + Put([]int) +} + +var segmentsPools [1024]sync.Pool + +func toPowerOfTwo(v int) int { + v-- + v |= v >> 1 + v |= v >> 2 + v |= v >> 4 + v |= v >> 8 + v |= v >> 16 + v++ + + return v +} + +const ( + cacheFrom = 16 + cacheToAndHigher = 1024 + cacheFromIndex = 15 + cacheToAndHigherIndex = 1023 +) + +var ( + segments0 = []int{0} + segments1 = []int{1} + segments2 = []int{2} + segments3 = []int{3} + segments4 = []int{4} +) + +var segmentsByRuneLength [5][]int = [5][]int{ + 0: segments0, + 1: segments1, + 2: segments2, + 3: segments3, + 4: segments4, +} + +func init() { + for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 { + func(i int) { + segmentsPools[i-1] = sync.Pool{New: func() interface{} { + return make([]int, 0, i) + }} + }(i) + } +} + +func getTableIndex(c int) int { + p := toPowerOfTwo(c) + switch { + case p >= cacheToAndHigher: + return cacheToAndHigherIndex + case p <= cacheFrom: + return cacheFromIndex + default: + return p - 1 + } +} + +func acquireSegments(c int) []int { + // make []int with less capacity than cacheFrom + // is faster than acquiring it from pool + if c < cacheFrom { + return make([]int, 0, c) + } + + return segmentsPools[getTableIndex(c)].Get().([]int)[:0] +} + +func releaseSegments(s []int) { + c := cap(s) + + // make []int with less capacity than cacheFrom + // is faster than acquiring it from pool + if c < cacheFrom { + return + } + + segmentsPools[getTableIndex(c)].Put(s) +} diff --git a/vendor/github.com/gobwas/glob/match/single.go b/vendor/github.com/gobwas/glob/match/single.go new file mode 100644 index 000000000..ee6e3954c --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/single.go @@ -0,0 +1,43 @@ +package match + +import ( + "fmt" + "github.com/gobwas/glob/util/runes" + "unicode/utf8" +) + +// single represents ? +type Single struct { + Separators []rune +} + +func NewSingle(s []rune) Single { + return Single{s} +} + +func (self Single) Match(s string) bool { + r, w := utf8.DecodeRuneInString(s) + if len(s) > w { + return false + } + + return runes.IndexRune(self.Separators, r) == -1 +} + +func (self Single) Len() int { + return lenOne +} + +func (self Single) Index(s string) (int, []int) { + for i, r := range s { + if runes.IndexRune(self.Separators, r) == -1 { + return i, segmentsByRuneLength[utf8.RuneLen(r)] + } + } + + return -1, nil +} + +func (self Single) String() string { + return fmt.Sprintf("", string(self.Separators)) +} diff --git a/vendor/github.com/gobwas/glob/match/suffix.go b/vendor/github.com/gobwas/glob/match/suffix.go new file mode 100644 index 000000000..85bea8c68 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/suffix.go @@ -0,0 +1,35 @@ +package match + +import ( + "fmt" + "strings" +) + +type Suffix struct { + Suffix string +} + +func NewSuffix(s string) Suffix { + return Suffix{s} +} + +func (self Suffix) Len() int { + return lenNo +} + +func (self Suffix) Match(s string) bool { + return strings.HasSuffix(s, self.Suffix) +} + +func (self Suffix) Index(s string) (int, []int) { + idx := strings.Index(s, self.Suffix) + if idx == -1 { + return -1, nil + } + + return 0, []int{idx + len(self.Suffix)} +} + +func (self Suffix) String() string { + return fmt.Sprintf("", self.Suffix) +} diff --git a/vendor/github.com/gobwas/glob/match/suffix_any.go b/vendor/github.com/gobwas/glob/match/suffix_any.go new file mode 100644 index 000000000..c5106f819 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/suffix_any.go @@ -0,0 +1,43 @@ +package match + +import ( + "fmt" + "strings" + + sutil "github.com/gobwas/glob/util/strings" +) + +type SuffixAny struct { + Suffix string + Separators []rune +} + +func NewSuffixAny(s string, sep []rune) SuffixAny { + return SuffixAny{s, sep} +} + +func (self SuffixAny) Index(s string) (int, []int) { + idx := strings.Index(s, self.Suffix) + if idx == -1 { + return -1, nil + } + + i := sutil.LastIndexAnyRunes(s[:idx], self.Separators) + 1 + + return i, []int{idx + len(self.Suffix) - i} +} + +func (self SuffixAny) Len() int { + return lenNo +} + +func (self SuffixAny) Match(s string) bool { + if !strings.HasSuffix(s, self.Suffix) { + return false + } + return sutil.IndexAnyRunes(s[:len(s)-len(self.Suffix)], self.Separators) == -1 +} + +func (self SuffixAny) String() string { + return fmt.Sprintf("", string(self.Separators), self.Suffix) +} diff --git a/vendor/github.com/gobwas/glob/match/super.go b/vendor/github.com/gobwas/glob/match/super.go new file mode 100644 index 000000000..3875950bb --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/super.go @@ -0,0 +1,33 @@ +package match + +import ( + "fmt" +) + +type Super struct{} + +func NewSuper() Super { + return Super{} +} + +func (self Super) Match(s string) bool { + return true +} + +func (self Super) Len() int { + return lenNo +} + +func (self Super) Index(s string) (int, []int) { + segments := acquireSegments(len(s) + 1) + for i := range s { + segments = append(segments, i) + } + segments = append(segments, len(s)) + + return 0, segments +} + +func (self Super) String() string { + return fmt.Sprintf("") +} diff --git a/vendor/github.com/gobwas/glob/match/text.go b/vendor/github.com/gobwas/glob/match/text.go new file mode 100644 index 000000000..0a17616d3 --- /dev/null +++ b/vendor/github.com/gobwas/glob/match/text.go @@ -0,0 +1,45 @@ +package match + +import ( + "fmt" + "strings" + "unicode/utf8" +) + +// raw represents raw string to match +type Text struct { + Str string + RunesLength int + BytesLength int + Segments []int +} + +func NewText(s string) Text { + return Text{ + Str: s, + RunesLength: utf8.RuneCountInString(s), + BytesLength: len(s), + Segments: []int{len(s)}, + } +} + +func (self Text) Match(s string) bool { + return self.Str == s +} + +func (self Text) Len() int { + return self.RunesLength +} + +func (self Text) Index(s string) (int, []int) { + index := strings.Index(s, self.Str) + if index == -1 { + return -1, nil + } + + return index, self.Segments +} + +func (self Text) String() string { + return fmt.Sprintf("", self.Str) +} diff --git a/vendor/github.com/gobwas/glob/readme.md b/vendor/github.com/gobwas/glob/readme.md new file mode 100644 index 000000000..f58144e73 --- /dev/null +++ b/vendor/github.com/gobwas/glob/readme.md @@ -0,0 +1,148 @@ +# glob.[go](https://golang.org) + +[![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url] + +> Go Globbing Library. + +## Install + +```shell + go get github.com/gobwas/glob +``` + +## Example + +```go + +package main + +import "github.com/gobwas/glob" + +func main() { + var g glob.Glob + + // create simple glob + g = glob.MustCompile("*.github.com") + g.Match("api.github.com") // true + + // quote meta characters and then create simple glob + g = glob.MustCompile(glob.QuoteMeta("*.github.com")) + g.Match("*.github.com") // true + + // create new glob with set of delimiters as ["."] + g = glob.MustCompile("api.*.com", '.') + g.Match("api.github.com") // true + g.Match("api.gi.hub.com") // false + + // create new glob with set of delimiters as ["."] + // but now with super wildcard + g = glob.MustCompile("api.**.com", '.') + g.Match("api.github.com") // true + g.Match("api.gi.hub.com") // true + + // create glob with single symbol wildcard + g = glob.MustCompile("?at") + g.Match("cat") // true + g.Match("fat") // true + g.Match("at") // false + + // create glob with single symbol wildcard and delimiters ['f'] + g = glob.MustCompile("?at", 'f') + g.Match("cat") // true + g.Match("fat") // false + g.Match("at") // false + + // create glob with character-list matchers + g = glob.MustCompile("[abc]at") + g.Match("cat") // true + g.Match("bat") // true + g.Match("fat") // false + g.Match("at") // false + + // create glob with character-list matchers + g = glob.MustCompile("[!abc]at") + g.Match("cat") // false + g.Match("bat") // false + g.Match("fat") // true + g.Match("at") // false + + // create glob with character-range matchers + g = glob.MustCompile("[a-c]at") + g.Match("cat") // true + g.Match("bat") // true + g.Match("fat") // false + g.Match("at") // false + + // create glob with character-range matchers + g = glob.MustCompile("[!a-c]at") + g.Match("cat") // false + g.Match("bat") // false + g.Match("fat") // true + g.Match("at") // false + + // create glob with pattern-alternatives list + g = glob.MustCompile("{cat,bat,[fr]at}") + g.Match("cat") // true + g.Match("bat") // true + g.Match("fat") // true + g.Match("rat") // true + g.Match("at") // false + g.Match("zat") // false +} + +``` + +## Performance + +This library is created for compile-once patterns. This means, that compilation could take time, but +strings matching is done faster, than in case when always parsing template. + +If you will not use compiled `glob.Glob` object, and do `g := glob.MustCompile(pattern); g.Match(...)` every time, then your code will be much more slower. + +Run `go test -bench=.` from source root to see the benchmarks: + +Pattern | Fixture | Match | Speed (ns/op) +--------|---------|-------|-------------- +`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 432 +`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 199 +`https://*.google.*` | `https://account.google.com` | `true` | 96 +`https://*.google.*` | `https://google.com` | `false` | 66 +`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 163 +`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 197 +`{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 22 +`{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 24 +`abc*` | `abcdef` | `true` | 8.15 +`abc*` | `af` | `false` | 5.68 +`*def` | `abcdef` | `true` | 8.84 +`*def` | `af` | `false` | 5.74 +`ab*ef` | `abcdef` | `true` | 15.2 +`ab*ef` | `af` | `false` | 10.4 + +The same things with `regexp` package: + +Pattern | Fixture | Match | Speed (ns/op) +--------|---------|-------|-------------- +`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 2553 +`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 1383 +`^https:\/\/.*\.google\..*$` | `https://account.google.com` | `true` | 1205 +`^https:\/\/.*\.google\..*$` | `https://google.com` | `false` | 767 +`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://yahoo.com` | `true` | 1435 +`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://google.com` | `false` | 1674 +`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `https://safe.gobwas.com` | `true` | 1039 +`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `http://safe.gobwas.com` | `false` | 272 +`^abc.*$` | `abcdef` | `true` | 237 +`^abc.*$` | `af` | `false` | 100 +`^.*def$` | `abcdef` | `true` | 464 +`^.*def$` | `af` | `false` | 265 +`^ab.*ef$` | `abcdef` | `true` | 375 +`^ab.*ef$` | `af` | `false` | 145 + +[godoc-image]: https://godoc.org/github.com/gobwas/glob?status.svg +[godoc-url]: https://godoc.org/github.com/gobwas/glob +[travis-image]: https://travis-ci.org/gobwas/glob.svg?branch=master +[travis-url]: https://travis-ci.org/gobwas/glob + +## Syntax + +Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm), +except that `**` is aka super-asterisk, that do not sensitive for separators. \ No newline at end of file diff --git a/vendor/github.com/gobwas/glob/syntax/ast/ast.go b/vendor/github.com/gobwas/glob/syntax/ast/ast.go new file mode 100644 index 000000000..3220a694a --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/ast/ast.go @@ -0,0 +1,122 @@ +package ast + +import ( + "bytes" + "fmt" +) + +type Node struct { + Parent *Node + Children []*Node + Value interface{} + Kind Kind +} + +func NewNode(k Kind, v interface{}, ch ...*Node) *Node { + n := &Node{ + Kind: k, + Value: v, + } + for _, c := range ch { + Insert(n, c) + } + return n +} + +func (a *Node) Equal(b *Node) bool { + if a.Kind != b.Kind { + return false + } + if a.Value != b.Value { + return false + } + if len(a.Children) != len(b.Children) { + return false + } + for i, c := range a.Children { + if !c.Equal(b.Children[i]) { + return false + } + } + return true +} + +func (a *Node) String() string { + var buf bytes.Buffer + buf.WriteString(a.Kind.String()) + if a.Value != nil { + buf.WriteString(" =") + buf.WriteString(fmt.Sprintf("%v", a.Value)) + } + if len(a.Children) > 0 { + buf.WriteString(" [") + for i, c := range a.Children { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(c.String()) + } + buf.WriteString("]") + } + return buf.String() +} + +func Insert(parent *Node, children ...*Node) { + parent.Children = append(parent.Children, children...) + for _, ch := range children { + ch.Parent = parent + } +} + +type List struct { + Not bool + Chars string +} + +type Range struct { + Not bool + Lo, Hi rune +} + +type Text struct { + Text string +} + +type Kind int + +const ( + KindNothing Kind = iota + KindPattern + KindList + KindRange + KindText + KindAny + KindSuper + KindSingle + KindAnyOf +) + +func (k Kind) String() string { + switch k { + case KindNothing: + return "Nothing" + case KindPattern: + return "Pattern" + case KindList: + return "List" + case KindRange: + return "Range" + case KindText: + return "Text" + case KindAny: + return "Any" + case KindSuper: + return "Super" + case KindSingle: + return "Single" + case KindAnyOf: + return "AnyOf" + default: + return "" + } +} diff --git a/vendor/github.com/gobwas/glob/syntax/ast/parser.go b/vendor/github.com/gobwas/glob/syntax/ast/parser.go new file mode 100644 index 000000000..429b40943 --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/ast/parser.go @@ -0,0 +1,157 @@ +package ast + +import ( + "errors" + "fmt" + "github.com/gobwas/glob/syntax/lexer" + "unicode/utf8" +) + +type Lexer interface { + Next() lexer.Token +} + +type parseFn func(*Node, Lexer) (parseFn, *Node, error) + +func Parse(lexer Lexer) (*Node, error) { + var parser parseFn + + root := NewNode(KindPattern, nil) + + var ( + tree *Node + err error + ) + for parser, tree = parserMain, root; parser != nil; { + parser, tree, err = parser(tree, lexer) + if err != nil { + return nil, err + } + } + + return root, nil +} + +func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) { + for { + token := lex.Next() + switch token.Type { + case lexer.EOF: + return nil, tree, nil + + case lexer.Error: + return nil, tree, errors.New(token.Raw) + + case lexer.Text: + Insert(tree, NewNode(KindText, Text{token.Raw})) + return parserMain, tree, nil + + case lexer.Any: + Insert(tree, NewNode(KindAny, nil)) + return parserMain, tree, nil + + case lexer.Super: + Insert(tree, NewNode(KindSuper, nil)) + return parserMain, tree, nil + + case lexer.Single: + Insert(tree, NewNode(KindSingle, nil)) + return parserMain, tree, nil + + case lexer.RangeOpen: + return parserRange, tree, nil + + case lexer.TermsOpen: + a := NewNode(KindAnyOf, nil) + Insert(tree, a) + + p := NewNode(KindPattern, nil) + Insert(a, p) + + return parserMain, p, nil + + case lexer.Separator: + p := NewNode(KindPattern, nil) + Insert(tree.Parent, p) + + return parserMain, p, nil + + case lexer.TermsClose: + return parserMain, tree.Parent.Parent, nil + + default: + return nil, tree, fmt.Errorf("unexpected token: %s", token) + } + } + return nil, tree, fmt.Errorf("unknown error") +} + +func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) { + var ( + not bool + lo rune + hi rune + chars string + ) + for { + token := lex.Next() + switch token.Type { + case lexer.EOF: + return nil, tree, errors.New("unexpected end") + + case lexer.Error: + return nil, tree, errors.New(token.Raw) + + case lexer.Not: + not = true + + case lexer.RangeLo: + r, w := utf8.DecodeRuneInString(token.Raw) + if len(token.Raw) > w { + return nil, tree, fmt.Errorf("unexpected length of lo character") + } + lo = r + + case lexer.RangeBetween: + // + + case lexer.RangeHi: + r, w := utf8.DecodeRuneInString(token.Raw) + if len(token.Raw) > w { + return nil, tree, fmt.Errorf("unexpected length of lo character") + } + + hi = r + + if hi < lo { + return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo)) + } + + case lexer.Text: + chars = token.Raw + + case lexer.RangeClose: + isRange := lo != 0 && hi != 0 + isChars := chars != "" + + if isChars == isRange { + return nil, tree, fmt.Errorf("could not parse range") + } + + if isRange { + Insert(tree, NewNode(KindRange, Range{ + Lo: lo, + Hi: hi, + Not: not, + })) + } else { + Insert(tree, NewNode(KindList, List{ + Chars: chars, + Not: not, + })) + } + + return parserMain, tree, nil + } + } +} diff --git a/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go b/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go new file mode 100644 index 000000000..a1c8d1962 --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/lexer/lexer.go @@ -0,0 +1,273 @@ +package lexer + +import ( + "bytes" + "fmt" + "github.com/gobwas/glob/util/runes" + "unicode/utf8" +) + +const ( + char_any = '*' + char_comma = ',' + char_single = '?' + char_escape = '\\' + char_range_open = '[' + char_range_close = ']' + char_terms_open = '{' + char_terms_close = '}' + char_range_not = '!' + char_range_between = '-' +) + +var specials = []byte{ + char_any, + char_single, + char_escape, + char_range_open, + char_range_close, + char_terms_open, + char_terms_close, +} + +func Special(c byte) bool { + return bytes.IndexByte(specials, c) != -1 +} + +type tokens []Token + +func (i *tokens) shift() (ret Token) { + ret = (*i)[0] + copy(*i, (*i)[1:]) + *i = (*i)[:len(*i)-1] + return +} + +func (i *tokens) push(v Token) { + *i = append(*i, v) +} + +func (i *tokens) empty() bool { + return len(*i) == 0 +} + +var eof rune = 0 + +type lexer struct { + data string + pos int + err error + + tokens tokens + termsLevel int + + lastRune rune + lastRuneSize int + hasRune bool +} + +func NewLexer(source string) *lexer { + l := &lexer{ + data: source, + tokens: tokens(make([]Token, 0, 4)), + } + return l +} + +func (l *lexer) Next() Token { + if l.err != nil { + return Token{Error, l.err.Error()} + } + if !l.tokens.empty() { + return l.tokens.shift() + } + + l.fetchItem() + return l.Next() +} + +func (l *lexer) peek() (r rune, w int) { + if l.pos == len(l.data) { + return eof, 0 + } + + r, w = utf8.DecodeRuneInString(l.data[l.pos:]) + if r == utf8.RuneError { + l.errorf("could not read rune") + r = eof + w = 0 + } + + return +} + +func (l *lexer) read() rune { + if l.hasRune { + l.hasRune = false + l.seek(l.lastRuneSize) + return l.lastRune + } + + r, s := l.peek() + l.seek(s) + + l.lastRune = r + l.lastRuneSize = s + + return r +} + +func (l *lexer) seek(w int) { + l.pos += w +} + +func (l *lexer) unread() { + if l.hasRune { + l.errorf("could not unread rune") + return + } + l.seek(-l.lastRuneSize) + l.hasRune = true +} + +func (l *lexer) errorf(f string, v ...interface{}) { + l.err = fmt.Errorf(f, v...) +} + +func (l *lexer) inTerms() bool { + return l.termsLevel > 0 +} + +func (l *lexer) termsEnter() { + l.termsLevel++ +} + +func (l *lexer) termsLeave() { + l.termsLevel-- +} + +var inTextBreakers = []rune{char_single, char_any, char_range_open, char_terms_open} +var inTermsBreakers = append(inTextBreakers, char_terms_close, char_comma) + +func (l *lexer) fetchItem() { + r := l.read() + switch { + case r == eof: + l.tokens.push(Token{EOF, ""}) + + case r == char_terms_open: + l.termsEnter() + l.tokens.push(Token{TermsOpen, string(r)}) + + case r == char_comma && l.inTerms(): + l.tokens.push(Token{Separator, string(r)}) + + case r == char_terms_close && l.inTerms(): + l.tokens.push(Token{TermsClose, string(r)}) + l.termsLeave() + + case r == char_range_open: + l.tokens.push(Token{RangeOpen, string(r)}) + l.fetchRange() + + case r == char_single: + l.tokens.push(Token{Single, string(r)}) + + case r == char_any: + if l.read() == char_any { + l.tokens.push(Token{Super, string(r) + string(r)}) + } else { + l.unread() + l.tokens.push(Token{Any, string(r)}) + } + + default: + l.unread() + + var breakers []rune + if l.inTerms() { + breakers = inTermsBreakers + } else { + breakers = inTextBreakers + } + l.fetchText(breakers) + } +} + +func (l *lexer) fetchRange() { + var wantHi bool + var wantClose bool + var seenNot bool + for { + r := l.read() + if r == eof { + l.errorf("unexpected end of input") + return + } + + if wantClose { + if r != char_range_close { + l.errorf("expected close range character") + } else { + l.tokens.push(Token{RangeClose, string(r)}) + } + return + } + + if wantHi { + l.tokens.push(Token{RangeHi, string(r)}) + wantClose = true + continue + } + + if !seenNot && r == char_range_not { + l.tokens.push(Token{Not, string(r)}) + seenNot = true + continue + } + + if n, w := l.peek(); n == char_range_between { + l.seek(w) + l.tokens.push(Token{RangeLo, string(r)}) + l.tokens.push(Token{RangeBetween, string(n)}) + wantHi = true + continue + } + + l.unread() // unread first peek and fetch as text + l.fetchText([]rune{char_range_close}) + wantClose = true + } +} + +func (l *lexer) fetchText(breakers []rune) { + var data []rune + var escaped bool + +reading: + for { + r := l.read() + if r == eof { + break + } + + if !escaped { + if r == char_escape { + escaped = true + continue + } + + if runes.IndexRune(breakers, r) != -1 { + l.unread() + break reading + } + } + + escaped = false + data = append(data, r) + } + + if len(data) > 0 { + l.tokens.push(Token{Text, string(data)}) + } +} diff --git a/vendor/github.com/gobwas/glob/syntax/lexer/token.go b/vendor/github.com/gobwas/glob/syntax/lexer/token.go new file mode 100644 index 000000000..2797c4e83 --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/lexer/token.go @@ -0,0 +1,88 @@ +package lexer + +import "fmt" + +type TokenType int + +const ( + EOF TokenType = iota + Error + Text + Char + Any + Super + Single + Not + Separator + RangeOpen + RangeClose + RangeLo + RangeHi + RangeBetween + TermsOpen + TermsClose +) + +func (tt TokenType) String() string { + switch tt { + case EOF: + return "eof" + + case Error: + return "error" + + case Text: + return "text" + + case Char: + return "char" + + case Any: + return "any" + + case Super: + return "super" + + case Single: + return "single" + + case Not: + return "not" + + case Separator: + return "separator" + + case RangeOpen: + return "range_open" + + case RangeClose: + return "range_close" + + case RangeLo: + return "range_lo" + + case RangeHi: + return "range_hi" + + case RangeBetween: + return "range_between" + + case TermsOpen: + return "terms_open" + + case TermsClose: + return "terms_close" + + default: + return "undef" + } +} + +type Token struct { + Type TokenType + Raw string +} + +func (t Token) String() string { + return fmt.Sprintf("%v<%q>", t.Type, t.Raw) +} diff --git a/vendor/github.com/gobwas/glob/syntax/syntax.go b/vendor/github.com/gobwas/glob/syntax/syntax.go new file mode 100644 index 000000000..1d168b148 --- /dev/null +++ b/vendor/github.com/gobwas/glob/syntax/syntax.go @@ -0,0 +1,14 @@ +package syntax + +import ( + "github.com/gobwas/glob/syntax/ast" + "github.com/gobwas/glob/syntax/lexer" +) + +func Parse(s string) (*ast.Node, error) { + return ast.Parse(lexer.NewLexer(s)) +} + +func Special(b byte) bool { + return lexer.Special(b) +} diff --git a/vendor/github.com/gobwas/glob/util/runes/runes.go b/vendor/github.com/gobwas/glob/util/runes/runes.go new file mode 100644 index 000000000..a72355641 --- /dev/null +++ b/vendor/github.com/gobwas/glob/util/runes/runes.go @@ -0,0 +1,154 @@ +package runes + +func Index(s, needle []rune) int { + ls, ln := len(s), len(needle) + + switch { + case ln == 0: + return 0 + case ln == 1: + return IndexRune(s, needle[0]) + case ln == ls: + if Equal(s, needle) { + return 0 + } + return -1 + case ln > ls: + return -1 + } + +head: + for i := 0; i < ls && ls-i >= ln; i++ { + for y := 0; y < ln; y++ { + if s[i+y] != needle[y] { + continue head + } + } + + return i + } + + return -1 +} + +func LastIndex(s, needle []rune) int { + ls, ln := len(s), len(needle) + + switch { + case ln == 0: + if ls == 0 { + return 0 + } + return ls + case ln == 1: + return IndexLastRune(s, needle[0]) + case ln == ls: + if Equal(s, needle) { + return 0 + } + return -1 + case ln > ls: + return -1 + } + +head: + for i := ls - 1; i >= 0 && i >= ln; i-- { + for y := ln - 1; y >= 0; y-- { + if s[i-(ln-y-1)] != needle[y] { + continue head + } + } + + return i - ln + 1 + } + + return -1 +} + +// IndexAny returns the index of the first instance of any Unicode code point +// from chars in s, or -1 if no Unicode code point from chars is present in s. +func IndexAny(s, chars []rune) int { + if len(chars) > 0 { + for i, c := range s { + for _, m := range chars { + if c == m { + return i + } + } + } + } + return -1 +} + +func Contains(s, needle []rune) bool { + return Index(s, needle) >= 0 +} + +func Max(s []rune) (max rune) { + for _, r := range s { + if r > max { + max = r + } + } + + return +} + +func Min(s []rune) rune { + min := rune(-1) + for _, r := range s { + if min == -1 { + min = r + continue + } + + if r < min { + min = r + } + } + + return min +} + +func IndexRune(s []rune, r rune) int { + for i, c := range s { + if c == r { + return i + } + } + return -1 +} + +func IndexLastRune(s []rune, r rune) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == r { + return i + } + } + + return -1 +} + +func Equal(a, b []rune) bool { + if len(a) == len(b) { + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + + return true + } + + return false +} + +// HasPrefix tests whether the string s begins with prefix. +func HasPrefix(s, prefix []rune) bool { + return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix) +} + +// HasSuffix tests whether the string s ends with suffix. +func HasSuffix(s, suffix []rune) bool { + return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix) +} diff --git a/vendor/github.com/gobwas/glob/util/strings/strings.go b/vendor/github.com/gobwas/glob/util/strings/strings.go new file mode 100644 index 000000000..e8ee1920b --- /dev/null +++ b/vendor/github.com/gobwas/glob/util/strings/strings.go @@ -0,0 +1,39 @@ +package strings + +import ( + "strings" + "unicode/utf8" +) + +func IndexAnyRunes(s string, rs []rune) int { + for _, r := range rs { + if i := strings.IndexRune(s, r); i != -1 { + return i + } + } + + return -1 +} + +func LastIndexAnyRunes(s string, rs []rune) int { + for _, r := range rs { + i := -1 + if 0 <= r && r < utf8.RuneSelf { + i = strings.LastIndexByte(s, byte(r)) + } else { + sub := s + for len(sub) > 0 { + j := strings.IndexRune(s, r) + if j == -1 { + break + } + i = j + sub = sub[i+1:] + } + } + if i != -1 { + return i + } + } + return -1 +} diff --git a/vendor/github.com/golang/mock/AUTHORS b/vendor/github.com/golang/mock/AUTHORS new file mode 100644 index 000000000..660b8ccc8 --- /dev/null +++ b/vendor/github.com/golang/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/github.com/golang/mock/CONTRIBUTORS b/vendor/github.com/golang/mock/CONTRIBUTORS new file mode 100644 index 000000000..def849cab --- /dev/null +++ b/vendor/github.com/golang/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/github.com/golang/mock/LICENSE b/vendor/github.com/golang/mock/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/github.com/golang/mock/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/github.com/golang/mock/gomock/call.go b/vendor/github.com/golang/mock/gomock/call.go new file mode 100644 index 000000000..3d54d9f5d --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/call.go @@ -0,0 +1,420 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestHelper // for triggering test failures on invalid call setup + + receiver interface{} // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]interface{}) []interface{} +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + t.Helper() + + // TODO: check arity, types. + margs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + margs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + margs[i] = Nil() + } else { + margs[i] = Eq(arg) + } + } + + origin := callerInfo(3) + actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} { + // Synthesize the zero value for each of the return args' types. + rets := make([]interface{}, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{t: t, receiver: receiver, method: method, methodType: methodType, + args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions} +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also +// sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also +// sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) DoAndReturn(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + vrets := v.Call(vargs) + rets := make([]interface{}, len(vrets)) + for i, ret := range vrets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) Do(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vargs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...interface{}) *Call { + c.t.Helper() + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]interface{}) []interface{} { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice, SetArg +// will copy value's elements into the nth argument. +func (c *Call) SetArg(n int, value interface{}) *Call { + c.t.Helper() + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface: + // nothing to do + case reflect.Slice: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []interface{}) []interface{} { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + c.t.Helper() + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true iff the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vargsType := c.methodType.In(c.methodType.NumIn() - 1) + vargs := reflect.MakeSlice(vargsType, 0, len(args)-i) + for _, arg := range args[i:] { + vargs = reflect.Append(vargs, reflect.ValueOf(arg)) + } + if m.Matches(vargs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i:], c.args[i]) + + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +func setSlice(arg interface{}, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func (c *Call) addAction(action func([]interface{}) []interface{}) { + c.actions = append(c.actions, action) +} diff --git a/vendor/github.com/golang/mock/gomock/callset.go b/vendor/github.com/golang/mock/gomock/callset.go new file mode 100644 index 000000000..c44a8a585 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/callset.go @@ -0,0 +1,108 @@ +// Copyright 2011 Google Inc. +// +// 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 gomock + +import ( + "bytes" + "fmt" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver interface{} + fname string +} + +func newCallSet() *callSet { + return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)} +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { + key := callSetKey{receiver, method} + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } + } + + if len(expected)+len(exhausted) == 0 { + fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, fmt.Errorf(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} diff --git a/vendor/github.com/golang/mock/gomock/controller.go b/vendor/github.com/golang/mock/gomock/controller.go new file mode 100644 index 000000000..6fde25f50 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/controller.go @@ -0,0 +1,235 @@ +// Copyright 2010 Google Inc. +// +// 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. + +// GoMock - a mock framework for Go. +// +// Standard usage: +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +// +// TODO: +// - Handle different argument/return types (e.g. ..., chan, map, interface). +package gomock + +import ( + "context" + "fmt" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. +// It is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +// TestHelper is a TestReporter that has the Helper method. It is satisfied +// by the standard library's *testing.T. +type TestHelper interface { + TestReporter + Helper() +} + +// A Controller represents the top-level control of a mock ecosystem. +// It defines the scope and lifetime of mock objects, as well as their expectations. +// It is safe to call Controller's methods from multiple goroutines. +type Controller struct { + // T should only be called within a generated mock. It is not intended to + // be used in user code and may be changed in future versions. T is the + // TestReporter passed in when creating the Controller via NewController. + // If the TestReporter does not implment a TestHelper it will be wrapped + // with a nopTestHelper. + T TestHelper + mu sync.Mutex + expectedCalls *callSet + finished bool +} + +func NewController(t TestReporter) *Controller { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + return &Controller{ + T: h, + expectedCalls: newCallSet(), + } +} + +type cancelReporter struct { + TestHelper + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...interface{}) { + r.TestHelper.Errorf(format, args...) +} +func (r *cancelReporter) Fatalf(format string, args ...interface{}) { + defer r.cancel() + r.TestHelper.Fatalf(format, args...) +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + h, ok := t.(TestHelper) + if !ok { + h = nopTestHelper{t} + } + + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{h, cancel}), ctx +} + +type nopTestHelper struct { + TestReporter +} + +func (h nopTestHelper) Helper() {} + +func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call { + ctrl.T.Helper() + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + ctrl.T.Helper() + + call := newCall(ctrl.T, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} { + ctrl.T.Helper() + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]interface{}) []interface{} { + ctrl.T.Helper() + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + origin := callerInfo(2) + ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequite calls, + // * and the prerequite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call(args) + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []interface{} + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +func (ctrl *Controller) Finish() { + ctrl.T.Helper() + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + ctrl.finished = true + + // If we're currently panicking, probably because this is a deferred call, + // pass through the panic. + if err := recover(); err != nil { + panic(err) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.T.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + ctrl.T.Fatalf("aborting test due to missing call(s)") + } +} + +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} diff --git a/vendor/github.com/golang/mock/gomock/matchers.go b/vendor/github.com/golang/mock/gomock/matchers.go new file mode 100644 index 000000000..189796f86 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/matchers.go @@ -0,0 +1,122 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x interface{}) bool + + // String describes what the matcher matches. + String() string +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(x interface{}) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type eqMatcher struct { + x interface{} +} + +func (e eqMatcher) Matches(x interface{}) bool { + return reflect.DeepEqual(e.x, x) +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %v", e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x interface{}) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x interface{}) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + // TODO: Improve this if we add a NotString method to the Matcher interface. + return "not(" + n.m.String() + ")" +} + +type assignableToTypeOfMatcher struct { + targetType reflect.Type +} + +func (m assignableToTypeOfMatcher) Matches(x interface{}) bool { + return reflect.TypeOf(x).AssignableTo(m.targetType) +} + +func (m assignableToTypeOfMatcher) String() string { + return "is assignable to " + m.targetType.Name() +} + +// Constructors +func Any() Matcher { return anyMatcher{} } +func Eq(x interface{}) Matcher { return eqMatcher{x} } +func Nil() Matcher { return nilMatcher{} } +func Not(x interface{}) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} + +// AssignableToTypeOf is a Matcher that matches if the parameter to the mock +// function is assignable to the type of the parameter to this function. +// +// Example usage: +// +// dbMock.EXPECT(). +// Insert(gomock.AssignableToTypeOf(&EmployeeRecord{})). +// Return(errors.New("DB error")) +// +func AssignableToTypeOf(x interface{}) Matcher { + return assignableToTypeOfMatcher{reflect.TypeOf(x)} +} diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore new file mode 100644 index 000000000..042091d9b --- /dev/null +++ b/vendor/github.com/golang/snappy/.gitignore @@ -0,0 +1,16 @@ +cmd/snappytool/snappytool +testdata/bench + +# These explicitly listed benchmark data files are for an obsolete version of +# snappy_test.go. +testdata/alice29.txt +testdata/asyoulik.txt +testdata/fireworks.jpeg +testdata/geo.protodata +testdata/html +testdata/html_x_4 +testdata/kppkn.gtb +testdata/lcet10.txt +testdata/paper-100k.pdf +testdata/plrabn12.txt +testdata/urls.10K diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS new file mode 100644 index 000000000..bcfa19520 --- /dev/null +++ b/vendor/github.com/golang/snappy/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of Snappy-Go authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Damian Gryski +Google Inc. +Jan Mercl <0xjnml@gmail.com> +Rodolfo Carvalho +Sebastien Binet diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS new file mode 100644 index 000000000..931ae3160 --- /dev/null +++ b/vendor/github.com/golang/snappy/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Snappy-Go repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Damian Gryski +Jan Mercl <0xjnml@gmail.com> +Kai Backman +Marc-Antoine Ruel +Nigel Tao +Rob Pike +Rodolfo Carvalho +Russ Cox +Sebastien Binet diff --git a/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/golang/snappy/LICENSE new file mode 100644 index 000000000..6050c10f4 --- /dev/null +++ b/vendor/github.com/golang/snappy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-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/github.com/golang/snappy/README b/vendor/github.com/golang/snappy/README new file mode 100644 index 000000000..cea12879a --- /dev/null +++ b/vendor/github.com/golang/snappy/README @@ -0,0 +1,107 @@ +The Snappy compression format in the Go programming language. + +To download and install from source: +$ go get github.com/golang/snappy + +Unless otherwise noted, the Snappy-Go source files are distributed +under the BSD-style license found in the LICENSE file. + + + +Benchmarks. + +The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten +or so files, the same set used by the C++ Snappy code (github.com/google/snappy +and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @ +3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29: + +"go test -test.bench=." + +_UFlat0-8 2.19GB/s ± 0% html +_UFlat1-8 1.41GB/s ± 0% urls +_UFlat2-8 23.5GB/s ± 2% jpg +_UFlat3-8 1.91GB/s ± 0% jpg_200 +_UFlat4-8 14.0GB/s ± 1% pdf +_UFlat5-8 1.97GB/s ± 0% html4 +_UFlat6-8 814MB/s ± 0% txt1 +_UFlat7-8 785MB/s ± 0% txt2 +_UFlat8-8 857MB/s ± 0% txt3 +_UFlat9-8 719MB/s ± 1% txt4 +_UFlat10-8 2.84GB/s ± 0% pb +_UFlat11-8 1.05GB/s ± 0% gaviota + +_ZFlat0-8 1.04GB/s ± 0% html +_ZFlat1-8 534MB/s ± 0% urls +_ZFlat2-8 15.7GB/s ± 1% jpg +_ZFlat3-8 740MB/s ± 3% jpg_200 +_ZFlat4-8 9.20GB/s ± 1% pdf +_ZFlat5-8 991MB/s ± 0% html4 +_ZFlat6-8 379MB/s ± 0% txt1 +_ZFlat7-8 352MB/s ± 0% txt2 +_ZFlat8-8 396MB/s ± 1% txt3 +_ZFlat9-8 327MB/s ± 1% txt4 +_ZFlat10-8 1.33GB/s ± 1% pb +_ZFlat11-8 605MB/s ± 1% gaviota + + + +"go test -test.bench=. -tags=noasm" + +_UFlat0-8 621MB/s ± 2% html +_UFlat1-8 494MB/s ± 1% urls +_UFlat2-8 23.2GB/s ± 1% jpg +_UFlat3-8 1.12GB/s ± 1% jpg_200 +_UFlat4-8 4.35GB/s ± 1% pdf +_UFlat5-8 609MB/s ± 0% html4 +_UFlat6-8 296MB/s ± 0% txt1 +_UFlat7-8 288MB/s ± 0% txt2 +_UFlat8-8 309MB/s ± 1% txt3 +_UFlat9-8 280MB/s ± 1% txt4 +_UFlat10-8 753MB/s ± 0% pb +_UFlat11-8 400MB/s ± 0% gaviota + +_ZFlat0-8 409MB/s ± 1% html +_ZFlat1-8 250MB/s ± 1% urls +_ZFlat2-8 12.3GB/s ± 1% jpg +_ZFlat3-8 132MB/s ± 0% jpg_200 +_ZFlat4-8 2.92GB/s ± 0% pdf +_ZFlat5-8 405MB/s ± 1% html4 +_ZFlat6-8 179MB/s ± 1% txt1 +_ZFlat7-8 170MB/s ± 1% txt2 +_ZFlat8-8 189MB/s ± 1% txt3 +_ZFlat9-8 164MB/s ± 1% txt4 +_ZFlat10-8 479MB/s ± 1% pb +_ZFlat11-8 270MB/s ± 1% gaviota + + + +For comparison (Go's encoded output is byte-for-byte identical to C++'s), here +are the numbers from C++ Snappy's + +make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log + +BM_UFlat/0 2.4GB/s html +BM_UFlat/1 1.4GB/s urls +BM_UFlat/2 21.8GB/s jpg +BM_UFlat/3 1.5GB/s jpg_200 +BM_UFlat/4 13.3GB/s pdf +BM_UFlat/5 2.1GB/s html4 +BM_UFlat/6 1.0GB/s txt1 +BM_UFlat/7 959.4MB/s txt2 +BM_UFlat/8 1.0GB/s txt3 +BM_UFlat/9 864.5MB/s txt4 +BM_UFlat/10 2.9GB/s pb +BM_UFlat/11 1.2GB/s gaviota + +BM_ZFlat/0 944.3MB/s html (22.31 %) +BM_ZFlat/1 501.6MB/s urls (47.78 %) +BM_ZFlat/2 14.3GB/s jpg (99.95 %) +BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %) +BM_ZFlat/4 8.3GB/s pdf (83.30 %) +BM_ZFlat/5 903.5MB/s html4 (22.52 %) +BM_ZFlat/6 336.0MB/s txt1 (57.88 %) +BM_ZFlat/7 312.3MB/s txt2 (61.91 %) +BM_ZFlat/8 353.1MB/s txt3 (54.99 %) +BM_ZFlat/9 289.9MB/s txt4 (66.26 %) +BM_ZFlat/10 1.2GB/s pb (19.68 %) +BM_ZFlat/11 527.4MB/s gaviota (37.72 %) diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go new file mode 100644 index 000000000..72efb0353 --- /dev/null +++ b/vendor/github.com/golang/snappy/decode.go @@ -0,0 +1,237 @@ +// Copyright 2011 The Snappy-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 snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = errors.New("snappy: corrupt input") + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = errors.New("snappy: decoded block is too large") + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = errors.New("snappy: unsupported input") + + errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrTooLarge + } + return int(v), n, nil +} + +const ( + decodeErrCodeCorrupt = 1 + decodeErrCodeUnsupportedLiteralLength = 2 +) + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= len(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + switch decode(dst, src[s:]) { + case 0: + return dst, nil + case decodeErrCodeUnsupportedLiteralLength: + return nil, errUnsupportedLiteralLength + } + return nil, ErrCorrupt +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + decoded: make([]byte, maxBlockSize), + buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), + } +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +type Reader struct { + r io.Reader + err error + decoded []byte + buf []byte + // decoded[i:j] contains decoded bytes that have not yet been passed on. + i, j int + readHeader bool +} + +// Reset discards any buffered data, resets all state, and switches the Snappy +// reader to read from r. This permits reusing a Reader rather than allocating +// a new one. +func (r *Reader) Reset(reader io.Reader) { + r.r = reader + r.err = nil + r.i = 0 + r.j = 0 + r.readHeader = false +} + +func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + return true +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + for { + if r.i < r.j { + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil + } + if !r.readFull(r.buf[:4], true) { + return 0, r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return 0, r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + if chunkLen > len(r.buf) { + r.err = ErrUnsupported + return 0, r.err + } + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return 0, r.err + } + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeUncompressedData: + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - checksumSize + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.decoded[:n], false) { + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return 0, r.err + } + for i := 0; i < len(magicBody); i++ { + if r.buf[i] != magicBody[i] { + r.err = ErrCorrupt + return 0, r.err + } + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + r.err = ErrUnsupported + return 0, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.readFull(r.buf[:chunkLen], false) { + return 0, r.err + } + } +} diff --git a/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/golang/snappy/decode_amd64.go new file mode 100644 index 000000000..fcd192b84 --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_amd64.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Snappy-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 +// +build gc +// +build !noasm + +package snappy + +// decode has the same semantics as in decode_other.go. +// +//go:noescape +func decode(dst, src []byte) int diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s new file mode 100644 index 000000000..e6179f65e --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_amd64.s @@ -0,0 +1,490 @@ +// 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 +// +build gc +// +build !noasm + +#include "textflag.h" + +// The asm code generally follows the pure Go code in decode_other.go, except +// where marked with a "!!!". + +// func decode(dst, src []byte) int +// +// All local variables fit into registers. The non-zero stack size is only to +// spill registers and push args when issuing a CALL. The register allocation: +// - AX scratch +// - BX scratch +// - CX length or x +// - DX offset +// - SI &src[s] +// - DI &dst[d] +// + R8 dst_base +// + R9 dst_len +// + R10 dst_base + dst_len +// + R11 src_base +// + R12 src_len +// + R13 src_base + src_len +// - R14 used by doCopy +// - R15 used by doCopy +// +// The registers R8-R13 (marked with a "+") are set at the start of the +// function, and after a CALL returns, and are not otherwise modified. +// +// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI. +// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI. +TEXT ·decode(SB), NOSPLIT, $48-56 + // Initialize SI, DI and R8-R13. + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, DI + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, SI + MOVQ R11, R13 + ADDQ R12, R13 + +loop: + // for s < len(src) + CMPQ SI, R13 + JEQ end + + // CX = uint32(src[s]) + // + // switch src[s] & 0x03 + MOVBLZX (SI), CX + MOVL CX, BX + ANDL $3, BX + CMPL BX, $1 + JAE tagCopy + + // ---------------------------------------- + // The code below handles literal tags. + + // case tagLiteral: + // x := uint32(src[s] >> 2) + // switch + SHRL $2, CX + CMPL CX, $60 + JAE tagLit60Plus + + // case x < 60: + // s++ + INCQ SI + +doLit: + // This is the end of the inner "switch", when we have a literal tag. + // + // We assume that CX == x and x fits in a uint32, where x is the variable + // used in the pure Go decode_other.go code. + + // length = int(x) + 1 + // + // Unlike the pure Go code, we don't need to check if length <= 0 because + // CX can hold 64 bits, so the increment cannot overflow. + INCQ CX + + // Prepare to check if copying length bytes will run past the end of dst or + // src. + // + // AX = len(dst) - d + // BX = len(src) - s + MOVQ R10, AX + SUBQ DI, AX + MOVQ R13, BX + SUBQ SI, BX + + // !!! Try a faster technique for short (16 or fewer bytes) copies. + // + // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { + // goto callMemmove // Fall back on calling runtime·memmove. + // } + // + // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s + // against 21 instead of 16, because it cannot assume that all of its input + // is contiguous in memory and so it needs to leave enough source bytes to + // read the next tag without refilling buffers, but Go's Decode assumes + // contiguousness (the src argument is a []byte). + CMPQ CX, $16 + JGT callMemmove + CMPQ AX, $16 + JLT callMemmove + CMPQ BX, $16 + JLT callMemmove + + // !!! Implement the copy from src to dst as a 16-byte load and store. + // (Decode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only length bytes, but that's + // OK. If the input is a valid Snappy encoding then subsequent iterations + // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a + // non-nil error), so the overrun will be ignored. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(SI), X0 + MOVOU X0, 0(DI) + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +callMemmove: + // if length > len(dst)-d || length > len(src)-s { etc } + CMPQ CX, AX + JGT errCorrupt + CMPQ CX, BX + JGT errCorrupt + + // copy(dst[d:], src[s:s+length]) + // + // This means calling runtime·memmove(&dst[d], &src[s], length), so we push + // DI, SI and CX as arguments. Coincidentally, we also need to spill those + // three registers to the stack, to save local variables across the CALL. + MOVQ DI, 0(SP) + MOVQ SI, 8(SP) + MOVQ CX, 16(SP) + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + MOVQ CX, 40(SP) + CALL runtime·memmove(SB) + + // Restore local variables: unspill registers from the stack and + // re-calculate R8-R13. + MOVQ 24(SP), DI + MOVQ 32(SP), SI + MOVQ 40(SP), CX + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, R13 + ADDQ R12, R13 + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +tagLit60Plus: + // !!! This fragment does the + // + // s += x - 58; if uint(s) > uint(len(src)) { etc } + // + // checks. In the asm version, we code it once instead of once per switch case. + ADDQ CX, SI + SUBQ $58, SI + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // case x == 60: + CMPL CX, $61 + JEQ tagLit61 + JA tagLit62Plus + + // x = uint32(src[s-1]) + MOVBLZX -1(SI), CX + JMP doLit + +tagLit61: + // case x == 61: + // x = uint32(src[s-2]) | uint32(src[s-1])<<8 + MOVWLZX -2(SI), CX + JMP doLit + +tagLit62Plus: + CMPL CX, $62 + JA tagLit63 + + // case x == 62: + // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + MOVWLZX -3(SI), CX + MOVBLZX -1(SI), BX + SHLL $16, BX + ORL BX, CX + JMP doLit + +tagLit63: + // case x == 63: + // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + MOVL -4(SI), CX + JMP doLit + +// The code above handles literal tags. +// ---------------------------------------- +// The code below handles copy tags. + +tagCopy4: + // case tagCopy4: + // s += 5 + ADDQ $5, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-5])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + MOVLQZX -4(SI), DX + JMP doCopy + +tagCopy2: + // case tagCopy2: + // s += 3 + ADDQ $3, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-3])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + MOVWQZX -2(SI), DX + JMP doCopy + +tagCopy: + // We have a copy tag. We assume that: + // - BX == src[s] & 0x03 + // - CX == src[s] + CMPQ BX, $2 + JEQ tagCopy2 + JA tagCopy4 + + // case tagCopy1: + // s += 2 + ADDQ $2, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + MOVQ CX, DX + ANDQ $0xe0, DX + SHLQ $3, DX + MOVBQZX -1(SI), BX + ORQ BX, DX + + // length = 4 + int(src[s-2])>>2&0x7 + SHRQ $2, CX + ANDQ $7, CX + ADDQ $4, CX + +doCopy: + // This is the end of the outer "switch", when we have a copy tag. + // + // We assume that: + // - CX == length && CX > 0 + // - DX == offset + + // if offset <= 0 { etc } + CMPQ DX, $0 + JLE errCorrupt + + // if d < offset { etc } + MOVQ DI, BX + SUBQ R8, BX + CMPQ BX, DX + JLT errCorrupt + + // if length > len(dst)-d { etc } + MOVQ R10, BX + SUBQ DI, BX + CMPQ CX, BX + JGT errCorrupt + + // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length + // + // Set: + // - R14 = len(dst)-d + // - R15 = &dst[d-offset] + MOVQ R10, R14 + SUBQ DI, R14 + MOVQ DI, R15 + SUBQ DX, R15 + + // !!! Try a faster technique for short (16 or fewer bytes) forward copies. + // + // First, try using two 8-byte load/stores, similar to the doLit technique + // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is + // still OK if offset >= 8. Note that this has to be two 8-byte load/stores + // and not one 16-byte load/store, and the first store has to be before the + // second load, due to the overlap if offset is in the range [8, 16). + // + // if length > 16 || offset < 8 || len(dst)-d < 16 { + // goto slowForwardCopy + // } + // copy 16 bytes + // d += length + CMPQ CX, $16 + JGT slowForwardCopy + CMPQ DX, $8 + JLT slowForwardCopy + CMPQ R14, $16 + JLT slowForwardCopy + MOVQ 0(R15), AX + MOVQ AX, 0(DI) + MOVQ 8(R15), BX + MOVQ BX, 8(DI) + ADDQ CX, DI + JMP loop + +slowForwardCopy: + // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we + // can still try 8-byte load stores, provided we can overrun up to 10 extra + // bytes. As above, the overrun will be fixed up by subsequent iterations + // of the outermost loop. + // + // The C++ snappy code calls this technique IncrementalCopyFastPath. Its + // commentary says: + // + // ---- + // + // The main part of this loop is a simple copy of eight bytes at a time + // until we've copied (at least) the requested amount of bytes. However, + // if d and d-offset are less than eight bytes apart (indicating a + // repeating pattern of length < 8), we first need to expand the pattern in + // order to get the correct results. For instance, if the buffer looks like + // this, with the eight-byte and patterns marked as + // intervals: + // + // abxxxxxxxxxxxx + // [------] d-offset + // [------] d + // + // a single eight-byte copy from to will repeat the pattern + // once, after which we can move two bytes without moving : + // + // ababxxxxxxxxxx + // [------] d-offset + // [------] d + // + // and repeat the exercise until the two no longer overlap. + // + // This allows us to do very well in the special case of one single byte + // repeated many times, without taking a big hit for more general cases. + // + // The worst case of extra writing past the end of the match occurs when + // offset == 1 and length == 1; the last copy will read from byte positions + // [0..7] and write to [4..11], whereas it was only supposed to write to + // position 1. Thus, ten excess bytes. + // + // ---- + // + // That "10 byte overrun" worst case is confirmed by Go's + // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy + // and finishSlowForwardCopy algorithm. + // + // if length > len(dst)-d-10 { + // goto verySlowForwardCopy + // } + SUBQ $10, R14 + CMPQ CX, R14 + JGT verySlowForwardCopy + +makeOffsetAtLeast8: + // !!! As above, expand the pattern so that offset >= 8 and we can use + // 8-byte load/stores. + // + // for offset < 8 { + // copy 8 bytes from dst[d-offset:] to dst[d:] + // length -= offset + // d += offset + // offset += offset + // // The two previous lines together means that d-offset, and therefore + // // R15, is unchanged. + // } + CMPQ DX, $8 + JGE fixUpSlowForwardCopy + MOVQ (R15), BX + MOVQ BX, (DI) + SUBQ DX, CX + ADDQ DX, DI + ADDQ DX, DX + JMP makeOffsetAtLeast8 + +fixUpSlowForwardCopy: + // !!! Add length (which might be negative now) to d (implied by DI being + // &dst[d]) so that d ends up at the right place when we jump back to the + // top of the loop. Before we do that, though, we save DI to AX so that, if + // length is positive, copying the remaining length bytes will write to the + // right place. + MOVQ DI, AX + ADDQ CX, DI + +finishSlowForwardCopy: + // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative + // length means that we overrun, but as above, that will be fixed up by + // subsequent iterations of the outermost loop. + CMPQ CX, $0 + JLE loop + MOVQ (R15), BX + MOVQ BX, (AX) + ADDQ $8, R15 + ADDQ $8, AX + SUBQ $8, CX + JMP finishSlowForwardCopy + +verySlowForwardCopy: + // verySlowForwardCopy is a simple implementation of forward copy. In C + // parlance, this is a do/while loop instead of a while loop, since we know + // that length > 0. In Go syntax: + // + // for { + // dst[d] = dst[d - offset] + // d++ + // length-- + // if length == 0 { + // break + // } + // } + MOVB (R15), BX + MOVB BX, (DI) + INCQ R15 + INCQ DI + DECQ CX + JNZ verySlowForwardCopy + JMP loop + +// The code above handles copy tags. +// ---------------------------------------- + +end: + // This is the end of the "for s < len(src)". + // + // if d != len(dst) { etc } + CMPQ DI, R10 + JNE errCorrupt + + // return 0 + MOVQ $0, ret+48(FP) + RET + +errCorrupt: + // return decodeErrCodeCorrupt + MOVQ $1, ret+48(FP) + RET diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go new file mode 100644 index 000000000..8c9f2049b --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_other.go @@ -0,0 +1,101 @@ +// Copyright 2016 The Snappy-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 !amd64 appengine !gc noasm + +package snappy + +// decode writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func decode(dst, src []byte) int { + var d, s, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length <= 0 { + return decodeErrCodeUnsupportedLiteralLength + } + if length > len(dst)-d || length > len(src)-s { + return decodeErrCodeCorrupt + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || d < offset || length > len(dst)-d { + return decodeErrCodeCorrupt + } + // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike + // the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + for end := d + length; d != end; d++ { + dst[d] = dst[d-offset] + } + } + if d != len(dst) { + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go new file mode 100644 index 000000000..8d393e904 --- /dev/null +++ b/vendor/github.com/golang/snappy/encode.go @@ -0,0 +1,285 @@ +// Copyright 2011 The Snappy-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 snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + for len(src) > 0 { + p := src + src = nil + if len(p) > maxBlockSize { + p, src = p[:maxBlockSize], p[maxBlockSize:] + } + if len(p) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], p) + } else { + d += encodeBlock(dst[d:], p) + } + } + return dst[:d] +} + +// inputMargin is the minimum number of extra input bytes to keep, inside +// encodeBlock's inner loop. On some architectures, this margin lets us +// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) +// literals can be implemented as a single load to and store from a 16-byte +// register. That literal's actual length can be as short as 1 byte, so this +// can copy up to 15 bytes too much, but that's OK as subsequent iterations of +// the encoding loop will fix up the copy overrun, and this inputMargin ensures +// that we don't overrun the dst and src buffers. +const inputMargin = 16 - 1 + +// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that +// could be encoded with a copy tag. This is the minimum with respect to the +// algorithm used by encodeBlock, not a minimum enforced by the file format. +// +// The encoded output must start with at least a 1 byte literal, as there are +// no previous bytes to copy. A minimal (1 byte) copy after that, generated +// from an emitCopy call in encodeBlock's main loop, would require at least +// another inputMargin bytes, for the reason above: we want any emitLiteral +// calls inside encodeBlock's main loop to use the fast path if possible, which +// requires being able to overrun by inputMargin bytes. Thus, +// minNonLiteralBlockSize equals 1 + 1 + inputMargin. +// +// The C++ code doesn't use this exact threshold, but it could, as discussed at +// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion +// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an +// optimization. It should not affect the encoded form. This is tested by +// TestSameEncodingAsCppShortCopies. +const minNonLiteralBlockSize = 1 + 1 + inputMargin + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +func MaxEncodedLen(srcLen int) int { + n := uint64(srcLen) + if n > 0xffffffff { + return -1 + } + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // That is, 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + n = 32 + n + n/6 + if n > 0xffffffff { + return -1 + } + return int(n) +} + +var errClosed = errors.New("snappy: Writer is closed") + +// NewWriter returns a new Writer that compresses to w. +// +// The Writer returned does not buffer writes. There is no need to Flush or +// Close such a Writer. +// +// Deprecated: the Writer returned is not suitable for many small writes, only +// for few large writes. Use NewBufferedWriter instead, which is efficient +// regardless of the frequency and shape of the writes, and remember to Close +// that Writer when done. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + obuf: make([]byte, obufLen), + } +} + +// NewBufferedWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// The Writer returned buffers writes. Users must call Close to guarantee all +// data has been forwarded to the underlying io.Writer. They may also call +// Flush zero or more times before calling Close. +func NewBufferedWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + ibuf: make([]byte, 0, maxBlockSize), + obuf: make([]byte, obufLen), + } +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +type Writer struct { + w io.Writer + err error + + // ibuf is a buffer for the incoming (uncompressed) bytes. + // + // Its use is optional. For backwards compatibility, Writers created by the + // NewWriter function have ibuf == nil, do not buffer incoming bytes, and + // therefore do not need to be Flush'ed or Close'd. + ibuf []byte + + // obuf is a buffer for the outgoing (compressed) bytes. + obuf []byte + + // wroteStreamHeader is whether we have written the stream header. + wroteStreamHeader bool +} + +// Reset discards the writer's state and switches the Snappy writer to write to +// w. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(writer io.Writer) { + w.w = writer + w.err = nil + if w.ibuf != nil { + w.ibuf = w.ibuf[:0] + } + w.wroteStreamHeader = false +} + +// Write satisfies the io.Writer interface. +func (w *Writer) Write(p []byte) (nRet int, errRet error) { + if w.ibuf == nil { + // Do not buffer incoming bytes. This does not perform or compress well + // if the caller of Writer.Write writes many small slices. This + // behavior is therefore deprecated, but still supported for backwards + // compatibility with code that doesn't explicitly Flush or Close. + return w.write(p) + } + + // The remainder of this method is based on bufio.Writer.Write from the + // standard library. + + for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { + var n int + if len(w.ibuf) == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, _ = w.write(p) + } else { + n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + w.Flush() + } + nRet += n + p = p[n:] + } + if w.err != nil { + return nRet, w.err + } + n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + nRet += n + return nRet, nil +} + +func (w *Writer) write(p []byte) (nRet int, errRet error) { + if w.err != nil { + return 0, w.err + } + for len(p) > 0 { + obufStart := len(magicChunk) + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + copy(w.obuf, magicChunk) + obufStart = 0 + } + + var uncompressed []byte + if len(p) > maxBlockSize { + uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] + } else { + uncompressed, p = p, nil + } + checksum := crc(uncompressed) + + // Compress the buffer, discarding the result if the improvement + // isn't at least 12.5%. + compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) + chunkType := uint8(chunkTypeCompressedData) + chunkLen := 4 + len(compressed) + obufEnd := obufHeaderLen + len(compressed) + if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { + chunkType = chunkTypeUncompressedData + chunkLen = 4 + len(uncompressed) + obufEnd = obufHeaderLen + } + + // Fill in the per-chunk header that comes before the body. + w.obuf[len(magicChunk)+0] = chunkType + w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) + w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) + w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) + w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) + w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) + w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) + w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) + + if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { + w.err = err + return nRet, err + } + if chunkType == chunkTypeUncompressedData { + if _, err := w.w.Write(uncompressed); err != nil { + w.err = err + return nRet, err + } + } + nRet += len(uncompressed) + } + return nRet, nil +} + +// Flush flushes the Writer to its underlying io.Writer. +func (w *Writer) Flush() error { + if w.err != nil { + return w.err + } + if len(w.ibuf) == 0 { + return nil + } + w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + return w.err +} + +// Close calls Flush and then closes the Writer. +func (w *Writer) Close() error { + w.Flush() + ret := w.err + if w.err == nil { + w.err = errClosed + } + return ret +} diff --git a/vendor/github.com/golang/snappy/encode_amd64.go b/vendor/github.com/golang/snappy/encode_amd64.go new file mode 100644 index 000000000..150d91bc8 --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_amd64.go @@ -0,0 +1,29 @@ +// Copyright 2016 The Snappy-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 +// +build gc +// +build !noasm + +package snappy + +// emitLiteral has the same semantics as in encode_other.go. +// +//go:noescape +func emitLiteral(dst, lit []byte) int + +// emitCopy has the same semantics as in encode_other.go. +// +//go:noescape +func emitCopy(dst []byte, offset, length int) int + +// extendMatch has the same semantics as in encode_other.go. +// +//go:noescape +func extendMatch(src []byte, i, j int) int + +// encodeBlock has the same semantics as in encode_other.go. +// +//go:noescape +func encodeBlock(dst, src []byte) (d int) diff --git a/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/golang/snappy/encode_amd64.s new file mode 100644 index 000000000..adfd979fe --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_amd64.s @@ -0,0 +1,730 @@ +// 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 +// +build gc +// +build !noasm + +#include "textflag.h" + +// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a +// Go toolchain regression. See https://github.com/golang/go/issues/15426 and +// https://github.com/golang/snappy/issues/29 +// +// As a workaround, the package was built with a known good assembler, and +// those instructions were disassembled by "objdump -d" to yield the +// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 +// style comments, in AT&T asm syntax. Note that rsp here is a physical +// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm). +// The instructions were then encoded as "BYTE $0x.." sequences, which assemble +// fine on Go 1.6. + +// The asm code generally follows the pure Go code in encode_other.go, except +// where marked with a "!!!". + +// ---------------------------------------------------------------------------- + +// func emitLiteral(dst, lit []byte) int +// +// All local variables fit into registers. The register allocation: +// - AX len(lit) +// - BX n +// - DX return value +// - DI &dst[i] +// - R10 &lit[0] +// +// The 24 bytes of stack space is to call runtime·memmove. +// +// The unusual register allocation of local variables, such as R10 for the +// source pointer, matches the allocation used at the call site in encodeBlock, +// which makes it easier to manually inline this function. +TEXT ·emitLiteral(SB), NOSPLIT, $24-56 + MOVQ dst_base+0(FP), DI + MOVQ lit_base+24(FP), R10 + MOVQ lit_len+32(FP), AX + MOVQ AX, DX + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT oneByte + CMPL BX, $256 + JLT twoBytes + +threeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + ADDQ $3, DX + JMP memmove + +twoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + ADDQ $2, DX + JMP memmove + +oneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + ADDQ $1, DX + +memmove: + MOVQ DX, ret+48(FP) + + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + CALL runtime·memmove(SB) + RET + +// ---------------------------------------------------------------------------- + +// func emitCopy(dst []byte, offset, length int) int +// +// All local variables fit into registers. The register allocation: +// - AX length +// - SI &dst[0] +// - DI &dst[i] +// - R11 offset +// +// The unusual register allocation of local variables, such as R11 for the +// offset, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·emitCopy(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), DI + MOVQ DI, SI + MOVQ offset+24(FP), R11 + MOVQ length+32(FP), AX + +loop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT step1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP loop0 + +step1: + // if length > 64 { etc } + CMPL AX, $64 + JLE step2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +step2: + // if length >= 12 || offset >= 2048 { goto step3 } + CMPL AX, $12 + JGE step3 + CMPL R11, $2048 + JGE step3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +step3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func extendMatch(src []byte, i, j int) int +// +// All local variables fit into registers. The register allocation: +// - DX &src[0] +// - SI &src[j] +// - R13 &src[len(src) - 8] +// - R14 &src[len(src)] +// - R15 &src[i] +// +// The unusual register allocation of local variables, such as R15 for a source +// pointer, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·extendMatch(SB), NOSPLIT, $0-48 + MOVQ src_base+0(FP), DX + MOVQ src_len+8(FP), R14 + MOVQ i+24(FP), R15 + MOVQ j+32(FP), SI + ADDQ DX, R14 + ADDQ DX, R15 + ADDQ DX, SI + MOVQ R14, R13 + SUBQ $8, R13 + +cmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA cmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE bsf + ADDQ $8, R15 + ADDQ $8, SI + JMP cmp8 + +bsf: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +cmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE extendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE extendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP cmp1 + +extendMatchEnd: + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func encodeBlock(dst, src []byte) (d int) +// +// All local variables fit into registers, other than "var table". The register +// allocation: +// - AX . . +// - BX . . +// - CX 56 shift (note that amd64 shifts by non-immediates must use CX). +// - DX 64 &src[0], tableSize +// - SI 72 &src[s] +// - DI 80 &dst[d] +// - R9 88 sLimit +// - R10 . &src[nextEmit] +// - R11 96 prevHash, currHash, nextHash, offset +// - R12 104 &src[base], skip +// - R13 . &src[nextS], &src[len(src) - 8] +// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x +// - R15 112 candidate +// +// The second column (56, 64, etc) is the stack offset to spill the registers +// when calling other functions. We could pack this slightly tighter, but it's +// simpler to have a dedicated spill map independent of the function called. +// +// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An +// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill +// local variables (registers) during calls gives 32768 + 56 + 64 = 32888. +TEXT ·encodeBlock(SB), 0, $32888-56 + MOVQ dst_base+0(FP), DI + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R14 + + // shift, tableSize := uint32(32-8), 1<<8 + MOVQ $24, CX + MOVQ $256, DX + +calcShift: + // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + // shift-- + // } + CMPQ DX, $16384 + JGE varTable + CMPQ DX, R14 + JGE varTable + SUBQ $1, CX + SHLQ $1, DX + JMP calcShift + +varTable: + // var table [maxTableSize]uint16 + // + // In the asm code, unlike the Go code, we can zero-initialize only the + // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU + // writes 16 bytes, so we can do only tableSize/8 writes instead of the + // 2048 writes that would zero-initialize all of table's 32768 bytes. + SHRQ $3, DX + LEAQ table-32768(SP), BX + PXOR X0, X0 + +memclr: + MOVOU X0, 0(BX) + ADDQ $16, BX + SUBQ $1, DX + JNZ memclr + + // !!! DX = &src[0] + MOVQ SI, DX + + // sLimit := len(src) - inputMargin + MOVQ R14, R9 + SUBQ $15, R9 + + // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't + // change for the rest of the function. + MOVQ CX, 56(SP) + MOVQ DX, 64(SP) + MOVQ R9, 88(SP) + + // nextEmit := 0 + MOVQ DX, R10 + + // s := 1 + ADDQ $1, SI + + // nextHash := hash(load32(src, s), shift) + MOVL 0(SI), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + +outer: + // for { etc } + + // skip := 32 + MOVQ $32, R12 + + // nextS := s + MOVQ SI, R13 + + // candidate := 0 + MOVQ $0, R15 + +inner0: + // for { etc } + + // s := nextS + MOVQ R13, SI + + // bytesBetweenHashLookups := skip >> 5 + MOVQ R12, R14 + SHRQ $5, R14 + + // nextS = s + bytesBetweenHashLookups + ADDQ R14, R13 + + // skip += bytesBetweenHashLookups + ADDQ R14, R12 + + // if nextS > sLimit { goto emitRemainder } + MOVQ R13, AX + SUBQ DX, AX + CMPQ AX, R9 + JA emitRemainder + + // candidate = int(table[nextHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[nextHash] = uint16(s) + MOVQ SI, AX + SUBQ DX, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // nextHash = hash(load32(src, nextS), shift) + MOVL 0(R13), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // if load32(src, s) != load32(src, candidate) { continue } break + MOVL 0(SI), AX + MOVL (DX)(R15*1), BX + CMPL AX, BX + JNE inner0 + +fourByteMatch: + // As per the encode_other.go code: + // + // A 4-byte match has been found. We'll later see etc. + + // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment + // on inputMargin in encode.go. + MOVQ SI, AX + SUBQ R10, AX + CMPQ AX, $16 + JLE emitLiteralFastPath + + // ---------------------------------------- + // Begin inline of the emitLiteral call. + // + // d += emitLiteral(dst[d:], src[nextEmit:s]) + + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT inlineEmitLiteralOneByte + CMPL BX, $256 + JLT inlineEmitLiteralTwoBytes + +inlineEmitLiteralThreeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralTwoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralOneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + +inlineEmitLiteralMemmove: + // Spill local variables (registers) onto the stack; call; unspill. + // + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)". + MOVQ SI, 72(SP) + MOVQ DI, 80(SP) + MOVQ R15, 112(SP) + CALL runtime·memmove(SB) + MOVQ 56(SP), CX + MOVQ 64(SP), DX + MOVQ 72(SP), SI + MOVQ 80(SP), DI + MOVQ 88(SP), R9 + MOVQ 112(SP), R15 + JMP inner1 + +inlineEmitLiteralEnd: + // End inline of the emitLiteral call. + // ---------------------------------------- + +emitLiteralFastPath: + // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". + MOVB AX, BX + SUBB $1, BX + SHLB $2, BX + MOVB BX, (DI) + ADDQ $1, DI + + // !!! Implement the copy from lit to dst as a 16-byte load and store. + // (Encode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only len(lit) bytes, but that's + // OK. Subsequent iterations will fix up the overrun. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(R10), X0 + MOVOU X0, 0(DI) + ADDQ AX, DI + +inner1: + // for { etc } + + // base := s + MOVQ SI, R12 + + // !!! offset := base - candidate + MOVQ R12, R11 + SUBQ R15, R11 + SUBQ DX, R11 + + // ---------------------------------------- + // Begin inline of the extendMatch call. + // + // s = extendMatch(src, candidate+4, s+4) + + // !!! R14 = &src[len(src)] + MOVQ src_len+32(FP), R14 + ADDQ DX, R14 + + // !!! R13 = &src[len(src) - 8] + MOVQ R14, R13 + SUBQ $8, R13 + + // !!! R15 = &src[candidate + 4] + ADDQ $4, R15 + ADDQ DX, R15 + + // !!! s += 4 + ADDQ $4, SI + +inlineExtendMatchCmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA inlineExtendMatchCmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE inlineExtendMatchBSF + ADDQ $8, R15 + ADDQ $8, SI + JMP inlineExtendMatchCmp8 + +inlineExtendMatchBSF: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + JMP inlineExtendMatchEnd + +inlineExtendMatchCmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE inlineExtendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE inlineExtendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP inlineExtendMatchCmp1 + +inlineExtendMatchEnd: + // End inline of the extendMatch call. + // ---------------------------------------- + + // ---------------------------------------- + // Begin inline of the emitCopy call. + // + // d += emitCopy(dst[d:], base-candidate, s-base) + + // !!! length := s - base + MOVQ SI, AX + SUBQ R12, AX + +inlineEmitCopyLoop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT inlineEmitCopyStep1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP inlineEmitCopyLoop0 + +inlineEmitCopyStep1: + // if length > 64 { etc } + CMPL AX, $64 + JLE inlineEmitCopyStep2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +inlineEmitCopyStep2: + // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } + CMPL AX, $12 + JGE inlineEmitCopyStep3 + CMPL R11, $2048 + JGE inlineEmitCopyStep3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + JMP inlineEmitCopyEnd + +inlineEmitCopyStep3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + +inlineEmitCopyEnd: + // End inline of the emitCopy call. + // ---------------------------------------- + + // nextEmit = s + MOVQ SI, R10 + + // if s >= sLimit { goto emitRemainder } + MOVQ SI, AX + SUBQ DX, AX + CMPQ AX, R9 + JAE emitRemainder + + // As per the encode_other.go code: + // + // We could immediately etc. + + // x := load64(src, s-1) + MOVQ -1(SI), R14 + + // prevHash := hash(uint32(x>>0), shift) + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // table[prevHash] = uint16(s-1) + MOVQ SI, AX + SUBQ DX, AX + SUBQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // currHash := hash(uint32(x>>8), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // candidate = int(table[currHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[currHash] = uint16(s) + ADDQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // if uint32(x>>8) == load32(src, candidate) { continue } + MOVL (DX)(R15*1), BX + CMPL R14, BX + JEQ inner1 + + // nextHash = hash(uint32(x>>16), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // s++ + ADDQ $1, SI + + // break out of the inner1 for loop, i.e. continue the outer loop. + JMP outer + +emitRemainder: + // if nextEmit < len(src) { etc } + MOVQ src_len+32(FP), AX + ADDQ DX, AX + CMPQ R10, AX + JEQ encodeBlockEnd + + // d += emitLiteral(dst[d:], src[nextEmit:]) + // + // Push args. + MOVQ DI, 0(SP) + MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ R10, 24(SP) + SUBQ R10, AX + MOVQ AX, 32(SP) + MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative. + + // Spill local variables (registers) onto the stack; call; unspill. + MOVQ DI, 80(SP) + CALL ·emitLiteral(SB) + MOVQ 80(SP), DI + + // Finish the "d +=" part of "d += emitLiteral(etc)". + ADDQ 48(SP), DI + +encodeBlockEnd: + MOVQ dst_base+0(FP), AX + SUBQ AX, DI + MOVQ DI, d+48(FP) + RET diff --git a/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/golang/snappy/encode_other.go new file mode 100644 index 000000000..dbcae905e --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_other.go @@ -0,0 +1,238 @@ +// Copyright 2016 The Snappy-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 !amd64 appengine !gc noasm + +package snappy + +func load32(b []byte, i int) uint32 { + b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func load64(b []byte, i int) uint64 { + b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= len(lit) && len(lit) <= 65536 +func emitLiteral(dst, lit []byte) int { + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[0] = 60<<2 | tagLiteral + dst[1] = uint8(n) + i = 2 + default: + dst[0] = 61<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + i = 3 + } + return i + copy(dst[i:], lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= 65535 +// 4 <= length && length <= 65535 +func emitCopy(dst []byte, offset, length int) int { + i := 0 + // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The + // threshold for this loop is a little higher (at 68 = 64 + 4), and the + // length emitted down below is is a little lower (at 60 = 64 - 4), because + // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed + // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as + // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as + // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a + // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an + // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. + for length >= 68 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[i+0] = 63<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 64 + } + if length > 64 { + // Emit a length 60 copy, encoded as 3 bytes. + dst[i+0] = 59<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 60 + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + return i + 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + dst[i+1] = uint8(offset) + return i + 2 +} + +// extendMatch returns the largest k such that k <= len(src) and that +// src[i:i+k-j] and src[j:k] have the same contents. +// +// It assumes that: +// 0 <= i && i < j && j <= len(src) +func extendMatch(src []byte, i, j int) int { + for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { + } + return j +} + +func hash(u, shift uint32) uint32 { + return (u * 0x1e35a7bd) >> shift +} + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlock(dst, src []byte) (d int) { + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + // The table element type is uint16, as s < sLimit and sLimit < len(src) + // and len(src) <= maxBlockSize and maxBlockSize == 65536. + const ( + maxTableSize = 1 << 14 + // tableMask is redundant, but helps the compiler eliminate bounds + // checks. + tableMask = maxTableSize - 1 + ) + shift := uint32(32 - 8) + for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + shift-- + } + // In Go, all array elements are zero-initialized, so there is no advantage + // to a smaller tableSize per se. However, it matches the C++ algorithm, + // and in the asm versions of this code, we can get away with zeroing only + // the first tableSize elements. + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + nextHash := hash(load32(src, s), shift) + + for { + // Copied from the C++ snappy implementation: + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. + // + // The "skip" variable keeps track of how many bytes there are since + // the last match; dividing it by 32 (ie. right-shifting by five) gives + // the number of bytes to move ahead for each iteration. + skip := 32 + + nextS := s + candidate := 0 + for { + s = nextS + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups + if nextS > sLimit { + goto emitRemainder + } + candidate = int(table[nextHash&tableMask]) + table[nextHash&tableMask] = uint16(s) + nextHash = hash(load32(src, nextS), shift) + if load32(src, s) == load32(src, candidate) { + break + } + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + + // Extend the 4-byte match as long as possible. + // + // This is an inlined version of: + // s = extendMatch(src, candidate+4, s+4) + s += 4 + for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { + } + + d += emitCopy(dst[d:], base-candidate, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load64(src, s-1) + prevHash := hash(uint32(x>>0), shift) + table[prevHash&tableMask] = uint16(s - 1) + currHash := hash(uint32(x>>8), shift) + candidate = int(table[currHash&tableMask]) + table[currHash&tableMask] = uint16(s) + if uint32(x>>8) != load32(src, candidate) { + nextHash = hash(uint32(x>>16), shift) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} diff --git a/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/golang/snappy/snappy.go new file mode 100644 index 000000000..ece692ea4 --- /dev/null +++ b/vendor/github.com/golang/snappy/snappy.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Snappy-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 snappy implements the Snappy compression format. It aims for very +// high speeds and reasonable compression. +// +// There are actually two Snappy formats: block and stream. They are related, +// but different: trying to decompress block-compressed data as a Snappy stream +// will fail, and vice versa. The block format is the Decode and Encode +// functions and the stream format is the Reader and Writer types. +// +// The block format, the more common case, is used when the complete size (the +// number of bytes) of the original data is known upfront, at the time +// compression starts. The stream format, also known as the framing format, is +// for when that isn't always true. +// +// The canonical, C++ implementation is at https://github.com/google/snappy and +// it only implements the block format. +package snappy // import "github.com/golang/snappy" + +import ( + "hash/crc32" +) + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer issued by most + encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) + +const ( + checksumSize = 4 + chunkHeaderSize = 4 + magicChunk = "\xff\x06\x00\x00" + magicBody + magicBody = "sNaPpY" + + // maxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + maxBlockSize = 65536 + + // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is + // hard coded to be a const instead of a variable, so that obufLen can also + // be a const. Their equivalence is confirmed by + // TestMaxEncodedLenOfMaxBlockSize. + maxEncodedLenOfMaxBlockSize = 76490 + + obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize + obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var crcTable = crc32.MakeTable(crc32.Castagnoli) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func crc(b []byte) uint32 { + c := crc32.Update(0, crcTable, b) + return uint32(c>>15|c<<17) + 0xa282ead8 +} diff --git a/vendor/github.com/gophercloud/gophercloud/.gitignore b/vendor/github.com/gophercloud/gophercloud/.gitignore deleted file mode 100644 index dd91ed205..000000000 --- a/vendor/github.com/gophercloud/gophercloud/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -**/*.swp -.idea -.vscode diff --git a/vendor/github.com/gophercloud/gophercloud/.travis.yml b/vendor/github.com/gophercloud/gophercloud/.travis.yml deleted file mode 100644 index 9153a00fc..000000000 --- a/vendor/github.com/gophercloud/gophercloud/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: go -sudo: false -install: -- GO111MODULE=off go get golang.org/x/crypto/ssh -- GO111MODULE=off go get -v -tags 'fixtures acceptance' ./... -- GO111MODULE=off go get github.com/wadey/gocovmerge -- GO111MODULE=off go get github.com/mattn/goveralls -- GO111MODULE=off go get golang.org/x/tools/cmd/goimports -go: -- "1.10" -- "1.11" -- "1.12" -- "tip" -env: - global: - - secure: "xSQsAG5wlL9emjbCdxzz/hYQsSpJ/bABO1kkbwMSISVcJ3Nk0u4ywF+LS4bgeOnwPfmFvNTOqVDu3RwEvMeWXSI76t1piCPcObutb2faKLVD/hLoAS76gYX+Z8yGWGHrSB7Do5vTPj1ERe2UljdrnsSeOXzoDwFxYRaZLX4bBOB4AyoGvRniil5QXPATiA1tsWX1VMicj8a4F8X+xeESzjt1Q5Iy31e7vkptu71bhvXCaoo5QhYwT+pLR9dN0S1b7Ro0KVvkRefmr1lUOSYd2e74h6Lc34tC1h3uYZCS4h47t7v5cOXvMNxinEj2C51RvbjvZI1RLVdkuAEJD1Iz4+Ote46nXbZ//6XRZMZz/YxQ13l7ux1PFjgEB6HAapmF5Xd8PRsgeTU9LRJxpiTJ3P5QJ3leS1va8qnziM5kYipj/Rn+V8g2ad/rgkRox9LSiR9VYZD2Pe45YCb1mTKSl2aIJnV7nkOqsShY5LNB4JZSg7xIffA+9YVDktw8dJlATjZqt7WvJJ49g6A61mIUV4C15q2JPGKTkZzDiG81NtmS7hFa7k0yaE2ELgYocbcuyUcAahhxntYTC0i23nJmEHVNiZmBO3u7EgpWe4KGVfumU+lt12tIn5b3dZRBBUk3QakKKozSK1QPHGpk/AZGrhu7H6l8to6IICKWtDcyMPQ=" - - GO111MODULE=on -before_script: -- go vet ./... -script: -- ./script/coverage -- ./script/unittest -- ./script/format -after_success: -- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out diff --git a/vendor/github.com/gophercloud/gophercloud/.zuul.yaml b/vendor/github.com/gophercloud/gophercloud/.zuul.yaml deleted file mode 100644 index 135e3b203..000000000 --- a/vendor/github.com/gophercloud/gophercloud/.zuul.yaml +++ /dev/null @@ -1,114 +0,0 @@ -- job: - name: gophercloud-unittest - parent: golang-test - description: | - Run gophercloud unit test - run: .zuul/playbooks/gophercloud-unittest/run.yaml - nodeset: ubuntu-xenial-ut - -- job: - name: gophercloud-acceptance-test - parent: golang-test - description: | - Run gophercloud acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml - -- job: - name: gophercloud-acceptance-test-ironic - parent: golang-test - description: | - Run gophercloud ironic acceptance test on master branch - run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml - -- job: - name: gophercloud-acceptance-test-stein - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on stein branch - vars: - global_env: - OS_BRANCH: stable/stein - -- job: - name: gophercloud-acceptance-test-rocky - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on rocky branch - vars: - global_env: - OS_BRANCH: stable/rocky - -- job: - name: gophercloud-acceptance-test-queens - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on queens branch - vars: - global_env: - OS_BRANCH: stable/queens - -- job: - name: gophercloud-acceptance-test-pike - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on pike branch - vars: - global_env: - OS_BRANCH: stable/pike - -- job: - name: gophercloud-acceptance-test-ocata - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on ocata branch - vars: - global_env: - OS_BRANCH: stable/ocata - -- job: - name: gophercloud-acceptance-test-newton - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on newton branch - vars: - global_env: - OS_BRANCH: stable/newton - -- job: - name: gophercloud-acceptance-test-mitaka - parent: gophercloud-acceptance-test - description: | - Run gophercloud acceptance test on mitaka branch - vars: - global_env: - OS_BRANCH: stable/mitaka - nodeset: ubuntu-trusty - -- project: - name: gophercloud/gophercloud - check: - jobs: - - gophercloud-unittest - - gophercloud-acceptance-test - - gophercloud-acceptance-test-ironic - recheck-mitaka: - jobs: - - gophercloud-acceptance-test-mitaka - recheck-newton: - jobs: - - gophercloud-acceptance-test-newton - recheck-ocata: - jobs: - - gophercloud-acceptance-test-ocata - recheck-pike: - jobs: - - gophercloud-acceptance-test-pike - recheck-queens: - jobs: - - gophercloud-acceptance-test-queens - recheck-rocky: - jobs: - - gophercloud-acceptance-test-rocky - recheck-stein: - jobs: - - gophercloud-acceptance-test-stein diff --git a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md deleted file mode 100644 index d0b120de1..000000000 --- a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md +++ /dev/null @@ -1,69 +0,0 @@ -## 0.4.0 (Unreleased) - -## 0.3.0 (July 31, 2019) - -IMPROVEMENTS - -* Added `baremetal/apiversions.List` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) -* Added `baremetal/apiversions.Get` [GH-1577](https://github.com/gophercloud/gophercloud/pull/1577) -* Added `compute/v2/extensions/servergroups.CreateOpts.Policy` [GH-1636](https://github.com/gophercloud/gophercloud/pull/1636) -* Added `identity/v3/extensions/trusts.Create` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) -* Added `identity/v3/extensions/trusts.Delete` [GH-1644](https://github.com/gophercloud/gophercloud/pull/1644) -* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/layer3/floatingips.FloatingIP` [GH-1647](https://github.com/gophercloud/gophercloud/issues/1646) -* Added `CreatedAt` and `UpdatedAt` to `networking/v2/extensions/security/groups.SecGroup` [GH-1654](https://github.com/gophercloud/gophercloud/issues/1654) -* Added `CreatedAt` and `UpdatedAt` to `networking/v2/networks.Network` [GH-1657](https://github.com/gophercloud/gophercloud/issues/1657) -* Added `keymanager/v1/containers.CreateSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) -* Added `keymanager/v1/containers.DeleteSecretRef` [GH-1659](https://github.com/gophercloud/gophercloud/issues/1659) -* Added `sharedfilesystems/v2/shares.GetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) -* Added `sharedfilesystems/v2/shares.GetMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) -* Added `sharedfilesystems/v2/shares.SetMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) -* Added `sharedfilesystems/v2/shares.UpdateMetadata` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) -* Added `sharedfilesystems/v2/shares.DeleteMetadatum` [GH-1656](https://github.com/gophercloud/gophercloud/issues/1656) -* Added `sharedfilesystems/v2/sharetypes.IDFromName` [GH-1662](https://github.com/gophercloud/gophercloud/issues/1662) - - - -BUG FIXES - -* Changed `baremetal/v1/nodes.CleanStep.Args` from `map[string]string` to `map[string]interface{}` [GH-1638](https://github.com/gophercloud/gophercloud/pull/1638) -* Removed `URLPath` and `ExpectedCodes` from `loadbalancer/v2/monitors.ToMonitorCreateMap` since Octavia now provides default values when these fields are not specified [GH-1640](https://github.com/gophercloud/gophercloud/pull/1540) - - -## 0.2.0 (June 17, 2019) - -IMPROVEMENTS - -* Added `networking/v2/extensions/qos/rules.ListBandwidthLimitRules` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) -* Added `networking/v2/extensions/qos/rules.GetBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) -* Added `networking/v2/extensions/qos/rules.CreateBandwidthLimitRule` [GH-1584](https://github.com/gophercloud/gophercloud/pull/1584) -* Added `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` [GH-1589](https://github.com/gophercloud/gophercloud/pull/1589) -* Added `networking/v2/extensions/qos/rules.DeleteBandwidthLimitRule` [GH-1590](https://github.com/gophercloud/gophercloud/pull/1590) -* Added `networking/v2/extensions/qos/policies.List` [GH-1591](https://github.com/gophercloud/gophercloud/pull/1591) -* Added `networking/v2/extensions/qos/policies.Get` [GH-1593](https://github.com/gophercloud/gophercloud/pull/1593) -* Added `networking/v2/extensions/qos/rules.ListDSCPMarkingRules` [GH-1594](https://github.com/gophercloud/gophercloud/pull/1594) -* Added `networking/v2/extensions/qos/policies.Create` [GH-1595](https://github.com/gophercloud/gophercloud/pull/1595) -* Added `compute/v2/extensions/diagnostics.Get` [GH-1592](https://github.com/gophercloud/gophercloud/pull/1592) -* Added `networking/v2/extensions/qos/policies.Update` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) -* Added `networking/v2/extensions/qos/policies.Delete` [GH-1603](https://github.com/gophercloud/gophercloud/pull/1603) -* Added `networking/v2/extensions/qos/rules.CreateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) -* Added `networking/v2/extensions/qos/rules.UpdateDSCPMarkingRule` [GH-1605](https://github.com/gophercloud/gophercloud/pull/1605) -* Added `networking/v2/extensions/qos/rules.GetDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) -* Added `networking/v2/extensions/qos/rules.DeleteDSCPMarkingRule` [GH-1609](https://github.com/gophercloud/gophercloud/pull/1609) -* Added `networking/v2/extensions/qos/rules.ListMinimumBandwidthRules` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) -* Added `networking/v2/extensions/qos/rules.GetMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) -* Added `networking/v2/extensions/qos/rules.CreateMinimumBandwidthRule` [GH-1615](https://github.com/gophercloud/gophercloud/pull/1615) -* Added `Hostname` to `baremetalintrospection/v1/introspection.Data` [GH-1627](https://github.com/gophercloud/gophercloud/pull/1627) -* Added `networking/v2/extensions/qos/rules.UpdateMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) -* Added `networking/v2/extensions/qos/rules.DeleteMinimumBandwidthRule` [GH-1624](https://github.com/gophercloud/gophercloud/pull/1624) -* Added `networking/v2/extensions/qos/ruletypes.GetRuleType` [GH-1625](https://github.com/gophercloud/gophercloud/pull/1625) -* Added `Extra` to `baremetalintrospection/v1/introspection.Data` [GH-1611](https://github.com/gophercloud/gophercloud/pull/1611) -* Added `blockstorage/extensions/volumeactions.SetImageMetadata` [GH-1621](https://github.com/gophercloud/gophercloud/pull/1621) - -BUG FIXES - -* Updated `networking/v2/extensions/qos/rules.UpdateBandwidthLimitRule` to use return code 200 [GH-1606](https://github.com/gophercloud/gophercloud/pull/1606) -* Fixed bug in `compute/v2/extensions/schedulerhints.SchedulerHints.Query` where contents will now be marshalled to a string [GH-1620](https://github.com/gophercloud/gophercloud/pull/1620) - -## 0.1.0 (May 27, 2019) - -Initial tagged release. diff --git a/vendor/github.com/gophercloud/gophercloud/LICENSE b/vendor/github.com/gophercloud/gophercloud/LICENSE deleted file mode 100644 index fbbbc9e4c..000000000 --- a/vendor/github.com/gophercloud/gophercloud/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Copyright 2012-2013 Rackspace, Inc. - -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. - ------- - - 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 diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md deleted file mode 100644 index ad29041d9..000000000 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# Gophercloud: an OpenStack SDK for Go -[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud) -[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) - -Gophercloud is an OpenStack Go SDK. - -## Useful links - -* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) -* [Effective Go](https://golang.org/doc/effective_go.html) - -## How to install - -Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH) -is pointing to an appropriate directory where you want to install Gophercloud: - -```bash -mkdir $HOME/go -export GOPATH=$HOME/go -``` - -To protect yourself against changes in your dependencies, we highly recommend choosing a -[dependency management solution](https://github.com/golang/go/wiki/PackageManagementTools) for -your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install -Gophercloud as a dependency like so: - -```bash -go get github.com/gophercloud/gophercloud - -# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud" - -godep save ./... -``` - -This will install all the source files you need into a `Godeps/_workspace` directory, which is -referenceable from your own source files when you use the `godep go` command. - -## Getting started - -### Credentials - -Because you'll be hitting an API, you will need to retrieve your OpenStack -credentials and either store them as environment variables or in your local Go -files. The first method is recommended because it decouples credential -information from source code, allowing you to push the latter to your version -control system without any security risk. - -You will need to retrieve the following: - -* username -* password -* a valid Keystone identity URL - -For users that have the OpenStack dashboard installed, there's a shortcut. If -you visit the `project/access_and_security` path in Horizon and click on the -"Download OpenStack RC File" button at the top right hand corner, you will -download a bash file that exports all of your access details to environment -variables. To execute the file, run `source admin-openrc.sh` and you will be -prompted for your password. - -### Authentication - -Once you have access to your credentials, you can begin plugging them into -Gophercloud. The next step is authentication, and this is handled by a base -"Provider" struct. To get one, you can either pass in your credentials -explicitly, or tell Gophercloud to use environment variables: - -```go -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack" - "github.com/gophercloud/gophercloud/openstack/utils" -) - -// Option 1: Pass in the values yourself -opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", -} - -// Option 2: Use a utility function to retrieve all your environment variables -opts, err := openstack.AuthOptionsFromEnv() -``` - -Once you have the `opts` variable, you can pass it in and get back a -`ProviderClient` struct: - -```go -provider, err := openstack.AuthenticatedClient(opts) -``` - -The `ProviderClient` is the top-level client that all of your OpenStack services -derive from. The provider contains all of the authentication details that allow -your Go code to access the API - such as the base URL and token ID. - -### Provision a server - -Once we have a base Provider, we inject it as a dependency into each OpenStack -service. In order to work with the Compute API, we need a Compute service -client; which can be created like so: - -```go -client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), -}) -``` - -We then use this `client` for any Compute API operation we want. In our case, -we want to provision a new server - so we invoke the `Create` method and pass -in the flavor ID (hardware specification) and image ID (operating system) we're -interested in: - -```go -import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" - -server, err := servers.Create(client, servers.CreateOpts{ - Name: "My new server!", - FlavorRef: "flavor_id", - ImageRef: "image_id", -}).Extract() -``` - -The above code sample creates a new server with the parameters, and embodies the -new resource in the `server` variable (a -[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct). - -## Advanced Usage - -Have a look at the [FAQ](./docs/FAQ.md) for some tips on customizing the way Gophercloud works. - -## Backwards-Compatibility Guarantees - -None. Vendor it and write tests covering the parts you use. - -## Contributing - -See the [contributing guide](./.github/CONTRIBUTING.md). - -## Help and feedback - -If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). - -## Thank You - -We'd like to extend special thanks and appreciation to the following: - -### OpenLab - - - -OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. - -### VEXXHOST - - - -VEXXHOST is providing their services to assist with the development and testing of Gophercloud. diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go deleted file mode 100644 index 5ffa8d1e0..000000000 --- a/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ /dev/null @@ -1,437 +0,0 @@ -package gophercloud - -/* -AuthOptions stores information needed to authenticate to an OpenStack Cloud. -You can populate one manually, or use a provider's AuthOptionsFromEnv() function -to read relevant information from the standard environment variables. Pass one -to a provider's AuthenticatedClient function to authenticate and obtain a -ProviderClient representing an active session on that provider. - -Its fields are the union of those recognized by each identity implementation and -provider. - -An example of manually providing authentication information: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(opts) - -An example of using AuthOptionsFromEnv(), where the environment variables can -be read from a file, such as a standard openrc file: - - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) -*/ -type AuthOptions struct { - // IdentityEndpoint specifies the HTTP endpoint that is required to work with - // the Identity API of the appropriate version. While it's ultimately needed by - // all of the identity services, it will often be populated by a provider-level - // function. - // - // The IdentityEndpoint is typically referred to as the "auth_url" or - // "OS_AUTH_URL" in the information provided by the cloud operator. - IdentityEndpoint string `json:"-"` - - // Username is required if using Identity V2 API. Consult with your provider's - // control panel to discover your account's username. In Identity V3, either - // UserID or a combination of Username and DomainID or DomainName are needed. - Username string `json:"username,omitempty"` - UserID string `json:"-"` - - Password string `json:"password,omitempty"` - - // At most one of DomainID and DomainName must be provided if using Username - // with Identity V3. Otherwise, either are optional. - DomainID string `json:"-"` - DomainName string `json:"name,omitempty"` - - // The TenantID and TenantName fields are optional for the Identity V2 API. - // The same fields are known as project_id and project_name in the Identity - // V3 API, but are collected as TenantID and TenantName here in both cases. - // Some providers allow you to specify a TenantName instead of the TenantId. - // Some require both. Your provider's authentication policies will determine - // how these fields influence authentication. - // If DomainID or DomainName are provided, they will also apply to TenantName. - // It is not currently possible to authenticate with Username and a Domain - // and scope to a Project in a different Domain by using TenantName. To - // accomplish that, the ProjectID will need to be provided as the TenantID - // option. - TenantID string `json:"tenantId,omitempty"` - TenantName string `json:"tenantName,omitempty"` - - // AllowReauth should be set to true if you grant permission for Gophercloud to - // cache your credentials in memory, and to allow Gophercloud to attempt to - // re-authenticate automatically if/when your token expires. If you set it to - // false, it will not cache these settings, but re-authentication will not be - // possible. This setting defaults to false. - // - // NOTE: The reauth function will try to re-authenticate endlessly if left - // unchecked. The way to limit the number of attempts is to provide a custom - // HTTP client to the provider client and provide a transport that implements - // the RoundTripper interface and stores the number of failed retries. For an - // example of this, see here: - // https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 - AllowReauth bool `json:"-"` - - // TokenID allows users to authenticate (possibly as another user) with an - // authentication token ID. - TokenID string `json:"-"` - - // Scope determines the scoping of the authentication request. - Scope *AuthScope `json:"-"` - - // Authentication through Application Credentials requires supplying name, project and secret - // For project we can use TenantID - ApplicationCredentialID string `json:"-"` - ApplicationCredentialName string `json:"-"` - ApplicationCredentialSecret string `json:"-"` -} - -// AuthScope allows a created token to be limited to a specific domain or project. -type AuthScope struct { - ProjectID string - ProjectName string - DomainID string - DomainName string -} - -// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder -// interface in the v2 tokens package -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { - // Populate the request map. - authMap := make(map[string]interface{}) - - if opts.Username != "" { - if opts.Password != "" { - authMap["passwordCredentials"] = map[string]interface{}{ - "username": opts.Username, - "password": opts.Password, - } - } else { - return nil, ErrMissingInput{Argument: "Password"} - } - } else if opts.TokenID != "" { - authMap["token"] = map[string]interface{}{ - "id": opts.TokenID, - } - } else { - return nil, ErrMissingInput{Argument: "Username"} - } - - if opts.TenantID != "" { - authMap["tenantId"] = opts.TenantID - } - if opts.TenantName != "" { - authMap["tenantName"] = opts.TenantName - } - - return map[string]interface{}{"auth": authMap}, nil -} - -func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { - type domainReq struct { - ID *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - } - - type projectReq struct { - Domain *domainReq `json:"domain,omitempty"` - Name *string `json:"name,omitempty"` - ID *string `json:"id,omitempty"` - } - - type userReq struct { - ID *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Password string `json:"password,omitempty"` - Domain *domainReq `json:"domain,omitempty"` - } - - type passwordReq struct { - User userReq `json:"user"` - } - - type tokenReq struct { - ID string `json:"id"` - } - - type applicationCredentialReq struct { - ID *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - User *userReq `json:"user,omitempty"` - Secret *string `json:"secret,omitempty"` - } - - type identityReq struct { - Methods []string `json:"methods"` - Password *passwordReq `json:"password,omitempty"` - Token *tokenReq `json:"token,omitempty"` - ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` - } - - type authReq struct { - Identity identityReq `json:"identity"` - } - - type request struct { - Auth authReq `json:"auth"` - } - - // Populate the request structure based on the provided arguments. Create and return an error - // if insufficient or incompatible information is present. - var req request - - if opts.Password == "" { - if opts.TokenID != "" { - // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication - // parameters. - if opts.Username != "" { - return nil, ErrUsernameWithToken{} - } - if opts.UserID != "" { - return nil, ErrUserIDWithToken{} - } - if opts.DomainID != "" { - return nil, ErrDomainIDWithToken{} - } - if opts.DomainName != "" { - return nil, ErrDomainNameWithToken{} - } - - // Configure the request for Token authentication. - req.Auth.Identity.Methods = []string{"token"} - req.Auth.Identity.Token = &tokenReq{ - ID: opts.TokenID, - } - - } else if opts.ApplicationCredentialID != "" { - // Configure the request for ApplicationCredentialID authentication. - // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 - // There are three kinds of possible application_credential requests - // 1. application_credential id + secret - // 2. application_credential name + secret + user_id - // 3. application_credential name + secret + username + domain_id / domain_name - if opts.ApplicationCredentialSecret == "" { - return nil, ErrAppCredMissingSecret{} - } - req.Auth.Identity.Methods = []string{"application_credential"} - req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ - ID: &opts.ApplicationCredentialID, - Secret: &opts.ApplicationCredentialSecret, - } - } else if opts.ApplicationCredentialName != "" { - if opts.ApplicationCredentialSecret == "" { - return nil, ErrAppCredMissingSecret{} - } - - var userRequest *userReq - - if opts.UserID != "" { - // UserID could be used without the domain information - userRequest = &userReq{ - ID: &opts.UserID, - } - } - - if userRequest == nil && opts.Username == "" { - // Make sure that Username or UserID are provided - return nil, ErrUsernameOrUserID{} - } - - if userRequest == nil && opts.DomainID != "" { - userRequest = &userReq{ - Name: &opts.Username, - Domain: &domainReq{ID: &opts.DomainID}, - } - } - - if userRequest == nil && opts.DomainName != "" { - userRequest = &userReq{ - Name: &opts.Username, - Domain: &domainReq{Name: &opts.DomainName}, - } - } - - // Make sure that DomainID or DomainName are provided among Username - if userRequest == nil { - return nil, ErrDomainIDOrDomainName{} - } - - req.Auth.Identity.Methods = []string{"application_credential"} - req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ - Name: &opts.ApplicationCredentialName, - User: userRequest, - Secret: &opts.ApplicationCredentialSecret, - } - } else { - // If no password or token ID or ApplicationCredential are available, authentication can't continue. - return nil, ErrMissingPassword{} - } - } else { - // Password authentication. - req.Auth.Identity.Methods = []string{"password"} - - // At least one of Username and UserID must be specified. - if opts.Username == "" && opts.UserID == "" { - return nil, ErrUsernameOrUserID{} - } - - if opts.Username != "" { - // If Username is provided, UserID may not be provided. - if opts.UserID != "" { - return nil, ErrUsernameOrUserID{} - } - - // Either DomainID or DomainName must also be specified. - if opts.DomainID == "" && opts.DomainName == "" { - return nil, ErrDomainIDOrDomainName{} - } - - if opts.DomainID != "" { - if opts.DomainName != "" { - return nil, ErrDomainIDOrDomainName{} - } - - // Configure the request for Username and Password authentication with a DomainID. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &opts.Username, - Password: opts.Password, - Domain: &domainReq{ID: &opts.DomainID}, - }, - } - } - - if opts.DomainName != "" { - // Configure the request for Username and Password authentication with a DomainName. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &opts.Username, - Password: opts.Password, - Domain: &domainReq{Name: &opts.DomainName}, - }, - } - } - } - - if opts.UserID != "" { - // If UserID is specified, neither DomainID nor DomainName may be. - if opts.DomainID != "" { - return nil, ErrDomainIDWithUserID{} - } - if opts.DomainName != "" { - return nil, ErrDomainNameWithUserID{} - } - - // Configure the request for UserID and Password authentication. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ID: &opts.UserID, Password: opts.Password}, - } - } - } - - b, err := BuildRequestBody(req, "") - if err != nil { - return nil, err - } - - if len(scope) != 0 { - b["auth"].(map[string]interface{})["scope"] = scope - } - - return b, nil -} - -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { - // For backwards compatibility. - // If AuthOptions.Scope was not set, try to determine it. - // This works well for common scenarios. - if opts.Scope == nil { - opts.Scope = new(AuthScope) - if opts.TenantID != "" { - opts.Scope.ProjectID = opts.TenantID - } else { - if opts.TenantName != "" { - opts.Scope.ProjectName = opts.TenantName - opts.Scope.DomainID = opts.DomainID - opts.Scope.DomainName = opts.DomainName - } - } - } - - if opts.Scope.ProjectName != "" { - // ProjectName provided: either DomainID or DomainName must also be supplied. - // ProjectID may not be supplied. - if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { - return nil, ErrScopeDomainIDOrDomainName{} - } - if opts.Scope.ProjectID != "" { - return nil, ErrScopeProjectIDOrProjectName{} - } - - if opts.Scope.DomainID != "" { - // ProjectName + DomainID - return map[string]interface{}{ - "project": map[string]interface{}{ - "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, - }, - }, nil - } - - if opts.Scope.DomainName != "" { - // ProjectName + DomainName - return map[string]interface{}{ - "project": map[string]interface{}{ - "name": &opts.Scope.ProjectName, - "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, - }, - }, nil - } - } else if opts.Scope.ProjectID != "" { - // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. - if opts.Scope.DomainID != "" { - return nil, ErrScopeProjectIDAlone{} - } - if opts.Scope.DomainName != "" { - return nil, ErrScopeProjectIDAlone{} - } - - // ProjectID - return map[string]interface{}{ - "project": map[string]interface{}{ - "id": &opts.Scope.ProjectID, - }, - }, nil - } else if opts.Scope.DomainID != "" { - // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. - if opts.Scope.DomainName != "" { - return nil, ErrScopeDomainIDOrDomainName{} - } - - // DomainID - return map[string]interface{}{ - "domain": map[string]interface{}{ - "id": &opts.Scope.DomainID, - }, - }, nil - } else if opts.Scope.DomainName != "" { - // DomainName - return map[string]interface{}{ - "domain": map[string]interface{}{ - "name": &opts.Scope.DomainName, - }, - }, nil - } - - return nil, nil -} - -func (opts AuthOptions) CanReauth() bool { - return opts.AllowReauth -} diff --git a/vendor/github.com/gophercloud/gophercloud/auth_result.go b/vendor/github.com/gophercloud/gophercloud/auth_result.go deleted file mode 100644 index 2e4699b97..000000000 --- a/vendor/github.com/gophercloud/gophercloud/auth_result.go +++ /dev/null @@ -1,52 +0,0 @@ -package gophercloud - -/* -AuthResult is the result from the request that was used to obtain a provider -client's Keystone token. It is returned from ProviderClient.GetAuthResult(). - -The following types satisfy this interface: - - github.com/gophercloud/gophercloud/openstack/identity/v2/tokens.CreateResult - github.com/gophercloud/gophercloud/openstack/identity/v3/tokens.CreateResult - -Usage example: - - import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - ) - - func GetAuthenticatedUserID(providerClient *gophercloud.ProviderClient) (string, error) { - r := providerClient.GetAuthResult() - if r == nil { - //ProviderClient did not use openstack.Authenticate(), e.g. because token - //was set manually with ProviderClient.SetToken() - return "", errors.New("no AuthResult available") - } - switch r := r.(type) { - case tokens2.CreateResult: - u, err := r.ExtractUser() - if err != nil { - return "", err - } - return u.ID, nil - case tokens3.CreateResult: - u, err := r.ExtractUser() - if err != nil { - return "", err - } - return u.ID, nil - default: - panic(fmt.Sprintf("got unexpected AuthResult type %t", r)) - } - } - -Both implementing types share a lot of methods by name, like ExtractUser() in -this example. But those methods cannot be part of the AuthResult interface -because the return types are different (in this case, type tokens2.User vs. -type tokens3.User). -*/ -type AuthResult interface { - ExtractTokenID() (string, error) -} diff --git a/vendor/github.com/gophercloud/gophercloud/doc.go b/vendor/github.com/gophercloud/gophercloud/doc.go deleted file mode 100644 index 953ca822a..000000000 --- a/vendor/github.com/gophercloud/gophercloud/doc.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Package gophercloud provides a multi-vendor interface to OpenStack-compatible -clouds. The library has a three-level hierarchy: providers, services, and -resources. - -Authenticating with Providers - -Provider structs represent the cloud providers that offer and manage a -collection of services. You will generally want to create one Provider -client per OpenStack cloud. - - It is now recommended to use the `clientconfig` package found at - https://github.com/gophercloud/utils/tree/master/openstack/clientconfig - for all authentication purposes. - - The below documentation is still relevant. clientconfig simply implements - the below and presents it in an easier and more flexible way. - -Use your OpenStack credentials to create a Provider client. The -IdentityEndpoint is typically refered to as "auth_url" or "OS_AUTH_URL" in -information provided by the cloud operator. Additionally, the cloud may refer to -TenantID or TenantName as project_id and project_name. Credentials are -specified like so: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(opts) - -You can authenticate with a token by doing: - - opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://openstack.example.com:5000/v2.0", - TokenID: "{token_id}", - TenantID: "{tenant_id}", - } - - provider, err := openstack.AuthenticatedClient(opts) - -You may also use the openstack.AuthOptionsFromEnv() helper function. This -function reads in standard environment variables frequently found in an -OpenStack `openrc` file. Again note that Gophercloud currently uses "tenant" -instead of "project". - - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) - -Service Clients - -Service structs are specific to a provider and handle all of the logic and -operations for a particular OpenStack service. Examples of services include: -Compute, Object Storage, Block Storage. In order to define one, you need to -pass in the parent provider, like so: - - opts := gophercloud.EndpointOpts{Region: "RegionOne"} - - client, err := openstack.NewComputeV2(provider, opts) - -Resources - -Resource structs are the domain models that services make use of in order -to work with and represent the state of API resources: - - server, err := servers.Get(client, "{serverId}").Extract() - -Intermediate Result structs are returned for API operations, which allow -generic access to the HTTP headers, response body, and any errors associated -with the network transaction. To turn a result into a usable resource struct, -you must call the Extract method which is chained to the response, or an -Extract function from an applicable extension: - - result := servers.Get(client, "{serverId}") - - // Attempt to extract the disk configuration from the OS-DCF disk config - // extension: - config, err := diskconfig.ExtractGet(result) - -All requests that enumerate a collection return a Pager struct that is used to -iterate through the results one page at a time. Use the EachPage method on that -Pager to handle each successive Page in a closure, then use the appropriate -extraction method from that request's package to interpret that Page as a slice -of results: - - err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) { - s, err := servers.ExtractServers(page) - if err != nil { - return false, err - } - - // Handle the []servers.Server slice. - - // Return "false" or an error to prematurely stop fetching new pages. - return true, nil - }) - -If you want to obtain the entire collection of pages without doing any -intermediary processing on each page, you can use the AllPages method: - - allPages, err := servers.List(client, nil).AllPages() - allServers, err := servers.ExtractServers(allPages) - -This top-level package contains utility functions and data types that are used -throughout the provider and service packages. Of particular note for end users -are the AuthOptions and EndpointOpts structs. -*/ -package gophercloud diff --git a/vendor/github.com/gophercloud/gophercloud/endpoint_search.go b/vendor/github.com/gophercloud/gophercloud/endpoint_search.go deleted file mode 100644 index 2fbc3c97f..000000000 --- a/vendor/github.com/gophercloud/gophercloud/endpoint_search.go +++ /dev/null @@ -1,76 +0,0 @@ -package gophercloud - -// Availability indicates to whom a specific service endpoint is accessible: -// the internet at large, internal networks only, or only to administrators. -// Different identity services use different terminology for these. Identity v2 -// lists them as different kinds of URLs within the service catalog ("adminURL", -// "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an -// endpoint's response. -type Availability string - -const ( - // AvailabilityAdmin indicates that an endpoint is only available to - // administrators. - AvailabilityAdmin Availability = "admin" - - // AvailabilityPublic indicates that an endpoint is available to everyone on - // the internet. - AvailabilityPublic Availability = "public" - - // AvailabilityInternal indicates that an endpoint is only available within - // the cluster's internal network. - AvailabilityInternal Availability = "internal" -) - -// EndpointOpts specifies search criteria used by queries against an -// OpenStack service catalog. The options must contain enough information to -// unambiguously identify one, and only one, endpoint within the catalog. -// -// Usually, these are passed to service client factory functions in a provider -// package, like "openstack.NewComputeV2()". -type EndpointOpts struct { - // Type [required] is the service type for the client (e.g., "compute", - // "object-store"). Generally, this will be supplied by the service client - // function, but a user-given value will be honored if provided. - Type string - - // Name [optional] is the service name for the client (e.g., "nova") as it - // appears in the service catalog. Services can have the same Type but a - // different Name, which is why both Type and Name are sometimes needed. - Name string - - // Region [required] is the geographic region in which the endpoint resides, - // generally specifying which datacenter should house your resources. - // Required only for services that span multiple regions. - Region string - - // Availability [optional] is the visibility of the endpoint to be returned. - // Valid types include the constants AvailabilityPublic, AvailabilityInternal, - // or AvailabilityAdmin from this package. - // - // Availability is not required, and defaults to AvailabilityPublic. Not all - // providers or services offer all Availability options. - Availability Availability -} - -/* -EndpointLocator is an internal function to be used by provider implementations. - -It provides an implementation that locates a single endpoint from a service -catalog for a specific ProviderClient based on user-provided EndpointOpts. The -provider then uses it to discover related ServiceClients. -*/ -type EndpointLocator func(EndpointOpts) (string, error) - -// ApplyDefaults is an internal method to be used by provider implementations. -// -// It sets EndpointOpts fields if not already set, including a default type. -// Currently, EndpointOpts.Availability defaults to the public endpoint. -func (eo *EndpointOpts) ApplyDefaults(t string) { - if eo.Type == "" { - eo.Type = t - } - if eo.Availability == "" { - eo.Availability = AvailabilityPublic - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/errors.go deleted file mode 100644 index 0bcb3af7f..000000000 --- a/vendor/github.com/gophercloud/gophercloud/errors.go +++ /dev/null @@ -1,471 +0,0 @@ -package gophercloud - -import ( - "fmt" - "strings" -) - -// BaseError is an error type that all other error types embed. -type BaseError struct { - DefaultErrString string - Info string -} - -func (e BaseError) Error() string { - e.DefaultErrString = "An error occurred while executing a Gophercloud request." - return e.choseErrString() -} - -func (e BaseError) choseErrString() string { - if e.Info != "" { - return e.Info - } - return e.DefaultErrString -} - -// ErrMissingInput is the error when input is required in a particular -// situation but not provided by the user -type ErrMissingInput struct { - BaseError - Argument string -} - -func (e ErrMissingInput) Error() string { - e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument) - return e.choseErrString() -} - -// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. -type ErrInvalidInput struct { - ErrMissingInput - Value interface{} -} - -func (e ErrInvalidInput) Error() string { - e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value) - return e.choseErrString() -} - -// ErrMissingEnvironmentVariable is the error when environment variable is required -// in a particular situation but not provided by the user -type ErrMissingEnvironmentVariable struct { - BaseError - EnvironmentVariable string -} - -func (e ErrMissingEnvironmentVariable) Error() string { - e.DefaultErrString = fmt.Sprintf("Missing environment variable [%s]", e.EnvironmentVariable) - return e.choseErrString() -} - -// ErrMissingAnyoneOfEnvironmentVariables is the error when anyone of the environment variables -// is required in a particular situation but not provided by the user -type ErrMissingAnyoneOfEnvironmentVariables struct { - BaseError - EnvironmentVariables []string -} - -func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Missing one of the following environment variables [%s]", - strings.Join(e.EnvironmentVariables, ", "), - ) - return e.choseErrString() -} - -// ErrUnexpectedResponseCode is returned by the Request method when a response code other than -// those listed in OkCodes is encountered. -type ErrUnexpectedResponseCode struct { - BaseError - URL string - Method string - Expected []int - Actual int - Body []byte -} - -func (e ErrUnexpectedResponseCode) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", - e.Expected, e.Method, e.URL, e.Actual, e.Body, - ) - return e.choseErrString() -} - -// ErrDefault400 is the default error type returned on a 400 HTTP response code. -type ErrDefault400 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault401 is the default error type returned on a 401 HTTP response code. -type ErrDefault401 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault403 is the default error type returned on a 403 HTTP response code. -type ErrDefault403 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault404 is the default error type returned on a 404 HTTP response code. -type ErrDefault404 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault405 is the default error type returned on a 405 HTTP response code. -type ErrDefault405 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault408 is the default error type returned on a 408 HTTP response code. -type ErrDefault408 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault409 is the default error type returned on a 409 HTTP response code. -type ErrDefault409 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault429 is the default error type returned on a 429 HTTP response code. -type ErrDefault429 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault500 is the default error type returned on a 500 HTTP response code. -type ErrDefault500 struct { - ErrUnexpectedResponseCode -} - -// ErrDefault503 is the default error type returned on a 503 HTTP response code. -type ErrDefault503 struct { - ErrUnexpectedResponseCode -} - -func (e ErrDefault400) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Bad request with: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault401) Error() string { - return "Authentication failed" -} -func (e ErrDefault403) Error() string { - e.DefaultErrString = fmt.Sprintf( - "Request forbidden: [%s %s], error message: %s", - e.Method, e.URL, e.Body, - ) - return e.choseErrString() -} -func (e ErrDefault404) Error() string { - return "Resource not found" -} -func (e ErrDefault405) Error() string { - return "Method not allowed" -} -func (e ErrDefault408) Error() string { - return "The server timed out waiting for the request" -} -func (e ErrDefault429) Error() string { - return "Too many requests have been sent in a given amount of time. Pause" + - " requests, wait up to one minute, and try again." -} -func (e ErrDefault500) Error() string { - return "Internal Server Error" -} -func (e ErrDefault503) Error() string { - return "The service is currently unable to handle the request due to a temporary" + - " overloading or maintenance. This is a temporary condition. Try again later." -} - -// Err400er is the interface resource error types implement to override the error message -// from a 400 error. -type Err400er interface { - Error400(ErrUnexpectedResponseCode) error -} - -// Err401er is the interface resource error types implement to override the error message -// from a 401 error. -type Err401er interface { - Error401(ErrUnexpectedResponseCode) error -} - -// Err403er is the interface resource error types implement to override the error message -// from a 403 error. -type Err403er interface { - Error403(ErrUnexpectedResponseCode) error -} - -// Err404er is the interface resource error types implement to override the error message -// from a 404 error. -type Err404er interface { - Error404(ErrUnexpectedResponseCode) error -} - -// Err405er is the interface resource error types implement to override the error message -// from a 405 error. -type Err405er interface { - Error405(ErrUnexpectedResponseCode) error -} - -// Err408er is the interface resource error types implement to override the error message -// from a 408 error. -type Err408er interface { - Error408(ErrUnexpectedResponseCode) error -} - -// Err409er is the interface resource error types implement to override the error message -// from a 409 error. -type Err409er interface { - Error409(ErrUnexpectedResponseCode) error -} - -// Err429er is the interface resource error types implement to override the error message -// from a 429 error. -type Err429er interface { - Error429(ErrUnexpectedResponseCode) error -} - -// Err500er is the interface resource error types implement to override the error message -// from a 500 error. -type Err500er interface { - Error500(ErrUnexpectedResponseCode) error -} - -// Err503er is the interface resource error types implement to override the error message -// from a 503 error. -type Err503er interface { - Error503(ErrUnexpectedResponseCode) error -} - -// ErrTimeOut is the error type returned when an operations times out. -type ErrTimeOut struct { - BaseError -} - -func (e ErrTimeOut) Error() string { - e.DefaultErrString = "A time out occurred" - return e.choseErrString() -} - -// ErrUnableToReauthenticate is the error type returned when reauthentication fails. -type ErrUnableToReauthenticate struct { - BaseError - ErrOriginal error -} - -func (e ErrUnableToReauthenticate) Error() string { - e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal) - return e.choseErrString() -} - -// ErrErrorAfterReauthentication is the error type returned when reauthentication -// succeeds, but an error occurs afterword (usually an HTTP error). -type ErrErrorAfterReauthentication struct { - BaseError - ErrOriginal error -} - -func (e ErrErrorAfterReauthentication) Error() string { - e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal) - return e.choseErrString() -} - -// ErrServiceNotFound is returned when no service in a service catalog matches -// the provided EndpointOpts. This is generally returned by provider service -// factory methods like "NewComputeV2()" and can mean that a service is not -// enabled for your account. -type ErrServiceNotFound struct { - BaseError -} - -func (e ErrServiceNotFound) Error() string { - e.DefaultErrString = "No suitable service could be found in the service catalog." - return e.choseErrString() -} - -// ErrEndpointNotFound is returned when no available endpoints match the -// provided EndpointOpts. This is also generally returned by provider service -// factory methods, and usually indicates that a region was specified -// incorrectly. -type ErrEndpointNotFound struct { - BaseError -} - -func (e ErrEndpointNotFound) Error() string { - e.DefaultErrString = "No suitable endpoint could be found in the service catalog." - return e.choseErrString() -} - -// ErrResourceNotFound is the error when trying to retrieve a resource's -// ID by name and the resource doesn't exist. -type ErrResourceNotFound struct { - BaseError - Name string - ResourceType string -} - -func (e ErrResourceNotFound) Error() string { - e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name) - return e.choseErrString() -} - -// ErrMultipleResourcesFound is the error when trying to retrieve a resource's -// ID by name and multiple resources have the user-provided name. -type ErrMultipleResourcesFound struct { - BaseError - Name string - Count int - ResourceType string -} - -func (e ErrMultipleResourcesFound) Error() string { - e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name) - return e.choseErrString() -} - -// ErrUnexpectedType is the error when an unexpected type is encountered -type ErrUnexpectedType struct { - BaseError - Expected string - Actual string -} - -func (e ErrUnexpectedType) Error() string { - e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual) - return e.choseErrString() -} - -func unacceptedAttributeErr(attribute string) string { - return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute) -} - -func redundantWithTokenErr(attribute string) string { - return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute) -} - -func redundantWithUserID(attribute string) string { - return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute) -} - -// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. -type ErrAPIKeyProvided struct{ BaseError } - -func (e ErrAPIKeyProvided) Error() string { - return unacceptedAttributeErr("APIKey") -} - -// ErrTenantIDProvided indicates that a TenantID was provided but can't be used. -type ErrTenantIDProvided struct{ BaseError } - -func (e ErrTenantIDProvided) Error() string { - return unacceptedAttributeErr("TenantID") -} - -// ErrTenantNameProvided indicates that a TenantName was provided but can't be used. -type ErrTenantNameProvided struct{ BaseError } - -func (e ErrTenantNameProvided) Error() string { - return unacceptedAttributeErr("TenantName") -} - -// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. -type ErrUsernameWithToken struct{ BaseError } - -func (e ErrUsernameWithToken) Error() string { - return redundantWithTokenErr("Username") -} - -// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. -type ErrUserIDWithToken struct{ BaseError } - -func (e ErrUserIDWithToken) Error() string { - return redundantWithTokenErr("UserID") -} - -// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. -type ErrDomainIDWithToken struct{ BaseError } - -func (e ErrDomainIDWithToken) Error() string { - return redundantWithTokenErr("DomainID") -} - -// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s -type ErrDomainNameWithToken struct{ BaseError } - -func (e ErrDomainNameWithToken) Error() string { - return redundantWithTokenErr("DomainName") -} - -// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. -type ErrUsernameOrUserID struct{ BaseError } - -func (e ErrUsernameOrUserID) Error() string { - return "Exactly one of Username and UserID must be provided for password authentication" -} - -// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. -type ErrDomainIDWithUserID struct{ BaseError } - -func (e ErrDomainIDWithUserID) Error() string { - return redundantWithUserID("DomainID") -} - -// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. -type ErrDomainNameWithUserID struct{ BaseError } - -func (e ErrDomainNameWithUserID) Error() string { - return redundantWithUserID("DomainName") -} - -// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. -// It may also indicate that both a DomainID and a DomainName were provided at once. -type ErrDomainIDOrDomainName struct{ BaseError } - -func (e ErrDomainIDOrDomainName) Error() string { - return "You must provide exactly one of DomainID or DomainName to authenticate by Username" -} - -// ErrMissingPassword indicates that no password was provided and no token is available. -type ErrMissingPassword struct{ BaseError } - -func (e ErrMissingPassword) Error() string { - return "You must provide a password to authenticate" -} - -// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. -type ErrScopeDomainIDOrDomainName struct{ BaseError } - -func (e ErrScopeDomainIDOrDomainName) Error() string { - return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName" -} - -// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. -type ErrScopeProjectIDOrProjectName struct{ BaseError } - -func (e ErrScopeProjectIDOrProjectName) Error() string { - return "You must provide at most one of ProjectID or ProjectName in a Scope" -} - -// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. -type ErrScopeProjectIDAlone struct{ BaseError } - -func (e ErrScopeProjectIDAlone) Error() string { - return "ProjectID must be supplied alone in a Scope" -} - -// ErrScopeEmpty indicates that no credentials were provided in a Scope. -type ErrScopeEmpty struct{ BaseError } - -func (e ErrScopeEmpty) Error() string { - return "You must provide either a Project or Domain in a Scope" -} - -// ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name -type ErrAppCredMissingSecret struct{ BaseError } - -func (e ErrAppCredMissingSecret) Error() string { - return "You must provide an Application Credential Secret" -} diff --git a/vendor/github.com/gophercloud/gophercloud/go.mod b/vendor/github.com/gophercloud/gophercloud/go.mod deleted file mode 100644 index d1ee3b472..000000000 --- a/vendor/github.com/gophercloud/gophercloud/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/gophercloud/gophercloud - -require ( - golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 - golang.org/x/sys v0.0.0-20190209173611-3b5209105503 // indirect - gopkg.in/yaml.v2 v2.2.2 -) diff --git a/vendor/github.com/gophercloud/gophercloud/go.sum b/vendor/github.com/gophercloud/gophercloud/go.sum deleted file mode 100644 index 33cb0be8a..000000000 --- a/vendor/github.com/gophercloud/gophercloud/go.sum +++ /dev/null @@ -1,8 +0,0 @@ -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go deleted file mode 100644 index 0e8d90ff8..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go +++ /dev/null @@ -1,125 +0,0 @@ -package openstack - -import ( - "os" - - "github.com/gophercloud/gophercloud" -) - -var nilOptions = gophercloud.AuthOptions{} - -/* -AuthOptionsFromEnv fills out an identity.AuthOptions structure with the -settings found on the various OpenStack OS_* environment variables. - -The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, -OS_PASSWORD and OS_PROJECT_ID. - -Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, -or an error will result. OS_PROJECT_ID, is optional. - -OS_TENANT_ID and OS_TENANT_NAME are deprecated forms of OS_PROJECT_ID and -OS_PROJECT_NAME and the latter are expected against a v3 auth api. - -If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will still be referred -as "tenant" in Gophercloud. - -If OS_PROJECT_NAME is set, it requires OS_PROJECT_ID to be set as well to -handle projects not on the default domain. - -To use this function, first set the OS_* environment variables (for example, -by sourcing an `openrc` file), then: - - opts, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(opts) -*/ -func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { - authURL := os.Getenv("OS_AUTH_URL") - username := os.Getenv("OS_USERNAME") - userID := os.Getenv("OS_USERID") - password := os.Getenv("OS_PASSWORD") - tenantID := os.Getenv("OS_TENANT_ID") - tenantName := os.Getenv("OS_TENANT_NAME") - domainID := os.Getenv("OS_DOMAIN_ID") - domainName := os.Getenv("OS_DOMAIN_NAME") - applicationCredentialID := os.Getenv("OS_APPLICATION_CREDENTIAL_ID") - applicationCredentialName := os.Getenv("OS_APPLICATION_CREDENTIAL_NAME") - applicationCredentialSecret := os.Getenv("OS_APPLICATION_CREDENTIAL_SECRET") - - // If OS_PROJECT_ID is set, overwrite tenantID with the value. - if v := os.Getenv("OS_PROJECT_ID"); v != "" { - tenantID = v - } - - // If OS_PROJECT_NAME is set, overwrite tenantName with the value. - if v := os.Getenv("OS_PROJECT_NAME"); v != "" { - tenantName = v - } - - if authURL == "" { - err := gophercloud.ErrMissingEnvironmentVariable{ - EnvironmentVariable: "OS_AUTH_URL", - } - return nilOptions, err - } - - if userID == "" && username == "" { - // Empty username and userID could be ignored, when applicationCredentialID and applicationCredentialSecret are set - if applicationCredentialID == "" && applicationCredentialSecret == "" { - err := gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ - EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, - } - return nilOptions, err - } - } - - if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { - err := gophercloud.ErrMissingEnvironmentVariable{ - EnvironmentVariable: "OS_PASSWORD", - } - return nilOptions, err - } - - if (applicationCredentialID != "" || applicationCredentialName != "") && applicationCredentialSecret == "" { - err := gophercloud.ErrMissingEnvironmentVariable{ - EnvironmentVariable: "OS_APPLICATION_CREDENTIAL_SECRET", - } - return nilOptions, err - } - - if domainID == "" && domainName == "" && tenantID == "" && tenantName != "" { - err := gophercloud.ErrMissingEnvironmentVariable{ - EnvironmentVariable: "OS_PROJECT_ID", - } - return nilOptions, err - } - - if applicationCredentialID == "" && applicationCredentialName != "" && applicationCredentialSecret != "" { - if userID == "" && username == "" { - return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ - EnvironmentVariables: []string{"OS_USERID", "OS_USERNAME"}, - } - } - if username != "" && domainID == "" && domainName == "" { - return nilOptions, gophercloud.ErrMissingAnyoneOfEnvironmentVariables{ - EnvironmentVariables: []string{"OS_DOMAIN_ID", "OS_DOMAIN_NAME"}, - } - } - } - - ao := gophercloud.AuthOptions{ - IdentityEndpoint: authURL, - UserID: userID, - Username: username, - Password: password, - TenantID: tenantID, - TenantName: tenantName, - DomainID: domainID, - DomainName: domainName, - ApplicationCredentialID: applicationCredentialID, - ApplicationCredentialName: applicationCredentialName, - ApplicationCredentialSecret: applicationCredentialSecret, - } - - return ao, nil -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/openstack/client.go deleted file mode 100644 index 50f239711..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/client.go +++ /dev/null @@ -1,438 +0,0 @@ -package openstack - -import ( - "fmt" - "reflect" - - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - "github.com/gophercloud/gophercloud/openstack/utils" -) - -const ( - // v2 represents Keystone v2. - // It should never increase beyond 2.0. - v2 = "v2.0" - - // v3 represents Keystone v3. - // The version can be anything from v3 to v3.x. - v3 = "v3" -) - -/* -NewClient prepares an unauthenticated ProviderClient instance. -Most users will probably prefer using the AuthenticatedClient function -instead. - -This is useful if you wish to explicitly control the version of the identity -service that's used for authentication explicitly, for example. - -A basic example of using this would be: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.NewClient(ao.IdentityEndpoint) - client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{}) -*/ -func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { - base, err := utils.BaseEndpoint(endpoint) - if err != nil { - return nil, err - } - - endpoint = gophercloud.NormalizeURL(endpoint) - base = gophercloud.NormalizeURL(base) - - p := new(gophercloud.ProviderClient) - p.IdentityBase = base - p.IdentityEndpoint = endpoint - p.UseTokenLock() - - return p, nil -} - -/* -AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint -specified by the options, acquires a token, and returns a Provider Client -instance that's ready to operate. - -If the full path to a versioned identity endpoint was specified (example: -http://example.com:5000/v3), that path will be used as the endpoint to query. - -If a versionless endpoint was specified (example: http://example.com:5000/), -the endpoint will be queried to determine which versions of the identity service -are available, then chooses the most recent or most supported version. - -Example: - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) -*/ -func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) { - client, err := NewClient(options.IdentityEndpoint) - if err != nil { - return nil, err - } - - err = Authenticate(client, options) - if err != nil { - return nil, err - } - return client, nil -} - -// Authenticate or re-authenticate against the most recent identity service -// supported at the provided endpoint. -func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { - versions := []*utils.Version{ - {ID: v2, Priority: 20, Suffix: "/v2.0/"}, - {ID: v3, Priority: 30, Suffix: "/v3/"}, - } - - chosen, endpoint, err := utils.ChooseVersion(client, versions) - if err != nil { - return err - } - - switch chosen.ID { - case v2: - return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) - case v3: - return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) - default: - // The switch statement must be out of date from the versions list. - return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) - } -} - -// AuthenticateV2 explicitly authenticates against the identity v2 endpoint. -func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - return v2auth(client, "", options, eo) -} - -func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { - v2Client, err := NewIdentityV2(client, eo) - if err != nil { - return err - } - - if endpoint != "" { - v2Client.Endpoint = endpoint - } - - v2Opts := tokens2.AuthOptions{ - IdentityEndpoint: options.IdentityEndpoint, - Username: options.Username, - Password: options.Password, - TenantID: options.TenantID, - TenantName: options.TenantName, - AllowReauth: options.AllowReauth, - TokenID: options.TokenID, - } - - result := tokens2.Create(v2Client, v2Opts) - - err = client.SetTokenAndAuthResult(result) - if err != nil { - return err - } - - catalog, err := result.ExtractServiceCatalog() - if err != nil { - return err - } - - if options.AllowReauth { - // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but - // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, - // this should retry authentication only once - tac := *client - tac.SetThrowaway(true) - tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) - tao := options - tao.AllowReauth = false - client.ReauthFunc = func() error { - err := v2auth(&tac, endpoint, tao, eo) - if err != nil { - return err - } - client.CopyTokenFrom(&tac) - return nil - } - } - client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { - return V2EndpointURL(catalog, opts) - } - - return nil -} - -// AuthenticateV3 explicitly authenticates against the identity v3 service. -func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - return v3auth(client, "", options, eo) -} - -func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { - // Override the generated service endpoint with the one returned by the version endpoint. - v3Client, err := NewIdentityV3(client, eo) - if err != nil { - return err - } - - if endpoint != "" { - v3Client.Endpoint = endpoint - } - - result := tokens3.Create(v3Client, opts) - - err = client.SetTokenAndAuthResult(result) - if err != nil { - return err - } - - catalog, err := result.ExtractServiceCatalog() - if err != nil { - return err - } - - if opts.CanReauth() { - // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but - // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, - // this should retry authentication only once - tac := *client - tac.SetThrowaway(true) - tac.ReauthFunc = nil - tac.SetTokenAndAuthResult(nil) - var tao tokens3.AuthOptionsBuilder - switch ot := opts.(type) { - case *gophercloud.AuthOptions: - o := *ot - o.AllowReauth = false - tao = &o - case *tokens3.AuthOptions: - o := *ot - o.AllowReauth = false - tao = &o - default: - tao = opts - } - client.ReauthFunc = func() error { - err := v3auth(&tac, endpoint, tao, eo) - if err != nil { - return err - } - client.CopyTokenFrom(&tac) - return nil - } - } - client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { - return V3EndpointURL(catalog, opts) - } - - return nil -} - -// NewIdentityV2 creates a ServiceClient that may be used to interact with the -// v2 identity service. -func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - endpoint := client.IdentityBase + "v2.0/" - clientType := "identity" - var err error - if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { - eo.ApplyDefaults(clientType) - endpoint, err = client.EndpointLocator(eo) - if err != nil { - return nil, err - } - } - - return &gophercloud.ServiceClient{ - ProviderClient: client, - Endpoint: endpoint, - Type: clientType, - }, nil -} - -// NewIdentityV3 creates a ServiceClient that may be used to access the v3 -// identity service. -func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - endpoint := client.IdentityBase + "v3/" - clientType := "identity" - var err error - if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { - eo.ApplyDefaults(clientType) - endpoint, err = client.EndpointLocator(eo) - if err != nil { - return nil, err - } - } - - // Ensure endpoint still has a suffix of v3. - // This is because EndpointLocator might have found a versionless - // endpoint or the published endpoint is still /v2.0. In both - // cases, we need to fix the endpoint to point to /v3. - base, err := utils.BaseEndpoint(endpoint) - if err != nil { - return nil, err - } - - base = gophercloud.NormalizeURL(base) - - endpoint = base + "v3/" - - return &gophercloud.ServiceClient{ - ProviderClient: client, - Endpoint: endpoint, - Type: clientType, - }, nil -} - -func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) { - sc := new(gophercloud.ServiceClient) - eo.ApplyDefaults(clientType) - url, err := client.EndpointLocator(eo) - if err != nil { - return sc, err - } - sc.ProviderClient = client - sc.Endpoint = url - sc.Type = clientType - return sc, nil -} - -// NewBareMetalV1 creates a ServiceClient that may be used with the v1 -// bare metal package. -func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "baremetal") -} - -// NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1 -// bare metal introspection package. -func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "baremetal-inspector") -} - -// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 -// object storage package. -func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "object-store") -} - -// NewComputeV2 creates a ServiceClient that may be used with the v2 compute -// package. -func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "compute") -} - -// NewNetworkV2 creates a ServiceClient that may be used with the v2 network -// package. -func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "network") - sc.ResourceBase = sc.Endpoint + "v2.0/" - return sc, err -} - -// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 -// block storage service. -func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volume") -} - -// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 -// block storage service. -func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev2") -} - -// NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service. -func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "volumev3") -} - -// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service. -func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "sharev2") -} - -// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1 -// CDN service. -func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "cdn") -} - -// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 -// orchestration service. -func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "orchestration") -} - -// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service. -func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "database") -} - -// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS -// service. -func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "dns") - sc.ResourceBase = sc.Endpoint + "v2/" - return sc, err -} - -// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 -// image service. -func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "image") - sc.ResourceBase = sc.Endpoint + "v2/" - return sc, err -} - -// NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2 -// load balancer service. -func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "load-balancer") - sc.ResourceBase = sc.Endpoint + "v2.0/" - return sc, err -} - -// NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering -// package. -func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "clustering") -} - -// NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging -// service. -func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "messaging") - sc.MoreHeaders = map[string]string{"Client-ID": clientID} - return sc, err -} - -// NewContainerV1 creates a ServiceClient that may be used with v1 container package -func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container") -} - -// NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key -// manager service. -func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - sc, err := initClientOpts(client, eo, "key-manager") - sc.ResourceBase = sc.Endpoint + "v1/" - return sc, err -} - -// NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management -// package. -func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "container-infra") -} - -// NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package. -func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - return initClientOpts(client, eo, "workflowv2") -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/doc.go deleted file mode 100644 index cedf1f4d3..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Package openstack contains resources for the individual OpenStack projects -supported in Gophercloud. It also includes functions to authenticate to an -OpenStack cloud and for provisioning various service-level clients. - -Example of Creating a Service Client - - ao, err := openstack.AuthOptionsFromEnv() - provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ - Region: os.Getenv("OS_REGION_NAME"), - }) -*/ -package openstack diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go b/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go deleted file mode 100644 index 12c8aebcf..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go +++ /dev/null @@ -1,107 +0,0 @@ -package openstack - -import ( - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" -) - -/* -V2EndpointURL discovers the endpoint URL for a specific service from a -ServiceCatalog acquired during the v2 identity service. - -The specified EndpointOpts are used to identify a unique, unambiguous endpoint -to return. It's an error both when multiple endpoints match the provided -criteria and when none do. The minimum that can be specified is a Type, but you -will also often need to specify a Name and/or a Region depending on what's -available on your OpenStack deployment. -*/ -func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { - // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided. - var endpoints = make([]tokens2.Endpoint, 0, 1) - for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { - for _, endpoint := range entry.Endpoints { - if opts.Region == "" || endpoint.Region == opts.Region { - endpoints = append(endpoints, endpoint) - } - } - } - } - - // Report an error if the options were ambiguous. - if len(endpoints) > 1 { - err := &ErrMultipleMatchingEndpointsV2{} - err.Endpoints = endpoints - return "", err - } - - // Extract the appropriate URL from the matching Endpoint. - for _, endpoint := range endpoints { - switch opts.Availability { - case gophercloud.AvailabilityPublic: - return gophercloud.NormalizeURL(endpoint.PublicURL), nil - case gophercloud.AvailabilityInternal: - return gophercloud.NormalizeURL(endpoint.InternalURL), nil - case gophercloud.AvailabilityAdmin: - return gophercloud.NormalizeURL(endpoint.AdminURL), nil - default: - err := &ErrInvalidAvailabilityProvided{} - err.Argument = "Availability" - err.Value = opts.Availability - return "", err - } - } - - // Report an error if there were no matching endpoints. - err := &gophercloud.ErrEndpointNotFound{} - return "", err -} - -/* -V3EndpointURL discovers the endpoint URL for a specific service from a Catalog -acquired during the v3 identity service. - -The specified EndpointOpts are used to identify a unique, unambiguous endpoint -to return. It's an error both when multiple endpoints match the provided -criteria and when none do. The minimum that can be specified is a Type, but you -will also often need to specify a Name and/or a Region depending on what's -available on your OpenStack deployment. -*/ -func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { - // Extract Endpoints from the catalog entries that match the requested Type, Interface, - // Name if provided, and Region if provided. - var endpoints = make([]tokens3.Endpoint, 0, 1) - for _, entry := range catalog.Entries { - if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { - for _, endpoint := range entry.Endpoints { - if opts.Availability != gophercloud.AvailabilityAdmin && - opts.Availability != gophercloud.AvailabilityPublic && - opts.Availability != gophercloud.AvailabilityInternal { - err := &ErrInvalidAvailabilityProvided{} - err.Argument = "Availability" - err.Value = opts.Availability - return "", err - } - if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && - (opts.Region == "" || endpoint.Region == opts.Region || endpoint.RegionID == opts.Region) { - endpoints = append(endpoints, endpoint) - } - } - } - } - - // Report an error if the options were ambiguous. - if len(endpoints) > 1 { - return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints} - } - - // Extract the URL from the matching Endpoint. - for _, endpoint := range endpoints { - return gophercloud.NormalizeURL(endpoint.URL), nil - } - - // Report an error if there were no matching endpoints. - err := &gophercloud.ErrEndpointNotFound{} - return "", err -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go b/vendor/github.com/gophercloud/gophercloud/openstack/errors.go deleted file mode 100644 index df410b1c6..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go +++ /dev/null @@ -1,71 +0,0 @@ -package openstack - -import ( - "fmt" - - "github.com/gophercloud/gophercloud" - tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" -) - -// ErrEndpointNotFound is the error when no suitable endpoint can be found -// in the user's catalog -type ErrEndpointNotFound struct{ gophercloud.BaseError } - -func (e ErrEndpointNotFound) Error() string { - return "No suitable endpoint could be found in the service catalog." -} - -// ErrInvalidAvailabilityProvided is the error when an invalid endpoint -// availability is provided -type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput } - -func (e ErrInvalidAvailabilityProvided) Error() string { - return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) -} - -// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint -// for the given options is found in the v2 catalog -type ErrMultipleMatchingEndpointsV2 struct { - gophercloud.BaseError - Endpoints []tokens2.Endpoint -} - -func (e ErrMultipleMatchingEndpointsV2) Error() string { - return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) -} - -// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint -// for the given options is found in the v3 catalog -type ErrMultipleMatchingEndpointsV3 struct { - gophercloud.BaseError - Endpoints []tokens3.Endpoint -} - -func (e ErrMultipleMatchingEndpointsV3) Error() string { - return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) -} - -// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not -// found -type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } - -func (e ErrNoAuthURL) Error() string { - return "Environment variable OS_AUTH_URL needs to be set." -} - -// ErrNoUsername is the error when the OS_USERNAME environment variable is not -// found -type ErrNoUsername struct{ gophercloud.ErrInvalidInput } - -func (e ErrNoUsername) Error() string { - return "Environment variable OS_USERNAME needs to be set." -} - -// ErrNoPassword is the error when the OS_PASSWORD environment variable is not -// found -type ErrNoPassword struct{ gophercloud.ErrInvalidInput } - -func (e ErrNoPassword) Error() string { - return "Environment variable OS_PASSWORD needs to be set." -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go deleted file mode 100644 index 45623369e..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Package tenants provides information and interaction with the -tenants API resource for the OpenStack Identity service. - -See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 -and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants -for more information. - -Example to List Tenants - - listOpts := tenants.ListOpts{ - Limit: 2, - } - - allPages, err := tenants.List(identityClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allTenants, err := tenants.ExtractTenants(allPages) - if err != nil { - panic(err) - } - - for _, tenant := range allTenants { - fmt.Printf("%+v\n", tenant) - } - -Example to Create a Tenant - - createOpts := tenants.CreateOpts{ - Name: "tenant_name", - Description: "this is a tenant", - Enabled: gophercloud.Enabled, - } - - tenant, err := tenants.Create(identityClient, createOpts).Extract() - if err != nil { - panic(err) - } - -Example to Update a Tenant - - tenantID := "e6db6ed6277c461a853458589063b295" - - updateOpts := tenants.UpdateOpts{ - Description: "this is a new description", - Enabled: gophercloud.Disabled, - } - - tenant, err := tenants.Update(identityClient, tenantID, updateOpts).Extract() - if err != nil { - panic(err) - } - -Example to Delete a Tenant - - tenantID := "e6db6ed6277c461a853458589063b295" - - err := tenants.Delete(identitYClient, tenantID).ExtractErr() - if err != nil { - panic(err) - } -*/ -package tenants diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go deleted file mode 100644 index f21a58f10..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go +++ /dev/null @@ -1,116 +0,0 @@ -package tenants - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOpts filters the Tenants that are returned by the List call. -type ListOpts struct { - // Marker is the ID of the last Tenant on the previous page. - Marker string `q:"marker"` - - // Limit specifies the page size. - Limit int `q:"limit"` -} - -// List enumerates the Tenants to which the current token has access. -func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { - url := listURL(client) - if opts != nil { - q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return pagination.Pager{Err: err} - } - url += q.String() - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return TenantPage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// CreateOpts represents the options needed when creating new tenant. -type CreateOpts struct { - // Name is the name of the tenant. - Name string `json:"name" required:"true"` - - // Description is the description of the tenant. - Description string `json:"description,omitempty"` - - // Enabled sets the tenant status to enabled or disabled. - Enabled *bool `json:"enabled,omitempty"` -} - -// CreateOptsBuilder enables extensions to add additional parameters to the -// Create request. -type CreateOptsBuilder interface { - ToTenantCreateMap() (map[string]interface{}, error) -} - -// ToTenantCreateMap assembles a request body based on the contents of -// a CreateOpts. -func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "tenant") -} - -// Create is the operation responsible for creating new tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { - b, err := opts.ToTenantCreateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - return -} - -// Get requests details on a single tenant by ID. -func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) - return -} - -// UpdateOptsBuilder allows extensions to add additional parameters to the -// Update request. -type UpdateOptsBuilder interface { - ToTenantUpdateMap() (map[string]interface{}, error) -} - -// UpdateOpts specifies the base attributes that may be updated on an existing -// tenant. -type UpdateOpts struct { - // Name is the name of the tenant. - Name string `json:"name,omitempty"` - - // Description is the description of the tenant. - Description *string `json:"description,omitempty"` - - // Enabled sets the tenant status to enabled or disabled. - Enabled *bool `json:"enabled,omitempty"` -} - -// ToTenantUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) { - return gophercloud.BuildRequestBody(opts, "tenant") -} - -// Update is the operation responsible for updating exist tenants by their TenantID. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { - b, err := opts.ToTenantUpdateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - return -} - -// Delete is the operation responsible for permanently deleting a tenant. -func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go deleted file mode 100644 index bb6c2c6b0..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go +++ /dev/null @@ -1,91 +0,0 @@ -package tenants - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// Tenant is a grouping of users in the identity service. -type Tenant struct { - // ID is a unique identifier for this tenant. - ID string `json:"id"` - - // Name is a friendlier user-facing name for this tenant. - Name string `json:"name"` - - // Description is a human-readable explanation of this Tenant's purpose. - Description string `json:"description"` - - // Enabled indicates whether or not a tenant is active. - Enabled bool `json:"enabled"` -} - -// TenantPage is a single page of Tenant results. -type TenantPage struct { - pagination.LinkedPageBase -} - -// IsEmpty determines whether or not a page of Tenants contains any results. -func (r TenantPage) IsEmpty() (bool, error) { - tenants, err := ExtractTenants(r) - return len(tenants) == 0, err -} - -// NextPageURL extracts the "next" link from the tenants_links section of the result. -func (r TenantPage) NextPageURL() (string, error) { - var s struct { - Links []gophercloud.Link `json:"tenants_links"` - } - err := r.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// ExtractTenants returns a slice of Tenants contained in a single page of -// results. -func ExtractTenants(r pagination.Page) ([]Tenant, error) { - var s struct { - Tenants []Tenant `json:"tenants"` - } - err := (r.(TenantPage)).ExtractInto(&s) - return s.Tenants, err -} - -type tenantResult struct { - gophercloud.Result -} - -// Extract interprets any tenantResults as a Tenant. -func (r tenantResult) Extract() (*Tenant, error) { - var s struct { - Tenant *Tenant `json:"tenant"` - } - err := r.ExtractInto(&s) - return s.Tenant, err -} - -// GetResult is the response from a Get request. Call its Extract method to -// interpret it as a Tenant. -type GetResult struct { - tenantResult -} - -// CreateResult is the response from a Create request. Call its Extract method -// to interpret it as a Tenant. -type CreateResult struct { - tenantResult -} - -// DeleteResult is the response from a Get request. Call its ExtractErr method -// to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// UpdateResult is the response from a Update request. Call its Extract method -// to interpret it as a Tenant. -type UpdateResult struct { - tenantResult -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go deleted file mode 100644 index 0f0266907..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go +++ /dev/null @@ -1,23 +0,0 @@ -package tenants - -import "github.com/gophercloud/gophercloud" - -func listURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("tenants") -} - -func getURL(client *gophercloud.ServiceClient, tenantID string) string { - return client.ServiceURL("tenants", tenantID) -} - -func createURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("tenants") -} - -func deleteURL(client *gophercloud.ServiceClient, tenantID string) string { - return client.ServiceURL("tenants", tenantID) -} - -func updateURL(client *gophercloud.ServiceClient, tenantID string) string { - return client.ServiceURL("tenants", tenantID) -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go deleted file mode 100644 index 5375eea87..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Package tokens provides information and interaction with the token API -resource for the OpenStack Identity service. - -For more information, see: -http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2 - -Example to Create an Unscoped Token from a Password - - authOpts := gophercloud.AuthOptions{ - Username: "user", - Password: "pass" - } - - token, err := tokens.Create(identityClient, authOpts).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token from a Tenant ID and Password - - authOpts := gophercloud.AuthOptions{ - Username: "user", - Password: "password", - TenantID: "fc394f2ab2df4114bde39905f800dc57" - } - - token, err := tokens.Create(identityClient, authOpts).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token from a Tenant Name and Password - - authOpts := gophercloud.AuthOptions{ - Username: "user", - Password: "password", - TenantName: "tenantname" - } - - token, err := tokens.Create(identityClient, authOpts).ExtractToken() - if err != nil { - panic(err) - } -*/ -package tokens diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go deleted file mode 100644 index ab32368cc..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go +++ /dev/null @@ -1,103 +0,0 @@ -package tokens - -import "github.com/gophercloud/gophercloud" - -// PasswordCredentialsV2 represents the required options to authenticate -// with a username and password. -type PasswordCredentialsV2 struct { - Username string `json:"username" required:"true"` - Password string `json:"password" required:"true"` -} - -// TokenCredentialsV2 represents the required options to authenticate -// with a token. -type TokenCredentialsV2 struct { - ID string `json:"id,omitempty" required:"true"` -} - -// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the -// AuthOptionsBuilder interface. -type AuthOptionsV2 struct { - PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` - - // The TenantID and TenantName fields are optional for the Identity V2 API. - // Some providers allow you to specify a TenantName instead of the TenantId. - // Some require both. Your provider's authentication policies will determine - // how these fields influence authentication. - TenantID string `json:"tenantId,omitempty"` - TenantName string `json:"tenantName,omitempty"` - - // TokenCredentials allows users to authenticate (possibly as another user) - // with an authentication token ID. - TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` -} - -// AuthOptionsBuilder allows extensions to add additional parameters to the -// token create request. -type AuthOptionsBuilder interface { - // ToTokenCreateMap assembles the Create request body, returning an error - // if parameters are missing or inconsistent. - ToTokenV2CreateMap() (map[string]interface{}, error) -} - -// AuthOptions are the valid options for Openstack Identity v2 authentication. -// For field descriptions, see gophercloud.AuthOptions. -type AuthOptions struct { - IdentityEndpoint string `json:"-"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - TenantID string `json:"tenantId,omitempty"` - TenantName string `json:"tenantName,omitempty"` - AllowReauth bool `json:"-"` - TokenID string -} - -// ToTokenV2CreateMap builds a token request body from the given AuthOptions. -func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { - v2Opts := AuthOptionsV2{ - TenantID: opts.TenantID, - TenantName: opts.TenantName, - } - - if opts.Password != "" { - v2Opts.PasswordCredentials = &PasswordCredentialsV2{ - Username: opts.Username, - Password: opts.Password, - } - } else { - v2Opts.TokenCredentials = &TokenCredentialsV2{ - ID: opts.TokenID, - } - } - - b, err := gophercloud.BuildRequestBody(v2Opts, "auth") - if err != nil { - return nil, err - } - return b, nil -} - -// Create authenticates to the identity service and attempts to acquire a Token. -// Generally, rather than interact with this call directly, end users should -// call openstack.AuthenticatedClient(), which abstracts all of the gory details -// about navigating service catalogs and such. -func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { - b, err := auth.ToTokenV2CreateMap() - if err != nil { - r.Err = err - return - } - _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 203}, - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - }) - return -} - -// Get validates and retrieves information for user's token. -func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { - _, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 203}, - }) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go deleted file mode 100644 index ee5da37f4..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go +++ /dev/null @@ -1,174 +0,0 @@ -package tokens - -import ( - "time" - - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" -) - -// Token provides only the most basic information related to an authentication -// token. -type Token struct { - // ID provides the primary means of identifying a user to the OpenStack API. - // OpenStack defines this field as an opaque value, so do not depend on its - // content. It is safe, however, to compare for equality. - ID string - - // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the - // authentication token becomes invalid. After this point in time, future - // API requests made using this authentication token will respond with - // errors. Either the caller will need to reauthenticate manually, or more - // preferably, the caller should exploit automatic re-authentication. - // See the AuthOptions structure for more details. - ExpiresAt time.Time - - // Tenant provides information about the tenant to which this token grants - // access. - Tenant tenants.Tenant -} - -// Role is a role for a user. -type Role struct { - Name string `json:"name"` -} - -// User is an OpenStack user. -type User struct { - ID string `json:"id"` - Name string `json:"name"` - UserName string `json:"username"` - Roles []Role `json:"roles"` -} - -// Endpoint represents a single API endpoint offered by a service. -// It provides the public and internal URLs, if supported, along with a region -// specifier, again if provided. -// -// The significance of the Region field will depend upon your provider. -// -// In addition, the interface offered by the service will have version -// information associated with it through the VersionId, VersionInfo, and -// VersionList fields, if provided or supported. -// -// In all cases, fields which aren't supported by the provider and service -// combined will assume a zero-value (""). -type Endpoint struct { - TenantID string `json:"tenantId"` - PublicURL string `json:"publicURL"` - InternalURL string `json:"internalURL"` - AdminURL string `json:"adminURL"` - Region string `json:"region"` - VersionID string `json:"versionId"` - VersionInfo string `json:"versionInfo"` - VersionList string `json:"versionList"` -} - -// CatalogEntry provides a type-safe interface to an Identity API V2 service -// catalog listing. -// -// Each class of service, such as cloud DNS or block storage services, will have -// a single CatalogEntry representing it. -// -// Note: when looking for the desired service, try, whenever possible, to key -// off the type field. Otherwise, you'll tie the representation of the service -// to a specific provider. -type CatalogEntry struct { - // Name will contain the provider-specified name for the service. - Name string `json:"name"` - - // Type will contain a type string if OpenStack defines a type for the - // service. Otherwise, for provider-specific services, the provider may assign - // their own type strings. - Type string `json:"type"` - - // Endpoints will let the caller iterate over all the different endpoints that - // may exist for the service. - Endpoints []Endpoint `json:"endpoints"` -} - -// ServiceCatalog provides a view into the service catalog from a previous, -// successful authentication. -type ServiceCatalog struct { - Entries []CatalogEntry -} - -// CreateResult is the response from a Create request. Use ExtractToken() to -// interpret it as a Token, or ExtractServiceCatalog() to interpret it as a -// service catalog. -type CreateResult struct { - gophercloud.Result -} - -// GetResult is the deferred response from a Get call, which is the same with a -// Created token. Use ExtractUser() to interpret it as a User. -type GetResult struct { - CreateResult -} - -// ExtractToken returns the just-created Token from a CreateResult. -func (r CreateResult) ExtractToken() (*Token, error) { - var s struct { - Access struct { - Token struct { - Expires string `json:"expires"` - ID string `json:"id"` - Tenant tenants.Tenant `json:"tenant"` - } `json:"token"` - } `json:"access"` - } - - err := r.ExtractInto(&s) - if err != nil { - return nil, err - } - - expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires) - if err != nil { - return nil, err - } - - return &Token{ - ID: s.Access.Token.ID, - ExpiresAt: expiresTs, - Tenant: s.Access.Token.Tenant, - }, nil -} - -// ExtractTokenID implements the gophercloud.AuthResult interface. The returned -// string is the same as the ID field of the Token struct returned from -// ExtractToken(). -func (r CreateResult) ExtractTokenID() (string, error) { - var s struct { - Access struct { - Token struct { - ID string `json:"id"` - } `json:"token"` - } `json:"access"` - } - err := r.ExtractInto(&s) - return s.Access.Token.ID, err -} - -// ExtractServiceCatalog returns the ServiceCatalog that was generated along -// with the user's Token. -func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { - var s struct { - Access struct { - Entries []CatalogEntry `json:"serviceCatalog"` - } `json:"access"` - } - err := r.ExtractInto(&s) - return &ServiceCatalog{Entries: s.Access.Entries}, err -} - -// ExtractUser returns the User from a GetResult. -func (r GetResult) ExtractUser() (*User, error) { - var s struct { - Access struct { - User User `json:"user"` - } `json:"access"` - } - err := r.ExtractInto(&s) - return &s.Access.User, err -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go deleted file mode 100644 index ee0a28f20..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go +++ /dev/null @@ -1,13 +0,0 @@ -package tokens - -import "github.com/gophercloud/gophercloud" - -// CreateURL generates the URL used to create new Tokens. -func CreateURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("tokens") -} - -// GetURL generates the URL used to Validate Tokens. -func GetURL(client *gophercloud.ServiceClient, token string) string { - return client.ServiceURL("tokens", token) -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go deleted file mode 100644 index 966e128f1..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Package tokens provides information and interaction with the token API -resource for the OpenStack Identity service. - -For more information, see: -http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3 - -Example to Create a Token From a Username and Password - - authOptions := tokens.AuthOptions{ - UserID: "username", - Password: "password", - } - - token, err := tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token From a Username, Password, and Domain - - authOptions := tokens.AuthOptions{ - UserID: "username", - Password: "password", - DomainID: "default", - } - - token, err := tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - - authOptions = tokens.AuthOptions{ - UserID: "username", - Password: "password", - DomainName: "default", - } - - token, err = tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token From a Token - - authOptions := tokens.AuthOptions{ - TokenID: "token_id", - } - - token, err := tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token from a Username and Password with Project ID Scope - - scope := tokens.Scope{ - ProjectID: "0fe36e73809d46aeae6705c39077b1b3", - } - - authOptions := tokens.AuthOptions{ - Scope: &scope, - UserID: "username", - Password: "password", - } - - token, err = tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token from a Username and Password with Domain ID Scope - - scope := tokens.Scope{ - DomainID: "default", - } - - authOptions := tokens.AuthOptions{ - Scope: &scope, - UserID: "username", - Password: "password", - } - - token, err = tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -Example to Create a Token from a Username and Password with Project Name Scope - - scope := tokens.Scope{ - ProjectName: "project_name", - DomainID: "default", - } - - authOptions := tokens.AuthOptions{ - Scope: &scope, - UserID: "username", - Password: "password", - } - - token, err = tokens.Create(identityClient, authOptions).ExtractToken() - if err != nil { - panic(err) - } - -*/ -package tokens diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go deleted file mode 100644 index e4d766b23..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go +++ /dev/null @@ -1,162 +0,0 @@ -package tokens - -import "github.com/gophercloud/gophercloud" - -// Scope allows a created token to be limited to a specific domain or project. -type Scope struct { - ProjectID string - ProjectName string - DomainID string - DomainName string -} - -// AuthOptionsBuilder provides the ability for extensions to add additional -// parameters to AuthOptions. Extensions must satisfy all required methods. -type AuthOptionsBuilder interface { - // ToTokenV3CreateMap assembles the Create request body, returning an error - // if parameters are missing or inconsistent. - ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) - ToTokenV3ScopeMap() (map[string]interface{}, error) - CanReauth() bool -} - -// AuthOptions represents options for authenticating a user. -type AuthOptions struct { - // IdentityEndpoint specifies the HTTP endpoint that is required to work with - // the Identity API of the appropriate version. While it's ultimately needed - // by all of the identity services, it will often be populated by a - // provider-level function. - IdentityEndpoint string `json:"-"` - - // Username is required if using Identity V2 API. Consult with your provider's - // control panel to discover your account's username. In Identity V3, either - // UserID or a combination of Username and DomainID or DomainName are needed. - Username string `json:"username,omitempty"` - UserID string `json:"id,omitempty"` - - Password string `json:"password,omitempty"` - - // At most one of DomainID and DomainName must be provided if using Username - // with Identity V3. Otherwise, either are optional. - DomainID string `json:"-"` - DomainName string `json:"name,omitempty"` - - // AllowReauth should be set to true if you grant permission for Gophercloud - // to cache your credentials in memory, and to allow Gophercloud to attempt - // to re-authenticate automatically if/when your token expires. If you set - // it to false, it will not cache these settings, but re-authentication will - // not be possible. This setting defaults to false. - AllowReauth bool `json:"-"` - - // TokenID allows users to authenticate (possibly as another user) with an - // authentication token ID. - TokenID string `json:"-"` - - // Authentication through Application Credentials requires supplying name, project and secret - // For project we can use TenantID - ApplicationCredentialID string `json:"-"` - ApplicationCredentialName string `json:"-"` - ApplicationCredentialSecret string `json:"-"` - - Scope Scope `json:"-"` -} - -// ToTokenV3CreateMap builds a request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { - gophercloudAuthOpts := gophercloud.AuthOptions{ - Username: opts.Username, - UserID: opts.UserID, - Password: opts.Password, - DomainID: opts.DomainID, - DomainName: opts.DomainName, - AllowReauth: opts.AllowReauth, - TokenID: opts.TokenID, - ApplicationCredentialID: opts.ApplicationCredentialID, - ApplicationCredentialName: opts.ApplicationCredentialName, - ApplicationCredentialSecret: opts.ApplicationCredentialSecret, - } - - return gophercloudAuthOpts.ToTokenV3CreateMap(scope) -} - -// ToTokenV3CreateMap builds a scope request body from AuthOptions. -func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { - scope := gophercloud.AuthScope(opts.Scope) - - gophercloudAuthOpts := gophercloud.AuthOptions{ - Scope: &scope, - DomainID: opts.DomainID, - DomainName: opts.DomainName, - } - - return gophercloudAuthOpts.ToTokenV3ScopeMap() -} - -func (opts *AuthOptions) CanReauth() bool { - return opts.AllowReauth -} - -func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { - return map[string]string{ - "X-Subject-Token": subjectToken, - } -} - -// Create authenticates and either generates a new token, or changes the Scope -// of an existing token. -func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { - scope, err := opts.ToTokenV3ScopeMap() - if err != nil { - r.Err = err - return - } - - b, err := opts.ToTokenV3CreateMap(scope) - if err != nil { - r.Err = err - return - } - - resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: map[string]string{"X-Auth-Token": ""}, - }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } - return -} - -// Get validates and retrieves information about another token. -func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { - resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - OkCodes: []int{200, 203}, - }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err - return -} - -// Validate determines if a specified token is valid or not. -func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - OkCodes: []int{200, 204, 404}, - }) - if err != nil { - return false, err - } - - return resp.StatusCode == 200 || resp.StatusCode == 204, nil -} - -// Revoke immediately makes specified token invalid. -func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { - _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - }) - return -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go deleted file mode 100644 index 6f26c96bc..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go +++ /dev/null @@ -1,178 +0,0 @@ -package tokens - -import ( - "time" - - "github.com/gophercloud/gophercloud" -) - -// Endpoint represents a single API endpoint offered by a service. -// It matches either a public, internal or admin URL. -// If supported, it contains a region specifier, again if provided. -// The significance of the Region field will depend upon your provider. -type Endpoint struct { - ID string `json:"id"` - Region string `json:"region"` - RegionID string `json:"region_id"` - Interface string `json:"interface"` - URL string `json:"url"` -} - -// CatalogEntry provides a type-safe interface to an Identity API V3 service -// catalog listing. Each class of service, such as cloud DNS or block storage -// services, could have multiple CatalogEntry representing it (one by interface -// type, e.g public, admin or internal). -// -// Note: when looking for the desired service, try, whenever possible, to key -// off the type field. Otherwise, you'll tie the representation of the service -// to a specific provider. -type CatalogEntry struct { - // Service ID - ID string `json:"id"` - - // Name will contain the provider-specified name for the service. - Name string `json:"name"` - - // Type will contain a type string if OpenStack defines a type for the - // service. Otherwise, for provider-specific services, the provider may - // assign their own type strings. - Type string `json:"type"` - - // Endpoints will let the caller iterate over all the different endpoints that - // may exist for the service. - Endpoints []Endpoint `json:"endpoints"` -} - -// ServiceCatalog provides a view into the service catalog from a previous, -// successful authentication. -type ServiceCatalog struct { - Entries []CatalogEntry `json:"catalog"` -} - -// Domain provides information about the domain to which this token grants -// access. -type Domain struct { - ID string `json:"id"` - Name string `json:"name"` -} - -// User represents a user resource that exists in the Identity Service. -type User struct { - Domain Domain `json:"domain"` - ID string `json:"id"` - Name string `json:"name"` -} - -// Role provides information about roles to which User is authorized. -type Role struct { - ID string `json:"id"` - Name string `json:"name"` -} - -// Project provides information about project to which User is authorized. -type Project struct { - Domain Domain `json:"domain"` - ID string `json:"id"` - Name string `json:"name"` -} - -// commonResult is the response from a request. A commonResult has various -// methods which can be used to extract different details about the result. -type commonResult struct { - gophercloud.Result -} - -// Extract is a shortcut for ExtractToken. -// This function is deprecated and still present for backward compatibility. -func (r commonResult) Extract() (*Token, error) { - return r.ExtractToken() -} - -// ExtractToken interprets a commonResult as a Token. -func (r commonResult) ExtractToken() (*Token, error) { - var s Token - err := r.ExtractInto(&s) - if err != nil { - return nil, err - } - - // Parse the token itself from the stored headers. - s.ID = r.Header.Get("X-Subject-Token") - - return &s, err -} - -// ExtractTokenID implements the gophercloud.AuthResult interface. The returned -// string is the same as the ID field of the Token struct returned from -// ExtractToken(). -func (r CreateResult) ExtractTokenID() (string, error) { - return r.Header.Get("X-Subject-Token"), r.Err -} - -// ExtractServiceCatalog returns the ServiceCatalog that was generated along -// with the user's Token. -func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) { - var s ServiceCatalog - err := r.ExtractInto(&s) - return &s, err -} - -// ExtractUser returns the User that is the owner of the Token. -func (r commonResult) ExtractUser() (*User, error) { - var s struct { - User *User `json:"user"` - } - err := r.ExtractInto(&s) - return s.User, err -} - -// ExtractRoles returns Roles to which User is authorized. -func (r commonResult) ExtractRoles() ([]Role, error) { - var s struct { - Roles []Role `json:"roles"` - } - err := r.ExtractInto(&s) - return s.Roles, err -} - -// ExtractProject returns Project to which User is authorized. -func (r commonResult) ExtractProject() (*Project, error) { - var s struct { - Project *Project `json:"project"` - } - err := r.ExtractInto(&s) - return s.Project, err -} - -// CreateResult is the response from a Create request. Use ExtractToken() -// to interpret it as a Token, or ExtractServiceCatalog() to interpret it -// as a service catalog. -type CreateResult struct { - commonResult -} - -// GetResult is the response from a Get request. Use ExtractToken() -// to interpret it as a Token, or ExtractServiceCatalog() to interpret it -// as a service catalog. -type GetResult struct { - commonResult -} - -// RevokeResult is response from a Revoke request. -type RevokeResult struct { - commonResult -} - -// Token is a string that grants a user access to a controlled set of services -// in an OpenStack provider. Each Token is valid for a set length of time. -type Token struct { - // ID is the issued token. - ID string `json:"id"` - - // ExpiresAt is the timestamp at which this token will no longer be accepted. - ExpiresAt time.Time `json:"expires_at"` -} - -func (r commonResult) ExtractInto(v interface{}) error { - return r.ExtractIntoStructPtr(v, "token") -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go deleted file mode 100644 index 2f864a31c..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go +++ /dev/null @@ -1,7 +0,0 @@ -package tokens - -import "github.com/gophercloud/gophercloud" - -func tokenURL(c *gophercloud.ServiceClient) string { - return c.ServiceURL("auth", "tokens") -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go b/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go deleted file mode 100644 index 40080f7af..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/utils/base_endpoint.go +++ /dev/null @@ -1,28 +0,0 @@ -package utils - -import ( - "net/url" - "regexp" - "strings" -) - -// BaseEndpoint will return a URL without the /vX.Y -// portion of the URL. -func BaseEndpoint(endpoint string) (string, error) { - u, err := url.Parse(endpoint) - if err != nil { - return "", err - } - - u.RawQuery, u.Fragment = "", "" - - path := u.Path - versionRe := regexp.MustCompile("v[0-9.]+/?") - - if version := versionRe.FindString(path); version != "" { - versionIndex := strings.Index(path, version) - u.Path = path[:versionIndex] - } - - return u.String(), nil -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go b/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go deleted file mode 100644 index 27da19f91..000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go +++ /dev/null @@ -1,111 +0,0 @@ -package utils - -import ( - "fmt" - "strings" - - "github.com/gophercloud/gophercloud" -) - -// Version is a supported API version, corresponding to a vN package within the appropriate service. -type Version struct { - ID string - Suffix string - Priority int -} - -var goodStatus = map[string]bool{ - "current": true, - "supported": true, - "stable": true, -} - -// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's -// published versions. -// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint. -func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) { - type linkResp struct { - Href string `json:"href"` - Rel string `json:"rel"` - } - - type valueResp struct { - ID string `json:"id"` - Status string `json:"status"` - Links []linkResp `json:"links"` - } - - type versionsResp struct { - Values []valueResp `json:"values"` - } - - type response struct { - Versions versionsResp `json:"versions"` - } - - normalize := func(endpoint string) string { - if !strings.HasSuffix(endpoint, "/") { - return endpoint + "/" - } - return endpoint - } - identityEndpoint := normalize(client.IdentityEndpoint) - - // If a full endpoint is specified, check version suffixes for a match first. - for _, v := range recognized { - if strings.HasSuffix(identityEndpoint, v.Suffix) { - return v, identityEndpoint, nil - } - } - - var resp response - _, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ - JSONResponse: &resp, - OkCodes: []int{200, 300}, - }) - - if err != nil { - return nil, "", err - } - - var highest *Version - var endpoint string - - for _, value := range resp.Versions.Values { - href := "" - for _, link := range value.Links { - if link.Rel == "self" { - href = normalize(link.Href) - } - } - - for _, version := range recognized { - if strings.Contains(value.ID, version.ID) { - // Prefer a version that exactly matches the provided endpoint. - if href == identityEndpoint { - if href == "" { - return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, client.IdentityBase) - } - return version, href, nil - } - - // Otherwise, find the highest-priority version with a whitelisted status. - if goodStatus[strings.ToLower(value.Status)] { - if highest == nil || version.Priority > highest.Priority { - highest = version - endpoint = href - } - } - } - } - } - - if highest == nil { - return nil, "", fmt.Errorf("No supported version available from endpoint %s", client.IdentityBase) - } - if endpoint == "" { - return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, client.IdentityBase) - } - - return highest, endpoint, nil -} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/pagination/http.go deleted file mode 100644 index 757295c42..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/http.go +++ /dev/null @@ -1,60 +0,0 @@ -package pagination - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/url" - "strings" - - "github.com/gophercloud/gophercloud" -) - -// PageResult stores the HTTP response that returned the current page of results. -type PageResult struct { - gophercloud.Result - url.URL -} - -// PageResultFrom parses an HTTP response as JSON and returns a PageResult containing the -// results, interpreting it as JSON if the content type indicates. -func PageResultFrom(resp *http.Response) (PageResult, error) { - var parsedBody interface{} - - defer resp.Body.Close() - rawBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return PageResult{}, err - } - - if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") { - err = json.Unmarshal(rawBody, &parsedBody) - if err != nil { - return PageResult{}, err - } - } else { - parsedBody = rawBody - } - - return PageResultFromParsed(resp, parsedBody), err -} - -// PageResultFromParsed constructs a PageResult from an HTTP response that has already had its -// body parsed as JSON (and closed). -func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { - return PageResult{ - Result: gophercloud.Result{ - Body: body, - Header: resp.Header, - }, - URL: *resp.Request.URL, - } -} - -// Request performs an HTTP request and extracts the http.Response from the result. -func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return client.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: headers, - OkCodes: []int{200, 204, 300}, - }) -} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/linked.go b/vendor/github.com/gophercloud/gophercloud/pagination/linked.go deleted file mode 100644 index 3656fb7f8..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/linked.go +++ /dev/null @@ -1,92 +0,0 @@ -package pagination - -import ( - "fmt" - "reflect" - - "github.com/gophercloud/gophercloud" -) - -// LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. -type LinkedPageBase struct { - PageResult - - // LinkPath lists the keys that should be traversed within a response to arrive at the "next" pointer. - // If any link along the path is missing, an empty URL will be returned. - // If any link results in an unexpected value type, an error will be returned. - // When left as "nil", []string{"links", "next"} will be used as a default. - LinkPath []string -} - -// NextPageURL extracts the pagination structure from a JSON response and returns the "next" link, if one is present. -// It assumes that the links are available in a "links" element of the top-level response object. -// If this is not the case, override NextPageURL on your result type. -func (current LinkedPageBase) NextPageURL() (string, error) { - var path []string - var key string - - if current.LinkPath == nil { - path = []string{"links", "next"} - } else { - path = current.LinkPath - } - - submap, ok := current.Body.(map[string]interface{}) - if !ok { - err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) - return "", err - } - - for { - key, path = path[0], path[1:len(path)] - - value, ok := submap[key] - if !ok { - return "", nil - } - - if len(path) > 0 { - submap, ok = value.(map[string]interface{}) - if !ok { - err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) - return "", err - } - } else { - if value == nil { - // Actual null element. - return "", nil - } - - url, ok := value.(string) - if !ok { - err := gophercloud.ErrUnexpectedType{} - err.Expected = "string" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) - return "", err - } - - return url, nil - } - } -} - -// IsEmpty satisifies the IsEmpty method of the Page interface -func (current LinkedPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { - return len(b) == 0, nil - } - err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) - return true, err -} - -// GetBody returns the linked page's body. This method is needed to satisfy the -// Page interface. -func (current LinkedPageBase) GetBody() interface{} { - return current.Body -} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/marker.go b/vendor/github.com/gophercloud/gophercloud/pagination/marker.go deleted file mode 100644 index 52e53bae8..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/marker.go +++ /dev/null @@ -1,58 +0,0 @@ -package pagination - -import ( - "fmt" - "reflect" - - "github.com/gophercloud/gophercloud" -) - -// MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. -// For convenience, embed the MarkedPageBase struct. -type MarkerPage interface { - Page - - // LastMarker returns the last "marker" value on this page. - LastMarker() (string, error) -} - -// MarkerPageBase is a page in a collection that's paginated by "limit" and "marker" query parameters. -type MarkerPageBase struct { - PageResult - - // Owner is a reference to the embedding struct. - Owner MarkerPage -} - -// NextPageURL generates the URL for the page of results after this one. -func (current MarkerPageBase) NextPageURL() (string, error) { - currentURL := current.URL - - mark, err := current.Owner.LastMarker() - if err != nil { - return "", err - } - - q := currentURL.Query() - q.Set("marker", mark) - currentURL.RawQuery = q.Encode() - - return currentURL.String(), nil -} - -// IsEmpty satisifies the IsEmpty method of the Page interface -func (current MarkerPageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { - return len(b) == 0, nil - } - err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) - return true, err -} - -// GetBody returns the linked page's body. This method is needed to satisfy the -// Page interface. -func (current MarkerPageBase) GetBody() interface{} { - return current.Body -} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/pager.go b/vendor/github.com/gophercloud/gophercloud/pagination/pager.go deleted file mode 100644 index 42c0b2dbe..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/pager.go +++ /dev/null @@ -1,251 +0,0 @@ -package pagination - -import ( - "errors" - "fmt" - "net/http" - "reflect" - "strings" - - "github.com/gophercloud/gophercloud" -) - -var ( - // ErrPageNotAvailable is returned from a Pager when a next or previous page is requested, but does not exist. - ErrPageNotAvailable = errors.New("The requested page does not exist.") -) - -// Page must be satisfied by the result type of any resource collection. -// It allows clients to interact with the resource uniformly, regardless of whether or not or how it's paginated. -// Generally, rather than implementing this interface directly, implementors should embed one of the concrete PageBase structs, -// instead. -// Depending on the pagination strategy of a particular resource, there may be an additional subinterface that the result type -// will need to implement. -type Page interface { - // NextPageURL generates the URL for the page of data that follows this collection. - // Return "" if no such page exists. - NextPageURL() (string, error) - - // IsEmpty returns true if this Page has no items in it. - IsEmpty() (bool, error) - - // GetBody returns the Page Body. This is used in the `AllPages` method. - GetBody() interface{} -} - -// Pager knows how to advance through a specific resource collection, one page at a time. -type Pager struct { - client *gophercloud.ServiceClient - - initialURL string - - createPage func(r PageResult) Page - - firstPage Page - - Err error - - // Headers supplies additional HTTP headers to populate on each paged request. - Headers map[string]string -} - -// NewPager constructs a manually-configured pager. -// Supply the URL for the first page, a function that requests a specific page given a URL, and a function that counts a page. -func NewPager(client *gophercloud.ServiceClient, initialURL string, createPage func(r PageResult) Page) Pager { - return Pager{ - client: client, - initialURL: initialURL, - createPage: createPage, - } -} - -// WithPageCreator returns a new Pager that substitutes a different page creation function. This is -// useful for overriding List functions in delegation. -func (p Pager) WithPageCreator(createPage func(r PageResult) Page) Pager { - return Pager{ - client: p.client, - initialURL: p.initialURL, - createPage: createPage, - } -} - -func (p Pager) fetchNextPage(url string) (Page, error) { - resp, err := Request(p.client, p.Headers, url) - if err != nil { - return nil, err - } - - remembered, err := PageResultFrom(resp) - if err != nil { - return nil, err - } - - return p.createPage(remembered), nil -} - -// EachPage iterates over each page returned by a Pager, yielding one at a time to a handler function. -// Return "false" from the handler to prematurely stop iterating. -func (p Pager) EachPage(handler func(Page) (bool, error)) error { - if p.Err != nil { - return p.Err - } - currentURL := p.initialURL - for { - var currentPage Page - - // if first page has already been fetched, no need to fetch it again - if p.firstPage != nil { - currentPage = p.firstPage - p.firstPage = nil - } else { - var err error - currentPage, err = p.fetchNextPage(currentURL) - if err != nil { - return err - } - } - - empty, err := currentPage.IsEmpty() - if err != nil { - return err - } - if empty { - return nil - } - - ok, err := handler(currentPage) - if err != nil { - return err - } - if !ok { - return nil - } - - currentURL, err = currentPage.NextPageURL() - if err != nil { - return err - } - if currentURL == "" { - return nil - } - } -} - -// AllPages returns all the pages from a `List` operation in a single page, -// allowing the user to retrieve all the pages at once. -func (p Pager) AllPages() (Page, error) { - // pagesSlice holds all the pages until they get converted into as Page Body. - var pagesSlice []interface{} - // body will contain the final concatenated Page body. - var body reflect.Value - - // Grab a first page to ascertain the page body type. - firstPage, err := p.fetchNextPage(p.initialURL) - if err != nil { - return nil, err - } - // Store the page type so we can use reflection to create a new mega-page of - // that type. - pageType := reflect.TypeOf(firstPage) - - // if it's a single page, just return the firstPage (first page) - if _, found := pageType.FieldByName("SinglePageBase"); found { - return firstPage, nil - } - - // store the first page to avoid getting it twice - p.firstPage = firstPage - - // Switch on the page body type. Recognized types are `map[string]interface{}`, - // `[]byte`, and `[]interface{}`. - switch pb := firstPage.GetBody().(type) { - case map[string]interface{}: - // key is the map key for the page body if the body type is `map[string]interface{}`. - var key string - // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { - b := page.GetBody().(map[string]interface{}) - for k, v := range b { - // If it's a linked page, we don't want the `links`, we want the other one. - if !strings.HasSuffix(k, "links") { - // check the field's type. we only want []interface{} (which is really []map[string]interface{}) - switch vt := v.(type) { - case []interface{}: - key = k - pagesSlice = append(pagesSlice, vt...) - } - } - } - return true, nil - }) - if err != nil { - return nil, err - } - // Set body to value of type `map[string]interface{}` - body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) - body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) - case []byte: - // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { - b := page.GetBody().([]byte) - pagesSlice = append(pagesSlice, b) - // seperate pages with a comma - pagesSlice = append(pagesSlice, []byte{10}) - return true, nil - }) - if err != nil { - return nil, err - } - if len(pagesSlice) > 0 { - // Remove the trailing comma. - pagesSlice = pagesSlice[:len(pagesSlice)-1] - } - var b []byte - // Combine the slice of slices in to a single slice. - for _, slice := range pagesSlice { - b = append(b, slice.([]byte)...) - } - // Set body to value of type `bytes`. - body = reflect.New(reflect.TypeOf(b)).Elem() - body.SetBytes(b) - case []interface{}: - // Iterate over the pages to concatenate the bodies. - err = p.EachPage(func(page Page) (bool, error) { - b := page.GetBody().([]interface{}) - pagesSlice = append(pagesSlice, b...) - return true, nil - }) - if err != nil { - return nil, err - } - // Set body to value of type `[]interface{}` - body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) - for i, s := range pagesSlice { - body.Index(i).Set(reflect.ValueOf(s)) - } - default: - err := gophercloud.ErrUnexpectedType{} - err.Expected = "map[string]interface{}/[]byte/[]interface{}" - err.Actual = fmt.Sprintf("%T", pb) - return nil, err - } - - // Each `Extract*` function is expecting a specific type of page coming back, - // otherwise the type assertion in those functions will fail. pageType is needed - // to create a type in this method that has the same type that the `Extract*` - // function is expecting and set the Body of that object to the concatenated - // pages. - page := reflect.New(pageType) - // Set the page body to be the concatenated pages. - page.Elem().FieldByName("Body").Set(body) - // Set any additional headers that were pass along. The `objectstorage` pacakge, - // for example, passes a Content-Type header. - h := make(http.Header) - for k, v := range p.Headers { - h.Add(k, v) - } - page.Elem().FieldByName("Header").Set(reflect.ValueOf(h)) - // Type assert the page to a Page interface so that the type assertion in the - // `Extract*` methods will work. - return page.Elem().Interface().(Page), err -} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/pkg.go b/vendor/github.com/gophercloud/gophercloud/pagination/pkg.go deleted file mode 100644 index 912daea36..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/pkg.go +++ /dev/null @@ -1,4 +0,0 @@ -/* -Package pagination contains utilities and convenience structs that implement common pagination idioms within OpenStack APIs. -*/ -package pagination diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/single.go b/vendor/github.com/gophercloud/gophercloud/pagination/single.go deleted file mode 100644 index 4251d6491..000000000 --- a/vendor/github.com/gophercloud/gophercloud/pagination/single.go +++ /dev/null @@ -1,33 +0,0 @@ -package pagination - -import ( - "fmt" - "reflect" - - "github.com/gophercloud/gophercloud" -) - -// SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. -type SinglePageBase PageResult - -// NextPageURL always returns "" to indicate that there are no more pages to return. -func (current SinglePageBase) NextPageURL() (string, error) { - return "", nil -} - -// IsEmpty satisifies the IsEmpty method of the Page interface -func (current SinglePageBase) IsEmpty() (bool, error) { - if b, ok := current.Body.([]interface{}); ok { - return len(b) == 0, nil - } - err := gophercloud.ErrUnexpectedType{} - err.Expected = "[]interface{}" - err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) - return true, err -} - -// GetBody returns the single page's body. This method is needed to satisfy the -// Page interface. -func (current SinglePageBase) GetBody() interface{} { - return current.Body -} diff --git a/vendor/github.com/gophercloud/gophercloud/params.go b/vendor/github.com/gophercloud/gophercloud/params.go deleted file mode 100644 index b9986660c..000000000 --- a/vendor/github.com/gophercloud/gophercloud/params.go +++ /dev/null @@ -1,491 +0,0 @@ -package gophercloud - -import ( - "encoding/json" - "fmt" - "net/url" - "reflect" - "strconv" - "strings" - "time" -) - -/* -BuildRequestBody builds a map[string]interface from the given `struct`. If -parent is not an empty string, the final map[string]interface returned will -encapsulate the built one. For example: - - disk := 1 - createOpts := flavors.CreateOpts{ - ID: "1", - Name: "m1.tiny", - Disk: &disk, - RAM: 512, - VCPUs: 1, - RxTxFactor: 1.0, - } - - body, err := gophercloud.BuildRequestBody(createOpts, "flavor") - -The above example can be run as-is, however it is recommended to look at how -BuildRequestBody is used within Gophercloud to more fully understand how it -fits within the request process as a whole rather than use it directly as shown -above. -*/ -func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { - optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { - optsValue = optsValue.Elem() - } - - optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { - optsType = optsType.Elem() - } - - optsMap := make(map[string]interface{}) - if optsValue.Kind() == reflect.Struct { - //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) - for i := 0; i < optsValue.NumField(); i++ { - v := optsValue.Field(i) - f := optsType.Field(i) - - if f.Name != strings.Title(f.Name) { - //fmt.Printf("Skipping field: %s...\n", f.Name) - continue - } - - //fmt.Printf("Starting on field: %s...\n", f.Name) - - zero := isZero(v) - //fmt.Printf("v is zero?: %v\n", zero) - - // if the field has a required tag that's set to "true" - if requiredTag := f.Tag.Get("required"); requiredTag == "true" { - //fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero) - // if the field's value is zero, return a missing-argument error - if zero { - // if the field has a 'required' tag, it can't have a zero-value - err := ErrMissingInput{} - err.Argument = f.Name - return nil, err - } - } - - if xorTag := f.Tag.Get("xor"); xorTag != "" { - //fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag) - xorField := optsValue.FieldByName(xorTag) - var xorFieldIsZero bool - if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { - xorFieldIsZero = true - } else { - if xorField.Kind() == reflect.Ptr { - xorField = xorField.Elem() - } - xorFieldIsZero = isZero(xorField) - } - if !(zero != xorFieldIsZero) { - err := ErrMissingInput{} - err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag) - err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag) - return nil, err - } - } - - if orTag := f.Tag.Get("or"); orTag != "" { - //fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag) - //fmt.Printf("field is zero?: %v\n", zero) - if zero { - orField := optsValue.FieldByName(orTag) - var orFieldIsZero bool - if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { - orFieldIsZero = true - } else { - if orField.Kind() == reflect.Ptr { - orField = orField.Elem() - } - orFieldIsZero = isZero(orField) - } - if orFieldIsZero { - err := ErrMissingInput{} - err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag) - err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag) - return nil, err - } - } - } - - jsonTag := f.Tag.Get("json") - if jsonTag == "-" { - continue - } - - if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) { - sliceValue := v - if sliceValue.Kind() == reflect.Ptr { - sliceValue = sliceValue.Elem() - } - - for i := 0; i < sliceValue.Len(); i++ { - element := sliceValue.Index(i) - if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) { - _, err := BuildRequestBody(element.Interface(), "") - if err != nil { - return nil, err - } - } - } - } - if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { - if zero { - //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) - if jsonTag != "" { - jsonTagPieces := strings.Split(jsonTag, ",") - if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { - if v.CanSet() { - if !v.IsNil() { - if v.Kind() == reflect.Ptr { - v.Set(reflect.Zero(v.Type())) - } - } - //fmt.Printf("value after change: %+v\n", optsValue.Field(i)) - } - } - } - continue - } - - //fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name) - _, err := BuildRequestBody(v.Interface(), f.Name) - if err != nil { - return nil, err - } - } - } - - //fmt.Printf("opts: %+v \n", opts) - - b, err := json.Marshal(opts) - if err != nil { - return nil, err - } - - //fmt.Printf("string(b): %s\n", string(b)) - - err = json.Unmarshal(b, &optsMap) - if err != nil { - return nil, err - } - - //fmt.Printf("optsMap: %+v\n", optsMap) - - if parent != "" { - optsMap = map[string]interface{}{parent: optsMap} - } - //fmt.Printf("optsMap after parent added: %+v\n", optsMap) - return optsMap, nil - } - // Return an error if the underlying type of 'opts' isn't a struct. - return nil, fmt.Errorf("Options type is not a struct.") -} - -// EnabledState is a convenience type, mostly used in Create and Update -// operations. Because the zero value of a bool is FALSE, we need to use a -// pointer instead to indicate zero-ness. -type EnabledState *bool - -// Convenience vars for EnabledState values. -var ( - iTrue = true - iFalse = false - - Enabled EnabledState = &iTrue - Disabled EnabledState = &iFalse -) - -// IPVersion is a type for the possible IP address versions. Valid instances -// are IPv4 and IPv6 -type IPVersion int - -const ( - // IPv4 is used for IP version 4 addresses - IPv4 IPVersion = 4 - // IPv6 is used for IP version 6 addresses - IPv6 IPVersion = 6 -) - -// IntToPointer is a function for converting integers into integer pointers. -// This is useful when passing in options to operations. -func IntToPointer(i int) *int { - return &i -} - -/* -MaybeString is an internal function to be used by request methods in individual -resource packages. - -It takes a string that might be a zero value and returns either a pointer to its -address or nil. This is useful for allowing users to conveniently omit values -from an options struct by leaving them zeroed, but still pass nil to the JSON -serializer so they'll be omitted from the request body. -*/ -func MaybeString(original string) *string { - if original != "" { - return &original - } - return nil -} - -/* -MaybeInt is an internal function to be used by request methods in individual -resource packages. - -Like MaybeString, it accepts an int that may or may not be a zero value, and -returns either a pointer to its address or nil. It's intended to hint that the -JSON serializer should omit its field. -*/ -func MaybeInt(original int) *int { - if original != 0 { - return &original - } - return nil -} - -/* -func isUnderlyingStructZero(v reflect.Value) bool { - switch v.Kind() { - case reflect.Ptr: - return isUnderlyingStructZero(v.Elem()) - default: - return isZero(v) - } -} -*/ - -var t time.Time - -func isZero(v reflect.Value) bool { - //fmt.Printf("\n\nchecking isZero for value: %+v\n", v) - switch v.Kind() { - case reflect.Ptr: - if v.IsNil() { - return true - } - return false - case reflect.Func, reflect.Map, reflect.Slice: - return v.IsNil() - case reflect.Array: - z := true - for i := 0; i < v.Len(); i++ { - z = z && isZero(v.Index(i)) - } - return z - case reflect.Struct: - if v.Type() == reflect.TypeOf(t) { - if v.Interface().(time.Time).IsZero() { - return true - } - return false - } - z := true - for i := 0; i < v.NumField(); i++ { - z = z && isZero(v.Field(i)) - } - return z - } - // Compare other types directly: - z := reflect.Zero(v.Type()) - //fmt.Printf("zero type for value: %+v\n\n\n", z) - return v.Interface() == z.Interface() -} - -/* -BuildQueryString is an internal function to be used by request methods in -individual resource packages. - -It accepts a tagged structure and expands it into a URL struct. Field names are -converted into query parameters based on a "q" tag. For example: - - type struct Something { - Bar string `q:"x_bar"` - Baz int `q:"lorem_ipsum"` - } - - instance := Something{ - Bar: "AAA", - Baz: "BBB", - } - -will be converted into "?x_bar=AAA&lorem_ipsum=BBB". - -The struct's fields may be strings, integers, or boolean values. Fields left at -their type's zero value will be omitted from the query. -*/ -func BuildQueryString(opts interface{}) (*url.URL, error) { - optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { - optsValue = optsValue.Elem() - } - - optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { - optsType = optsType.Elem() - } - - params := url.Values{} - - if optsValue.Kind() == reflect.Struct { - for i := 0; i < optsValue.NumField(); i++ { - v := optsValue.Field(i) - f := optsType.Field(i) - qTag := f.Tag.Get("q") - - // if the field has a 'q' tag, it goes in the query string - if qTag != "" { - tags := strings.Split(qTag, ",") - - // if the field is set, add it to the slice of query pieces - if !isZero(v) { - loop: - switch v.Kind() { - case reflect.Ptr: - v = v.Elem() - goto loop - case reflect.String: - params.Add(tags[0], v.String()) - case reflect.Int: - params.Add(tags[0], strconv.FormatInt(v.Int(), 10)) - case reflect.Bool: - params.Add(tags[0], strconv.FormatBool(v.Bool())) - case reflect.Slice: - switch v.Type().Elem() { - case reflect.TypeOf(0): - for i := 0; i < v.Len(); i++ { - params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10)) - } - default: - for i := 0; i < v.Len(); i++ { - params.Add(tags[0], v.Index(i).String()) - } - } - case reflect.Map: - if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String { - var s []string - for _, k := range v.MapKeys() { - value := v.MapIndex(k).String() - s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value)) - } - params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", "))) - } - } - } else { - // if the field has a 'required' tag, it can't have a zero-value - if requiredTag := f.Tag.Get("required"); requiredTag == "true" { - return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name) - } - } - } - } - - return &url.URL{RawQuery: params.Encode()}, nil - } - // Return an error if the underlying type of 'opts' isn't a struct. - return nil, fmt.Errorf("Options type is not a struct.") -} - -/* -BuildHeaders is an internal function to be used by request methods in -individual resource packages. - -It accepts an arbitrary tagged structure and produces a string map that's -suitable for use as the HTTP headers of an outgoing request. Field names are -mapped to header names based in "h" tags. - - type struct Something { - Bar string `h:"x_bar"` - Baz int `h:"lorem_ipsum"` - } - - instance := Something{ - Bar: "AAA", - Baz: "BBB", - } - -will be converted into: - - map[string]string{ - "x_bar": "AAA", - "lorem_ipsum": "BBB", - } - -Untagged fields and fields left at their zero values are skipped. Integers, -booleans and string values are supported. -*/ -func BuildHeaders(opts interface{}) (map[string]string, error) { - optsValue := reflect.ValueOf(opts) - if optsValue.Kind() == reflect.Ptr { - optsValue = optsValue.Elem() - } - - optsType := reflect.TypeOf(opts) - if optsType.Kind() == reflect.Ptr { - optsType = optsType.Elem() - } - - optsMap := make(map[string]string) - if optsValue.Kind() == reflect.Struct { - for i := 0; i < optsValue.NumField(); i++ { - v := optsValue.Field(i) - f := optsType.Field(i) - hTag := f.Tag.Get("h") - - // if the field has a 'h' tag, it goes in the header - if hTag != "" { - tags := strings.Split(hTag, ",") - - // if the field is set, add it to the slice of query pieces - if !isZero(v) { - switch v.Kind() { - case reflect.String: - optsMap[tags[0]] = v.String() - case reflect.Int: - optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10) - case reflect.Bool: - optsMap[tags[0]] = strconv.FormatBool(v.Bool()) - } - } else { - // if the field has a 'required' tag, it can't have a zero-value - if requiredTag := f.Tag.Get("required"); requiredTag == "true" { - return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name) - } - } - } - - } - return optsMap, nil - } - // Return an error if the underlying type of 'opts' isn't a struct. - return optsMap, fmt.Errorf("Options type is not a struct.") -} - -// IDSliceToQueryString takes a slice of elements and converts them into a query -// string. For example, if name=foo and slice=[]int{20, 40, 60}, then the -// result would be `?name=20&name=40&name=60' -func IDSliceToQueryString(name string, ids []int) string { - str := "" - for k, v := range ids { - if k == 0 { - str += "?" - } else { - str += "&" - } - str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v)) - } - return str -} - -// IntWithinRange returns TRUE if an integer falls within a defined range, and -// FALSE if not. -func IntWithinRange(val, min, max int) bool { - return val > min && val < max -} diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go deleted file mode 100644 index fce00462f..000000000 --- a/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ /dev/null @@ -1,501 +0,0 @@ -package gophercloud - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net/http" - "strings" - "sync" -) - -// DefaultUserAgent is the default User-Agent string set in the request header. -const DefaultUserAgent = "gophercloud/2.0.0" - -// UserAgent represents a User-Agent header. -type UserAgent struct { - // prepend is the slice of User-Agent strings to prepend to DefaultUserAgent. - // All the strings to prepend are accumulated and prepended in the Join method. - prepend []string -} - -// Prepend prepends a user-defined string to the default User-Agent string. Users -// may pass in one or more strings to prepend. -func (ua *UserAgent) Prepend(s ...string) { - ua.prepend = append(s, ua.prepend...) -} - -// Join concatenates all the user-defined User-Agend strings with the default -// Gophercloud User-Agent string. -func (ua *UserAgent) Join() string { - uaSlice := append(ua.prepend, DefaultUserAgent) - return strings.Join(uaSlice, " ") -} - -// ProviderClient stores details that are required to interact with any -// services within a specific provider's API. -// -// Generally, you acquire a ProviderClient by calling the NewClient method in -// the appropriate provider's child package, providing whatever authentication -// credentials are required. -type ProviderClient struct { - // IdentityBase is the base URL used for a particular provider's identity - // service - it will be used when issuing authenticatation requests. It - // should point to the root resource of the identity service, not a specific - // identity version. - IdentityBase string - - // IdentityEndpoint is the identity endpoint. This may be a specific version - // of the identity service. If this is the case, this endpoint is used rather - // than querying versions first. - IdentityEndpoint string - - // TokenID is the ID of the most recently issued valid token. - // NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. - // To safely read or write this value, call `Token` or `SetToken`, respectively - TokenID string - - // EndpointLocator describes how this provider discovers the endpoints for - // its constituent services. - EndpointLocator EndpointLocator - - // HTTPClient allows users to interject arbitrary http, https, or other transit behaviors. - HTTPClient http.Client - - // UserAgent represents the User-Agent header in the HTTP request. - UserAgent UserAgent - - // ReauthFunc is the function used to re-authenticate the user if the request - // fails with a 401 HTTP response code. This a needed because there may be multiple - // authentication functions for different Identity service versions. - ReauthFunc func() error - - // Throwaway determines whether if this client is a throw-away client. It's a copy of user's provider client - // with the token and reauth func zeroed. Such client can be used to perform reauthorization. - Throwaway bool - - // Context is the context passed to the HTTP request. - Context context.Context - - // mut is a mutex for the client. It protects read and write access to client attributes such as getting - // and setting the TokenID. - mut *sync.RWMutex - - // reauthmut is a mutex for reauthentication it attempts to ensure that only one reauthentication - // attempt happens at one time. - reauthmut *reauthlock - - authResult AuthResult -} - -// reauthlock represents a set of attributes used to help in the reauthentication process. -type reauthlock struct { - sync.RWMutex - reauthing bool - reauthingErr error - done *sync.Cond -} - -// AuthenticatedHeaders returns a map of HTTP headers that are common for all -// authenticated service requests. Blocks if Reauthenticate is in progress. -func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { - if client.IsThrowaway() { - return - } - if client.reauthmut != nil { - client.reauthmut.Lock() - for client.reauthmut.reauthing { - client.reauthmut.done.Wait() - } - client.reauthmut.Unlock() - } - t := client.Token() - if t == "" { - return - } - return map[string]string{"X-Auth-Token": t} -} - -// UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. -// If the application's ProviderClient is not used concurrently, this doesn't need to be called. -func (client *ProviderClient) UseTokenLock() { - client.mut = new(sync.RWMutex) - client.reauthmut = new(reauthlock) -} - -// GetAuthResult returns the result from the request that was used to obtain a -// provider client's Keystone token. -// -// The result is nil when authentication has not yet taken place, when the token -// was set manually with SetToken(), or when a ReauthFunc was used that does not -// record the AuthResult. -func (client *ProviderClient) GetAuthResult() AuthResult { - if client.mut != nil { - client.mut.RLock() - defer client.mut.RUnlock() - } - return client.authResult -} - -// Token safely reads the value of the auth token from the ProviderClient. Applications should -// call this method to access the token instead of the TokenID field -func (client *ProviderClient) Token() string { - if client.mut != nil { - client.mut.RLock() - defer client.mut.RUnlock() - } - return client.TokenID -} - -// SetToken safely sets the value of the auth token in the ProviderClient. Applications may -// use this method in a custom ReauthFunc. -// -// WARNING: This function is deprecated. Use SetTokenAndAuthResult() instead. -func (client *ProviderClient) SetToken(t string) { - if client.mut != nil { - client.mut.Lock() - defer client.mut.Unlock() - } - client.TokenID = t - client.authResult = nil -} - -// SetTokenAndAuthResult safely sets the value of the auth token in the -// ProviderClient and also records the AuthResult that was returned from the -// token creation request. Applications may call this in a custom ReauthFunc. -func (client *ProviderClient) SetTokenAndAuthResult(r AuthResult) error { - tokenID := "" - var err error - if r != nil { - tokenID, err = r.ExtractTokenID() - if err != nil { - return err - } - } - - if client.mut != nil { - client.mut.Lock() - defer client.mut.Unlock() - } - client.TokenID = tokenID - client.authResult = r - return nil -} - -// CopyTokenFrom safely copies the token from another ProviderClient into the -// this one. -func (client *ProviderClient) CopyTokenFrom(other *ProviderClient) { - if client.mut != nil { - client.mut.Lock() - defer client.mut.Unlock() - } - if other.mut != nil && other.mut != client.mut { - other.mut.RLock() - defer other.mut.RUnlock() - } - client.TokenID = other.TokenID - client.authResult = other.authResult -} - -// IsThrowaway safely reads the value of the client Throwaway field. -func (client *ProviderClient) IsThrowaway() bool { - if client.reauthmut != nil { - client.reauthmut.RLock() - defer client.reauthmut.RUnlock() - } - return client.Throwaway -} - -// SetThrowaway safely sets the value of the client Throwaway field. -func (client *ProviderClient) SetThrowaway(v bool) { - if client.reauthmut != nil { - client.reauthmut.Lock() - defer client.reauthmut.Unlock() - } - client.Throwaway = v -} - -// Reauthenticate calls client.ReauthFunc in a thread-safe way. If this is -// called because of a 401 response, the caller may pass the previous token. In -// this case, the reauthentication can be skipped if another thread has already -// reauthenticated in the meantime. If no previous token is known, an empty -// string should be passed instead to force unconditional reauthentication. -func (client *ProviderClient) Reauthenticate(previousToken string) (err error) { - if client.ReauthFunc == nil { - return nil - } - - if client.reauthmut == nil { - return client.ReauthFunc() - } - - client.reauthmut.Lock() - if client.reauthmut.reauthing { - for !client.reauthmut.reauthing { - client.reauthmut.done.Wait() - } - err = client.reauthmut.reauthingErr - client.reauthmut.Unlock() - return err - } - client.reauthmut.Unlock() - - client.reauthmut.Lock() - client.reauthmut.reauthing = true - client.reauthmut.done = sync.NewCond(client.reauthmut) - client.reauthmut.reauthingErr = nil - client.reauthmut.Unlock() - - if previousToken == "" || client.TokenID == previousToken { - err = client.ReauthFunc() - } - - client.reauthmut.Lock() - client.reauthmut.reauthing = false - client.reauthmut.reauthingErr = err - client.reauthmut.done.Broadcast() - client.reauthmut.Unlock() - return -} - -// RequestOpts customizes the behavior of the provider.Request() method. -type RequestOpts struct { - // JSONBody, if provided, will be encoded as JSON and used as the body of the HTTP request. The - // content type of the request will default to "application/json" unless overridden by MoreHeaders. - // It's an error to specify both a JSONBody and a RawBody. - JSONBody interface{} - // RawBody contains an io.Reader that will be consumed by the request directly. No content-type - // will be set unless one is provided explicitly by MoreHeaders. - RawBody io.Reader - // JSONResponse, if provided, will be populated with the contents of the response body parsed as - // JSON. - JSONResponse interface{} - // OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If - // the response has a different code, an error will be returned. - OkCodes []int - // MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is - // provided with a blank value (""), that header will be *omitted* instead: use this to suppress - // the default Accept header or an inferred Content-Type, for example. - MoreHeaders map[string]string - // ErrorContext specifies the resource error type to return if an error is encountered. - // This lets resources override default error messages based on the response status code. - ErrorContext error -} - -var applicationJSON = "application/json" - -// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication -// header will automatically be provided. -func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - var body io.Reader - var contentType *string - - // Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided - // io.ReadSeeker as-is. Default the content-type to application/json. - if options.JSONBody != nil { - if options.RawBody != nil { - return nil, errors.New("please provide only one of JSONBody or RawBody to gophercloud.Request()") - } - - rendered, err := json.Marshal(options.JSONBody) - if err != nil { - return nil, err - } - - body = bytes.NewReader(rendered) - contentType = &applicationJSON - } - - if options.RawBody != nil { - body = options.RawBody - } - - // Construct the http.Request. - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - if client.Context != nil { - req = req.WithContext(client.Context) - } - - // Populate the request headers. Apply options.MoreHeaders last, to give the caller the chance to - // modify or omit any header. - if contentType != nil { - req.Header.Set("Content-Type", *contentType) - } - req.Header.Set("Accept", applicationJSON) - - // Set the User-Agent header - req.Header.Set("User-Agent", client.UserAgent.Join()) - - if options.MoreHeaders != nil { - for k, v := range options.MoreHeaders { - if v != "" { - req.Header.Set(k, v) - } else { - req.Header.Del(k) - } - } - } - - // get latest token from client - for k, v := range client.AuthenticatedHeaders() { - req.Header.Set(k, v) - } - - // Set connection parameter to close the connection immediately when we've got the response - req.Close = true - - prereqtok := req.Header.Get("X-Auth-Token") - - // Issue the request. - resp, err := client.HTTPClient.Do(req) - if err != nil { - return nil, err - } - - // Allow default OkCodes if none explicitly set - okc := options.OkCodes - if okc == nil { - okc = defaultOkCodes(method) - } - - // Validate the HTTP response status. - var ok bool - for _, code := range okc { - if resp.StatusCode == code { - ok = true - break - } - } - - if !ok { - body, _ := ioutil.ReadAll(resp.Body) - resp.Body.Close() - respErr := ErrUnexpectedResponseCode{ - URL: url, - Method: method, - Expected: options.OkCodes, - Actual: resp.StatusCode, - Body: body, - } - - errType := options.ErrorContext - switch resp.StatusCode { - case http.StatusBadRequest: - err = ErrDefault400{respErr} - if error400er, ok := errType.(Err400er); ok { - err = error400er.Error400(respErr) - } - case http.StatusUnauthorized: - if client.ReauthFunc != nil { - err = client.Reauthenticate(prereqtok) - if err != nil { - e := &ErrUnableToReauthenticate{} - e.ErrOriginal = respErr - return nil, e - } - if options.RawBody != nil { - if seeker, ok := options.RawBody.(io.Seeker); ok { - seeker.Seek(0, 0) - } - } - resp, err = client.Request(method, url, options) - if err != nil { - switch err.(type) { - case *ErrUnexpectedResponseCode: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err.(*ErrUnexpectedResponseCode) - return nil, e - default: - e := &ErrErrorAfterReauthentication{} - e.ErrOriginal = err - return nil, e - } - } - return resp, nil - } - err = ErrDefault401{respErr} - if error401er, ok := errType.(Err401er); ok { - err = error401er.Error401(respErr) - } - case http.StatusForbidden: - err = ErrDefault403{respErr} - if error403er, ok := errType.(Err403er); ok { - err = error403er.Error403(respErr) - } - case http.StatusNotFound: - err = ErrDefault404{respErr} - if error404er, ok := errType.(Err404er); ok { - err = error404er.Error404(respErr) - } - case http.StatusMethodNotAllowed: - err = ErrDefault405{respErr} - if error405er, ok := errType.(Err405er); ok { - err = error405er.Error405(respErr) - } - case http.StatusRequestTimeout: - err = ErrDefault408{respErr} - if error408er, ok := errType.(Err408er); ok { - err = error408er.Error408(respErr) - } - case http.StatusConflict: - err = ErrDefault409{respErr} - if error409er, ok := errType.(Err409er); ok { - err = error409er.Error409(respErr) - } - case 429: - err = ErrDefault429{respErr} - if error429er, ok := errType.(Err429er); ok { - err = error429er.Error429(respErr) - } - case http.StatusInternalServerError: - err = ErrDefault500{respErr} - if error500er, ok := errType.(Err500er); ok { - err = error500er.Error500(respErr) - } - case http.StatusServiceUnavailable: - err = ErrDefault503{respErr} - if error503er, ok := errType.(Err503er); ok { - err = error503er.Error503(respErr) - } - } - - if err == nil { - err = respErr - } - - return resp, err - } - - // Parse the response body as JSON, if requested to do so. - if options.JSONResponse != nil { - defer resp.Body.Close() - if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { - return nil, err - } - } - - return resp, nil -} - -func defaultOkCodes(method string) []int { - switch { - case method == "GET": - return []int{200} - case method == "POST": - return []int{201, 202} - case method == "PUT": - return []int{201, 202} - case method == "PATCH": - return []int{200, 202, 204} - case method == "DELETE": - return []int{202, 204} - } - - return []int{} -} diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go deleted file mode 100644 index 94a16bff0..000000000 --- a/vendor/github.com/gophercloud/gophercloud/results.go +++ /dev/null @@ -1,448 +0,0 @@ -package gophercloud - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "reflect" - "strconv" - "time" -) - -/* -Result is an internal type to be used by individual resource packages, but its -methods will be available on a wide variety of user-facing embedding types. - -It acts as a base struct that other Result types, returned from request -functions, can embed for convenience. All Results capture basic information -from the HTTP transaction that was performed, including the response body, -HTTP headers, and any errors that happened. - -Generally, each Result type will have an Extract method that can be used to -further interpret the result's payload in a specific context. Extensions or -providers can then provide additional extraction functions to pull out -provider- or extension-specific information as well. -*/ -type Result struct { - // Body is the payload of the HTTP response from the server. In most cases, - // this will be the deserialized JSON structure. - Body interface{} - - // Header contains the HTTP header structure from the original response. - Header http.Header - - // Err is an error that occurred during the operation. It's deferred until - // extraction to make it easier to chain the Extract call. - Err error -} - -// ExtractInto allows users to provide an object into which `Extract` will extract -// the `Result.Body`. This would be useful for OpenStack providers that have -// different fields in the response object than OpenStack proper. -func (r Result) ExtractInto(to interface{}) error { - if r.Err != nil { - return r.Err - } - - if reader, ok := r.Body.(io.Reader); ok { - if readCloser, ok := reader.(io.Closer); ok { - defer readCloser.Close() - } - return json.NewDecoder(reader).Decode(to) - } - - b, err := json.Marshal(r.Body) - if err != nil { - return err - } - err = json.Unmarshal(b, to) - - return err -} - -func (r Result) extractIntoPtr(to interface{}, label string) error { - if label == "" { - return r.ExtractInto(&to) - } - - var m map[string]interface{} - err := r.ExtractInto(&m) - if err != nil { - return err - } - - b, err := json.Marshal(m[label]) - if err != nil { - return err - } - - toValue := reflect.ValueOf(to) - if toValue.Kind() == reflect.Ptr { - toValue = toValue.Elem() - } - - switch toValue.Kind() { - case reflect.Slice: - typeOfV := toValue.Type().Elem() - if typeOfV.Kind() == reflect.Struct { - if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { - newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - - if mSlice, ok := m[label].([]interface{}); ok { - for _, v := range mSlice { - // For each iteration of the slice, we create a new struct. - // This is to work around a bug where elements of a slice - // are reused and not overwritten when the same copy of the - // struct is used: - // - // https://github.com/golang/go/issues/21092 - // https://github.com/golang/go/issues/24155 - // https://play.golang.org/p/NHo3ywlPZli - newType := reflect.New(typeOfV).Elem() - - b, err := json.Marshal(v) - if err != nil { - return err - } - - // This is needed for structs with an UnmarshalJSON method. - // Technically this is just unmarshalling the response into - // a struct that is never used, but it's good enough to - // trigger the UnmarshalJSON method. - for i := 0; i < newType.NumField(); i++ { - s := newType.Field(i).Addr().Interface() - - // Unmarshal is used rather than NewDecoder to also work - // around the above-mentioned bug. - err = json.Unmarshal(b, s) - if err != nil { - return err - } - } - - newSlice = reflect.Append(newSlice, newType) - } - } - - // "to" should now be properly modeled to receive the - // JSON response body and unmarshal into all the correct - // fields of the struct or composed extension struct - // at the end of this method. - toValue.Set(newSlice) - } - } - case reflect.Struct: - typeOfV := toValue.Type() - if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { - for i := 0; i < toValue.NumField(); i++ { - toField := toValue.Field(i) - if toField.Kind() == reflect.Struct { - s := toField.Addr().Interface() - err = json.NewDecoder(bytes.NewReader(b)).Decode(s) - if err != nil { - return err - } - } - } - } - } - - err = json.Unmarshal(b, &to) - return err -} - -// ExtractIntoStructPtr will unmarshal the Result (r) into the provided -// interface{} (to). -// -// NOTE: For internal use only -// -// `to` must be a pointer to an underlying struct type -// -// If provided, `label` will be filtered out of the response -// body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { - if r.Err != nil { - return r.Err - } - - t := reflect.TypeOf(to) - if k := t.Kind(); k != reflect.Ptr { - return fmt.Errorf("Expected pointer, got %v", k) - } - switch t.Elem().Kind() { - case reflect.Struct: - return r.extractIntoPtr(to, label) - default: - return fmt.Errorf("Expected pointer to struct, got: %v", t) - } -} - -// ExtractIntoSlicePtr will unmarshal the Result (r) into the provided -// interface{} (to). -// -// NOTE: For internal use only -// -// `to` must be a pointer to an underlying slice type -// -// If provided, `label` will be filtered out of the response -// body prior to `r` being unmarshalled into `to`. -func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { - if r.Err != nil { - return r.Err - } - - t := reflect.TypeOf(to) - if k := t.Kind(); k != reflect.Ptr { - return fmt.Errorf("Expected pointer, got %v", k) - } - switch t.Elem().Kind() { - case reflect.Slice: - return r.extractIntoPtr(to, label) - default: - return fmt.Errorf("Expected pointer to slice, got: %v", t) - } -} - -// PrettyPrintJSON creates a string containing the full response body as -// pretty-printed JSON. It's useful for capturing test fixtures and for -// debugging extraction bugs. If you include its output in an issue related to -// a buggy extraction function, we will all love you forever. -func (r Result) PrettyPrintJSON() string { - pretty, err := json.MarshalIndent(r.Body, "", " ") - if err != nil { - panic(err.Error()) - } - return string(pretty) -} - -// ErrResult is an internal type to be used by individual resource packages, but -// its methods will be available on a wide variety of user-facing embedding -// types. -// -// It represents results that only contain a potential error and -// nothing else. Usually, if the operation executed successfully, the Err field -// will be nil; otherwise it will be stocked with a relevant error. Use the -// ExtractErr method -// to cleanly pull it out. -type ErrResult struct { - Result -} - -// ExtractErr is a function that extracts error information, or nil, from a result. -func (r ErrResult) ExtractErr() error { - return r.Err -} - -/* -HeaderResult is an internal type to be used by individual resource packages, but -its methods will be available on a wide variety of user-facing embedding types. - -It represents a result that only contains an error (possibly nil) and an -http.Header. This is used, for example, by the objectstorage packages in -openstack, because most of the operations don't return response bodies, but do -have relevant information in headers. -*/ -type HeaderResult struct { - Result -} - -// ExtractInto allows users to provide an object into which `Extract` will -// extract the http.Header headers of the result. -func (r HeaderResult) ExtractInto(to interface{}) error { - if r.Err != nil { - return r.Err - } - - tmpHeaderMap := map[string]string{} - for k, v := range r.Header { - if len(v) > 0 { - tmpHeaderMap[k] = v[0] - } - } - - b, err := json.Marshal(tmpHeaderMap) - if err != nil { - return err - } - err = json.Unmarshal(b, to) - - return err -} - -// RFC3339Milli describes a common time format used by some API responses. -const RFC3339Milli = "2006-01-02T15:04:05.999999Z" - -type JSONRFC3339Milli time.Time - -func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { - b := bytes.NewBuffer(data) - dec := json.NewDecoder(b) - var s string - if err := dec.Decode(&s); err != nil { - return err - } - t, err := time.Parse(RFC3339Milli, s) - if err != nil { - return err - } - *jt = JSONRFC3339Milli(t) - return nil -} - -const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" - -type JSONRFC3339MilliNoZ time.Time - -func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - t, err := time.Parse(RFC3339MilliNoZ, s) - if err != nil { - return err - } - *jt = JSONRFC3339MilliNoZ(t) - return nil -} - -type JSONRFC1123 time.Time - -func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - t, err := time.Parse(time.RFC1123, s) - if err != nil { - return err - } - *jt = JSONRFC1123(t) - return nil -} - -type JSONUnix time.Time - -func (jt *JSONUnix) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - unix, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - t = time.Unix(unix, 0) - *jt = JSONUnix(t) - return nil -} - -// RFC3339NoZ is the time format used in Heat (Orchestration). -const RFC3339NoZ = "2006-01-02T15:04:05" - -type JSONRFC3339NoZ time.Time - -func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - t, err := time.Parse(RFC3339NoZ, s) - if err != nil { - return err - } - *jt = JSONRFC3339NoZ(t) - return nil -} - -// RFC3339ZNoT is the time format used in Zun (Containers Service). -const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" - -type JSONRFC3339ZNoT time.Time - -func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - t, err := time.Parse(RFC3339ZNoT, s) - if err != nil { - return err - } - *jt = JSONRFC3339ZNoT(t) - return nil -} - -// RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). -const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" - -type JSONRFC3339ZNoTNoZ time.Time - -func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - if s == "" { - return nil - } - t, err := time.Parse(RFC3339ZNoTNoZ, s) - if err != nil { - return err - } - *jt = JSONRFC3339ZNoTNoZ(t) - return nil -} - -/* -Link is an internal type to be used in packages of collection resources that are -paginated in a certain way. - -It's a response substructure common to many paginated collection results that is -used to point to related pages. Usually, the one we care about is the one with -Rel field set to "next". -*/ -type Link struct { - Href string `json:"href"` - Rel string `json:"rel"` -} - -/* -ExtractNextURL is an internal function useful for packages of collection -resources that are paginated in a certain way. - -It attempts to extract the "next" URL from slice of Link structs, or -"" if no such URL is present. -*/ -func ExtractNextURL(links []Link) (string, error) { - var url string - - for _, l := range links { - if l.Rel == "next" { - url = l.Href - } - } - - if url == "" { - return "", nil - } - - return url, nil -} diff --git a/vendor/github.com/gophercloud/gophercloud/service_client.go b/vendor/github.com/gophercloud/gophercloud/service_client.go deleted file mode 100644 index f222f05a6..000000000 --- a/vendor/github.com/gophercloud/gophercloud/service_client.go +++ /dev/null @@ -1,154 +0,0 @@ -package gophercloud - -import ( - "io" - "net/http" - "strings" -) - -// ServiceClient stores details required to interact with a specific service API implemented by a provider. -// Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. -type ServiceClient struct { - // ProviderClient is a reference to the provider that implements this service. - *ProviderClient - - // Endpoint is the base URL of the service's API, acquired from a service catalog. - // It MUST end with a /. - Endpoint string - - // ResourceBase is the base URL shared by the resources within a service's API. It should include - // the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used - // as-is, instead. - ResourceBase string - - // This is the service client type (e.g. compute, sharev2). - // NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS. - // It is only exported because it gets set in a different package. - Type string - - // The microversion of the service to use. Set this to use a particular microversion. - Microversion string - - // MoreHeaders allows users (or Gophercloud) to set service-wide headers on requests. Put another way, - // values set in this field will be set on all the HTTP requests the service client sends. - MoreHeaders map[string]string -} - -// ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. -func (client *ServiceClient) ResourceBaseURL() string { - if client.ResourceBase != "" { - return client.ResourceBase - } - return client.Endpoint -} - -// ServiceURL constructs a URL for a resource belonging to this provider. -func (client *ServiceClient) ServiceURL(parts ...string) string { - return client.ResourceBaseURL() + strings.Join(parts, "/") -} - -func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) { - if v, ok := (JSONBody).(io.Reader); ok { - opts.RawBody = v - } else if JSONBody != nil { - opts.JSONBody = JSONBody - } - - if JSONResponse != nil { - opts.JSONResponse = JSONResponse - } - - if opts.MoreHeaders == nil { - opts.MoreHeaders = make(map[string]string) - } - - if client.Microversion != "" { - client.setMicroversionHeader(opts) - } -} - -// Get calls `Request` with the "GET" HTTP verb. -func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, nil, JSONResponse, opts) - return client.Request("GET", url, opts) -} - -// Post calls `Request` with the "POST" HTTP verb. -func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, JSONBody, JSONResponse, opts) - return client.Request("POST", url, opts) -} - -// Put calls `Request` with the "PUT" HTTP verb. -func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, JSONBody, JSONResponse, opts) - return client.Request("PUT", url, opts) -} - -// Patch calls `Request` with the "PATCH" HTTP verb. -func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, JSONBody, JSONResponse, opts) - return client.Request("PATCH", url, opts) -} - -// Delete calls `Request` with the "DELETE" HTTP verb. -func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, nil, nil, opts) - return client.Request("DELETE", url, opts) -} - -// Head calls `Request` with the "HEAD" HTTP verb. -func (client *ServiceClient) Head(url string, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = new(RequestOpts) - } - client.initReqOpts(url, nil, nil, opts) - return client.Request("HEAD", url, opts) -} - -func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) { - switch client.Type { - case "compute": - opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion - case "sharev2": - opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion - case "volume": - opts.MoreHeaders["X-OpenStack-Volume-API-Version"] = client.Microversion - case "baremetal": - opts.MoreHeaders["X-OpenStack-Ironic-API-Version"] = client.Microversion - case "baremetal-introspection": - opts.MoreHeaders["X-OpenStack-Ironic-Inspector-API-Version"] = client.Microversion - } - - if client.Type != "" { - opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion - } -} - -// Request carries out the HTTP operation for the service client -func (client *ServiceClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { - if len(client.MoreHeaders) > 0 { - if options == nil { - options = new(RequestOpts) - } - for k, v := range client.MoreHeaders { - options.MoreHeaders[k] = v - } - } - return client.ProviderClient.Request(method, url, options) -} diff --git a/vendor/github.com/gophercloud/gophercloud/util.go b/vendor/github.com/gophercloud/gophercloud/util.go deleted file mode 100644 index 68f9a5d3e..000000000 --- a/vendor/github.com/gophercloud/gophercloud/util.go +++ /dev/null @@ -1,102 +0,0 @@ -package gophercloud - -import ( - "fmt" - "net/url" - "path/filepath" - "strings" - "time" -) - -// WaitFor polls a predicate function, once per second, up to a timeout limit. -// This is useful to wait for a resource to transition to a certain state. -// To handle situations when the predicate might hang indefinitely, the -// predicate will be prematurely cancelled after the timeout. -// Resource packages will wrap this in a more convenient function that's -// specific to a certain resource, but it can also be useful on its own. -func WaitFor(timeout int, predicate func() (bool, error)) error { - type WaitForResult struct { - Success bool - Error error - } - - start := time.Now().Unix() - - for { - // If a timeout is set, and that's been exceeded, shut it down. - if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) { - return fmt.Errorf("A timeout occurred") - } - - time.Sleep(1 * time.Second) - - var result WaitForResult - ch := make(chan bool, 1) - go func() { - defer close(ch) - satisfied, err := predicate() - result.Success = satisfied - result.Error = err - }() - - select { - case <-ch: - if result.Error != nil { - return result.Error - } - if result.Success { - return nil - } - // If the predicate has not finished by the timeout, cancel it. - case <-time.After(time.Duration(timeout) * time.Second): - return fmt.Errorf("A timeout occurred") - } - } -} - -// NormalizeURL is an internal function to be used by provider clients. -// -// It ensures that each endpoint URL has a closing `/`, as expected by -// ServiceClient's methods. -func NormalizeURL(url string) string { - if !strings.HasSuffix(url, "/") { - return url + "/" - } - return url -} - -// NormalizePathURL is used to convert rawPath to a fqdn, using basePath as -// a reference in the filesystem, if necessary. basePath is assumed to contain -// either '.' when first used, or the file:// type fqdn of the parent resource. -// e.g. myFavScript.yaml => file://opt/lib/myFavScript.yaml -func NormalizePathURL(basePath, rawPath string) (string, error) { - u, err := url.Parse(rawPath) - if err != nil { - return "", err - } - // if a scheme is defined, it must be a fqdn already - if u.Scheme != "" { - return u.String(), nil - } - // if basePath is a url, then child resources are assumed to be relative to it - bu, err := url.Parse(basePath) - if err != nil { - return "", err - } - var basePathSys, absPathSys string - if bu.Scheme != "" { - basePathSys = filepath.FromSlash(bu.Path) - absPathSys = filepath.Join(basePathSys, rawPath) - bu.Path = filepath.ToSlash(absPathSys) - return bu.String(), nil - } - - absPathSys = filepath.Join(basePath, rawPath) - u.Path = filepath.ToSlash(absPathSys) - if err != nil { - return "", err - } - u.Scheme = "file" - return u.String(), nil - -} diff --git a/vendor/github.com/hashicorp/go-syslog/.gitignore b/vendor/github.com/hashicorp/go-syslog/.gitignore deleted file mode 100644 index 00268614f..000000000 --- a/vendor/github.com/hashicorp/go-syslog/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/vendor/github.com/hashicorp/go-syslog/LICENSE b/vendor/github.com/hashicorp/go-syslog/LICENSE deleted file mode 100644 index a5df10e67..000000000 --- a/vendor/github.com/hashicorp/go-syslog/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Armon Dadgar - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/hashicorp/go-syslog/README.md b/vendor/github.com/hashicorp/go-syslog/README.md deleted file mode 100644 index bbfae8f9b..000000000 --- a/vendor/github.com/hashicorp/go-syslog/README.md +++ /dev/null @@ -1,11 +0,0 @@ -go-syslog -========= - -This repository provides a very simple `gsyslog` package. The point of this -package is to allow safe importing of syslog without introducing cross-compilation -issues. The stdlib `log/syslog` cannot be imported on Windows systems, and without -conditional compilation this adds complications. - -Instead, `gsyslog` provides a very simple wrapper around `log/syslog` but returns -a runtime error if attempting to initialize on a non Linux or OSX system. - diff --git a/vendor/github.com/hashicorp/go-syslog/builtin.go b/vendor/github.com/hashicorp/go-syslog/builtin.go deleted file mode 100644 index 72bdd61c9..000000000 --- a/vendor/github.com/hashicorp/go-syslog/builtin.go +++ /dev/null @@ -1,214 +0,0 @@ -// This file is taken from the log/syslog in the standard lib. -// However, there is a bug with overwhelming syslog that causes writes -// to block indefinitely. This is fixed by adding a write deadline. -// -// +build !windows,!nacl,!plan9 - -package gsyslog - -import ( - "errors" - "fmt" - "log/syslog" - "net" - "os" - "strings" - "sync" - "time" -) - -const severityMask = 0x07 -const facilityMask = 0xf8 -const localDeadline = 20 * time.Millisecond -const remoteDeadline = 50 * time.Millisecond - -// A builtinWriter is a connection to a syslog server. -type builtinWriter struct { - priority syslog.Priority - tag string - hostname string - network string - raddr string - - mu sync.Mutex // guards conn - conn serverConn -} - -// This interface and the separate syslog_unix.go file exist for -// Solaris support as implemented by gccgo. On Solaris you can not -// simply open a TCP connection to the syslog daemon. The gccgo -// sources have a syslog_solaris.go file that implements unixSyslog to -// return a type that satisfies this interface and simply calls the C -// library syslog function. -type serverConn interface { - writeString(p syslog.Priority, hostname, tag, s, nl string) error - close() error -} - -type netConn struct { - local bool - conn net.Conn -} - -// New establishes a new connection to the system log daemon. Each -// write to the returned writer sends a log message with the given -// priority and prefix. -func newBuiltin(priority syslog.Priority, tag string) (w *builtinWriter, err error) { - return dialBuiltin("", "", priority, tag) -} - -// Dial establishes a connection to a log daemon by connecting to -// address raddr on the specified network. Each write to the returned -// writer sends a log message with the given facility, severity and -// tag. -// If network is empty, Dial will connect to the local syslog server. -func dialBuiltin(network, raddr string, priority syslog.Priority, tag string) (*builtinWriter, error) { - if priority < 0 || priority > syslog.LOG_LOCAL7|syslog.LOG_DEBUG { - return nil, errors.New("log/syslog: invalid priority") - } - - if tag == "" { - tag = os.Args[0] - } - hostname, _ := os.Hostname() - - w := &builtinWriter{ - priority: priority, - tag: tag, - hostname: hostname, - network: network, - raddr: raddr, - } - - w.mu.Lock() - defer w.mu.Unlock() - - err := w.connect() - if err != nil { - return nil, err - } - return w, err -} - -// connect makes a connection to the syslog server. -// It must be called with w.mu held. -func (w *builtinWriter) connect() (err error) { - if w.conn != nil { - // ignore err from close, it makes sense to continue anyway - w.conn.close() - w.conn = nil - } - - if w.network == "" { - w.conn, err = unixSyslog() - if w.hostname == "" { - w.hostname = "localhost" - } - } else { - var c net.Conn - c, err = net.DialTimeout(w.network, w.raddr, remoteDeadline) - if err == nil { - w.conn = &netConn{conn: c} - if w.hostname == "" { - w.hostname = c.LocalAddr().String() - } - } - } - return -} - -// Write sends a log message to the syslog daemon. -func (w *builtinWriter) Write(b []byte) (int, error) { - return w.writeAndRetry(w.priority, string(b)) -} - -// Close closes a connection to the syslog daemon. -func (w *builtinWriter) Close() error { - w.mu.Lock() - defer w.mu.Unlock() - - if w.conn != nil { - err := w.conn.close() - w.conn = nil - return err - } - return nil -} - -func (w *builtinWriter) writeAndRetry(p syslog.Priority, s string) (int, error) { - pr := (w.priority & facilityMask) | (p & severityMask) - - w.mu.Lock() - defer w.mu.Unlock() - - if w.conn != nil { - if n, err := w.write(pr, s); err == nil { - return n, err - } - } - if err := w.connect(); err != nil { - return 0, err - } - return w.write(pr, s) -} - -// write generates and writes a syslog formatted string. The -// format is as follows: TIMESTAMP HOSTNAME TAG[PID]: MSG -func (w *builtinWriter) write(p syslog.Priority, msg string) (int, error) { - // ensure it ends in a \n - nl := "" - if !strings.HasSuffix(msg, "\n") { - nl = "\n" - } - - err := w.conn.writeString(p, w.hostname, w.tag, msg, nl) - if err != nil { - return 0, err - } - // Note: return the length of the input, not the number of - // bytes printed by Fprintf, because this must behave like - // an io.Writer. - return len(msg), nil -} - -func (n *netConn) writeString(p syslog.Priority, hostname, tag, msg, nl string) error { - if n.local { - // Compared to the network form below, the changes are: - // 1. Use time.Stamp instead of time.RFC3339. - // 2. Drop the hostname field from the Fprintf. - timestamp := time.Now().Format(time.Stamp) - n.conn.SetWriteDeadline(time.Now().Add(localDeadline)) - _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", - p, timestamp, - tag, os.Getpid(), msg, nl) - return err - } - timestamp := time.Now().Format(time.RFC3339) - n.conn.SetWriteDeadline(time.Now().Add(remoteDeadline)) - _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s", - p, timestamp, hostname, - tag, os.Getpid(), msg, nl) - return err -} - -func (n *netConn) close() error { - return n.conn.Close() -} - -// unixSyslog opens a connection to the syslog daemon running on the -// local machine using a Unix domain socket. -func unixSyslog() (conn serverConn, err error) { - logTypes := []string{"unixgram", "unix"} - logPaths := []string{"/dev/log", "/var/run/syslog", "/var/run/log"} - for _, network := range logTypes { - for _, path := range logPaths { - conn, err := net.DialTimeout(network, path, localDeadline) - if err != nil { - continue - } else { - return &netConn{conn: conn, local: true}, nil - } - } - } - return nil, errors.New("Unix syslog delivery error") -} diff --git a/vendor/github.com/hashicorp/go-syslog/go.mod b/vendor/github.com/hashicorp/go-syslog/go.mod deleted file mode 100644 index 0e4c2d0dc..000000000 --- a/vendor/github.com/hashicorp/go-syslog/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/hashicorp/go-syslog diff --git a/vendor/github.com/hashicorp/go-syslog/syslog.go b/vendor/github.com/hashicorp/go-syslog/syslog.go deleted file mode 100644 index 3f5a6f3fb..000000000 --- a/vendor/github.com/hashicorp/go-syslog/syslog.go +++ /dev/null @@ -1,27 +0,0 @@ -package gsyslog - -// Priority maps to the syslog priority levels -type Priority int - -const ( - LOG_EMERG Priority = iota - LOG_ALERT - LOG_CRIT - LOG_ERR - LOG_WARNING - LOG_NOTICE - LOG_INFO - LOG_DEBUG -) - -// Syslogger interface is used to write log messages to syslog -type Syslogger interface { - // WriteLevel is used to write a message at a given level - WriteLevel(Priority, []byte) error - - // Write is used to write a message at the default level - Write([]byte) (int, error) - - // Close is used to close the connection to the logger - Close() error -} diff --git a/vendor/github.com/hashicorp/go-syslog/unix.go b/vendor/github.com/hashicorp/go-syslog/unix.go deleted file mode 100644 index 70b71802e..000000000 --- a/vendor/github.com/hashicorp/go-syslog/unix.go +++ /dev/null @@ -1,123 +0,0 @@ -// +build linux darwin dragonfly freebsd netbsd openbsd solaris - -package gsyslog - -import ( - "fmt" - "log/syslog" - "strings" -) - -// builtinLogger wraps the Golang implementation of a -// syslog.Writer to provide the Syslogger interface -type builtinLogger struct { - *builtinWriter -} - -// NewLogger is used to construct a new Syslogger -func NewLogger(p Priority, facility, tag string) (Syslogger, error) { - fPriority, err := facilityPriority(facility) - if err != nil { - return nil, err - } - priority := syslog.Priority(p) | fPriority - l, err := newBuiltin(priority, tag) - if err != nil { - return nil, err - } - return &builtinLogger{l}, nil -} - -// DialLogger is used to construct a new Syslogger that establishes connection to remote syslog server -func DialLogger(network, raddr string, p Priority, facility, tag string) (Syslogger, error) { - fPriority, err := facilityPriority(facility) - if err != nil { - return nil, err - } - - priority := syslog.Priority(p) | fPriority - - l, err := dialBuiltin(network, raddr, priority, tag) - if err != nil { - return nil, err - } - - return &builtinLogger{l}, nil -} - -// WriteLevel writes out a message at the given priority -func (b *builtinLogger) WriteLevel(p Priority, buf []byte) error { - var err error - m := string(buf) - switch p { - case LOG_EMERG: - _, err = b.writeAndRetry(syslog.LOG_EMERG, m) - case LOG_ALERT: - _, err = b.writeAndRetry(syslog.LOG_ALERT, m) - case LOG_CRIT: - _, err = b.writeAndRetry(syslog.LOG_CRIT, m) - case LOG_ERR: - _, err = b.writeAndRetry(syslog.LOG_ERR, m) - case LOG_WARNING: - _, err = b.writeAndRetry(syslog.LOG_WARNING, m) - case LOG_NOTICE: - _, err = b.writeAndRetry(syslog.LOG_NOTICE, m) - case LOG_INFO: - _, err = b.writeAndRetry(syslog.LOG_INFO, m) - case LOG_DEBUG: - _, err = b.writeAndRetry(syslog.LOG_DEBUG, m) - default: - err = fmt.Errorf("Unknown priority: %v", p) - } - return err -} - -// facilityPriority converts a facility string into -// an appropriate priority level or returns an error -func facilityPriority(facility string) (syslog.Priority, error) { - facility = strings.ToUpper(facility) - switch facility { - case "KERN": - return syslog.LOG_KERN, nil - case "USER": - return syslog.LOG_USER, nil - case "MAIL": - return syslog.LOG_MAIL, nil - case "DAEMON": - return syslog.LOG_DAEMON, nil - case "AUTH": - return syslog.LOG_AUTH, nil - case "SYSLOG": - return syslog.LOG_SYSLOG, nil - case "LPR": - return syslog.LOG_LPR, nil - case "NEWS": - return syslog.LOG_NEWS, nil - case "UUCP": - return syslog.LOG_UUCP, nil - case "CRON": - return syslog.LOG_CRON, nil - case "AUTHPRIV": - return syslog.LOG_AUTHPRIV, nil - case "FTP": - return syslog.LOG_FTP, nil - case "LOCAL0": - return syslog.LOG_LOCAL0, nil - case "LOCAL1": - return syslog.LOG_LOCAL1, nil - case "LOCAL2": - return syslog.LOG_LOCAL2, nil - case "LOCAL3": - return syslog.LOG_LOCAL3, nil - case "LOCAL4": - return syslog.LOG_LOCAL4, nil - case "LOCAL5": - return syslog.LOG_LOCAL5, nil - case "LOCAL6": - return syslog.LOG_LOCAL6, nil - case "LOCAL7": - return syslog.LOG_LOCAL7, nil - default: - return 0, fmt.Errorf("invalid syslog facility: %s", facility) - } -} diff --git a/vendor/github.com/hashicorp/go-syslog/unsupported.go b/vendor/github.com/hashicorp/go-syslog/unsupported.go deleted file mode 100644 index b8ca3a5c7..000000000 --- a/vendor/github.com/hashicorp/go-syslog/unsupported.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build windows plan9 nacl - -package gsyslog - -import ( - "fmt" -) - -// NewLogger is used to construct a new Syslogger -func NewLogger(p Priority, facility, tag string) (Syslogger, error) { - return nil, fmt.Errorf("Platform does not support syslog") -} - -// DialLogger is used to construct a new Syslogger that establishes connection to remote syslog server -func DialLogger(network, raddr string, p Priority, facility, tag string) (Syslogger, error) { - return nil, fmt.Errorf("Platform does not support syslog") -} diff --git a/vendor/github.com/jimstudt/http-authentication/LICENSE b/vendor/github.com/jimstudt/http-authentication/LICENSE deleted file mode 100644 index 70def10f9..000000000 --- a/vendor/github.com/jimstudt/http-authentication/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Jim Studt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/jimstudt/http-authentication/basic/README.md b/vendor/github.com/jimstudt/http-authentication/basic/README.md deleted file mode 100644 index 96a2b0cf0..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# auth/htpasswd [![GoDoc](https://godoc.org/github.com/jimstudt/http-authentication/basic?status.png)](http://godoc.org/github.com/jimstudt/http-authentication/basic) - -Authenticate using Apache-style htpasswd files and HTTP Basic Authentication. - -`htpasswd` has supported a number of different password hashing schemes over the -decades. Most common and sane ones are supported directly. For some you will need -to add another package. - -| Style | Status | -|-------|--------| -| plain | yes+ | -| md5 | yes | -| sha | yes | -| crypt | no (conflicts with plain) | -| bcrypt | no (can add with another package) | - -The standard set of systems will use *Plain*, *Sha*, and *MD5* systems while filtering out *bcrypt*. -Because of its complexity, *bcrypt* will be in another package which you can import and -add if you need it. *Plain* accepts both Apache style plain text and nginx style where the -password is preceded by {PLAIN}. - -## Usage - -~~~ go -import ( - "github.com/codegangsta/martini" - "github.com/jimstudt/http-authentication/basic" - "log" -) - -func main() { - m := martini.Classic() - - pw,err := basic.New("My Realm", "./my-htpasswd-file", htpasswd.DefaultSystems, nil) - if ( err != nil) { - log.Fatalf("Unable to read my htpassword file: %s", err.Error()) - } - - // authenticate every request - m.Use( pw.ServeHTTP) - - // You will also want to call pw.Reload(nil) to reprocess the password file when it changes. - - // You can use pw.ReloadOn( syscall.SIGHUP, nil ) to make it automatically - // reload on a HUP signal. - - // And those 'nil' arguments are where you pass a function to be notified about illegally - // formatted entries, or unsupported hash systems. See the API documents. - - // If you only want to authenticate some requests, then it goes like this... - // m.Get("/secure/thing", pw.ServeHTTP, myRealHandler) - // ... if pw.ServeHTTP does the 401 then your handler will not be called - - m.Run() - -} -~~~ - -## API Documentation - -The API is documented using godoc and also available at [godoc.org](http://godoc.org/github.com/jimstudt/http-authentication/basic) -~~~ - - - diff --git a/vendor/github.com/jimstudt/http-authentication/basic/bcrypt.go b/vendor/github.com/jimstudt/http-authentication/basic/bcrypt.go deleted file mode 100644 index 4fc32b4b5..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/bcrypt.go +++ /dev/null @@ -1,15 +0,0 @@ -package basic - -import ( - "fmt" - "strings" -) - -// Reject any password encoded using bcrypt. -func RejectBcrypt(src string) (EncodedPasswd, error) { - if strings.HasPrefix(src, "$2y$") { - return nil, fmt.Errorf("bcrypt passwords are not accepted: %s", src) - } - - return nil, nil -} diff --git a/vendor/github.com/jimstudt/http-authentication/basic/htpasswd.go b/vendor/github.com/jimstudt/http-authentication/basic/htpasswd.go deleted file mode 100644 index bc4bf8e8b..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/htpasswd.go +++ /dev/null @@ -1,208 +0,0 @@ -// Package htpasswd provides HTTP Basic Authentication using Apache-style htpasswd files -// for the user and password data. -// -// It supports most common hashing systems used over the decades and can be easily extended -// by the programmer to support others. (See the sha.go source file as a guide.) -// -// You will want to use something like... -// myauth := htpasswd.New("My Realm", "./my-htpasswd-file", htpasswd.DefaultSystems, nil) -// m.Use(myauth.Handler) -// ...to configure your authentication and then use the myauth.Handler as a middleware handler in your Martini stack. -// You should read about that nil, as well as Reread() too. -package basic - -import ( - "bufio" - "encoding/base64" - "fmt" - "net/http" - "os" - "os/signal" - "strings" - "sync" -) - -// An EncodedPasswd is created from the encoded password in a password file by a PasswdParser. -// -// The password files consist of lines like "user:passwd-encoding". The user part is stripped off and -// the passwd-encoding part is captured in an EncodedPasswd. -type EncodedPasswd interface { - // Return true if the string matches the password. - // This may cache the result in the case of expensive comparison functions. - MatchesPassword(pw string) bool -} - -// Examine an encoded password, and if it is formatted correctly and sane, return an -// EncodedPasswd which will recognize it. -// -// If the format is not understood, then return nil -// so that another parser may have a chance. If the format is understood but not sane, -// return an error to prevent other formats from possibly claiming it -// -// You may write and supply one of these functions to support a format (e.g. bcrypt) not -// already included in this package. Use sha.c as a template, it is simple but not too simple. -type PasswdParser func(pw string) (EncodedPasswd, error) - -type passwdTable map[string]EncodedPasswd - -// A BadLineHandler is used to notice bad lines in a password file. If not nil, it will be -// called for each bad line with a descriptive error. Think about what you do with these, they -// will sometimes contain hashed passwords. -type BadLineHandler func(err error) - -// An HtpasswdFile encompasses an Apache-style htpasswd file for HTTP Basic authentication -type HtpasswdFile struct { - realm string - filePath string - mutex sync.Mutex - passwds passwdTable - parsers []PasswdParser -} - -// An array of PasswdParser including all builtin parsers. Notice that Plain is last, since it accepts anything -var DefaultSystems []PasswdParser = []PasswdParser{AcceptMd5, AcceptSha, RejectBcrypt, AcceptPlain} - -// New creates an HtpasswdFile from an Apache-style htpasswd file for HTTP Basic Authentication. -// -// The realm is presented to the user in the login dialog. -// -// The filename must exist and be accessible to the process, as well as being a valid htpasswd file. -// -// parsers is a list of functions to handle various hashing systems. In practice you will probably -// just pass htpasswd.DefaultSystems, but you could make your own to explicitly reject some formats or -// implement your own. -// -// bad is a function, which if not nil will be called for each malformed or rejected entry in -// the password file. -func New(realm string, filename string, parsers []PasswdParser, bad BadLineHandler) (*HtpasswdFile, error) { - bf := HtpasswdFile{ - realm: realm, - filePath: filename, - parsers: parsers, - } - - if err := bf.Reload(bad); err != nil { - return nil, err - } - - return &bf, nil -} - -// A Martini middleware handler to enforce HTTP Basic Auth using the policy read from the htpasswd file. -func (bf *HtpasswdFile) ServeHTTP(res http.ResponseWriter, req *http.Request) { - // if everything works, we return, otherwise we get to the - // end where we do an http.Error to stop the request - auth := req.Header.Get("Authorization") - - if auth != "" { - userPassword, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) - if err == nil { - parts := strings.SplitN(string(userPassword), ":", 2) - if len(parts) == 2 { - user := parts[0] - pw := parts[1] - - bf.mutex.Lock() - matcher, ok := bf.passwds[user] - bf.mutex.Unlock() - - if ok && matcher.MatchesPassword(pw) { - // we are good - return - } - } - } - } - - res.Header().Set("WWW-Authenticate", "Basic realm=\""+bf.realm+"\"") - http.Error(res, "Not Authorized", http.StatusUnauthorized) -} - -// Reread the password file for this HtpasswdFile. -// You will need to call this to notice any changes to the password file. -// This function is thread safe. Someone versed in fsnotify might make it -// happen automatically. Likewise you might also connect a SIGHUP handler to -// this function. -func (bf *HtpasswdFile) Reload(bad BadLineHandler) error { - // with the file... - f, err := os.Open(bf.filePath) - if err != nil { - return err - } - defer f.Close() - - // ... and a new map ... - newPasswdMap := passwdTable{} - - // ... for each line ... - scanner := bufio.NewScanner(f) - for scanner.Scan() { - line := scanner.Text() - - // ... add it to the map, noting errors along the way - if perr := bf.addHtpasswdUser(&newPasswdMap, line); perr != nil && bad != nil { - bad(perr) - } - } - if err := scanner.Err(); err != nil { - return fmt.Errorf("Error scanning htpasswd file: %s", err.Error()) - } - - // .. finally, safely swap in the new map - bf.mutex.Lock() - bf.passwds = newPasswdMap - bf.mutex.Unlock() - - return nil -} - -// Reload the htpasswd file on a signal. If there is an error, the old data will be kept instead. -// Typically you would use syscall.SIGHUP for the value of "when" -func (bf *HtpasswdFile) ReloadOn(when os.Signal, onbad BadLineHandler) { - // this is rather common with code in digest, but I don't have a common area... - c := make(chan os.Signal, 1) - signal.Notify(c, when) - - go func() { - for { - _ = <-c - bf.Reload(onbad) - } - }() -} - -// Process a line from an htpasswd file and add it to the user/password map. We may -// encounter some malformed lines, this will not be an error, but we will log them if -// the caller has given us a logger. -func (bf *HtpasswdFile) addHtpasswdUser(pwmap *passwdTable, rawLine string) error { - // ignore white space lines - line := strings.TrimSpace(rawLine) - if line == "" { - return nil - } - - // split "user:encoding" at colon - parts := strings.SplitN(line, ":", 2) - if len(parts) != 2 { - return fmt.Errorf("malformed line, no colon: %s", line) - } - - user := parts[0] - encoding := parts[1] - - // give each parser a shot. The first one to produce a matcher wins. - // If one produces an error then stop (to prevent Plain from catching it) - for _, p := range bf.parsers { - matcher, err := p(encoding) - if err != nil { - return err - } - if matcher != nil { - (*pwmap)[user] = matcher - return nil // we are done, we took to first match - } - } - - // No one liked this line - return fmt.Errorf("unable to recognize password for %s in %s", user, encoding) -} diff --git a/vendor/github.com/jimstudt/http-authentication/basic/md5.go b/vendor/github.com/jimstudt/http-authentication/basic/md5.go deleted file mode 100644 index 0ac793a97..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/md5.go +++ /dev/null @@ -1,143 +0,0 @@ -package basic - -import ( - "bytes" - "crypto/md5" - "fmt" - "strings" -) - -type md5Password struct { - salt string - hashed string -} - -// Accept valid MD5 encoded passwords -func AcceptMd5(src string) (EncodedPasswd, error) { - if !strings.HasPrefix(src, "$apr1$") { - return nil, nil - } - - rest := strings.TrimPrefix(src, "$apr1$") - mparts := strings.SplitN(rest, "$", 2) - if len(mparts) != 2 { - return nil, fmt.Errorf("malformed md5 password: %s", src) - } - - salt, hashed := mparts[0], mparts[1] - return &md5Password{salt, hashed}, nil -} - -// Reject any MD5 encoded password -func RejectMd5(src string) (EncodedPasswd, error) { - if !strings.HasPrefix(src, "$apr1$") { - return nil, nil - } - return nil, fmt.Errorf("md5 password rejected: %s", src) -} - -// This is the MD5 hashing function out of Apache's htpasswd program. The algorithm -// is insane, but we have to match it. Mercifully I found a PHP variant of it at -// http://stackoverflow.com/questions/2994637/how-to-edit-htpasswd-using-php -// in an answer. That reads better than the original C, and is easy to instrument. -// We will eventually go back to the original apr_md5.c for inspiration when the -// PHP gets too weird. -// The algorithm makes more sense if you imagine the original authors in a pub, -// drinking beer and rolling dice as the fundamental design process. -func apr1Md5(password string, salt string) string { - - // start with a hash of password and salt - initBin := md5.Sum([]byte(password + salt + password)) - - // begin an initial string with hash and salt - initText := bytes.NewBufferString(password + "$apr1$" + salt) - - // add crap to the string willy-nilly - for i := len(password); i > 0; i -= 16 { - lim := i - if lim > 16 { - lim = 16 - } - initText.Write(initBin[0:lim]) - } - - // add more crap to the string willy-nilly - for i := len(password); i > 0; i >>= 1 { - if (i & 1) == 1 { - initText.WriteByte(byte(0)) - } else { - initText.WriteByte(password[0]) - } - } - - // Begin our hashing in earnest using our initial string - bin := md5.Sum(initText.Bytes()) - - n := bytes.NewBuffer([]byte{}) - - for i := 0; i < 1000; i++ { - // prepare to make a new muddle - n.Reset() - - // alternate password+crap+bin with bin+crap+password - if (i & 1) == 1 { - n.WriteString(password) - } else { - n.Write(bin[:]) - } - - // usually add the salt, but not always - if i%3 != 0 { - n.WriteString(salt) - } - - // usually add the password but not always - if i%7 != 0 { - n.WriteString(password) - } - - // the back half of that alternation - if (i & 1) == 1 { - n.Write(bin[:]) - } else { - n.WriteString(password) - } - - // replace bin with the md5 of this muddle - bin = md5.Sum(n.Bytes()) - } - - // At this point we stop transliterating the PHP code and flip back to - // reading the Apache source. The PHP uses their base64 library, but that - // uses the wrong character set so needs to be repaired afterwards and reversed - // and it is just really weird to read. - - result := bytes.NewBuffer([]byte{}) - - // This is our own little similar-to-base64-but-not-quite filler - fill := func(a byte, b byte, c byte) { - v := (uint(a) << 16) + (uint(b) << 8) + uint(c) // take our 24 input bits - - for i := 0; i < 4; i++ { // and pump out a character for each 6 bits - result.WriteByte("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[v&0x3f]) - v >>= 6 - } - } - - // The order of these indices is strange, be careful - fill(bin[0], bin[6], bin[12]) - fill(bin[1], bin[7], bin[13]) - fill(bin[2], bin[8], bin[14]) - fill(bin[3], bin[9], bin[15]) - fill(bin[4], bin[10], bin[5]) // 5? Yes. - fill(0, 0, bin[11]) - - resultString := string(result.Bytes()[0:22]) // we wrote two extras since we only need 22. - - return resultString -} - -func (m *md5Password) MatchesPassword(pw string) bool { - hashed := apr1Md5(pw, m.salt) - return constantTimeEquals(hashed, m.hashed) -} diff --git a/vendor/github.com/jimstudt/http-authentication/basic/plain.go b/vendor/github.com/jimstudt/http-authentication/basic/plain.go deleted file mode 100644 index 5435e954b..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/plain.go +++ /dev/null @@ -1,28 +0,0 @@ -package basic - -import ( - "fmt" -) - -type plainPassword struct { - password string -} - -// Accept any password in the plain text encoding. -// Be careful: This matches any line, so it *must* be the last parser in you list. -func AcceptPlain(pw string) (EncodedPasswd, error) { - return &plainPassword{pw}, nil -} - -// Reject any plain text encoded passoword. -// Be careful: This matches any line, so it *must* be the last parser in you list. -func RejectPlain(pw string) (EncodedPasswd, error) { - return nil, fmt.Errorf("plain password rejected: %s", pw) -} - -func (p *plainPassword) MatchesPassword(pw string) bool { - // Notice: nginx prefixes plain passwords with {PLAIN}, so we see if that would - // let us match too. I'd split {PLAIN} off, but someone probably uses that - // in their password. It's a big planet. - return constantTimeEquals(pw, p.password) || constantTimeEquals("{PLAIN}"+pw, p.password) -} diff --git a/vendor/github.com/jimstudt/http-authentication/basic/sha.go b/vendor/github.com/jimstudt/http-authentication/basic/sha.go deleted file mode 100644 index dbe9f9a5a..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/sha.go +++ /dev/null @@ -1,43 +0,0 @@ -package basic - -import ( - "crypto/sha1" - "crypto/subtle" - "encoding/base64" - "fmt" - "strings" -) - -type shaPassword struct { - hashed []byte -} - -// Accept valid SHA encoded passwords. -func AcceptSha(src string) (EncodedPasswd, error) { - if !strings.HasPrefix(src, "{SHA}") { - return nil, nil - } - - b64 := strings.TrimPrefix(src, "{SHA}") - hashed, err := base64.StdEncoding.DecodeString(b64) - if err != nil { - return nil, fmt.Errorf("Malformed sha1(%s): %s", src, err.Error()) - } - if len(hashed) != sha1.Size { - return nil, fmt.Errorf("Malformed sha1(%s): wrong length", src) - } - return &shaPassword{hashed}, nil -} - -// Reject any password encoded as SHA. -func RejectSha(src string) (EncodedPasswd, error) { - if !strings.HasPrefix(src, "{SHA}") { - return nil, nil - } - return nil, fmt.Errorf("sha password rejected: %s", src) -} - -func (s *shaPassword) MatchesPassword(pw string) bool { - h := sha1.Sum([]byte(pw)) - return subtle.ConstantTimeCompare(h[:], s.hashed) == 1 -} diff --git a/vendor/github.com/jimstudt/http-authentication/basic/util.go b/vendor/github.com/jimstudt/http-authentication/basic/util.go deleted file mode 100644 index daeb30cf3..000000000 --- a/vendor/github.com/jimstudt/http-authentication/basic/util.go +++ /dev/null @@ -1,18 +0,0 @@ -package basic - -import ( - "crypto/sha1" - "crypto/subtle" -) - -func constantTimeEquals(a string, b string) bool { - // compare SHA-1 as a gatekeeper in constant time - // then check that we didn't get by because of a collision - aSha := sha1.Sum([]byte(a)) - bSha := sha1.Sum([]byte(b)) - if subtle.ConstantTimeCompare(aSha[:], bSha[:]) == 1 { - // yes, this bit isn't constant, but you had to make a Sha1 collision to get here - return a == b - } - return false -} diff --git a/vendor/github.com/kelseyhightower/envconfig/.travis.yml b/vendor/github.com/kelseyhightower/envconfig/.travis.yml deleted file mode 100644 index 04b97aed6..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.4.x - - 1.5.x - - 1.6.x - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - - 1.12.x - - tip diff --git a/vendor/github.com/kelseyhightower/envconfig/LICENSE b/vendor/github.com/kelseyhightower/envconfig/LICENSE deleted file mode 100644 index 4bfa7a84d..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013 Kelsey Hightower - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/kelseyhightower/envconfig/MAINTAINERS b/vendor/github.com/kelseyhightower/envconfig/MAINTAINERS deleted file mode 100644 index 6527a9f2c..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/MAINTAINERS +++ /dev/null @@ -1,2 +0,0 @@ -Kelsey Hightower kelsey.hightower@gmail.com github.com/kelseyhightower -Travis Parker travis.parker@gmail.com github.com/teepark diff --git a/vendor/github.com/kelseyhightower/envconfig/README.md b/vendor/github.com/kelseyhightower/envconfig/README.md deleted file mode 100644 index 33408d645..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# envconfig - -[![Build Status](https://travis-ci.org/kelseyhightower/envconfig.svg)](https://travis-ci.org/kelseyhightower/envconfig) - -```Go -import "github.com/kelseyhightower/envconfig" -``` - -## Documentation - -See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig) - -## Usage - -Set some environment variables: - -```Bash -export MYAPP_DEBUG=false -export MYAPP_PORT=8080 -export MYAPP_USER=Kelsey -export MYAPP_RATE="0.5" -export MYAPP_TIMEOUT="3m" -export MYAPP_USERS="rob,ken,robert" -export MYAPP_COLORCODES="red:1,green:2,blue:3" -``` - -Write some code: - -```Go -package main - -import ( - "fmt" - "log" - "time" - - "github.com/kelseyhightower/envconfig" -) - -type Specification struct { - Debug bool - Port int - User string - Users []string - Rate float32 - Timeout time.Duration - ColorCodes map[string]int -} - -func main() { - var s Specification - err := envconfig.Process("myapp", &s) - if err != nil { - log.Fatal(err.Error()) - } - format := "Debug: %v\nPort: %d\nUser: %s\nRate: %f\nTimeout: %s\n" - _, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate, s.Timeout) - if err != nil { - log.Fatal(err.Error()) - } - - fmt.Println("Users:") - for _, u := range s.Users { - fmt.Printf(" %s\n", u) - } - - fmt.Println("Color codes:") - for k, v := range s.ColorCodes { - fmt.Printf(" %s: %d\n", k, v) - } -} -``` - -Results: - -```Bash -Debug: false -Port: 8080 -User: Kelsey -Rate: 0.500000 -Timeout: 3m0s -Users: - rob - ken - robert -Color codes: - red: 1 - green: 2 - blue: 3 -``` - -## Struct Tag Support - -Envconfig supports the use of struct tags to specify alternate, default, and required -environment variables. - -For example, consider the following struct: - -```Go -type Specification struct { - ManualOverride1 string `envconfig:"manual_override_1"` - DefaultVar string `default:"foobar"` - RequiredVar string `required:"true"` - IgnoredVar string `ignored:"true"` - AutoSplitVar string `split_words:"true"` - RequiredAndAutoSplitVar string `required:"true" split_words:"true"` -} -``` - -Envconfig has automatic support for CamelCased struct elements when the -`split_words:"true"` tag is supplied. Without this tag, `AutoSplitVar` above -would look for an environment variable called `MYAPP_AUTOSPLITVAR`. With the -setting applied it will look for `MYAPP_AUTO_SPLIT_VAR`. Note that numbers -will get globbed into the previous word. If the setting does not do the -right thing, you may use a manual override. - -Envconfig will process value for `ManualOverride1` by populating it with the -value for `MYAPP_MANUAL_OVERRIDE_1`. Without this struct tag, it would have -instead looked up `MYAPP_MANUALOVERRIDE1`. With the `split_words:"true"` tag -it would have looked up `MYAPP_MANUAL_OVERRIDE1`. - -```Bash -export MYAPP_MANUAL_OVERRIDE_1="this will be the value" - -# export MYAPP_MANUALOVERRIDE1="and this will not" -``` - -If envconfig can't find an environment variable value for `MYAPP_DEFAULTVAR`, -it will populate it with "foobar" as a default value. - -If envconfig can't find an environment variable value for `MYAPP_REQUIREDVAR`, -it will return an error when asked to process the struct. If -`MYAPP_REQUIREDVAR` is present but empty, envconfig will not return an error. - -If envconfig can't find an environment variable in the form `PREFIX_MYVAR`, and there -is a struct tag defined, it will try to populate your variable with an environment -variable that directly matches the envconfig tag in your struct definition: - -```shell -export SERVICE_HOST=127.0.0.1 -export MYAPP_DEBUG=true -``` -```Go -type Specification struct { - ServiceHost string `envconfig:"SERVICE_HOST"` - Debug bool -} -``` - -Envconfig won't process a field with the "ignored" tag set to "true", even if a corresponding -environment variable is set. - -## Supported Struct Field Types - -envconfig supports these struct field types: - - * string - * int8, int16, int32, int64 - * bool - * float32, float64 - * slices of any supported type - * maps (keys and values of any supported type) - * [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler) - * [encoding.BinaryUnmarshaler](https://golang.org/pkg/encoding/#BinaryUnmarshaler) - * [time.Duration](https://golang.org/pkg/time/#Duration) - -Embedded structs using these fields are also supported. - -## Custom Decoders - -Any field whose type (or pointer-to-type) implements `envconfig.Decoder` can -control its own deserialization: - -```Bash -export DNS_SERVER=8.8.8.8 -``` - -```Go -type IPDecoder net.IP - -func (ipd *IPDecoder) Decode(value string) error { - *ipd = IPDecoder(net.ParseIP(value)) - return nil -} - -type DNSConfig struct { - Address IPDecoder `envconfig:"DNS_SERVER"` -} -``` - -Also, envconfig will use a `Set(string) error` method like from the -[flag.Value](https://godoc.org/flag#Value) interface if implemented. diff --git a/vendor/github.com/kelseyhightower/envconfig/doc.go b/vendor/github.com/kelseyhightower/envconfig/doc.go deleted file mode 100644 index f28561cd1..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2013 Kelsey Hightower. All rights reserved. -// Use of this source code is governed by the MIT License that can be found in -// the LICENSE file. - -// Package envconfig implements decoding of environment variables based on a user -// defined specification. A typical use is using environment variables for -// configuration settings. -package envconfig diff --git a/vendor/github.com/kelseyhightower/envconfig/env_os.go b/vendor/github.com/kelseyhightower/envconfig/env_os.go deleted file mode 100644 index eba07a6c6..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/env_os.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build appengine go1.5 - -package envconfig - -import "os" - -var lookupEnv = os.LookupEnv diff --git a/vendor/github.com/kelseyhightower/envconfig/env_syscall.go b/vendor/github.com/kelseyhightower/envconfig/env_syscall.go deleted file mode 100644 index 425454008..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/env_syscall.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !appengine,!go1.5 - -package envconfig - -import "syscall" - -var lookupEnv = syscall.Getenv diff --git a/vendor/github.com/kelseyhightower/envconfig/envconfig.go b/vendor/github.com/kelseyhightower/envconfig/envconfig.go deleted file mode 100644 index 3f16108db..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/envconfig.go +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright (c) 2013 Kelsey Hightower. All rights reserved. -// Use of this source code is governed by the MIT License that can be found in -// the LICENSE file. - -package envconfig - -import ( - "encoding" - "errors" - "fmt" - "os" - "reflect" - "regexp" - "strconv" - "strings" - "time" -) - -// ErrInvalidSpecification indicates that a specification is of the wrong type. -var ErrInvalidSpecification = errors.New("specification must be a struct pointer") - -var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)") -var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)") - -// A ParseError occurs when an environment variable cannot be converted to -// the type required by a struct field during assignment. -type ParseError struct { - KeyName string - FieldName string - TypeName string - Value string - Err error -} - -// Decoder has the same semantics as Setter, but takes higher precedence. -// It is provided for historical compatibility. -type Decoder interface { - Decode(value string) error -} - -// Setter is implemented by types can self-deserialize values. -// Any type that implements flag.Value also implements Setter. -type Setter interface { - Set(value string) error -} - -func (e *ParseError) Error() string { - return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s. details: %[5]s", e.KeyName, e.FieldName, e.Value, e.TypeName, e.Err) -} - -// varInfo maintains information about the configuration variable -type varInfo struct { - Name string - Alt string - Key string - Field reflect.Value - Tags reflect.StructTag -} - -// GatherInfo gathers information about the specified struct -func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) { - s := reflect.ValueOf(spec) - - if s.Kind() != reflect.Ptr { - return nil, ErrInvalidSpecification - } - s = s.Elem() - if s.Kind() != reflect.Struct { - return nil, ErrInvalidSpecification - } - typeOfSpec := s.Type() - - // over allocate an info array, we will extend if needed later - infos := make([]varInfo, 0, s.NumField()) - for i := 0; i < s.NumField(); i++ { - f := s.Field(i) - ftype := typeOfSpec.Field(i) - if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) { - continue - } - - for f.Kind() == reflect.Ptr { - if f.IsNil() { - if f.Type().Elem().Kind() != reflect.Struct { - // nil pointer to a non-struct: leave it alone - break - } - // nil pointer to struct: create a zero instance - f.Set(reflect.New(f.Type().Elem())) - } - f = f.Elem() - } - - // Capture information about the config variable - info := varInfo{ - Name: ftype.Name, - Field: f, - Tags: ftype.Tag, - Alt: strings.ToUpper(ftype.Tag.Get("envconfig")), - } - - // Default to the field name as the env var name (will be upcased) - info.Key = info.Name - - // Best effort to un-pick camel casing as separate words - if isTrue(ftype.Tag.Get("split_words")) { - words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1) - if len(words) > 0 { - var name []string - for _, words := range words { - if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 { - name = append(name, m[1], m[2]) - } else { - name = append(name, words[0]) - } - } - - info.Key = strings.Join(name, "_") - } - } - if info.Alt != "" { - info.Key = info.Alt - } - if prefix != "" { - info.Key = fmt.Sprintf("%s_%s", prefix, info.Key) - } - info.Key = strings.ToUpper(info.Key) - infos = append(infos, info) - - if f.Kind() == reflect.Struct { - // honor Decode if present - if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil { - innerPrefix := prefix - if !ftype.Anonymous { - innerPrefix = info.Key - } - - embeddedPtr := f.Addr().Interface() - embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr) - if err != nil { - return nil, err - } - infos = append(infos[:len(infos)-1], embeddedInfos...) - - continue - } - } - } - return infos, nil -} - -// CheckDisallowed checks that no environment variables with the prefix are set -// that we don't know how or want to parse. This is likely only meaningful with -// a non-empty prefix. -func CheckDisallowed(prefix string, spec interface{}) error { - infos, err := gatherInfo(prefix, spec) - if err != nil { - return err - } - - vars := make(map[string]struct{}) - for _, info := range infos { - vars[info.Key] = struct{}{} - } - - if prefix != "" { - prefix = strings.ToUpper(prefix) + "_" - } - - for _, env := range os.Environ() { - if !strings.HasPrefix(env, prefix) { - continue - } - v := strings.SplitN(env, "=", 2)[0] - if _, found := vars[v]; !found { - return fmt.Errorf("unknown environment variable %s", v) - } - } - - return nil -} - -// Process populates the specified struct based on environment variables -func Process(prefix string, spec interface{}) error { - infos, err := gatherInfo(prefix, spec) - - for _, info := range infos { - - // `os.Getenv` cannot differentiate between an explicitly set empty value - // and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`, - // but it is only available in go1.5 or newer. We're using Go build tags - // here to use os.LookupEnv for >=go1.5 - value, ok := lookupEnv(info.Key) - if !ok && info.Alt != "" { - value, ok = lookupEnv(info.Alt) - } - - def := info.Tags.Get("default") - if def != "" && !ok { - value = def - } - - req := info.Tags.Get("required") - if !ok && def == "" { - if isTrue(req) { - key := info.Key - if info.Alt != "" { - key = info.Alt - } - return fmt.Errorf("required key %s missing value", key) - } - continue - } - - err = processField(value, info.Field) - if err != nil { - return &ParseError{ - KeyName: info.Key, - FieldName: info.Name, - TypeName: info.Field.Type().String(), - Value: value, - Err: err, - } - } - } - - return err -} - -// MustProcess is the same as Process but panics if an error occurs -func MustProcess(prefix string, spec interface{}) { - if err := Process(prefix, spec); err != nil { - panic(err) - } -} - -func processField(value string, field reflect.Value) error { - typ := field.Type() - - decoder := decoderFrom(field) - if decoder != nil { - return decoder.Decode(value) - } - // look for Set method if Decode not defined - setter := setterFrom(field) - if setter != nil { - return setter.Set(value) - } - - if t := textUnmarshaler(field); t != nil { - return t.UnmarshalText([]byte(value)) - } - - if b := binaryUnmarshaler(field); b != nil { - return b.UnmarshalBinary([]byte(value)) - } - - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - if field.IsNil() { - field.Set(reflect.New(typ)) - } - field = field.Elem() - } - - switch typ.Kind() { - case reflect.String: - field.SetString(value) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - var ( - val int64 - err error - ) - if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" { - var d time.Duration - d, err = time.ParseDuration(value) - val = int64(d) - } else { - val, err = strconv.ParseInt(value, 0, typ.Bits()) - } - if err != nil { - return err - } - - field.SetInt(val) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - val, err := strconv.ParseUint(value, 0, typ.Bits()) - if err != nil { - return err - } - field.SetUint(val) - case reflect.Bool: - val, err := strconv.ParseBool(value) - if err != nil { - return err - } - field.SetBool(val) - case reflect.Float32, reflect.Float64: - val, err := strconv.ParseFloat(value, typ.Bits()) - if err != nil { - return err - } - field.SetFloat(val) - case reflect.Slice: - sl := reflect.MakeSlice(typ, 0, 0) - if typ.Elem().Kind() == reflect.Uint8 { - sl = reflect.ValueOf([]byte(value)) - } else if len(strings.TrimSpace(value)) != 0 { - vals := strings.Split(value, ",") - sl = reflect.MakeSlice(typ, len(vals), len(vals)) - for i, val := range vals { - err := processField(val, sl.Index(i)) - if err != nil { - return err - } - } - } - field.Set(sl) - case reflect.Map: - mp := reflect.MakeMap(typ) - if len(strings.TrimSpace(value)) != 0 { - pairs := strings.Split(value, ",") - for _, pair := range pairs { - kvpair := strings.Split(pair, ":") - if len(kvpair) != 2 { - return fmt.Errorf("invalid map item: %q", pair) - } - k := reflect.New(typ.Key()).Elem() - err := processField(kvpair[0], k) - if err != nil { - return err - } - v := reflect.New(typ.Elem()).Elem() - err = processField(kvpair[1], v) - if err != nil { - return err - } - mp.SetMapIndex(k, v) - } - } - field.Set(mp) - } - - return nil -} - -func interfaceFrom(field reflect.Value, fn func(interface{}, *bool)) { - // it may be impossible for a struct field to fail this check - if !field.CanInterface() { - return - } - var ok bool - fn(field.Interface(), &ok) - if !ok && field.CanAddr() { - fn(field.Addr().Interface(), &ok) - } -} - -func decoderFrom(field reflect.Value) (d Decoder) { - interfaceFrom(field, func(v interface{}, ok *bool) { d, *ok = v.(Decoder) }) - return d -} - -func setterFrom(field reflect.Value) (s Setter) { - interfaceFrom(field, func(v interface{}, ok *bool) { s, *ok = v.(Setter) }) - return s -} - -func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) { - interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) }) - return t -} - -func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) { - interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) }) - return b -} - -func isTrue(s string) bool { - b, _ := strconv.ParseBool(s) - return b -} diff --git a/vendor/github.com/kelseyhightower/envconfig/go.mod b/vendor/github.com/kelseyhightower/envconfig/go.mod deleted file mode 100644 index 1561d1e4e..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/kelseyhightower/envconfig diff --git a/vendor/github.com/kelseyhightower/envconfig/usage.go b/vendor/github.com/kelseyhightower/envconfig/usage.go deleted file mode 100644 index 1e6d0a8f3..000000000 --- a/vendor/github.com/kelseyhightower/envconfig/usage.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2016 Kelsey Hightower and others. All rights reserved. -// Use of this source code is governed by the MIT License that can be found in -// the LICENSE file. - -package envconfig - -import ( - "encoding" - "fmt" - "io" - "os" - "reflect" - "strconv" - "strings" - "text/tabwriter" - "text/template" -) - -const ( - // DefaultListFormat constant to use to display usage in a list format - DefaultListFormat = `This application is configured via the environment. The following environment -variables can be used: -{{range .}} -{{usage_key .}} - [description] {{usage_description .}} - [type] {{usage_type .}} - [default] {{usage_default .}} - [required] {{usage_required .}}{{end}} -` - // DefaultTableFormat constant to use to display usage in a tabular format - DefaultTableFormat = `This application is configured via the environment. The following environment -variables can be used: - -KEY TYPE DEFAULT REQUIRED DESCRIPTION -{{range .}}{{usage_key .}} {{usage_type .}} {{usage_default .}} {{usage_required .}} {{usage_description .}} -{{end}}` -) - -var ( - decoderType = reflect.TypeOf((*Decoder)(nil)).Elem() - setterType = reflect.TypeOf((*Setter)(nil)).Elem() - textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() - binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() -) - -func implementsInterface(t reflect.Type) bool { - return t.Implements(decoderType) || - reflect.PtrTo(t).Implements(decoderType) || - t.Implements(setterType) || - reflect.PtrTo(t).Implements(setterType) || - t.Implements(textUnmarshalerType) || - reflect.PtrTo(t).Implements(textUnmarshalerType) || - t.Implements(binaryUnmarshalerType) || - reflect.PtrTo(t).Implements(binaryUnmarshalerType) -} - -// toTypeDescription converts Go types into a human readable description -func toTypeDescription(t reflect.Type) string { - switch t.Kind() { - case reflect.Array, reflect.Slice: - if t.Elem().Kind() == reflect.Uint8 { - return "String" - } - return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem())) - case reflect.Map: - return fmt.Sprintf( - "Comma-separated list of %s:%s pairs", - toTypeDescription(t.Key()), - toTypeDescription(t.Elem()), - ) - case reflect.Ptr: - return toTypeDescription(t.Elem()) - case reflect.Struct: - if implementsInterface(t) && t.Name() != "" { - return t.Name() - } - return "" - case reflect.String: - name := t.Name() - if name != "" && name != "string" { - return name - } - return "String" - case reflect.Bool: - name := t.Name() - if name != "" && name != "bool" { - return name - } - return "True or False" - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - name := t.Name() - if name != "" && !strings.HasPrefix(name, "int") { - return name - } - return "Integer" - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - name := t.Name() - if name != "" && !strings.HasPrefix(name, "uint") { - return name - } - return "Unsigned Integer" - case reflect.Float32, reflect.Float64: - name := t.Name() - if name != "" && !strings.HasPrefix(name, "float") { - return name - } - return "Float" - } - return fmt.Sprintf("%+v", t) -} - -// Usage writes usage information to stdout using the default header and table format -func Usage(prefix string, spec interface{}) error { - // The default is to output the usage information as a table - // Create tabwriter instance to support table output - tabs := tabwriter.NewWriter(os.Stdout, 1, 0, 4, ' ', 0) - - err := Usagef(prefix, spec, tabs, DefaultTableFormat) - tabs.Flush() - return err -} - -// Usagef writes usage information to the specified io.Writer using the specifed template specification -func Usagef(prefix string, spec interface{}, out io.Writer, format string) error { - - // Specify the default usage template functions - functions := template.FuncMap{ - "usage_key": func(v varInfo) string { return v.Key }, - "usage_description": func(v varInfo) string { return v.Tags.Get("desc") }, - "usage_type": func(v varInfo) string { return toTypeDescription(v.Field.Type()) }, - "usage_default": func(v varInfo) string { return v.Tags.Get("default") }, - "usage_required": func(v varInfo) (string, error) { - req := v.Tags.Get("required") - if req != "" { - reqB, err := strconv.ParseBool(req) - if err != nil { - return "", err - } - if reqB { - req = "true" - } - } - return req, nil - }, - } - - tmpl, err := template.New("envconfig").Funcs(functions).Parse(format) - if err != nil { - return err - } - - return Usaget(prefix, spec, out, tmpl) -} - -// Usaget writes usage information to the specified io.Writer using the specified template -func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error { - // gather first - infos, err := gatherInfo(prefix, spec) - if err != nil { - return err - } - - return tmpl.Execute(out, infos) -} diff --git a/vendor/github.com/klauspost/cpuid/.gitignore b/vendor/github.com/klauspost/cpuid/.gitignore deleted file mode 100644 index daf913b1b..000000000 --- a/vendor/github.com/klauspost/cpuid/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof diff --git a/vendor/github.com/klauspost/cpuid/.travis.yml b/vendor/github.com/klauspost/cpuid/.travis.yml deleted file mode 100644 index 630192d59..000000000 --- a/vendor/github.com/klauspost/cpuid/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go - -sudo: false - -os: - - linux - - osx -go: - - 1.8.x - - 1.9.x - - 1.10.x - - master - -script: - - go vet ./... - - go test -v ./... - - go test -race ./... - - diff <(gofmt -d .) <("") - -matrix: - allow_failures: - - go: 'master' - fast_finish: true diff --git a/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt b/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt deleted file mode 100644 index 2ef4714f7..000000000 --- a/vendor/github.com/klauspost/cpuid/CONTRIBUTING.txt +++ /dev/null @@ -1,35 +0,0 @@ -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2015- Klaus Post & Contributors. -Email: klauspost@gmail.com - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. diff --git a/vendor/github.com/klauspost/cpuid/LICENSE b/vendor/github.com/klauspost/cpuid/LICENSE deleted file mode 100644 index 5cec7ee94..000000000 --- a/vendor/github.com/klauspost/cpuid/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Klaus Post - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/vendor/github.com/klauspost/cpuid/README.md b/vendor/github.com/klauspost/cpuid/README.md deleted file mode 100644 index a7fb41fbe..000000000 --- a/vendor/github.com/klauspost/cpuid/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# cpuid -Package cpuid provides information about the CPU running the current program. - -CPU features are detected on startup, and kept for fast access through the life of the application. -Currently x86 / x64 (AMD64) is supported, and no external C (cgo) code is used, which should make the library very easy to use. - -You can access the CPU information by accessing the shared CPU variable of the cpuid library. - -Package home: https://github.com/klauspost/cpuid - -[![GoDoc][1]][2] [![Build Status][3]][4] - -[1]: https://godoc.org/github.com/klauspost/cpuid?status.svg -[2]: https://godoc.org/github.com/klauspost/cpuid -[3]: https://travis-ci.org/klauspost/cpuid.svg -[4]: https://travis-ci.org/klauspost/cpuid - -# features -## CPU Instructions -* **CMOV** (i686 CMOV) -* **NX** (NX (No-Execute) bit) -* **AMD3DNOW** (AMD 3DNOW) -* **AMD3DNOWEXT** (AMD 3DNowExt) -* **MMX** (standard MMX) -* **MMXEXT** (SSE integer functions or AMD MMX ext) -* **SSE** (SSE functions) -* **SSE2** (P4 SSE functions) -* **SSE3** (Prescott SSE3 functions) -* **SSSE3** (Conroe SSSE3 functions) -* **SSE4** (Penryn SSE4.1 functions) -* **SSE4A** (AMD Barcelona microarchitecture SSE4a instructions) -* **SSE42** (Nehalem SSE4.2 functions) -* **AVX** (AVX functions) -* **AVX2** (AVX2 functions) -* **FMA3** (Intel FMA 3) -* **FMA4** (Bulldozer FMA4 functions) -* **XOP** (Bulldozer XOP functions) -* **F16C** (Half-precision floating-point conversion) -* **BMI1** (Bit Manipulation Instruction Set 1) -* **BMI2** (Bit Manipulation Instruction Set 2) -* **TBM** (AMD Trailing Bit Manipulation) -* **LZCNT** (LZCNT instruction) -* **POPCNT** (POPCNT instruction) -* **AESNI** (Advanced Encryption Standard New Instructions) -* **CLMUL** (Carry-less Multiplication) -* **HTT** (Hyperthreading (enabled)) -* **HLE** (Hardware Lock Elision) -* **RTM** (Restricted Transactional Memory) -* **RDRAND** (RDRAND instruction is available) -* **RDSEED** (RDSEED instruction is available) -* **ADX** (Intel ADX (Multi-Precision Add-Carry Instruction Extensions)) -* **SHA** (Intel SHA Extensions) -* **AVX512F** (AVX-512 Foundation) -* **AVX512DQ** (AVX-512 Doubleword and Quadword Instructions) -* **AVX512IFMA** (AVX-512 Integer Fused Multiply-Add Instructions) -* **AVX512PF** (AVX-512 Prefetch Instructions) -* **AVX512ER** (AVX-512 Exponential and Reciprocal Instructions) -* **AVX512CD** (AVX-512 Conflict Detection Instructions) -* **AVX512BW** (AVX-512 Byte and Word Instructions) -* **AVX512VL** (AVX-512 Vector Length Extensions) -* **AVX512VBMI** (AVX-512 Vector Bit Manipulation Instructions) -* **MPX** (Intel MPX (Memory Protection Extensions)) -* **ERMS** (Enhanced REP MOVSB/STOSB) -* **RDTSCP** (RDTSCP Instruction) -* **CX16** (CMPXCHG16B Instruction) -* **SGX** (Software Guard Extensions, with activation details) - -## Performance -* **RDTSCP()** Returns current cycle count. Can be used for benchmarking. -* **SSE2SLOW** (SSE2 is supported, but usually not faster) -* **SSE3SLOW** (SSE3 is supported, but usually not faster) -* **ATOM** (Atom processor, some SSSE3 instructions are slower) -* **Cache line** (Probable size of a cache line). -* **L1, L2, L3 Cache size** on newer Intel/AMD CPUs. - -## Cpu Vendor/VM -* **Intel** -* **AMD** -* **VIA** -* **Transmeta** -* **NSC** -* **KVM** (Kernel-based Virtual Machine) -* **MSVM** (Microsoft Hyper-V or Windows Virtual PC) -* **VMware** -* **XenHVM** -* **Bhyve** -* **Hygon** - -# installing - -```go get github.com/klauspost/cpuid``` - -# example - -```Go -package main - -import ( - "fmt" - "github.com/klauspost/cpuid" -) - -func main() { - // Print basic CPU information: - fmt.Println("Name:", cpuid.CPU.BrandName) - fmt.Println("PhysicalCores:", cpuid.CPU.PhysicalCores) - fmt.Println("ThreadsPerCore:", cpuid.CPU.ThreadsPerCore) - fmt.Println("LogicalCores:", cpuid.CPU.LogicalCores) - fmt.Println("Family", cpuid.CPU.Family, "Model:", cpuid.CPU.Model) - fmt.Println("Features:", cpuid.CPU.Features) - fmt.Println("Cacheline bytes:", cpuid.CPU.CacheLine) - fmt.Println("L1 Data Cache:", cpuid.CPU.Cache.L1D, "bytes") - fmt.Println("L1 Instruction Cache:", cpuid.CPU.Cache.L1D, "bytes") - fmt.Println("L2 Cache:", cpuid.CPU.Cache.L2, "bytes") - fmt.Println("L3 Cache:", cpuid.CPU.Cache.L3, "bytes") - - // Test if we have a specific feature: - if cpuid.CPU.SSE() { - fmt.Println("We have Streaming SIMD Extensions") - } -} -``` - -Sample output: -``` ->go run main.go -Name: Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz -PhysicalCores: 2 -ThreadsPerCore: 2 -LogicalCores: 4 -Family 6 Model: 42 -Features: CMOV,MMX,MMXEXT,SSE,SSE2,SSE3,SSSE3,SSE4.1,SSE4.2,AVX,AESNI,CLMUL -Cacheline bytes: 64 -We have Streaming SIMD Extensions -``` - -# private package - -In the "private" folder you can find an autogenerated version of the library you can include in your own packages. - -For this purpose all exports are removed, and functions and constants are lowercased. - -This is not a recommended way of using the library, but provided for convenience, if it is difficult for you to use external packages. - -# license - -This code is published under an MIT license. See LICENSE file for more information. diff --git a/vendor/github.com/klauspost/cpuid/cpuid.go b/vendor/github.com/klauspost/cpuid/cpuid.go deleted file mode 100644 index db9591321..000000000 --- a/vendor/github.com/klauspost/cpuid/cpuid.go +++ /dev/null @@ -1,1049 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -// Package cpuid provides information about the CPU running the current program. -// -// CPU features are detected on startup, and kept for fast access through the life of the application. -// Currently x86 / x64 (AMD64) is supported. -// -// You can access the CPU information by accessing the shared CPU variable of the cpuid library. -// -// Package home: https://github.com/klauspost/cpuid -package cpuid - -import "strings" - -// Vendor is a representation of a CPU vendor. -type Vendor int - -const ( - Other Vendor = iota - Intel - AMD - VIA - Transmeta - NSC - KVM // Kernel-based Virtual Machine - MSVM // Microsoft Hyper-V or Windows Virtual PC - VMware - XenHVM - Bhyve - Hygon -) - -const ( - CMOV = 1 << iota // i686 CMOV - NX // NX (No-Execute) bit - AMD3DNOW // AMD 3DNOW - AMD3DNOWEXT // AMD 3DNowExt - MMX // standard MMX - MMXEXT // SSE integer functions or AMD MMX ext - SSE // SSE functions - SSE2 // P4 SSE functions - SSE3 // Prescott SSE3 functions - SSSE3 // Conroe SSSE3 functions - SSE4 // Penryn SSE4.1 functions - SSE4A // AMD Barcelona microarchitecture SSE4a instructions - SSE42 // Nehalem SSE4.2 functions - AVX // AVX functions - AVX2 // AVX2 functions - FMA3 // Intel FMA 3 - FMA4 // Bulldozer FMA4 functions - XOP // Bulldozer XOP functions - F16C // Half-precision floating-point conversion - BMI1 // Bit Manipulation Instruction Set 1 - BMI2 // Bit Manipulation Instruction Set 2 - TBM // AMD Trailing Bit Manipulation - LZCNT // LZCNT instruction - POPCNT // POPCNT instruction - AESNI // Advanced Encryption Standard New Instructions - CLMUL // Carry-less Multiplication - HTT // Hyperthreading (enabled) - HLE // Hardware Lock Elision - RTM // Restricted Transactional Memory - RDRAND // RDRAND instruction is available - RDSEED // RDSEED instruction is available - ADX // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - SHA // Intel SHA Extensions - AVX512F // AVX-512 Foundation - AVX512DQ // AVX-512 Doubleword and Quadword Instructions - AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions - AVX512PF // AVX-512 Prefetch Instructions - AVX512ER // AVX-512 Exponential and Reciprocal Instructions - AVX512CD // AVX-512 Conflict Detection Instructions - AVX512BW // AVX-512 Byte and Word Instructions - AVX512VL // AVX-512 Vector Length Extensions - AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions - MPX // Intel MPX (Memory Protection Extensions) - ERMS // Enhanced REP MOVSB/STOSB - RDTSCP // RDTSCP Instruction - CX16 // CMPXCHG16B Instruction - SGX // Software Guard Extensions - IBPB // Indirect Branch Restricted Speculation (IBRS) and Indirect Branch Predictor Barrier (IBPB) - STIBP // Single Thread Indirect Branch Predictors - - // Performance indicators - SSE2SLOW // SSE2 is supported, but usually not faster - SSE3SLOW // SSE3 is supported, but usually not faster - ATOM // Atom processor, some SSSE3 instructions are slower -) - -var flagNames = map[Flags]string{ - CMOV: "CMOV", // i686 CMOV - NX: "NX", // NX (No-Execute) bit - AMD3DNOW: "AMD3DNOW", // AMD 3DNOW - AMD3DNOWEXT: "AMD3DNOWEXT", // AMD 3DNowExt - MMX: "MMX", // Standard MMX - MMXEXT: "MMXEXT", // SSE integer functions or AMD MMX ext - SSE: "SSE", // SSE functions - SSE2: "SSE2", // P4 SSE2 functions - SSE3: "SSE3", // Prescott SSE3 functions - SSSE3: "SSSE3", // Conroe SSSE3 functions - SSE4: "SSE4.1", // Penryn SSE4.1 functions - SSE4A: "SSE4A", // AMD Barcelona microarchitecture SSE4a instructions - SSE42: "SSE4.2", // Nehalem SSE4.2 functions - AVX: "AVX", // AVX functions - AVX2: "AVX2", // AVX functions - FMA3: "FMA3", // Intel FMA 3 - FMA4: "FMA4", // Bulldozer FMA4 functions - XOP: "XOP", // Bulldozer XOP functions - F16C: "F16C", // Half-precision floating-point conversion - BMI1: "BMI1", // Bit Manipulation Instruction Set 1 - BMI2: "BMI2", // Bit Manipulation Instruction Set 2 - TBM: "TBM", // AMD Trailing Bit Manipulation - LZCNT: "LZCNT", // LZCNT instruction - POPCNT: "POPCNT", // POPCNT instruction - AESNI: "AESNI", // Advanced Encryption Standard New Instructions - CLMUL: "CLMUL", // Carry-less Multiplication - HTT: "HTT", // Hyperthreading (enabled) - HLE: "HLE", // Hardware Lock Elision - RTM: "RTM", // Restricted Transactional Memory - RDRAND: "RDRAND", // RDRAND instruction is available - RDSEED: "RDSEED", // RDSEED instruction is available - ADX: "ADX", // Intel ADX (Multi-Precision Add-Carry Instruction Extensions) - SHA: "SHA", // Intel SHA Extensions - AVX512F: "AVX512F", // AVX-512 Foundation - AVX512DQ: "AVX512DQ", // AVX-512 Doubleword and Quadword Instructions - AVX512IFMA: "AVX512IFMA", // AVX-512 Integer Fused Multiply-Add Instructions - AVX512PF: "AVX512PF", // AVX-512 Prefetch Instructions - AVX512ER: "AVX512ER", // AVX-512 Exponential and Reciprocal Instructions - AVX512CD: "AVX512CD", // AVX-512 Conflict Detection Instructions - AVX512BW: "AVX512BW", // AVX-512 Byte and Word Instructions - AVX512VL: "AVX512VL", // AVX-512 Vector Length Extensions - AVX512VBMI: "AVX512VBMI", // AVX-512 Vector Bit Manipulation Instructions - MPX: "MPX", // Intel MPX (Memory Protection Extensions) - ERMS: "ERMS", // Enhanced REP MOVSB/STOSB - RDTSCP: "RDTSCP", // RDTSCP Instruction - CX16: "CX16", // CMPXCHG16B Instruction - SGX: "SGX", // Software Guard Extensions - IBPB: "IBPB", // Indirect Branch Restricted Speculation and Indirect Branch Predictor Barrier - STIBP: "STIBP", // Single Thread Indirect Branch Predictors - - // Performance indicators - SSE2SLOW: "SSE2SLOW", // SSE2 supported, but usually not faster - SSE3SLOW: "SSE3SLOW", // SSE3 supported, but usually not faster - ATOM: "ATOM", // Atom processor, some SSSE3 instructions are slower - -} - -// CPUInfo contains information about the detected system CPU. -type CPUInfo struct { - BrandName string // Brand name reported by the CPU - VendorID Vendor // Comparable CPU vendor ID - Features Flags // Features of the CPU - PhysicalCores int // Number of physical processor cores in your CPU. Will be 0 if undetectable. - ThreadsPerCore int // Number of threads per physical core. Will be 1 if undetectable. - LogicalCores int // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable. - Family int // CPU family number - Model int // CPU model number - CacheLine int // Cache line size in bytes. Will be 0 if undetectable. - Cache struct { - L1I int // L1 Instruction Cache (per core or shared). Will be -1 if undetected - L1D int // L1 Data Cache (per core or shared). Will be -1 if undetected - L2 int // L2 Cache (per core or shared). Will be -1 if undetected - L3 int // L3 Instruction Cache (per core or shared). Will be -1 if undetected - } - SGX SGXSupport - maxFunc uint32 - maxExFunc uint32 -} - -var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) -var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) -var xgetbv func(index uint32) (eax, edx uint32) -var rdtscpAsm func() (eax, ebx, ecx, edx uint32) - -// CPU contains information about the CPU as detected on startup, -// or when Detect last was called. -// -// Use this as the primary entry point to you data, -// this way queries are -var CPU CPUInfo - -func init() { - initCPU() - Detect() -} - -// Detect will re-detect current CPU info. -// This will replace the content of the exported CPU variable. -// -// Unless you expect the CPU to change while you are running your program -// you should not need to call this function. -// If you call this, you must ensure that no other goroutine is accessing the -// exported CPU variable. -func Detect() { - CPU.maxFunc = maxFunctionID() - CPU.maxExFunc = maxExtendedFunction() - CPU.BrandName = brandName() - CPU.CacheLine = cacheLine() - CPU.Family, CPU.Model = familyModel() - CPU.Features = support() - CPU.SGX = hasSGX(CPU.Features&SGX != 0) - CPU.ThreadsPerCore = threadsPerCore() - CPU.LogicalCores = logicalCores() - CPU.PhysicalCores = physicalCores() - CPU.VendorID = vendorID() - CPU.cacheSize() -} - -// Generated here: http://play.golang.org/p/BxFH2Gdc0G - -// Cmov indicates support of CMOV instructions -func (c CPUInfo) Cmov() bool { - return c.Features&CMOV != 0 -} - -// Amd3dnow indicates support of AMD 3DNOW! instructions -func (c CPUInfo) Amd3dnow() bool { - return c.Features&AMD3DNOW != 0 -} - -// Amd3dnowExt indicates support of AMD 3DNOW! Extended instructions -func (c CPUInfo) Amd3dnowExt() bool { - return c.Features&AMD3DNOWEXT != 0 -} - -// MMX indicates support of MMX instructions -func (c CPUInfo) MMX() bool { - return c.Features&MMX != 0 -} - -// MMXExt indicates support of MMXEXT instructions -// (SSE integer functions or AMD MMX ext) -func (c CPUInfo) MMXExt() bool { - return c.Features&MMXEXT != 0 -} - -// SSE indicates support of SSE instructions -func (c CPUInfo) SSE() bool { - return c.Features&SSE != 0 -} - -// SSE2 indicates support of SSE 2 instructions -func (c CPUInfo) SSE2() bool { - return c.Features&SSE2 != 0 -} - -// SSE3 indicates support of SSE 3 instructions -func (c CPUInfo) SSE3() bool { - return c.Features&SSE3 != 0 -} - -// SSSE3 indicates support of SSSE 3 instructions -func (c CPUInfo) SSSE3() bool { - return c.Features&SSSE3 != 0 -} - -// SSE4 indicates support of SSE 4 (also called SSE 4.1) instructions -func (c CPUInfo) SSE4() bool { - return c.Features&SSE4 != 0 -} - -// SSE42 indicates support of SSE4.2 instructions -func (c CPUInfo) SSE42() bool { - return c.Features&SSE42 != 0 -} - -// AVX indicates support of AVX instructions -// and operating system support of AVX instructions -func (c CPUInfo) AVX() bool { - return c.Features&AVX != 0 -} - -// AVX2 indicates support of AVX2 instructions -func (c CPUInfo) AVX2() bool { - return c.Features&AVX2 != 0 -} - -// FMA3 indicates support of FMA3 instructions -func (c CPUInfo) FMA3() bool { - return c.Features&FMA3 != 0 -} - -// FMA4 indicates support of FMA4 instructions -func (c CPUInfo) FMA4() bool { - return c.Features&FMA4 != 0 -} - -// XOP indicates support of XOP instructions -func (c CPUInfo) XOP() bool { - return c.Features&XOP != 0 -} - -// F16C indicates support of F16C instructions -func (c CPUInfo) F16C() bool { - return c.Features&F16C != 0 -} - -// BMI1 indicates support of BMI1 instructions -func (c CPUInfo) BMI1() bool { - return c.Features&BMI1 != 0 -} - -// BMI2 indicates support of BMI2 instructions -func (c CPUInfo) BMI2() bool { - return c.Features&BMI2 != 0 -} - -// TBM indicates support of TBM instructions -// (AMD Trailing Bit Manipulation) -func (c CPUInfo) TBM() bool { - return c.Features&TBM != 0 -} - -// Lzcnt indicates support of LZCNT instruction -func (c CPUInfo) Lzcnt() bool { - return c.Features&LZCNT != 0 -} - -// Popcnt indicates support of POPCNT instruction -func (c CPUInfo) Popcnt() bool { - return c.Features&POPCNT != 0 -} - -// HTT indicates the processor has Hyperthreading enabled -func (c CPUInfo) HTT() bool { - return c.Features&HTT != 0 -} - -// SSE2Slow indicates that SSE2 may be slow on this processor -func (c CPUInfo) SSE2Slow() bool { - return c.Features&SSE2SLOW != 0 -} - -// SSE3Slow indicates that SSE3 may be slow on this processor -func (c CPUInfo) SSE3Slow() bool { - return c.Features&SSE3SLOW != 0 -} - -// AesNi indicates support of AES-NI instructions -// (Advanced Encryption Standard New Instructions) -func (c CPUInfo) AesNi() bool { - return c.Features&AESNI != 0 -} - -// Clmul indicates support of CLMUL instructions -// (Carry-less Multiplication) -func (c CPUInfo) Clmul() bool { - return c.Features&CLMUL != 0 -} - -// NX indicates support of NX (No-Execute) bit -func (c CPUInfo) NX() bool { - return c.Features&NX != 0 -} - -// SSE4A indicates support of AMD Barcelona microarchitecture SSE4a instructions -func (c CPUInfo) SSE4A() bool { - return c.Features&SSE4A != 0 -} - -// HLE indicates support of Hardware Lock Elision -func (c CPUInfo) HLE() bool { - return c.Features&HLE != 0 -} - -// RTM indicates support of Restricted Transactional Memory -func (c CPUInfo) RTM() bool { - return c.Features&RTM != 0 -} - -// Rdrand indicates support of RDRAND instruction is available -func (c CPUInfo) Rdrand() bool { - return c.Features&RDRAND != 0 -} - -// Rdseed indicates support of RDSEED instruction is available -func (c CPUInfo) Rdseed() bool { - return c.Features&RDSEED != 0 -} - -// ADX indicates support of Intel ADX (Multi-Precision Add-Carry Instruction Extensions) -func (c CPUInfo) ADX() bool { - return c.Features&ADX != 0 -} - -// SHA indicates support of Intel SHA Extensions -func (c CPUInfo) SHA() bool { - return c.Features&SHA != 0 -} - -// AVX512F indicates support of AVX-512 Foundation -func (c CPUInfo) AVX512F() bool { - return c.Features&AVX512F != 0 -} - -// AVX512DQ indicates support of AVX-512 Doubleword and Quadword Instructions -func (c CPUInfo) AVX512DQ() bool { - return c.Features&AVX512DQ != 0 -} - -// AVX512IFMA indicates support of AVX-512 Integer Fused Multiply-Add Instructions -func (c CPUInfo) AVX512IFMA() bool { - return c.Features&AVX512IFMA != 0 -} - -// AVX512PF indicates support of AVX-512 Prefetch Instructions -func (c CPUInfo) AVX512PF() bool { - return c.Features&AVX512PF != 0 -} - -// AVX512ER indicates support of AVX-512 Exponential and Reciprocal Instructions -func (c CPUInfo) AVX512ER() bool { - return c.Features&AVX512ER != 0 -} - -// AVX512CD indicates support of AVX-512 Conflict Detection Instructions -func (c CPUInfo) AVX512CD() bool { - return c.Features&AVX512CD != 0 -} - -// AVX512BW indicates support of AVX-512 Byte and Word Instructions -func (c CPUInfo) AVX512BW() bool { - return c.Features&AVX512BW != 0 -} - -// AVX512VL indicates support of AVX-512 Vector Length Extensions -func (c CPUInfo) AVX512VL() bool { - return c.Features&AVX512VL != 0 -} - -// AVX512VBMI indicates support of AVX-512 Vector Bit Manipulation Instructions -func (c CPUInfo) AVX512VBMI() bool { - return c.Features&AVX512VBMI != 0 -} - -// MPX indicates support of Intel MPX (Memory Protection Extensions) -func (c CPUInfo) MPX() bool { - return c.Features&MPX != 0 -} - -// ERMS indicates support of Enhanced REP MOVSB/STOSB -func (c CPUInfo) ERMS() bool { - return c.Features&ERMS != 0 -} - -// RDTSCP Instruction is available. -func (c CPUInfo) RDTSCP() bool { - return c.Features&RDTSCP != 0 -} - -// CX16 indicates if CMPXCHG16B instruction is available. -func (c CPUInfo) CX16() bool { - return c.Features&CX16 != 0 -} - -// TSX is split into HLE (Hardware Lock Elision) and RTM (Restricted Transactional Memory) detection. -// So TSX simply checks that. -func (c CPUInfo) TSX() bool { - return c.Features&(HLE|RTM) == HLE|RTM -} - -// Atom indicates an Atom processor -func (c CPUInfo) Atom() bool { - return c.Features&ATOM != 0 -} - -// Intel returns true if vendor is recognized as Intel -func (c CPUInfo) Intel() bool { - return c.VendorID == Intel -} - -// AMD returns true if vendor is recognized as AMD -func (c CPUInfo) AMD() bool { - return c.VendorID == AMD -} - -// Hygon returns true if vendor is recognized as Hygon -func (c CPUInfo) Hygon() bool { - return c.VendorID == Hygon -} - -// Transmeta returns true if vendor is recognized as Transmeta -func (c CPUInfo) Transmeta() bool { - return c.VendorID == Transmeta -} - -// NSC returns true if vendor is recognized as National Semiconductor -func (c CPUInfo) NSC() bool { - return c.VendorID == NSC -} - -// VIA returns true if vendor is recognized as VIA -func (c CPUInfo) VIA() bool { - return c.VendorID == VIA -} - -// RTCounter returns the 64-bit time-stamp counter -// Uses the RDTSCP instruction. The value 0 is returned -// if the CPU does not support the instruction. -func (c CPUInfo) RTCounter() uint64 { - if !c.RDTSCP() { - return 0 - } - a, _, _, d := rdtscpAsm() - return uint64(a) | (uint64(d) << 32) -} - -// Ia32TscAux returns the IA32_TSC_AUX part of the RDTSCP. -// This variable is OS dependent, but on Linux contains information -// about the current cpu/core the code is running on. -// If the RDTSCP instruction isn't supported on the CPU, the value 0 is returned. -func (c CPUInfo) Ia32TscAux() uint32 { - if !c.RDTSCP() { - return 0 - } - _, _, ecx, _ := rdtscpAsm() - return ecx -} - -// LogicalCPU will return the Logical CPU the code is currently executing on. -// This is likely to change when the OS re-schedules the running thread -// to another CPU. -// If the current core cannot be detected, -1 will be returned. -func (c CPUInfo) LogicalCPU() int { - if c.maxFunc < 1 { - return -1 - } - _, ebx, _, _ := cpuid(1) - return int(ebx >> 24) -} - -// VM Will return true if the cpu id indicates we are in -// a virtual machine. This is only a hint, and will very likely -// have many false negatives. -func (c CPUInfo) VM() bool { - switch c.VendorID { - case MSVM, KVM, VMware, XenHVM, Bhyve: - return true - } - return false -} - -// Flags contains detected cpu features and caracteristics -type Flags uint64 - -// String returns a string representation of the detected -// CPU features. -func (f Flags) String() string { - return strings.Join(f.Strings(), ",") -} - -// Strings returns and array of the detected features. -func (f Flags) Strings() []string { - s := support() - r := make([]string, 0, 20) - for i := uint(0); i < 64; i++ { - key := Flags(1 << i) - val := flagNames[key] - if s&key != 0 { - r = append(r, val) - } - } - return r -} - -func maxExtendedFunction() uint32 { - eax, _, _, _ := cpuid(0x80000000) - return eax -} - -func maxFunctionID() uint32 { - a, _, _, _ := cpuid(0) - return a -} - -func brandName() string { - if maxExtendedFunction() >= 0x80000004 { - v := make([]uint32, 0, 48) - for i := uint32(0); i < 3; i++ { - a, b, c, d := cpuid(0x80000002 + i) - v = append(v, a, b, c, d) - } - return strings.Trim(string(valAsString(v...)), " ") - } - return "unknown" -} - -func threadsPerCore() int { - mfi := maxFunctionID() - if mfi < 0x4 || vendorID() != Intel { - return 1 - } - - if mfi < 0xb { - _, b, _, d := cpuid(1) - if (d & (1 << 28)) != 0 { - // v will contain logical core count - v := (b >> 16) & 255 - if v > 1 { - a4, _, _, _ := cpuid(4) - // physical cores - v2 := (a4 >> 26) + 1 - if v2 > 0 { - return int(v) / int(v2) - } - } - } - return 1 - } - _, b, _, _ := cpuidex(0xb, 0) - if b&0xffff == 0 { - return 1 - } - return int(b & 0xffff) -} - -func logicalCores() int { - mfi := maxFunctionID() - switch vendorID() { - case Intel: - // Use this on old Intel processors - if mfi < 0xb { - if mfi < 1 { - return 0 - } - // CPUID.1:EBX[23:16] represents the maximum number of addressable IDs (initial APIC ID) - // that can be assigned to logical processors in a physical package. - // The value may not be the same as the number of logical processors that are present in the hardware of a physical package. - _, ebx, _, _ := cpuid(1) - logical := (ebx >> 16) & 0xff - return int(logical) - } - _, b, _, _ := cpuidex(0xb, 1) - return int(b & 0xffff) - case AMD, Hygon: - _, b, _, _ := cpuid(1) - return int((b >> 16) & 0xff) - default: - return 0 - } -} - -func familyModel() (int, int) { - if maxFunctionID() < 0x1 { - return 0, 0 - } - eax, _, _, _ := cpuid(1) - family := ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff) - model := ((eax >> 4) & 0xf) + ((eax >> 12) & 0xf0) - return int(family), int(model) -} - -func physicalCores() int { - switch vendorID() { - case Intel: - return logicalCores() / threadsPerCore() - case AMD, Hygon: - if maxExtendedFunction() >= 0x80000008 { - _, _, c, _ := cpuid(0x80000008) - return int(c&0xff) + 1 - } - } - return 0 -} - -// Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID -var vendorMapping = map[string]Vendor{ - "AMDisbetter!": AMD, - "AuthenticAMD": AMD, - "CentaurHauls": VIA, - "GenuineIntel": Intel, - "TransmetaCPU": Transmeta, - "GenuineTMx86": Transmeta, - "Geode by NSC": NSC, - "VIA VIA VIA ": VIA, - "KVMKVMKVMKVM": KVM, - "Microsoft Hv": MSVM, - "VMwareVMware": VMware, - "XenVMMXenVMM": XenHVM, - "bhyve bhyve ": Bhyve, - "HygonGenuine": Hygon, -} - -func vendorID() Vendor { - _, b, c, d := cpuid(0) - v := valAsString(b, d, c) - vend, ok := vendorMapping[string(v)] - if !ok { - return Other - } - return vend -} - -func cacheLine() int { - if maxFunctionID() < 0x1 { - return 0 - } - - _, ebx, _, _ := cpuid(1) - cache := (ebx & 0xff00) >> 5 // cflush size - if cache == 0 && maxExtendedFunction() >= 0x80000006 { - _, _, ecx, _ := cpuid(0x80000006) - cache = ecx & 0xff // cacheline size - } - // TODO: Read from Cache and TLB Information - return int(cache) -} - -func (c *CPUInfo) cacheSize() { - c.Cache.L1D = -1 - c.Cache.L1I = -1 - c.Cache.L2 = -1 - c.Cache.L3 = -1 - vendor := vendorID() - switch vendor { - case Intel: - if maxFunctionID() < 4 { - return - } - for i := uint32(0); ; i++ { - eax, ebx, ecx, _ := cpuidex(4, i) - cacheType := eax & 15 - if cacheType == 0 { - break - } - cacheLevel := (eax >> 5) & 7 - coherency := int(ebx&0xfff) + 1 - partitions := int((ebx>>12)&0x3ff) + 1 - associativity := int((ebx>>22)&0x3ff) + 1 - sets := int(ecx) + 1 - size := associativity * partitions * coherency * sets - switch cacheLevel { - case 1: - if cacheType == 1 { - // 1 = Data Cache - c.Cache.L1D = size - } else if cacheType == 2 { - // 2 = Instruction Cache - c.Cache.L1I = size - } else { - if c.Cache.L1D < 0 { - c.Cache.L1I = size - } - if c.Cache.L1I < 0 { - c.Cache.L1I = size - } - } - case 2: - c.Cache.L2 = size - case 3: - c.Cache.L3 = size - } - } - case AMD, Hygon: - // Untested. - if maxExtendedFunction() < 0x80000005 { - return - } - _, _, ecx, edx := cpuid(0x80000005) - c.Cache.L1D = int(((ecx >> 24) & 0xFF) * 1024) - c.Cache.L1I = int(((edx >> 24) & 0xFF) * 1024) - - if maxExtendedFunction() < 0x80000006 { - return - } - _, _, ecx, _ = cpuid(0x80000006) - c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024) - } - - return -} - -type SGXSupport struct { - Available bool - SGX1Supported bool - SGX2Supported bool - MaxEnclaveSizeNot64 int64 - MaxEnclaveSize64 int64 -} - -func hasSGX(available bool) (rval SGXSupport) { - rval.Available = available - - if !available { - return - } - - a, _, _, d := cpuidex(0x12, 0) - rval.SGX1Supported = a&0x01 != 0 - rval.SGX2Supported = a&0x02 != 0 - rval.MaxEnclaveSizeNot64 = 1 << (d & 0xFF) // pow 2 - rval.MaxEnclaveSize64 = 1 << ((d >> 8) & 0xFF) // pow 2 - - return -} - -func support() Flags { - mfi := maxFunctionID() - vend := vendorID() - if mfi < 0x1 { - return 0 - } - rval := uint64(0) - _, _, c, d := cpuid(1) - if (d & (1 << 15)) != 0 { - rval |= CMOV - } - if (d & (1 << 23)) != 0 { - rval |= MMX - } - if (d & (1 << 25)) != 0 { - rval |= MMXEXT - } - if (d & (1 << 25)) != 0 { - rval |= SSE - } - if (d & (1 << 26)) != 0 { - rval |= SSE2 - } - if (c & 1) != 0 { - rval |= SSE3 - } - if (c & 0x00000200) != 0 { - rval |= SSSE3 - } - if (c & 0x00080000) != 0 { - rval |= SSE4 - } - if (c & 0x00100000) != 0 { - rval |= SSE42 - } - if (c & (1 << 25)) != 0 { - rval |= AESNI - } - if (c & (1 << 1)) != 0 { - rval |= CLMUL - } - if c&(1<<23) != 0 { - rval |= POPCNT - } - if c&(1<<30) != 0 { - rval |= RDRAND - } - if c&(1<<29) != 0 { - rval |= F16C - } - if c&(1<<13) != 0 { - rval |= CX16 - } - if vend == Intel && (d&(1<<28)) != 0 && mfi >= 4 { - if threadsPerCore() > 1 { - rval |= HTT - } - } - - // Check XGETBV, OXSAVE and AVX bits - if c&(1<<26) != 0 && c&(1<<27) != 0 && c&(1<<28) != 0 { - // Check for OS support - eax, _ := xgetbv(0) - if (eax & 0x6) == 0x6 { - rval |= AVX - if (c & 0x00001000) != 0 { - rval |= FMA3 - } - } - } - - // Check AVX2, AVX2 requires OS support, but BMI1/2 don't. - if mfi >= 7 { - _, ebx, ecx, edx := cpuidex(7, 0) - if (rval&AVX) != 0 && (ebx&0x00000020) != 0 { - rval |= AVX2 - } - if (ebx & 0x00000008) != 0 { - rval |= BMI1 - if (ebx & 0x00000100) != 0 { - rval |= BMI2 - } - } - if ebx&(1<<2) != 0 { - rval |= SGX - } - if ebx&(1<<4) != 0 { - rval |= HLE - } - if ebx&(1<<9) != 0 { - rval |= ERMS - } - if ebx&(1<<11) != 0 { - rval |= RTM - } - if ebx&(1<<14) != 0 { - rval |= MPX - } - if ebx&(1<<18) != 0 { - rval |= RDSEED - } - if ebx&(1<<19) != 0 { - rval |= ADX - } - if ebx&(1<<29) != 0 { - rval |= SHA - } - if edx&(1<<26) != 0 { - rval |= IBPB - } - if edx&(1<<27) != 0 { - rval |= STIBP - } - - // Only detect AVX-512 features if XGETBV is supported - if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) { - // Check for OS support - eax, _ := xgetbv(0) - - // Verify that XCR0[7:5] = ‘111b’ (OPMASK state, upper 256-bit of ZMM0-ZMM15 and - // ZMM16-ZMM31 state are enabled by OS) - /// and that XCR0[2:1] = ‘11b’ (XMM state and YMM state are enabled by OS). - if (eax>>5)&7 == 7 && (eax>>1)&3 == 3 { - if ebx&(1<<16) != 0 { - rval |= AVX512F - } - if ebx&(1<<17) != 0 { - rval |= AVX512DQ - } - if ebx&(1<<21) != 0 { - rval |= AVX512IFMA - } - if ebx&(1<<26) != 0 { - rval |= AVX512PF - } - if ebx&(1<<27) != 0 { - rval |= AVX512ER - } - if ebx&(1<<28) != 0 { - rval |= AVX512CD - } - if ebx&(1<<30) != 0 { - rval |= AVX512BW - } - if ebx&(1<<31) != 0 { - rval |= AVX512VL - } - // ecx - if ecx&(1<<1) != 0 { - rval |= AVX512VBMI - } - } - } - } - - if maxExtendedFunction() >= 0x80000001 { - _, _, c, d := cpuid(0x80000001) - if (c & (1 << 5)) != 0 { - rval |= LZCNT - rval |= POPCNT - } - if (d & (1 << 31)) != 0 { - rval |= AMD3DNOW - } - if (d & (1 << 30)) != 0 { - rval |= AMD3DNOWEXT - } - if (d & (1 << 23)) != 0 { - rval |= MMX - } - if (d & (1 << 22)) != 0 { - rval |= MMXEXT - } - if (c & (1 << 6)) != 0 { - rval |= SSE4A - } - if d&(1<<20) != 0 { - rval |= NX - } - if d&(1<<27) != 0 { - rval |= RDTSCP - } - - /* Allow for selectively disabling SSE2 functions on AMD processors - with SSE2 support but not SSE4a. This includes Athlon64, some - Opteron, and some Sempron processors. MMX, SSE, or 3DNow! are faster - than SSE2 often enough to utilize this special-case flag. - AV_CPU_FLAG_SSE2 and AV_CPU_FLAG_SSE2SLOW are both set in this case - so that SSE2 is used unless explicitly disabled by checking - AV_CPU_FLAG_SSE2SLOW. */ - if vendorID() != Intel && - rval&SSE2 != 0 && (c&0x00000040) == 0 { - rval |= SSE2SLOW - } - - /* XOP and FMA4 use the AVX instruction coding scheme, so they can't be - * used unless the OS has AVX support. */ - if (rval & AVX) != 0 { - if (c & 0x00000800) != 0 { - rval |= XOP - } - if (c & 0x00010000) != 0 { - rval |= FMA4 - } - } - - if vendorID() == Intel { - family, model := familyModel() - if family == 6 && (model == 9 || model == 13 || model == 14) { - /* 6/9 (pentium-m "banias"), 6/13 (pentium-m "dothan"), and - * 6/14 (core1 "yonah") theoretically support sse2, but it's - * usually slower than mmx. */ - if (rval & SSE2) != 0 { - rval |= SSE2SLOW - } - if (rval & SSE3) != 0 { - rval |= SSE3SLOW - } - } - /* The Atom processor has SSSE3 support, which is useful in many cases, - * but sometimes the SSSE3 version is slower than the SSE2 equivalent - * on the Atom, but is generally faster on other processors supporting - * SSSE3. This flag allows for selectively disabling certain SSSE3 - * functions on the Atom. */ - if family == 6 && model == 28 { - rval |= ATOM - } - } - } - return Flags(rval) -} - -func valAsString(values ...uint32) []byte { - r := make([]byte, 4*len(values)) - for i, v := range values { - dst := r[i*4:] - dst[0] = byte(v & 0xff) - dst[1] = byte((v >> 8) & 0xff) - dst[2] = byte((v >> 16) & 0xff) - dst[3] = byte((v >> 24) & 0xff) - switch { - case dst[0] == 0: - return r[:i*4] - case dst[1] == 0: - return r[:i*4+1] - case dst[2] == 0: - return r[:i*4+2] - case dst[3] == 0: - return r[:i*4+3] - } - } - return r -} diff --git a/vendor/github.com/klauspost/cpuid/cpuid_386.s b/vendor/github.com/klauspost/cpuid/cpuid_386.s deleted file mode 100644 index 4d731711e..000000000 --- a/vendor/github.com/klauspost/cpuid/cpuid_386.s +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -// +build 386,!gccgo - -// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) -TEXT ·asmCpuid(SB), 7, $0 - XORL CX, CX - MOVL op+0(FP), AX - CPUID - MOVL AX, eax+4(FP) - MOVL BX, ebx+8(FP) - MOVL CX, ecx+12(FP) - MOVL DX, edx+16(FP) - RET - -// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) -TEXT ·asmCpuidex(SB), 7, $0 - MOVL op+0(FP), AX - MOVL op2+4(FP), CX - CPUID - MOVL AX, eax+8(FP) - MOVL BX, ebx+12(FP) - MOVL CX, ecx+16(FP) - MOVL DX, edx+20(FP) - RET - -// func xgetbv(index uint32) (eax, edx uint32) -TEXT ·asmXgetbv(SB), 7, $0 - MOVL index+0(FP), CX - BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV - MOVL AX, eax+4(FP) - MOVL DX, edx+8(FP) - RET - -// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) -TEXT ·asmRdtscpAsm(SB), 7, $0 - BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP - MOVL AX, eax+0(FP) - MOVL BX, ebx+4(FP) - MOVL CX, ecx+8(FP) - MOVL DX, edx+12(FP) - RET diff --git a/vendor/github.com/klauspost/cpuid/cpuid_amd64.s b/vendor/github.com/klauspost/cpuid/cpuid_amd64.s deleted file mode 100644 index 3c1d60e42..000000000 --- a/vendor/github.com/klauspost/cpuid/cpuid_amd64.s +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -//+build amd64,!gccgo - -// func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) -TEXT ·asmCpuid(SB), 7, $0 - XORQ CX, CX - MOVL op+0(FP), AX - CPUID - MOVL AX, eax+8(FP) - MOVL BX, ebx+12(FP) - MOVL CX, ecx+16(FP) - MOVL DX, edx+20(FP) - RET - -// func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) -TEXT ·asmCpuidex(SB), 7, $0 - MOVL op+0(FP), AX - MOVL op2+4(FP), CX - CPUID - MOVL AX, eax+8(FP) - MOVL BX, ebx+12(FP) - MOVL CX, ecx+16(FP) - MOVL DX, edx+20(FP) - RET - -// func asmXgetbv(index uint32) (eax, edx uint32) -TEXT ·asmXgetbv(SB), 7, $0 - MOVL index+0(FP), CX - BYTE $0x0f; BYTE $0x01; BYTE $0xd0 // XGETBV - MOVL AX, eax+8(FP) - MOVL DX, edx+12(FP) - RET - -// func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) -TEXT ·asmRdtscpAsm(SB), 7, $0 - BYTE $0x0F; BYTE $0x01; BYTE $0xF9 // RDTSCP - MOVL AX, eax+0(FP) - MOVL BX, ebx+4(FP) - MOVL CX, ecx+8(FP) - MOVL DX, edx+12(FP) - RET diff --git a/vendor/github.com/klauspost/cpuid/detect_intel.go b/vendor/github.com/klauspost/cpuid/detect_intel.go deleted file mode 100644 index a5f04dd6d..000000000 --- a/vendor/github.com/klauspost/cpuid/detect_intel.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -// +build 386,!gccgo amd64,!gccgo - -package cpuid - -func asmCpuid(op uint32) (eax, ebx, ecx, edx uint32) -func asmCpuidex(op, op2 uint32) (eax, ebx, ecx, edx uint32) -func asmXgetbv(index uint32) (eax, edx uint32) -func asmRdtscpAsm() (eax, ebx, ecx, edx uint32) - -func initCPU() { - cpuid = asmCpuid - cpuidex = asmCpuidex - xgetbv = asmXgetbv - rdtscpAsm = asmRdtscpAsm -} diff --git a/vendor/github.com/klauspost/cpuid/detect_ref.go b/vendor/github.com/klauspost/cpuid/detect_ref.go deleted file mode 100644 index 909c5d9a7..000000000 --- a/vendor/github.com/klauspost/cpuid/detect_ref.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2015 Klaus Post, released under MIT License. See LICENSE file. - -// +build !amd64,!386 gccgo - -package cpuid - -func initCPU() { - cpuid = func(op uint32) (eax, ebx, ecx, edx uint32) { - return 0, 0, 0, 0 - } - - cpuidex = func(op, op2 uint32) (eax, ebx, ecx, edx uint32) { - return 0, 0, 0, 0 - } - - xgetbv = func(index uint32) (eax, edx uint32) { - return 0, 0 - } - - rdtscpAsm = func() (eax, ebx, ecx, edx uint32) { - return 0, 0, 0, 0 - } -} diff --git a/vendor/github.com/klauspost/cpuid/generate.go b/vendor/github.com/klauspost/cpuid/generate.go deleted file mode 100644 index 90e7a98d2..000000000 --- a/vendor/github.com/klauspost/cpuid/generate.go +++ /dev/null @@ -1,4 +0,0 @@ -package cpuid - -//go:generate go run private-gen.go -//go:generate gofmt -w ./private diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/clientset_generated.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 000000000..3b341cde9 --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,82 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned" + appv1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1" + fakeappv1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/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{tracker: o} + 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 + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// AppV1beta1 retrieves the AppV1beta1Client +func (c *Clientset) AppV1beta1() appv1beta1.AppV1beta1Interface { + return &fakeappv1beta1.FakeAppV1beta1{Fake: &c.Fake} +} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/doc.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/doc.go new file mode 100644 index 000000000..5bf835605 --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/register.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/register.go new file mode 100644 index 000000000..3c2e65914 --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +package fake + +import ( + appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + 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{ + appv1beta1.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/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/doc.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/doc.go new file mode 100644 index 000000000..2f80c73bd --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_app_client.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_app_client.go new file mode 100644 index 000000000..70909506c --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_app_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +package fake + +import ( + v1beta1 "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeAppV1beta1 struct { + *testing.Fake +} + +func (c *FakeAppV1beta1) Applications(namespace string) v1beta1.ApplicationInterface { + return &FakeApplications{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeAppV1beta1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_application.go b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_application.go new file mode 100644 index 000000000..b5c74ce67 --- /dev/null +++ b/vendor/github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/typed/app/v1beta1/fake/fake_application.go @@ -0,0 +1,140 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by main. DO NOT EDIT. + +package fake + +import ( + v1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" + 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" +) + +// FakeApplications implements ApplicationInterface +type FakeApplications struct { + Fake *FakeAppV1beta1 + ns string +} + +var applicationsResource = schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"} + +var applicationsKind = schema.GroupVersionKind{Group: "app.k8s.io", Version: "v1beta1", Kind: "Application"} + +// Get takes name of the application, and returns the corresponding application object, and an error if there is any. +func (c *FakeApplications) Get(name string, options v1.GetOptions) (result *v1beta1.Application, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(applicationsResource, c.ns, name), &v1beta1.Application{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.Application), err +} + +// List takes label and field selectors, and returns the list of Applications that match those selectors. +func (c *FakeApplications) List(opts v1.ListOptions) (result *v1beta1.ApplicationList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(applicationsResource, applicationsKind, c.ns, opts), &v1beta1.ApplicationList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta1.ApplicationList{ListMeta: obj.(*v1beta1.ApplicationList).ListMeta} + for _, item := range obj.(*v1beta1.ApplicationList).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 applications. +func (c *FakeApplications) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(applicationsResource, c.ns, opts)) + +} + +// Create takes the representation of a application and creates it. Returns the server's representation of the application, and an error, if there is any. +func (c *FakeApplications) Create(application *v1beta1.Application) (result *v1beta1.Application, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(applicationsResource, c.ns, application), &v1beta1.Application{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.Application), err +} + +// Update takes the representation of a application and updates it. Returns the server's representation of the application, and an error, if there is any. +func (c *FakeApplications) Update(application *v1beta1.Application) (result *v1beta1.Application, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(applicationsResource, c.ns, application), &v1beta1.Application{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.Application), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeApplications) UpdateStatus(application *v1beta1.Application) (*v1beta1.Application, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(applicationsResource, "status", c.ns, application), &v1beta1.Application{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.Application), err +} + +// Delete takes name of the application and deletes it. Returns an error if one occurs. +func (c *FakeApplications) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(applicationsResource, c.ns, name), &v1beta1.Application{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeApplications) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(applicationsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1beta1.ApplicationList{}) + return err +} + +// Patch applies the patch and returns the patched application. +func (c *FakeApplications) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1beta1.Application, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(applicationsResource, c.ns, name, pt, data, subresources...), &v1beta1.Application{}) + + if obj == nil { + return nil, err + } + return obj.(*v1beta1.Application), err +} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/controller/application/application_controller.go b/vendor/github.com/kubernetes-sigs/application/pkg/controller/application/application_controller.go deleted file mode 100644 index 3cec2d4ae..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/controller/application/application_controller.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package application - -import ( - appv1beta1 "github.com/kubernetes-sigs/application/pkg/apis/app/v1beta1" - reconciler "github.com/kubernetes-sigs/application/pkg/genericreconciler" - kbc "github.com/kubernetes-sigs/application/pkg/kbcontroller" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// Constants -const ( - NameLabelKey = "app.kubernetes.io/name" - VersionLabelKey = "app.kubernetes.io/version" - InstanceLabelKey = "app.kubernetes.io/instance" - PartOfLabelKey = "app.kubernetes.io/part-of" - ComponentLabelKey = "app.kubernetes.io/component" - ManagedByLabelKey = "app.kubernetes.io/managed-by" -) - -// Add creates a new Application Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller -// and Start it when the Manager is Started. -func Add(mgr manager.Manager) error { - return kbc.CreateController("application", mgr, &appv1beta1.Application{}, newReconciler(mgr)) -} - -// newReconciler returns a new reconcile.Reconciler -func newReconciler(mgr manager.Manager) reconcile.Reconciler { - r := &reconciler.Reconciler{ - Manager: mgr, // why do we need manager ? - Handle: &appv1beta1.Application{}, - } - r.Init() - return r -} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/doc.go b/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/doc.go deleted file mode 100644 index 45d7f7c35..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package genericreconciler contains generic reconciler loop -package genericreconciler diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/genericreconciler.go b/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/genericreconciler.go deleted file mode 100644 index f6029ae22..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/genericreconciler.go +++ /dev/null @@ -1,335 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package genericreconciler - -import ( - "context" - "fmt" - "github.com/kubernetes-sigs/application/pkg/component" - cr "github.com/kubernetes-sigs/application/pkg/customresource" - "github.com/kubernetes-sigs/application/pkg/resource" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - urt "k8s.io/apimachinery/pkg/util/runtime" - "log" - "reflect" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -func handleErrorArr(info string, name string, e error, errs []error) []error { - HandleError(info, name, e) - return append(errs, e) -} - -// HandleError common error handling routine -func HandleError(info string, name string, e error) error { - urt.HandleError(fmt.Errorf("Failed: [%s] %s. %s", name, info, e.Error())) - return e -} - -func (gr *Reconciler) observe(observables ...resource.Observable) (*resource.ObjectBag, error) { - var returnval = new(resource.ObjectBag) - var err error - for _, obs := range observables { - var resources []resource.Object - if obs.Labels != nil { - opts := client.ListOptions{ - LabelSelector: labels.SelectorFromSet(obs.Labels), - } - opts.Raw = &metav1.ListOptions{TypeMeta: obs.Type} - err = gr.List(context.TODO(), obs.ObjList.(runtime.Object), &opts) - if err == nil { - items, err := meta.ExtractList(obs.ObjList.(runtime.Object)) - if err == nil { - for _, item := range items { - resources = append(resources, resource.Object{Obj: item.(metav1.Object)}) - } - } - } - } else { - var obj = obs.Obj.(metav1.Object) - name := obj.GetName() - namespace := obj.GetNamespace() - otype := reflect.TypeOf(obj).String() - err = gr.Get(context.TODO(), - types.NamespacedName{Name: name, Namespace: namespace}, - obs.Obj.(runtime.Object)) - if err == nil { - resources = append(resources, resource.Object{Obj: obs.Obj}) - } else { - log.Printf(" >>>ERR get: %s", otype+"/"+namespace+"/"+name) - } - } - if err != nil { - return nil, err - } - for _, resource := range resources { - returnval.Add(resource) - } - } - return returnval, nil -} - -func specDiffers(o1, o2 metav1.Object) bool { - // Not all k8s objects have Spec - // example ConfigMap - // TODO strategic merge patch diff in generic controller loop - e := reflect.Indirect(reflect.ValueOf(o1)).FieldByName("Spec") - o := reflect.Indirect(reflect.ValueOf(o2)).FieldByName("Spec") - if !e.IsValid() { - // handling ConfigMap - e = reflect.Indirect(reflect.ValueOf(o1)).FieldByName("Data") - o = reflect.Indirect(reflect.ValueOf(o2)).FieldByName("Data") - } - - if e.IsValid() && o.IsValid() { - if reflect.DeepEqual(e.Interface(), o.Interface()) { - return false - } - } - return true -} - -// If both ownerRefs have the same group/kind/name but different uid, that means at least one of them doesn't exist anymore. -// If we compare `uid` in this function, we'd set both as owners which is not what we want -// Because in the case that the original owner is already gone, we want its dependent to be garbage collected with it. -func isReferringSameObject(a, b metav1.OwnerReference) bool { - aGV, err := schema.ParseGroupVersion(a.APIVersion) - if err != nil { - return false - } - bGV, err := schema.ParseGroupVersion(b.APIVersion) - if err != nil { - return false - } - return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name -} - -func injectOwnerRefs(o metav1.Object, ref *metav1.OwnerReference) bool { - if ref == nil { - return false - } - objRefs := o.GetOwnerReferences() - for _, r := range objRefs { - if isReferringSameObject(*ref, r) { - return false - } - } - objRefs = append(objRefs, *ref) - o.SetOwnerReferences(objRefs) - return true -} - -// ReconcileCR is a generic function that reconciles expected and observed resources -func (gr *Reconciler) ReconcileCR(namespacedname types.NamespacedName, handle cr.Handle) error { - var status interface{} - expected := &resource.ObjectBag{} - update := false - rsrc := handle.NewRsrc() - name := reflect.TypeOf(rsrc).String() + "/" + namespacedname.String() - err := gr.Get(context.TODO(), namespacedname, rsrc.(runtime.Object)) - if err == nil { - o := rsrc.(metav1.Object) - err = rsrc.Validate() - status = rsrc.NewStatus() - if err == nil { - rsrc.ApplyDefaults() - components := rsrc.Components() - for _, component := range components { - if o.GetDeletionTimestamp() == nil { - err = gr.ReconcileComponent(name, component, status, expected) - } else { - err = gr.FinalizeComponent(name, component, status, expected) - } - } - } - } else { - if errors.IsNotFound(err) { - urt.HandleError(fmt.Errorf("not found %s. %s", name, err.Error())) - return nil - } - } - update = rsrc.UpdateRsrcStatus(status, err) - - if update { - err = gr.Update(context.TODO(), rsrc.(runtime.Object)) - } - if err != nil { - urt.HandleError(fmt.Errorf("error updating %s. %s", name, err.Error())) - } - - return err -} - -// ObserveAndMutate is a function that is called to observe and mutate expected resources -func (gr *Reconciler) ObserveAndMutate(crname string, c component.Component, status interface{}, mutate bool, aggregated *resource.ObjectBag) (*resource.ObjectBag, *resource.ObjectBag, error) { - var err error - var expected, observed, dependent *resource.ObjectBag - emptybag := &resource.ObjectBag{} - - // Get dependenta objects - dependent, err = gr.observe(resource.ObservablesFromObjects(gr.Scheme, c.DependentResources(c.CR), c.Labels())...) - if err != nil { - return emptybag, emptybag, fmt.Errorf("Failed getting dependent resources: %s", err.Error()) - } - - // Get Expected resources - expected, err = c.ExpectedResources(c.CR, c.Labels(), dependent, aggregated) - if err != nil { - return emptybag, emptybag, fmt.Errorf("Failed gathering expected resources: %s", err.Error()) - } - - // Get observables - observables := c.Observables(gr.Scheme, c.CR, c.Labels(), expected) - - // Observe observables - observed, err = gr.observe(observables...) - if err != nil { - return emptybag, emptybag, fmt.Errorf("Failed observing resources: %s", err.Error()) - } - - // Mutate expected objects - if mutate { - expected, err = c.Mutate(c.CR, c.Labels(), status, expected, dependent, observed) - if err != nil { - return emptybag, emptybag, fmt.Errorf("Failed mutating resources: %s", err.Error()) - } - - // Get observables - observables := c.Observables(gr.Scheme, c.CR, c.Labels(), expected) - - // Observe observables - observed, err = gr.observe(observables...) - if err != nil { - return emptybag, emptybag, fmt.Errorf("Failed observing resources after mutation: %s", err.Error()) - } - } - - return expected, observed, err -} - -// FinalizeComponent is a function that finalizes component -func (gr *Reconciler) FinalizeComponent(crname string, c component.Component, status interface{}, aggregated *resource.ObjectBag) error { - cname := crname + "(cmpnt:" + c.Name + ")" - log.Printf("%s finalizing component\n", cname) - defer log.Printf("%s finalizing component completed", cname) - - expected, observed, err := gr.ObserveAndMutate(crname, c, status, false, aggregated) - - if err != nil { - HandleError("", crname, err) - } - aggregated.Add(expected.Items()...) - err = c.Finalize(c.CR, status, observed) - return err -} - -// ReconcileComponent is a generic function that reconciles expected and observed resources -func (gr *Reconciler) ReconcileComponent(crname string, c component.Component, status interface{}, aggregated *resource.ObjectBag) error { - errs := []error{} - var reconciled *resource.ObjectBag = new(resource.ObjectBag) - - cname := crname + "(cmpnt:" + c.Name + ")" - log.Printf("%s reconciling component\n", cname) - defer log.Printf("%s reconciling component completed\n", cname) - - expected, observed, err := gr.ObserveAndMutate(crname, c, status, true, aggregated) - - // Reconciliation logic is straight-forward: - // This method gets the list of expected resources and observed resources - // We compare the 2 lists and: - // create(rsrc) where rsrc is in expected but not in observed - // delete(rsrc) where rsrc is in observed but not in expected - // update(rsrc) where rsrc is in observed and expected - // - // We have a notion of Managed and Referred resources - // Only Managed resources are CRUD'd - // Missing Reffered resources are treated as errors and surfaced as such in the status field - // - - if err != nil { - errs = handleErrorArr("", crname, err, errs) - } else { - aggregated.Add(expected.Items()...) - } - - for _, e := range expected.Items() { - seen := false - eNamespace := e.Obj.GetNamespace() - eName := e.Obj.GetName() - eKind := reflect.TypeOf(e.Obj).String() - eRsrcInfo := eNamespace + "/" + eKind + "/" + eName - for _, o := range observed.Items() { - if (eName != o.Obj.GetName()) || (eNamespace != o.Obj.GetNamespace()) || - (eKind != reflect.TypeOf(o.Obj).String()) { - continue - } - // rsrc is seen in both expected and observed, update it if needed - e.Obj.SetResourceVersion(o.Obj.GetResourceVersion()) - e.Obj.SetOwnerReferences(o.Obj.GetOwnerReferences()) - if e.Lifecycle == resource.LifecycleManaged && (specDiffers(e.Obj, o.Obj) && c.Differs(e.Obj, o.Obj) || injectOwnerRefs(e.Obj, c.OwnerRef)) { - if err := gr.Update(context.TODO(), e.Obj.(runtime.Object).DeepCopyObject()); err != nil { - errs = handleErrorArr("update", eRsrcInfo, err, errs) - } - } - reconciled.Add(o) - seen = true - break - } - // rsrc is in expected but not in observed - create - if !seen { - if e.Lifecycle == resource.LifecycleManaged { - injectOwnerRefs(e.Obj, c.OwnerRef) - if err := gr.Create(context.TODO(), e.Obj.(runtime.Object)); err != nil { - errs = handleErrorArr("Create", cname, err, errs) - } else { - reconciled.Add(e) - } - } else { - err := fmt.Errorf("missing resource not managed by %s: %s", cname, eRsrcInfo) - errs = handleErrorArr("missing resource", cname, err, errs) - } - } - } - - err = utilerrors.NewAggregate(errs) - c.UpdateComponentStatus(c.CR, status, reconciled, err) - return err -} - -// Reconcile expected by kubebuilder -func (gr *Reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { - err := gr.ReconcileCR(request.NamespacedName, gr.Handle) - if err != nil { - fmt.Printf("err: %s", err.Error()) - } - return reconcile.Result{}, err -} - -// AddToSchemes for adding Application to scheme -var AddToSchemes runtime.SchemeBuilder - -// Init sets up Reconciler -func (gr *Reconciler) Init() { - gr.Client = gr.Manager.GetClient() - gr.Scheme = gr.Manager.GetScheme() - AddToSchemes.AddToScheme(gr.Scheme) -} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/types.go b/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/types.go deleted file mode 100644 index dc5618810..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/types.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package genericreconciler - -import ( - cr "github.com/kubernetes-sigs/application/pkg/customresource" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -var _ reconcile.Reconciler = &Reconciler{} - -// Reconciler defines fields needed for all airflow controllers -// +k8s:deepcopy-gen=false -type Reconciler struct { - client.Client - Scheme *runtime.Scheme - Handle cr.Handle - Manager manager.Manager -} - -// ReconcilerConfig config defines reconciler parameters -// +k8s:deepcopy-gen=false -type ReconcilerConfig struct { -} - -// KVmap is a map[string]string -type KVmap map[string]string diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/utils.go b/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/utils.go deleted file mode 100644 index ecc363264..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/genericreconciler/utils.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package genericreconciler - -// Merge is used to merge multiple maps into the target map -func (out KVmap) Merge(kvmaps ...KVmap) { - for _, kvmap := range kvmaps { - for k, v := range kvmap { - out[k] = v - } - } -} diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/doc.go b/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/doc.go deleted file mode 100644 index 3dc5cb172..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/doc.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package kbcontroller contains methods to integrate with kube-builder manager -package kbcontroller diff --git a/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/kbcontroller.go b/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/kbcontroller.go deleted file mode 100644 index eaa546306..000000000 --- a/vendor/github.com/kubernetes-sigs/application/pkg/kbcontroller/kbcontroller.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kbcontroller - -import ( - cr "github.com/kubernetes-sigs/application/pkg/customresource" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -// CreateController creates a new Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller and Start it when the Manager is Started. -func CreateController(name string, mgr manager.Manager, handle cr.Handle, r reconcile.Reconciler) error { - // Create a new controller - c, err := controller.New(name+"-ctrl", mgr, controller.Options{Reconciler: r}) - if err != nil { - return err - } - - // Watch for changes to Base resource - err = c.Watch(&source.Kind{Type: handle.NewRsrc().(runtime.Object)}, - &handler.EnqueueRequestForObject{}) - if err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/code_framework.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/code_framework.go deleted file mode 100644 index 77a48e73a..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/code_framework.go +++ /dev/null @@ -1,12 +0,0 @@ -package v1alpha1 - -type CodeFramework string - -const ( - Ruby CodeFramework = "ruby" - Go CodeFramework = "go" - Java CodeFramework = "Java" - JavaTomcat CodeFramework = "JavaTomcat" - Nodejs CodeFramework = "Nodejs" - Python CodeFramework = "python" -) diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/doc.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/doc.go deleted file mode 100644 index af3e3591a..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 v1alpha1 contains API Schema definitions for the devops v1alpha1 API group -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen=package,register -// +k8s:conversion-gen=github.com/kubesphere/s2ioperator/pkg/apis/devops -// +k8s:defaulter-gen=TypeMeta -// +groupName=devops.kubesphere.io -package v1alpha1 diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/openapi_generated.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/openapi_generated.go deleted file mode 100644 index 70007c03d..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/openapi_generated.go +++ /dev/null @@ -1,15134 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2019 The Kubesphere 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 openapi-gen. DO NOT EDIT. - -// This file was autogenerated by openapi-gen. Do not edit it manually! - -package v1alpha1 - -import ( - spec "github.com/go-openapi/spec" - resource "k8s.io/apimachinery/pkg/api/resource" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - intstr "k8s.io/apimachinery/pkg/util/intstr" - common "k8s.io/kube-openapi/pkg/common" -) - -func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { - return map[string]common.OpenAPIDefinition{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig": schema_pkg_apis_devops_v1alpha1_AuthConfig(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.CGroupLimits": schema_pkg_apis_devops_v1alpha1_CGroupLimits(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ContainerConfig": schema_pkg_apis_devops_v1alpha1_ContainerConfig(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ContainerInfo": schema_pkg_apis_devops_v1alpha1_ContainerInfo(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfig": schema_pkg_apis_devops_v1alpha1_DockerConfig(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfigEntry": schema_pkg_apis_devops_v1alpha1_DockerConfigEntry(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfigJson": schema_pkg_apis_devops_v1alpha1_DockerConfigJson(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.EnvironmentSpec": schema_pkg_apis_devops_v1alpha1_EnvironmentSpec(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.Parameter": schema_pkg_apis_devops_v1alpha1_Parameter(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ProxyConfig": schema_pkg_apis_devops_v1alpha1_ProxyConfig(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iAutoScale": schema_pkg_apis_devops_v1alpha1_S2iAutoScale(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildResult": schema_pkg_apis_devops_v1alpha1_S2iBuildResult(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildSource": schema_pkg_apis_devops_v1alpha1_S2iBuildSource(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilder": schema_pkg_apis_devops_v1alpha1_S2iBuilder(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderList": schema_pkg_apis_devops_v1alpha1_S2iBuilderList(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderSpec": schema_pkg_apis_devops_v1alpha1_S2iBuilderSpec(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderStatus": schema_pkg_apis_devops_v1alpha1_S2iBuilderStatus(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplate": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplate(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateList": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateList(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateSpec(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus": schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateStatus(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iConfig": schema_pkg_apis_devops_v1alpha1_S2iConfig(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRun": schema_pkg_apis_devops_v1alpha1_S2iRun(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunList": schema_pkg_apis_devops_v1alpha1_S2iRunList(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunSpec": schema_pkg_apis_devops_v1alpha1_S2iRunSpec(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunStatus": schema_pkg_apis_devops_v1alpha1_S2iRunStatus(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.UserDefineTemplate": schema_pkg_apis_devops_v1alpha1_UserDefineTemplate(ref), - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec": schema_pkg_apis_devops_v1alpha1_VolumeSpec(ref), - "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource": schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref), - "k8s.io/api/core/v1.Affinity": schema_k8sio_api_core_v1_Affinity(ref), - "k8s.io/api/core/v1.AttachedVolume": schema_k8sio_api_core_v1_AttachedVolume(ref), - "k8s.io/api/core/v1.AvoidPods": schema_k8sio_api_core_v1_AvoidPods(ref), - "k8s.io/api/core/v1.AzureDiskVolumeSource": schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref), - "k8s.io/api/core/v1.AzureFilePersistentVolumeSource": schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref), - "k8s.io/api/core/v1.AzureFileVolumeSource": schema_k8sio_api_core_v1_AzureFileVolumeSource(ref), - "k8s.io/api/core/v1.Binding": schema_k8sio_api_core_v1_Binding(ref), - "k8s.io/api/core/v1.CSIPersistentVolumeSource": schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref), - "k8s.io/api/core/v1.CSIVolumeSource": schema_k8sio_api_core_v1_CSIVolumeSource(ref), - "k8s.io/api/core/v1.Capabilities": schema_k8sio_api_core_v1_Capabilities(ref), - "k8s.io/api/core/v1.CephFSPersistentVolumeSource": schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref), - "k8s.io/api/core/v1.CephFSVolumeSource": schema_k8sio_api_core_v1_CephFSVolumeSource(ref), - "k8s.io/api/core/v1.CinderPersistentVolumeSource": schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref), - "k8s.io/api/core/v1.CinderVolumeSource": schema_k8sio_api_core_v1_CinderVolumeSource(ref), - "k8s.io/api/core/v1.ClientIPConfig": schema_k8sio_api_core_v1_ClientIPConfig(ref), - "k8s.io/api/core/v1.ComponentCondition": schema_k8sio_api_core_v1_ComponentCondition(ref), - "k8s.io/api/core/v1.ComponentStatus": schema_k8sio_api_core_v1_ComponentStatus(ref), - "k8s.io/api/core/v1.ComponentStatusList": schema_k8sio_api_core_v1_ComponentStatusList(ref), - "k8s.io/api/core/v1.ConfigMap": schema_k8sio_api_core_v1_ConfigMap(ref), - "k8s.io/api/core/v1.ConfigMapEnvSource": schema_k8sio_api_core_v1_ConfigMapEnvSource(ref), - "k8s.io/api/core/v1.ConfigMapKeySelector": schema_k8sio_api_core_v1_ConfigMapKeySelector(ref), - "k8s.io/api/core/v1.ConfigMapList": schema_k8sio_api_core_v1_ConfigMapList(ref), - "k8s.io/api/core/v1.ConfigMapNodeConfigSource": schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref), - "k8s.io/api/core/v1.ConfigMapProjection": schema_k8sio_api_core_v1_ConfigMapProjection(ref), - "k8s.io/api/core/v1.ConfigMapVolumeSource": schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref), - "k8s.io/api/core/v1.Container": schema_k8sio_api_core_v1_Container(ref), - "k8s.io/api/core/v1.ContainerImage": schema_k8sio_api_core_v1_ContainerImage(ref), - "k8s.io/api/core/v1.ContainerPort": schema_k8sio_api_core_v1_ContainerPort(ref), - "k8s.io/api/core/v1.ContainerState": schema_k8sio_api_core_v1_ContainerState(ref), - "k8s.io/api/core/v1.ContainerStateRunning": schema_k8sio_api_core_v1_ContainerStateRunning(ref), - "k8s.io/api/core/v1.ContainerStateTerminated": schema_k8sio_api_core_v1_ContainerStateTerminated(ref), - "k8s.io/api/core/v1.ContainerStateWaiting": schema_k8sio_api_core_v1_ContainerStateWaiting(ref), - "k8s.io/api/core/v1.ContainerStatus": schema_k8sio_api_core_v1_ContainerStatus(ref), - "k8s.io/api/core/v1.DaemonEndpoint": schema_k8sio_api_core_v1_DaemonEndpoint(ref), - "k8s.io/api/core/v1.DownwardAPIProjection": schema_k8sio_api_core_v1_DownwardAPIProjection(ref), - "k8s.io/api/core/v1.DownwardAPIVolumeFile": schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref), - "k8s.io/api/core/v1.DownwardAPIVolumeSource": schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref), - "k8s.io/api/core/v1.EmptyDirVolumeSource": schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref), - "k8s.io/api/core/v1.EndpointAddress": schema_k8sio_api_core_v1_EndpointAddress(ref), - "k8s.io/api/core/v1.EndpointPort": schema_k8sio_api_core_v1_EndpointPort(ref), - "k8s.io/api/core/v1.EndpointSubset": schema_k8sio_api_core_v1_EndpointSubset(ref), - "k8s.io/api/core/v1.Endpoints": schema_k8sio_api_core_v1_Endpoints(ref), - "k8s.io/api/core/v1.EndpointsList": schema_k8sio_api_core_v1_EndpointsList(ref), - "k8s.io/api/core/v1.EnvFromSource": schema_k8sio_api_core_v1_EnvFromSource(ref), - "k8s.io/api/core/v1.EnvVar": schema_k8sio_api_core_v1_EnvVar(ref), - "k8s.io/api/core/v1.EnvVarSource": schema_k8sio_api_core_v1_EnvVarSource(ref), - "k8s.io/api/core/v1.EphemeralContainer": schema_k8sio_api_core_v1_EphemeralContainer(ref), - "k8s.io/api/core/v1.EphemeralContainerCommon": schema_k8sio_api_core_v1_EphemeralContainerCommon(ref), - "k8s.io/api/core/v1.EphemeralContainers": schema_k8sio_api_core_v1_EphemeralContainers(ref), - "k8s.io/api/core/v1.Event": schema_k8sio_api_core_v1_Event(ref), - "k8s.io/api/core/v1.EventList": schema_k8sio_api_core_v1_EventList(ref), - "k8s.io/api/core/v1.EventSeries": schema_k8sio_api_core_v1_EventSeries(ref), - "k8s.io/api/core/v1.EventSource": schema_k8sio_api_core_v1_EventSource(ref), - "k8s.io/api/core/v1.ExecAction": schema_k8sio_api_core_v1_ExecAction(ref), - "k8s.io/api/core/v1.FCVolumeSource": schema_k8sio_api_core_v1_FCVolumeSource(ref), - "k8s.io/api/core/v1.FlexPersistentVolumeSource": schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref), - "k8s.io/api/core/v1.FlexVolumeSource": schema_k8sio_api_core_v1_FlexVolumeSource(ref), - "k8s.io/api/core/v1.FlockerVolumeSource": schema_k8sio_api_core_v1_FlockerVolumeSource(ref), - "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource": schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref), - "k8s.io/api/core/v1.GitRepoVolumeSource": schema_k8sio_api_core_v1_GitRepoVolumeSource(ref), - "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource": schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref), - "k8s.io/api/core/v1.GlusterfsVolumeSource": schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref), - "k8s.io/api/core/v1.HTTPGetAction": schema_k8sio_api_core_v1_HTTPGetAction(ref), - "k8s.io/api/core/v1.HTTPHeader": schema_k8sio_api_core_v1_HTTPHeader(ref), - "k8s.io/api/core/v1.Handler": schema_k8sio_api_core_v1_Handler(ref), - "k8s.io/api/core/v1.HostAlias": schema_k8sio_api_core_v1_HostAlias(ref), - "k8s.io/api/core/v1.HostPathVolumeSource": schema_k8sio_api_core_v1_HostPathVolumeSource(ref), - "k8s.io/api/core/v1.ISCSIPersistentVolumeSource": schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref), - "k8s.io/api/core/v1.ISCSIVolumeSource": schema_k8sio_api_core_v1_ISCSIVolumeSource(ref), - "k8s.io/api/core/v1.KeyToPath": schema_k8sio_api_core_v1_KeyToPath(ref), - "k8s.io/api/core/v1.Lifecycle": schema_k8sio_api_core_v1_Lifecycle(ref), - "k8s.io/api/core/v1.LimitRange": schema_k8sio_api_core_v1_LimitRange(ref), - "k8s.io/api/core/v1.LimitRangeItem": schema_k8sio_api_core_v1_LimitRangeItem(ref), - "k8s.io/api/core/v1.LimitRangeList": schema_k8sio_api_core_v1_LimitRangeList(ref), - "k8s.io/api/core/v1.LimitRangeSpec": schema_k8sio_api_core_v1_LimitRangeSpec(ref), - "k8s.io/api/core/v1.List": schema_k8sio_api_core_v1_List(ref), - "k8s.io/api/core/v1.LoadBalancerIngress": schema_k8sio_api_core_v1_LoadBalancerIngress(ref), - "k8s.io/api/core/v1.LoadBalancerStatus": schema_k8sio_api_core_v1_LoadBalancerStatus(ref), - "k8s.io/api/core/v1.LocalObjectReference": schema_k8sio_api_core_v1_LocalObjectReference(ref), - "k8s.io/api/core/v1.LocalVolumeSource": schema_k8sio_api_core_v1_LocalVolumeSource(ref), - "k8s.io/api/core/v1.NFSVolumeSource": schema_k8sio_api_core_v1_NFSVolumeSource(ref), - "k8s.io/api/core/v1.Namespace": schema_k8sio_api_core_v1_Namespace(ref), - "k8s.io/api/core/v1.NamespaceCondition": schema_k8sio_api_core_v1_NamespaceCondition(ref), - "k8s.io/api/core/v1.NamespaceList": schema_k8sio_api_core_v1_NamespaceList(ref), - "k8s.io/api/core/v1.NamespaceSpec": schema_k8sio_api_core_v1_NamespaceSpec(ref), - "k8s.io/api/core/v1.NamespaceStatus": schema_k8sio_api_core_v1_NamespaceStatus(ref), - "k8s.io/api/core/v1.Node": schema_k8sio_api_core_v1_Node(ref), - "k8s.io/api/core/v1.NodeAddress": schema_k8sio_api_core_v1_NodeAddress(ref), - "k8s.io/api/core/v1.NodeAffinity": schema_k8sio_api_core_v1_NodeAffinity(ref), - "k8s.io/api/core/v1.NodeCondition": schema_k8sio_api_core_v1_NodeCondition(ref), - "k8s.io/api/core/v1.NodeConfigSource": schema_k8sio_api_core_v1_NodeConfigSource(ref), - "k8s.io/api/core/v1.NodeConfigStatus": schema_k8sio_api_core_v1_NodeConfigStatus(ref), - "k8s.io/api/core/v1.NodeDaemonEndpoints": schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref), - "k8s.io/api/core/v1.NodeList": schema_k8sio_api_core_v1_NodeList(ref), - "k8s.io/api/core/v1.NodeProxyOptions": schema_k8sio_api_core_v1_NodeProxyOptions(ref), - "k8s.io/api/core/v1.NodeResources": schema_k8sio_api_core_v1_NodeResources(ref), - "k8s.io/api/core/v1.NodeSelector": schema_k8sio_api_core_v1_NodeSelector(ref), - "k8s.io/api/core/v1.NodeSelectorRequirement": schema_k8sio_api_core_v1_NodeSelectorRequirement(ref), - "k8s.io/api/core/v1.NodeSelectorTerm": schema_k8sio_api_core_v1_NodeSelectorTerm(ref), - "k8s.io/api/core/v1.NodeSpec": schema_k8sio_api_core_v1_NodeSpec(ref), - "k8s.io/api/core/v1.NodeStatus": schema_k8sio_api_core_v1_NodeStatus(ref), - "k8s.io/api/core/v1.NodeSystemInfo": schema_k8sio_api_core_v1_NodeSystemInfo(ref), - "k8s.io/api/core/v1.ObjectFieldSelector": schema_k8sio_api_core_v1_ObjectFieldSelector(ref), - "k8s.io/api/core/v1.ObjectReference": schema_k8sio_api_core_v1_ObjectReference(ref), - "k8s.io/api/core/v1.PersistentVolume": schema_k8sio_api_core_v1_PersistentVolume(ref), - "k8s.io/api/core/v1.PersistentVolumeClaim": schema_k8sio_api_core_v1_PersistentVolumeClaim(ref), - "k8s.io/api/core/v1.PersistentVolumeClaimCondition": schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref), - "k8s.io/api/core/v1.PersistentVolumeClaimList": schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref), - "k8s.io/api/core/v1.PersistentVolumeClaimSpec": schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref), - "k8s.io/api/core/v1.PersistentVolumeClaimStatus": schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref), - "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref), - "k8s.io/api/core/v1.PersistentVolumeList": schema_k8sio_api_core_v1_PersistentVolumeList(ref), - "k8s.io/api/core/v1.PersistentVolumeSource": schema_k8sio_api_core_v1_PersistentVolumeSource(ref), - "k8s.io/api/core/v1.PersistentVolumeSpec": schema_k8sio_api_core_v1_PersistentVolumeSpec(ref), - "k8s.io/api/core/v1.PersistentVolumeStatus": schema_k8sio_api_core_v1_PersistentVolumeStatus(ref), - "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource": schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref), - "k8s.io/api/core/v1.Pod": schema_k8sio_api_core_v1_Pod(ref), - "k8s.io/api/core/v1.PodAffinity": schema_k8sio_api_core_v1_PodAffinity(ref), - "k8s.io/api/core/v1.PodAffinityTerm": schema_k8sio_api_core_v1_PodAffinityTerm(ref), - "k8s.io/api/core/v1.PodAntiAffinity": schema_k8sio_api_core_v1_PodAntiAffinity(ref), - "k8s.io/api/core/v1.PodAttachOptions": schema_k8sio_api_core_v1_PodAttachOptions(ref), - "k8s.io/api/core/v1.PodCondition": schema_k8sio_api_core_v1_PodCondition(ref), - "k8s.io/api/core/v1.PodDNSConfig": schema_k8sio_api_core_v1_PodDNSConfig(ref), - "k8s.io/api/core/v1.PodDNSConfigOption": schema_k8sio_api_core_v1_PodDNSConfigOption(ref), - "k8s.io/api/core/v1.PodExecOptions": schema_k8sio_api_core_v1_PodExecOptions(ref), - "k8s.io/api/core/v1.PodIP": schema_k8sio_api_core_v1_PodIP(ref), - "k8s.io/api/core/v1.PodList": schema_k8sio_api_core_v1_PodList(ref), - "k8s.io/api/core/v1.PodLogOptions": schema_k8sio_api_core_v1_PodLogOptions(ref), - "k8s.io/api/core/v1.PodPortForwardOptions": schema_k8sio_api_core_v1_PodPortForwardOptions(ref), - "k8s.io/api/core/v1.PodProxyOptions": schema_k8sio_api_core_v1_PodProxyOptions(ref), - "k8s.io/api/core/v1.PodReadinessGate": schema_k8sio_api_core_v1_PodReadinessGate(ref), - "k8s.io/api/core/v1.PodSecurityContext": schema_k8sio_api_core_v1_PodSecurityContext(ref), - "k8s.io/api/core/v1.PodSignature": schema_k8sio_api_core_v1_PodSignature(ref), - "k8s.io/api/core/v1.PodSpec": schema_k8sio_api_core_v1_PodSpec(ref), - "k8s.io/api/core/v1.PodStatus": schema_k8sio_api_core_v1_PodStatus(ref), - "k8s.io/api/core/v1.PodStatusResult": schema_k8sio_api_core_v1_PodStatusResult(ref), - "k8s.io/api/core/v1.PodTemplate": schema_k8sio_api_core_v1_PodTemplate(ref), - "k8s.io/api/core/v1.PodTemplateList": schema_k8sio_api_core_v1_PodTemplateList(ref), - "k8s.io/api/core/v1.PodTemplateSpec": schema_k8sio_api_core_v1_PodTemplateSpec(ref), - "k8s.io/api/core/v1.PortworxVolumeSource": schema_k8sio_api_core_v1_PortworxVolumeSource(ref), - "k8s.io/api/core/v1.PreferAvoidPodsEntry": schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref), - "k8s.io/api/core/v1.PreferredSchedulingTerm": schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref), - "k8s.io/api/core/v1.Probe": schema_k8sio_api_core_v1_Probe(ref), - "k8s.io/api/core/v1.ProjectedVolumeSource": schema_k8sio_api_core_v1_ProjectedVolumeSource(ref), - "k8s.io/api/core/v1.QuobyteVolumeSource": schema_k8sio_api_core_v1_QuobyteVolumeSource(ref), - "k8s.io/api/core/v1.RBDPersistentVolumeSource": schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref), - "k8s.io/api/core/v1.RBDVolumeSource": schema_k8sio_api_core_v1_RBDVolumeSource(ref), - "k8s.io/api/core/v1.RangeAllocation": schema_k8sio_api_core_v1_RangeAllocation(ref), - "k8s.io/api/core/v1.ReplicationController": schema_k8sio_api_core_v1_ReplicationController(ref), - "k8s.io/api/core/v1.ReplicationControllerCondition": schema_k8sio_api_core_v1_ReplicationControllerCondition(ref), - "k8s.io/api/core/v1.ReplicationControllerList": schema_k8sio_api_core_v1_ReplicationControllerList(ref), - "k8s.io/api/core/v1.ReplicationControllerSpec": schema_k8sio_api_core_v1_ReplicationControllerSpec(ref), - "k8s.io/api/core/v1.ReplicationControllerStatus": schema_k8sio_api_core_v1_ReplicationControllerStatus(ref), - "k8s.io/api/core/v1.ResourceFieldSelector": schema_k8sio_api_core_v1_ResourceFieldSelector(ref), - "k8s.io/api/core/v1.ResourceQuota": schema_k8sio_api_core_v1_ResourceQuota(ref), - "k8s.io/api/core/v1.ResourceQuotaList": schema_k8sio_api_core_v1_ResourceQuotaList(ref), - "k8s.io/api/core/v1.ResourceQuotaSpec": schema_k8sio_api_core_v1_ResourceQuotaSpec(ref), - "k8s.io/api/core/v1.ResourceQuotaStatus": schema_k8sio_api_core_v1_ResourceQuotaStatus(ref), - "k8s.io/api/core/v1.ResourceRequirements": schema_k8sio_api_core_v1_ResourceRequirements(ref), - "k8s.io/api/core/v1.SELinuxOptions": schema_k8sio_api_core_v1_SELinuxOptions(ref), - "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource": schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref), - "k8s.io/api/core/v1.ScaleIOVolumeSource": schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref), - "k8s.io/api/core/v1.ScopeSelector": schema_k8sio_api_core_v1_ScopeSelector(ref), - "k8s.io/api/core/v1.ScopedResourceSelectorRequirement": schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref), - "k8s.io/api/core/v1.Secret": schema_k8sio_api_core_v1_Secret(ref), - "k8s.io/api/core/v1.SecretEnvSource": schema_k8sio_api_core_v1_SecretEnvSource(ref), - "k8s.io/api/core/v1.SecretKeySelector": schema_k8sio_api_core_v1_SecretKeySelector(ref), - "k8s.io/api/core/v1.SecretList": schema_k8sio_api_core_v1_SecretList(ref), - "k8s.io/api/core/v1.SecretProjection": schema_k8sio_api_core_v1_SecretProjection(ref), - "k8s.io/api/core/v1.SecretReference": schema_k8sio_api_core_v1_SecretReference(ref), - "k8s.io/api/core/v1.SecretVolumeSource": schema_k8sio_api_core_v1_SecretVolumeSource(ref), - "k8s.io/api/core/v1.SecurityContext": schema_k8sio_api_core_v1_SecurityContext(ref), - "k8s.io/api/core/v1.SerializedReference": schema_k8sio_api_core_v1_SerializedReference(ref), - "k8s.io/api/core/v1.Service": schema_k8sio_api_core_v1_Service(ref), - "k8s.io/api/core/v1.ServiceAccount": schema_k8sio_api_core_v1_ServiceAccount(ref), - "k8s.io/api/core/v1.ServiceAccountList": schema_k8sio_api_core_v1_ServiceAccountList(ref), - "k8s.io/api/core/v1.ServiceAccountTokenProjection": schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref), - "k8s.io/api/core/v1.ServiceList": schema_k8sio_api_core_v1_ServiceList(ref), - "k8s.io/api/core/v1.ServicePort": schema_k8sio_api_core_v1_ServicePort(ref), - "k8s.io/api/core/v1.ServiceProxyOptions": schema_k8sio_api_core_v1_ServiceProxyOptions(ref), - "k8s.io/api/core/v1.ServiceSpec": schema_k8sio_api_core_v1_ServiceSpec(ref), - "k8s.io/api/core/v1.ServiceStatus": schema_k8sio_api_core_v1_ServiceStatus(ref), - "k8s.io/api/core/v1.SessionAffinityConfig": schema_k8sio_api_core_v1_SessionAffinityConfig(ref), - "k8s.io/api/core/v1.StorageOSPersistentVolumeSource": schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref), - "k8s.io/api/core/v1.StorageOSVolumeSource": schema_k8sio_api_core_v1_StorageOSVolumeSource(ref), - "k8s.io/api/core/v1.Sysctl": schema_k8sio_api_core_v1_Sysctl(ref), - "k8s.io/api/core/v1.TCPSocketAction": schema_k8sio_api_core_v1_TCPSocketAction(ref), - "k8s.io/api/core/v1.Taint": schema_k8sio_api_core_v1_Taint(ref), - "k8s.io/api/core/v1.Toleration": schema_k8sio_api_core_v1_Toleration(ref), - "k8s.io/api/core/v1.TopologySelectorLabelRequirement": schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref), - "k8s.io/api/core/v1.TopologySelectorTerm": schema_k8sio_api_core_v1_TopologySelectorTerm(ref), - "k8s.io/api/core/v1.TopologySpreadConstraint": schema_k8sio_api_core_v1_TopologySpreadConstraint(ref), - "k8s.io/api/core/v1.TypedLocalObjectReference": schema_k8sio_api_core_v1_TypedLocalObjectReference(ref), - "k8s.io/api/core/v1.Volume": schema_k8sio_api_core_v1_Volume(ref), - "k8s.io/api/core/v1.VolumeDevice": schema_k8sio_api_core_v1_VolumeDevice(ref), - "k8s.io/api/core/v1.VolumeMount": schema_k8sio_api_core_v1_VolumeMount(ref), - "k8s.io/api/core/v1.VolumeNodeAffinity": schema_k8sio_api_core_v1_VolumeNodeAffinity(ref), - "k8s.io/api/core/v1.VolumeProjection": schema_k8sio_api_core_v1_VolumeProjection(ref), - "k8s.io/api/core/v1.VolumeSource": schema_k8sio_api_core_v1_VolumeSource(ref), - "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource": schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref), - "k8s.io/api/core/v1.WeightedPodAffinityTerm": schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref), - "k8s.io/api/core/v1.WindowsSecurityContextOptions": schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref), - "k8s.io/apimachinery/pkg/api/resource.Quantity": schema_apimachinery_pkg_api_resource_Quantity(ref), - "k8s.io/apimachinery/pkg/api/resource.int64Amount": schema_apimachinery_pkg_api_resource_int64Amount(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup": schema_pkg_apis_meta_v1_APIGroup(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList": schema_pkg_apis_meta_v1_APIGroupList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource": schema_pkg_apis_meta_v1_APIResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList": schema_pkg_apis_meta_v1_APIResourceList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions": schema_pkg_apis_meta_v1_APIVersions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.CreateOptions": schema_pkg_apis_meta_v1_CreateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.DeleteOptions": schema_pkg_apis_meta_v1_DeleteOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Duration": schema_pkg_apis_meta_v1_Duration(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ExportOptions": schema_pkg_apis_meta_v1_ExportOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1": schema_pkg_apis_meta_v1_FieldsV1(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GetOptions": schema_pkg_apis_meta_v1_GetOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupKind": schema_pkg_apis_meta_v1_GroupKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupResource": schema_pkg_apis_meta_v1_GroupResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersion": schema_pkg_apis_meta_v1_GroupVersion(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery": schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionKind": schema_pkg_apis_meta_v1_GroupVersionKind(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionResource": schema_pkg_apis_meta_v1_GroupVersionResource(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.InternalEvent": schema_pkg_apis_meta_v1_InternalEvent(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector": schema_pkg_apis_meta_v1_LabelSelector(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement": schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.List": schema_pkg_apis_meta_v1_List(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta": schema_pkg_apis_meta_v1_ListMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ListOptions": schema_pkg_apis_meta_v1_ListOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry": schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime": schema_pkg_apis_meta_v1_MicroTime(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta": schema_pkg_apis_meta_v1_ObjectMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference": schema_pkg_apis_meta_v1_OwnerReference(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata": schema_pkg_apis_meta_v1_PartialObjectMetadata(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadataList": schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Patch": schema_pkg_apis_meta_v1_Patch(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.PatchOptions": schema_pkg_apis_meta_v1_PatchOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions": schema_pkg_apis_meta_v1_Preconditions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.RootPaths": schema_pkg_apis_meta_v1_RootPaths(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR": schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Status": schema_pkg_apis_meta_v1_Status(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause": schema_pkg_apis_meta_v1_StatusCause(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails": schema_pkg_apis_meta_v1_StatusDetails(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Table": schema_pkg_apis_meta_v1_Table(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition": schema_pkg_apis_meta_v1_TableColumnDefinition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableOptions": schema_pkg_apis_meta_v1_TableOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow": schema_pkg_apis_meta_v1_TableRow(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition": schema_pkg_apis_meta_v1_TableRowCondition(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Time": schema_pkg_apis_meta_v1_Time(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.Timestamp": schema_pkg_apis_meta_v1_Timestamp(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta": schema_pkg_apis_meta_v1_TypeMeta(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref), - "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref), - "k8s.io/apimachinery/pkg/runtime.RawExtension": schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref), - "k8s.io/apimachinery/pkg/runtime.TypeMeta": schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref), - "k8s.io/apimachinery/pkg/runtime.Unknown": schema_k8sio_apimachinery_pkg_runtime_Unknown(ref), - "k8s.io/apimachinery/pkg/util/intstr.IntOrString": schema_apimachinery_pkg_util_intstr_IntOrString(ref), - "k8s.io/apimachinery/pkg/version.Info": schema_k8sio_apimachinery_pkg_version_Info(ref), - } -} - -func schema_pkg_apis_devops_v1alpha1_AuthConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AuthConfig is our abstraction of the Registry authorization information for whatever docker client we happen to be based on", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "username": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "password": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "email": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "serverAddress": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_CGroupLimits(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "CGroupLimits holds limits used to constrain container resources.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "memoryLimitBytes": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "cpuShares": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "cpuPeriod": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "cpuQuota": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "memorySwap": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "parent": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"memoryLimitBytes", "cpuShares", "cpuPeriod", "cpuQuota", "memorySwap", "parent"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_ContainerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerConfig is the abstraction of the docker client provider (formerly go-dockerclient, now either engine-api or kube docker client) container.Config type that is leveraged by s2i or origin", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "Labels": { - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "Env": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"Labels", "Env"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_ContainerInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "builderImage": { - SchemaProps: spec.SchemaProps{ - Description: "BaseImage are the images this template will use.", - Type: []string{"string"}, - Format: "", - }, - }, - "runtimeImage": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "runtimeArtifacts": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec"), - }, - }, - }, - }, - }, - "buildVolumes": { - SchemaProps: spec.SchemaProps{ - Description: "BuildVolumes specifies a list of volumes to mount to container running the build.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_DockerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DockerConfig contains the configuration for a Docker connection.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "endPoint": { - SchemaProps: spec.SchemaProps{ - Description: "Endpoint is the docker network endpoint or socket", - Type: []string{"string"}, - Format: "", - }, - }, - "certFile": { - SchemaProps: spec.SchemaProps{ - Description: "CertFile is the certificate file path for a TLS connection", - Type: []string{"string"}, - Format: "", - }, - }, - "keyFile": { - SchemaProps: spec.SchemaProps{ - Description: "KeyFile is the key file path for a TLS connection", - Type: []string{"string"}, - Format: "", - }, - }, - "caFile": { - SchemaProps: spec.SchemaProps{ - Description: "CAFile is the certificate authority file path for a TLS connection", - Type: []string{"string"}, - Format: "", - }, - }, - "useTLS": { - SchemaProps: spec.SchemaProps{ - Description: "UseTLS indicates if TLS must be used", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tlsVerify": { - SchemaProps: spec.SchemaProps{ - Description: "TLSVerify indicates if TLS peer must be verified", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"endPoint", "certFile", "keyFile", "caFile", "useTLS", "tlsVerify"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_DockerConfigEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "username": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "password": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "email": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "serverAddress": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"username", "password", "email"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_DockerConfigJson(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "auths": { - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfigEntry"), - }, - }, - }, - }, - }, - }, - Required: []string{"auths"}, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfigEntry"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_EnvironmentSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EnvironmentSpec specifies a single environment variable.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "value"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_Parameter(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "description": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "key": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "optValues": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "required": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, - "defaultValue": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_ProxyConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ProxyConfig holds proxy configuration.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "httpProxy": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "httpsProxy": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iAutoScale(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "initReplicas": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int32", - }, - }, - "containers": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"kind", "name"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuildResult(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "imageName": { - SchemaProps: spec.SchemaProps{ - Description: "ImageName is the name of artifact", - Type: []string{"string"}, - Format: "", - }, - }, - "imageSize": { - SchemaProps: spec.SchemaProps{ - Description: "The size in bytes of the image", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "imageID": { - SchemaProps: spec.SchemaProps{ - Description: "Image ID.", - Type: []string{"string"}, - Format: "", - }, - }, - "imageCreated": { - SchemaProps: spec.SchemaProps{ - Description: "Image created time.", - Type: []string{"string"}, - Format: "", - }, - }, - "imageRepoTags": { - SchemaProps: spec.SchemaProps{ - Description: "image tags.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "commandPull": { - SchemaProps: spec.SchemaProps{ - Description: "Command for pull image.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuildSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "sourceUrl": { - SchemaProps: spec.SchemaProps{ - Description: "SourceURL is url of the codes such as https://github.com/a/b.git", - Type: []string{"string"}, - Format: "", - }, - }, - "revisionId": { - SchemaProps: spec.SchemaProps{ - Description: "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", - Type: []string{"string"}, - Format: "", - }, - }, - "binaryName": { - SchemaProps: spec.SchemaProps{ - Description: "Binary file Name", - Type: []string{"string"}, - Format: "", - }, - }, - "binarySize": { - SchemaProps: spec.SchemaProps{ - Description: "Binary file Size", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "builderImage": { - SchemaProps: spec.SchemaProps{ - Description: "// BuilderImage describes which image is used for building the result images.", - Type: []string{"string"}, - Format: "", - }, - }, - "description": { - SchemaProps: spec.SchemaProps{ - Description: "Description is a result image description label. The default is no description.", - Type: []string{"string"}, - Format: "", - }, - }, - "commitID": { - SchemaProps: spec.SchemaProps{ - Description: "CommitID represents an arbitrary extended object reference in Git as SHA-1", - Type: []string{"string"}, - Format: "", - }, - }, - "committerName": { - SchemaProps: spec.SchemaProps{ - Description: "CommitterName contains the name of the committer", - Type: []string{"string"}, - Format: "", - }, - }, - "committerEmail": { - SchemaProps: spec.SchemaProps{ - Description: "CommitterEmail contains the e-mail of the committer", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilder(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilder is the Schema for the s2ibuilders API", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderSpec", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderList contains a list of S2iBuilder", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilder"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilder", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderSpec defines the desired state of S2iBuilder", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "config": { - SchemaProps: spec.SchemaProps{ - Description: "INSERT ADDITIONAL SPEC FIELDS - desired state of cluster Important: Run \"make\" to regenerate code after modifying this file", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iConfig"), - }, - }, - "fromTemplate": { - SchemaProps: spec.SchemaProps{ - Description: "FromTemplate define some inputs from user", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.UserDefineTemplate"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iConfig", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.UserDefineTemplate"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderStatus defines the observed state of S2iBuilder", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "runCount": { - SchemaProps: spec.SchemaProps{ - Description: "RunCount represent the sum of s2irun of this builder", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "lastRunState": { - SchemaProps: spec.SchemaProps{ - Description: "LastRunState return the state of the newest run of this builder", - Type: []string{"string"}, - Format: "", - }, - }, - "lastRunName": { - SchemaProps: spec.SchemaProps{ - Description: "LastRunState return the name of the newest run of this builder", - Type: []string{"string"}, - Format: "", - }, - }, - "lastRunStartTime": { - SchemaProps: spec.SchemaProps{ - Description: "LastRunStartTime return the startTime of the newest run of this builder", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - }, - Required: []string{"runCount"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderTemplate is the Schema for the s2ibuildertemplates API", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateSpec", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplateStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderTemplateList contains a list of S2iBuilderTemplate", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplate"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuilderTemplate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderTemplateSpec defines the desired state of S2iBuilderTemplate", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "defaultBaseImage": { - SchemaProps: spec.SchemaProps{ - Description: "DefaultBaseImage is the image that will be used by default", - Type: []string{"string"}, - Format: "", - }, - }, - "containerInfo": { - SchemaProps: spec.SchemaProps{ - Description: "Images are the images this template will use.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ContainerInfo"), - }, - }, - }, - }, - }, - "codeFramework": { - SchemaProps: spec.SchemaProps{ - Description: "CodeFramework means which language this template is designed for and which framework is using if has framework. Like Java, NodeJS etc", - Type: []string{"string"}, - Format: "", - }, - }, - "environment": { - SchemaProps: spec.SchemaProps{ - Description: "Parameters is a set of environment variables to be passed to the image.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.Parameter"), - }, - }, - }, - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Description: "Version of template", - Type: []string{"string"}, - Format: "", - }, - }, - "description": { - SchemaProps: spec.SchemaProps{ - Description: "Description illustrate the purpose of this template", - Type: []string{"string"}, - Format: "", - }, - }, - "iconPath": { - SchemaProps: spec.SchemaProps{ - Description: "IconPath is used for frontend display", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ContainerInfo", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.Parameter"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iBuilderTemplateStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iBuilderTemplateStatus defines the observed state of S2iBuilderTemplate", - Type: []string{"object"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "displayName": { - SchemaProps: spec.SchemaProps{ - Description: "DisplayName is a result image display-name label. This defaults to the output image name.", - Type: []string{"string"}, - Format: "", - }, - }, - "description": { - SchemaProps: spec.SchemaProps{ - Description: "Description is a result image description label. The default is no description.", - Type: []string{"string"}, - Format: "", - }, - }, - "builderImage": { - SchemaProps: spec.SchemaProps{ - Description: "BuilderImage describes which image is used for building the result images.", - Type: []string{"string"}, - Format: "", - }, - }, - "builderImageVersion": { - SchemaProps: spec.SchemaProps{ - Description: "BuilderImageVersion provides optional version information about the builder image.", - Type: []string{"string"}, - Format: "", - }, - }, - "builderBaseImageVersion": { - SchemaProps: spec.SchemaProps{ - Description: "BuilderBaseImageVersion provides optional version information about the builder base image.", - Type: []string{"string"}, - Format: "", - }, - }, - "runtimeImage": { - SchemaProps: spec.SchemaProps{ - Description: "RuntimeImage specifies the image that will be a base for resulting image and will be used for running an application. By default, BuilderImage is used for building and running, but the latter may be overridden.", - Type: []string{"string"}, - Format: "", - }, - }, - "outputImageName": { - SchemaProps: spec.SchemaProps{ - Description: "OutputImageName is a result image name without tag, default is latest. tag will append to ImageName in the end", - Type: []string{"string"}, - Format: "", - }, - }, - "runtimeImagePullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "RuntimeImagePullPolicy specifies when to pull a runtime image.", - Type: []string{"string"}, - Format: "", - }, - }, - "runtimeAuthentication": { - SchemaProps: spec.SchemaProps{ - Description: "RuntimeAuthentication holds the authentication information for pulling the runtime Docker images from private repositories.", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig"), - }, - }, - "runtimeArtifacts": { - SchemaProps: spec.SchemaProps{ - Description: "RuntimeArtifacts specifies a list of source/destination pairs that will be copied from builder to a runtime image. Source can be a file or directory. Destination must be a directory. Regardless whether it is an absolute or relative path, it will be placed into image's WORKDIR. Destination also can be empty or equals to \".\", in this case it just refers to a root of WORKDIR. In case it's empty, S2I will try to get this list from io.openshift.s2i.assemble-input-files label on a RuntimeImage.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec"), - }, - }, - }, - }, - }, - "dockerConfig": { - SchemaProps: spec.SchemaProps{ - Description: "DockerConfig describes how to access host docker daemon.", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfig"), - }, - }, - "pullAuthentication": { - SchemaProps: spec.SchemaProps{ - Description: "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig"), - }, - }, - "pushAuthentication": { - SchemaProps: spec.SchemaProps{ - Description: "PullAuthentication holds the authentication information for pulling the Docker images from private repositories", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig"), - }, - }, - "incrementalAuthentication": { - SchemaProps: spec.SchemaProps{ - Description: "IncrementalAuthentication holds the authentication information for pulling the previous image from private repositories", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig"), - }, - }, - "dockerNetworkMode": { - SchemaProps: spec.SchemaProps{ - Description: "DockerNetworkMode is used to set the docker network setting to --net=container: when the builder is invoked from a container.", - Type: []string{"string"}, - Format: "", - }, - }, - "preserveWorkingDir": { - SchemaProps: spec.SchemaProps{ - Description: "PreserveWorkingDir describes if working directory should be left after processing.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "imageName": { - SchemaProps: spec.SchemaProps{ - Description: "ImageName Contains the registry address and reponame, tag should set by field tag alone", - Type: []string{"string"}, - Format: "", - }, - }, - "tag": { - SchemaProps: spec.SchemaProps{ - Description: "Tag is a result image tag name.", - Type: []string{"string"}, - Format: "", - }, - }, - "builderPullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "BuilderPullPolicy specifies when to pull the builder image", - Type: []string{"string"}, - Format: "", - }, - }, - "previousImagePullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "PreviousImagePullPolicy specifies when to pull the previously build image when doing incremental build", - Type: []string{"string"}, - Format: "", - }, - }, - "incremental": { - SchemaProps: spec.SchemaProps{ - Description: "Incremental describes whether to try to perform incremental build.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "incrementalFromTag": { - SchemaProps: spec.SchemaProps{ - Description: "IncrementalFromTag sets an alternative image tag to look for existing artifacts. Tag is used by default if this is not set.", - Type: []string{"string"}, - Format: "", - }, - }, - "removePreviousImage": { - SchemaProps: spec.SchemaProps{ - Description: "RemovePreviousImage describes if previous image should be removed after successful build. This applies only to incremental builds.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "environment": { - SchemaProps: spec.SchemaProps{ - Description: "Environment is a map of environment variables to be passed to the image.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.EnvironmentSpec"), - }, - }, - }, - }, - }, - "labelNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "LabelNamespace provides the namespace under which the labels will be generated.", - Type: []string{"string"}, - Format: "", - }, - }, - "callbackUrl": { - SchemaProps: spec.SchemaProps{ - Description: "CallbackURL is a URL which is called upon successful build to inform about that fact.", - Type: []string{"string"}, - Format: "", - }, - }, - "scriptsUrl": { - SchemaProps: spec.SchemaProps{ - Description: "ScriptsURL is a URL describing where to fetch the S2I scripts from during build process. This url can be a reference within the builder image if the scheme is specified as image://", - Type: []string{"string"}, - Format: "", - }, - }, - "destination": { - SchemaProps: spec.SchemaProps{ - Description: "Destination specifies a location where the untar operation will place its artifacts.", - Type: []string{"string"}, - Format: "", - }, - }, - "workingDir": { - SchemaProps: spec.SchemaProps{ - Description: "WorkingDir describes temporary directory used for downloading sources, scripts and tar operations.", - Type: []string{"string"}, - Format: "", - }, - }, - "workingSourceDir": { - SchemaProps: spec.SchemaProps{ - Description: "WorkingSourceDir describes the subdirectory off of WorkingDir set up during the repo download that is later used as the root for ignore processing", - Type: []string{"string"}, - Format: "", - }, - }, - "layeredBuild": { - SchemaProps: spec.SchemaProps{ - Description: "LayeredBuild describes if this is build which layered scripts and sources on top of BuilderImage.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "contextDir": { - SchemaProps: spec.SchemaProps{ - Description: "Specify a relative directory inside the application repository that should be used as a root directory for the application.", - Type: []string{"string"}, - Format: "", - }, - }, - "assembleUser": { - SchemaProps: spec.SchemaProps{ - Description: "AssembleUser specifies the user to run the assemble script in container", - Type: []string{"string"}, - Format: "", - }, - }, - "runImage": { - SchemaProps: spec.SchemaProps{ - Description: "RunImage will trigger a \"docker run ...\" invocation of the produced image so the user can see if it operates as he would expect", - Type: []string{"boolean"}, - Format: "", - }, - }, - "usage": { - SchemaProps: spec.SchemaProps{ - Description: "Usage allows for properly shortcircuiting s2i logic when `s2i usage` is invoked", - Type: []string{"boolean"}, - Format: "", - }, - }, - "injections": { - SchemaProps: spec.SchemaProps{ - Description: "Injections specifies a list source/destination folders that are injected to the container that runs assemble. All files we inject will be truncated after the assemble script finishes.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec"), - }, - }, - }, - }, - }, - "cgroupLimits": { - SchemaProps: spec.SchemaProps{ - Description: "CGroupLimits describes the cgroups limits that will be applied to any containers run by s2i.", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.CGroupLimits"), - }, - }, - "dropCapabilities": { - SchemaProps: spec.SchemaProps{ - Description: "DropCapabilities contains a list of capabilities to drop when executing containers", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "scriptDownloadProxyConfig": { - SchemaProps: spec.SchemaProps{ - Description: "ScriptDownloadProxyConfig optionally specifies the http and https proxy to use when downloading scripts", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ProxyConfig"), - }, - }, - "excludeRegExp": { - SchemaProps: spec.SchemaProps{ - Description: "ExcludeRegExp contains a string representation of the regular expression desired for deciding which files to exclude from the tar stream", - Type: []string{"string"}, - Format: "", - }, - }, - "blockOnBuild": { - SchemaProps: spec.SchemaProps{ - Description: "BlockOnBuild prevents s2i from performing a docker build operation if one is necessary to execute ONBUILD commands, or to layer source code into the container for images that don't have a tar binary available, if the image contains ONBUILD commands that would be executed.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "hasOnBuild": { - SchemaProps: spec.SchemaProps{ - Description: "HasOnBuild will be set to true if the builder image contains ONBUILD instructions", - Type: []string{"boolean"}, - Format: "", - }, - }, - "buildVolumes": { - SchemaProps: spec.SchemaProps{ - Description: "BuildVolumes specifies a list of volumes to mount to container running the build.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "labels": { - SchemaProps: spec.SchemaProps{ - Description: "Labels specify labels and their values to be applied to the resulting image. Label keys must have non-zero length. The labels defined here override generated labels in case they have the same name.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "securityOpt": { - SchemaProps: spec.SchemaProps{ - Description: "SecurityOpt are passed as options to the docker containers launched by s2i.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "keepSymlinks": { - SchemaProps: spec.SchemaProps{ - Description: "KeepSymlinks indicates to copy symlinks as symlinks. Default behavior is to follow symlinks and copy files by content.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "asDockerfile": { - SchemaProps: spec.SchemaProps{ - Description: "AsDockerfile indicates the path where the Dockerfile should be written instead of building a new image.", - Type: []string{"string"}, - Format: "", - }, - }, - "imageWorkDir": { - SchemaProps: spec.SchemaProps{ - Description: "ImageWorkDir is the default working directory for the builder image.", - Type: []string{"string"}, - Format: "", - }, - }, - "imageScriptsUrl": { - SchemaProps: spec.SchemaProps{ - Description: "ImageScriptsURL is the default location to find the assemble/run scripts for a builder image. This url can be a reference within the builder image if the scheme is specified as image://", - Type: []string{"string"}, - Format: "", - }, - }, - "addHost": { - SchemaProps: spec.SchemaProps{ - Description: "AddHost Add a line to /etc/hosts for test purpose or private use in LAN. Its format is host:IP,muliple hosts can be added by using multiple --add-host", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "export": { - SchemaProps: spec.SchemaProps{ - Description: "Export Push the result image to specify image registry in tag", - Type: []string{"boolean"}, - Format: "", - }, - }, - "sourceUrl": { - SchemaProps: spec.SchemaProps{ - Description: "SourceURL is url of the codes such as https://github.com/a/b.git", - Type: []string{"string"}, - Format: "", - }, - }, - "isBinaryURL": { - SchemaProps: spec.SchemaProps{ - Description: "IsBinaryURL explain the type of SourceURL. If it is IsBinaryURL, it will download the file directly without using git.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "gitSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "GitSecretRef is the BasicAuth Secret of Git Clone", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "revisionId": { - SchemaProps: spec.SchemaProps{ - Description: "The RevisionId is a branch name or a SHA-1 hash of every important thing about the commit", - Type: []string{"string"}, - Format: "", - }, - }, - "taintKey": { - SchemaProps: spec.SchemaProps{ - Description: "The name of taint.", - Type: []string{"string"}, - Format: "", - }, - }, - "nodeAffinityKey": { - SchemaProps: spec.SchemaProps{ - Description: "The key of Node Affinity.", - Type: []string{"string"}, - Format: "", - }, - }, - "nodeAffinityValues": { - SchemaProps: spec.SchemaProps{ - Description: "The values of Node Affinity.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "outputBuildResult": { - SchemaProps: spec.SchemaProps{ - Description: "Whether output build result to status.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"imageName", "sourceUrl"}, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.AuthConfig", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.CGroupLimits", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.DockerConfig", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.EnvironmentSpec", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.ProxyConfig", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.VolumeSpec", "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iRun(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iRun is the Schema for the s2iruns API", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunSpec", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRunStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iRunList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iRunList contains a list of S2iRun", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRun"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iRun", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iRunSpec defines the desired state of S2iRun", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "builderName": { - SchemaProps: spec.SchemaProps{ - Description: "BuilderName specify the name of s2ibuilder, required", - Type: []string{"string"}, - Format: "", - }, - }, - "backoffLimit": { - SchemaProps: spec.SchemaProps{ - Description: "BackoffLimit limits the restart count of each s2irun. Default is 0", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "secondsAfterFinished": { - SchemaProps: spec.SchemaProps{ - Description: "SecondsAfterFinished if is set and greater than zero, and the job created by s2irun become successful or failed , the job will be auto deleted after SecondsAfterFinished", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "newTag": { - SchemaProps: spec.SchemaProps{ - Description: "NewTag override the default tag in its s2ibuilder, image name cannot be changed.", - Type: []string{"string"}, - Format: "", - }, - }, - "newRevisionId": { - SchemaProps: spec.SchemaProps{ - Description: "NewRevisionId override the default NewRevisionId in its s2ibuilder.", - Type: []string{"string"}, - Format: "", - }, - }, - "newSourceURL": { - SchemaProps: spec.SchemaProps{ - Description: "NewSourceURL is used to download new binary artifacts", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"builderName"}, - }, - }, - } -} - -func schema_pkg_apis_devops_v1alpha1_S2iRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "S2iRunStatus defines the observed state of S2iRun", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "startTime": { - SchemaProps: spec.SchemaProps{ - Description: "StartTime represent when this run began", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "completionTime": { - SchemaProps: spec.SchemaProps{ - Description: "Represents time when the job was completed. It is not guaranteed to be set in happens-before order across separate operations. It is represented in RFC3339 form and is in UTC.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "runState": { - SchemaProps: spec.SchemaProps{ - Description: "RunState indicates whether this job is done or failed", - Type: []string{"string"}, - Format: "", - }, - }, - "logURL": { - SchemaProps: spec.SchemaProps{ - Description: "LogURL is uesd for external log handler to let user know where is log located in", - Type: []string{"string"}, - Format: "", - }, - }, - "kubernetesJobName": { - SchemaProps: spec.SchemaProps{ - Description: "KubernetesJobName is the job name in k8s", - Type: []string{"string"}, - Format: "", - }, - }, - "s2iBuildResult": { - SchemaProps: spec.SchemaProps{ - Description: "S2i build result info.", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildResult"), - }, - }, - "s2iBuildSource": { - SchemaProps: spec.SchemaProps{ - Description: "S2i build source info.", - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildSource"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildResult", "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.S2iBuildSource", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_UserDefineTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name specify a template to use, so many fields in Config can left empty", - Type: []string{"string"}, - Format: "", - }, - }, - "parameters": { - SchemaProps: spec.SchemaProps{ - Description: "Parameters must use with `template`, fill some parameters which template will use", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.Parameter"), - }, - }, - }, - }, - }, - "builderImage": { - SchemaProps: spec.SchemaProps{ - Description: "BaseImage specify which version of this template to use", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1.Parameter"}, - } -} - -func schema_pkg_apis_devops_v1alpha1_VolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "VolumeSpec represents a single volume mount point.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "source": { - SchemaProps: spec.SchemaProps{ - Description: "Source is a reference to the volume source.", - Type: []string{"string"}, - Format: "", - }, - }, - "destination": { - SchemaProps: spec.SchemaProps{ - Description: "Destination is the path to mount the volume to - absolute or relative.", - Type: []string{"string"}, - Format: "", - }, - }, - "keep": { - SchemaProps: spec.SchemaProps{ - Description: "Keep indicates if the mounted data should be kept in the final image.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_AWSElasticBlockStoreVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Persistent Disk resource in AWS.\n\nAn AWS EBS disk must exist before mounting to a container. The disk must also be in the same AWS zone as the kubelet. An AWS EBS disk can only be mounted as read/write once. AWS EBS volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeID": { - SchemaProps: spec.SchemaProps{ - Description: "Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Type: []string{"string"}, - Format: "", - }, - }, - "partition": { - SchemaProps: spec.SchemaProps{ - Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty).", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Specify \"true\" to force and set the ReadOnly property in VolumeMounts to \"true\". If omitted, the default is \"false\". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"volumeID"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Affinity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Affinity is a group of affinity scheduling rules.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "nodeAffinity": { - SchemaProps: spec.SchemaProps{ - Description: "Describes node affinity scheduling rules for the pod.", - Ref: ref("k8s.io/api/core/v1.NodeAffinity"), - }, - }, - "podAffinity": { - SchemaProps: spec.SchemaProps{ - Description: "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).", - Ref: ref("k8s.io/api/core/v1.PodAffinity"), - }, - }, - "podAntiAffinity": { - SchemaProps: spec.SchemaProps{ - Description: "Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).", - Ref: ref("k8s.io/api/core/v1.PodAntiAffinity"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeAffinity", "k8s.io/api/core/v1.PodAffinity", "k8s.io/api/core/v1.PodAntiAffinity"}, - } -} - -func schema_k8sio_api_core_v1_AttachedVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AttachedVolume describes a volume attached to a node", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the attached volume", - Type: []string{"string"}, - Format: "", - }, - }, - "devicePath": { - SchemaProps: spec.SchemaProps{ - Description: "DevicePath represents the device path where the volume should be available", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "devicePath"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_AvoidPods(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AvoidPods describes pods that should avoid this node. This is the value for a Node annotation with key scheduler.alpha.kubernetes.io/preferAvoidPods and will eventually become a field of NodeStatus.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "preferAvoidPods": { - SchemaProps: spec.SchemaProps{ - Description: "Bounded-sized list of signatures of pods that should avoid this node, sorted in timestamp order from oldest to newest. Size of the slice is unspecified.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PreferAvoidPodsEntry"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PreferAvoidPodsEntry"}, - } -} - -func schema_k8sio_api_core_v1_AzureDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "diskName": { - SchemaProps: spec.SchemaProps{ - Description: "The Name of the data disk in the blob storage", - Type: []string{"string"}, - Format: "", - }, - }, - "diskURI": { - SchemaProps: spec.SchemaProps{ - Description: "The URI the data disk in the blob storage", - Type: []string{"string"}, - Format: "", - }, - }, - "cachingMode": { - SchemaProps: spec.SchemaProps{ - Description: "Host Caching mode: None, Read Only, Read Write.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"diskName", "diskURI"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_AzureFilePersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "secretName": { - SchemaProps: spec.SchemaProps{ - Description: "the name of secret that contains Azure Storage Account Name and Key", - Type: []string{"string"}, - Format: "", - }, - }, - "shareName": { - SchemaProps: spec.SchemaProps{ - Description: "Share Name", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "the namespace of the secret that contains Azure Storage Account Name and Key default is the same as the Pod", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"secretName", "shareName"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_AzureFileVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "secretName": { - SchemaProps: spec.SchemaProps{ - Description: "the name of secret that contains Azure Storage Account Name and Key", - Type: []string{"string"}, - Format: "", - }, - }, - "shareName": { - SchemaProps: spec.SchemaProps{ - Description: "Share Name", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"secretName", "shareName"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Binding(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Binding ties one object to another; for example, a pod is bound to a node by a scheduler. Deprecated in 1.7, please use the bindings subresource of pods instead.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "target": { - SchemaProps: spec.SchemaProps{ - Description: "The target object that you want to bind to the standard object.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - }, - Required: []string{"target"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_CSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents storage that is managed by an external CSI volume driver (Beta feature)", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "driver": { - SchemaProps: spec.SchemaProps{ - Description: "Driver is the name of the driver to use for this volume. Required.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeHandle": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeHandle is the unique volume name returned by the CSI volume plugin’s CreateVolume to refer to the volume on all subsequent calls. Required.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: The value to pass to ControllerPublishVolumeRequest. Defaults to false (read/write).", - Type: []string{"boolean"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\".", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeAttributes": { - SchemaProps: spec.SchemaProps{ - Description: "Attributes of the volume to publish.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "controllerPublishSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "ControllerPublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerPublishVolume and ControllerUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "nodeStageSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "NodeStageSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodeStageVolume and NodeStageVolume and NodeUnstageVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "nodePublishSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "controllerExpandSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "ControllerExpandSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI ControllerExpandVolume call. This is an alpha field and requires enabling ExpandCSIVolumes feature gate. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secrets are passed.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - }, - Required: []string{"driver", "volumeHandle"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_CSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a source location of a volume to mount, managed by an external CSI driver", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "driver": { - SchemaProps: spec.SchemaProps{ - Description: "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies a read-only configuration for the volume. Defaults to false (read/write).", - Type: []string{"boolean"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeAttributes": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "nodePublishSecretRef": { - SchemaProps: spec.SchemaProps{ - Description: "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - Required: []string{"driver"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_Capabilities(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Adds and removes POSIX capabilities from running containers.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "add": { - SchemaProps: spec.SchemaProps{ - Description: "Added capabilities", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "drop": { - SchemaProps: spec.SchemaProps{ - Description: "Removed capabilities", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_CephFSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "monitors": { - SchemaProps: spec.SchemaProps{ - Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", - Type: []string{"string"}, - Format: "", - }, - }, - "user": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretFile": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"monitors"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_CephFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Ceph Filesystem mount that lasts the lifetime of a pod Cephfs volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "monitors": { - SchemaProps: spec.SchemaProps{ - Description: "Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Used as the mounted root, rather than the full Ceph tree, default is /", - Type: []string{"string"}, - Format: "", - }, - }, - "user": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretFile": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"monitors"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_CinderPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeID": { - SchemaProps: spec.SchemaProps{ - Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - }, - Required: []string{"volumeID"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_CinderVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a cinder volume resource in Openstack. A Cinder volume must exist before mounting to a container. The volume must also be in the same region as the kubelet. Cinder volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeID": { - SchemaProps: spec.SchemaProps{ - Description: "volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: points to a secret object containing parameters used to connect to OpenStack.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - Required: []string{"volumeID"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_ClientIPConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ClientIPConfig represents the configurations of Client IP based session affinity.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "timeoutSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "timeoutSeconds specifies the seconds of ClientIP type session sticky time. The value must be >0 && <=86400(for 1 day) if ServiceAffinity == \"ClientIP\". Default value is 10800(for 3 hours).", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ComponentCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Information about the condition of a component.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of condition for a component. Valid value: \"Healthy\"", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the condition for a component. Valid values for \"Healthy\": \"True\", \"False\", or \"Unknown\".", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Message about the condition for a component. For example, information about a health check.", - Type: []string{"string"}, - Format: "", - }, - }, - "error": { - SchemaProps: spec.SchemaProps{ - Description: "Condition error code for a component. For example, a health check error code.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ComponentStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ComponentStatus (and ComponentStatusList) holds the cluster validation info.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of component conditions observed", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ComponentCondition"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ComponentCondition", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ComponentStatusList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Status of all the conditions for the component as a list of ComponentStatus objects.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of ComponentStatus objects.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ComponentStatus"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ComponentStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ConfigMap(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap holds configuration data for pods to consume.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "data": { - SchemaProps: spec.SchemaProps{ - Description: "Data contains the configuration data. Each key must consist of alphanumeric characters, '-', '_' or '.'. Values with non-UTF-8 byte sequences must use the BinaryData field. The keys stored in Data must not overlap with the keys in the BinaryData field, this is enforced during validation process.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "binaryData": { - SchemaProps: spec.SchemaProps{ - Description: "BinaryData contains the binary data. Each key must consist of alphanumeric characters, '-', '_' or '.'. BinaryData can contain byte sequences that are not in the UTF-8 range. The keys stored in BinaryData must not overlap with the ones in the Data field, this is enforced during validation process. Using this field will require 1.10+ apiserver and kubelet.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "byte", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ConfigMapEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ConfigMapEnvSource selects a ConfigMap to populate the environment variables with.\n\nThe contents of the target ConfigMap's Data field will represent the key-value pairs as environment variables.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the ConfigMap must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ConfigMapKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Selects a key from a ConfigMap.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "key": { - SchemaProps: spec.SchemaProps{ - Description: "The key to select.", - Type: []string{"string"}, - Format: "", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the ConfigMap or its key must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"key"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ConfigMapList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ConfigMapList is a resource containing a list of ConfigMap objects.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is the list of ConfigMaps.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ConfigMap"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMap", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ConfigMapNodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "ResourceVersion is the metadata.ResourceVersion of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", - Type: []string{"string"}, - Format: "", - }, - }, - "kubeletConfigKey": { - SchemaProps: spec.SchemaProps{ - Description: "KubeletConfigKey declares which key of the referenced ConfigMap corresponds to the KubeletConfiguration structure This field is required in all cases.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"namespace", "name", "kubeletConfigKey"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ConfigMapProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Adapts a ConfigMap into a projected volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a projected volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. Note that this is identical to a configmap volume source without the default mode.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.KeyToPath"), - }, - }, - }, - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the ConfigMap or its keys must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.KeyToPath"}, - } -} - -func schema_k8sio_api_core_v1_ConfigMapVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Adapts a ConfigMap into a volume.\n\nThe contents of the target ConfigMap's Data field will be presented in a volume as files using the keys in the Data field as the file names, unless the items element is populated with specific mappings of keys to paths. ConfigMap volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.KeyToPath"), - }, - }, - }, - }, - }, - "defaultMode": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the ConfigMap or its keys must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.KeyToPath"}, - } -} - -func schema_k8sio_api_core_v1_Container(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A single application container that you want to run within a pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", - Type: []string{"string"}, - Format: "", - }, - }, - "command": { - SchemaProps: spec.SchemaProps{ - Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "args": { - SchemaProps: spec.SchemaProps{ - Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "workingDir": { - SchemaProps: spec.SchemaProps{ - Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "ports": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-map-keys": []interface{}{ - "containerPort", - "protocol", - }, - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "containerPort", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerPort"), - }, - }, - }, - }, - }, - "envFrom": { - SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvFromSource"), - }, - }, - }, - }, - }, - "env": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of environment variables to set in the container. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvVar"), - }, - }, - }, - }, - }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", - Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), - }, - }, - "volumeMounts": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "mountPath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeMount"), - }, - }, - }, - }, - }, - "volumeDevices": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "devicePath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeDevice"), - }, - }, - }, - }, - }, - "livenessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "readinessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "startupProbe": { - SchemaProps: spec.SchemaProps{ - Description: "StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, when it might take a long time to load data or warm a cache, than during steady-state operation. This cannot be updated. This is an alpha feature enabled by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "lifecycle": { - SchemaProps: spec.SchemaProps{ - Description: "Actions that the management system should take in response to container lifecycle events. Cannot be updated.", - Ref: ref("k8s.io/api/core/v1.Lifecycle"), - }, - }, - "terminationMessagePath": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "terminationMessagePolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "imagePullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", - Type: []string{"string"}, - Format: "", - }, - }, - "securityContext": { - SchemaProps: spec.SchemaProps{ - Description: "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", - Ref: ref("k8s.io/api/core/v1.SecurityContext"), - }, - }, - "stdin": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stdinOnce": { - SchemaProps: spec.SchemaProps{ - Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tty": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, - } -} - -func schema_k8sio_api_core_v1_ContainerImage(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Describe a container image", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "names": { - SchemaProps: spec.SchemaProps{ - Description: "Names by which this image is known. e.g. [\"k8s.gcr.io/hyperkube:v1.0.7\", \"dockerhub.io/google_containers/hyperkube:v1.0.7\"]", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "sizeBytes": { - SchemaProps: spec.SchemaProps{ - Description: "The size of the image in bytes.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - }, - Required: []string{"names"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ContainerPort(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerPort represents a network port in a single container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.", - Type: []string{"string"}, - Format: "", - }, - }, - "hostPort": { - SchemaProps: spec.SchemaProps{ - Description: "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "containerPort": { - SchemaProps: spec.SchemaProps{ - Description: "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "protocol": { - SchemaProps: spec.SchemaProps{ - Description: "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".", - Type: []string{"string"}, - Format: "", - }, - }, - "hostIP": { - SchemaProps: spec.SchemaProps{ - Description: "What host IP to bind the external port to.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"containerPort"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ContainerState(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerState holds a possible state of container. Only one of its members may be specified. If none of them is specified, the default one is ContainerStateWaiting.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "waiting": { - SchemaProps: spec.SchemaProps{ - Description: "Details about a waiting container", - Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), - }, - }, - "running": { - SchemaProps: spec.SchemaProps{ - Description: "Details about a running container", - Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), - }, - }, - "terminated": { - SchemaProps: spec.SchemaProps{ - Description: "Details about a terminated container", - Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, - } -} - -func schema_k8sio_api_core_v1_ContainerStateRunning(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerStateRunning is a running state of a container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "startedAt": { - SchemaProps: spec.SchemaProps{ - Description: "Time at which the container was last (re-)started", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_ContainerStateTerminated(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerStateTerminated is a terminated state of a container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "exitCode": { - SchemaProps: spec.SchemaProps{ - Description: "Exit status from the last termination of the container", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "signal": { - SchemaProps: spec.SchemaProps{ - Description: "Signal from the last termination of the container", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "(brief) reason from the last termination of the container", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Message regarding the last termination of the container", - Type: []string{"string"}, - Format: "", - }, - }, - "startedAt": { - SchemaProps: spec.SchemaProps{ - Description: "Time at which previous execution of the container started", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "finishedAt": { - SchemaProps: spec.SchemaProps{ - Description: "Time at which the container last terminated", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "containerID": { - SchemaProps: spec.SchemaProps{ - Description: "Container's ID in the format 'docker://'", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"exitCode"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_ContainerStateWaiting(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerStateWaiting is a waiting state of a container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "(brief) reason the container is not yet running.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Message regarding why the container is not yet running.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ContainerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ContainerStatus contains details for the current status of this container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "This must be a DNS_LABEL. Each container in a pod must have a unique name. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "Details about the container's current condition.", - Ref: ref("k8s.io/api/core/v1.ContainerState"), - }, - }, - "lastState": { - SchemaProps: spec.SchemaProps{ - Description: "Details about the container's last termination condition.", - Ref: ref("k8s.io/api/core/v1.ContainerState"), - }, - }, - "ready": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies whether the container has passed its readiness probe.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "restartCount": { - SchemaProps: spec.SchemaProps{ - Description: "The number of times the container has been restarted, currently based on the number of dead containers that have not yet been removed. Note that this is calculated from dead containers. But those containers are subject to garbage collection. This value will get capped at 5 by GC.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "The image the container is running. More info: https://kubernetes.io/docs/concepts/containers/images", - Type: []string{"string"}, - Format: "", - }, - }, - "imageID": { - SchemaProps: spec.SchemaProps{ - Description: "ImageID of the container's image.", - Type: []string{"string"}, - Format: "", - }, - }, - "containerID": { - SchemaProps: spec.SchemaProps{ - Description: "Container's ID in the format 'docker://'.", - Type: []string{"string"}, - Format: "", - }, - }, - "started": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies whether the container has passed its startup probe. Initialized as false, becomes true after startupProbe is considered successful. Resets to false when the container is restarted, or if kubelet loses state temporarily. Is always true when no startupProbe is defined.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"name", "ready", "restartCount", "image", "imageID"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerState"}, - } -} - -func schema_k8sio_api_core_v1_DaemonEndpoint(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DaemonEndpoint contains information about a single Daemon endpoint.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "Port": { - SchemaProps: spec.SchemaProps{ - Description: "Port number of the given endpoint.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"Port"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_DownwardAPIProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents downward API info for projecting into a projected volume. Note that this is identical to a downwardAPI volume source without the default mode.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is a list of DownwardAPIVolume file", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, - } -} - -func schema_k8sio_api_core_v1_DownwardAPIVolumeFile(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DownwardAPIVolumeFile represents information to create the file containing the pod field", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'", - Type: []string{"string"}, - Format: "", - }, - }, - "fieldRef": { - SchemaProps: spec.SchemaProps{ - Description: "Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.", - Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), - }, - }, - "resourceFieldRef": { - SchemaProps: spec.SchemaProps{ - Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.", - Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), - }, - }, - "mode": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"path"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector"}, - } -} - -func schema_k8sio_api_core_v1_DownwardAPIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DownwardAPIVolumeSource represents a volume containing downward API info. Downward API volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is a list of downward API volume file", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeFile"), - }, - }, - }, - }, - }, - "defaultMode": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.DownwardAPIVolumeFile"}, - } -} - -func schema_k8sio_api_core_v1_EmptyDirVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents an empty directory for a pod. Empty directory volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "medium": { - SchemaProps: spec.SchemaProps{ - Description: "What type of storage medium should back this directory. The default is \"\" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", - Type: []string{"string"}, - Format: "", - }, - }, - "sizeLimit": { - SchemaProps: spec.SchemaProps{ - Description: "Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir", - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_EndpointAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EndpointAddress is a tuple that describes single IP address.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ip": { - SchemaProps: spec.SchemaProps{ - Description: "The IP of this endpoint. May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), or link-local multicast ((224.0.0.0/24). IPv6 is also accepted but not fully supported on all platforms. Also, certain kubernetes components, like kube-proxy, are not IPv6 ready.", - Type: []string{"string"}, - Format: "", - }, - }, - "hostname": { - SchemaProps: spec.SchemaProps{ - Description: "The Hostname of this endpoint", - Type: []string{"string"}, - Format: "", - }, - }, - "nodeName": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Node hosting this endpoint. This can be used to determine endpoints local to a node.", - Type: []string{"string"}, - Format: "", - }, - }, - "targetRef": { - SchemaProps: spec.SchemaProps{ - Description: "Reference to object providing the endpoint.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - }, - Required: []string{"ip"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_EndpointPort(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EndpointPort is a tuple that describes a single port.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "The name of this port. This must match the 'name' field in the corresponding ServicePort. Must be a DNS_LABEL. Optional only if one port is defined.", - Type: []string{"string"}, - Format: "", - }, - }, - "port": { - SchemaProps: spec.SchemaProps{ - Description: "The port number of the endpoint.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "protocol": { - SchemaProps: spec.SchemaProps{ - Description: "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"port"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_EndpointSubset(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n }\nThe resulting set of endpoints can be viewed as:\n a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],\n b: [ 10.10.1.1:309, 10.10.2.2:309 ]", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "addresses": { - SchemaProps: spec.SchemaProps{ - Description: "IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EndpointAddress"), - }, - }, - }, - }, - }, - "notReadyAddresses": { - SchemaProps: spec.SchemaProps{ - Description: "IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EndpointAddress"), - }, - }, - }, - }, - }, - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "Port numbers available on the related IP addresses.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EndpointPort"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.EndpointAddress", "k8s.io/api/core/v1.EndpointPort"}, - } -} - -func schema_k8sio_api_core_v1_Endpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Endpoints is a collection of endpoints that implement the actual service. Example:\n Name: \"mysvc\",\n Subsets: [\n {\n Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],\n Ports: [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]\n },\n {\n Addresses: [{\"ip\": \"10.10.3.3\"}],\n Ports: [{\"name\": \"a\", \"port\": 93}, {\"name\": \"b\", \"port\": 76}]\n },\n ]", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "subsets": { - SchemaProps: spec.SchemaProps{ - Description: "The set of all endpoints is the union of all subsets. Addresses are placed into subsets according to the IPs they share. A single address with multiple ports, some of which are ready and some of which are not (because they come from different containers) will result in the address being displayed in different subsets for the different ports. No address will appear in both Addresses and NotReadyAddresses in the same subset. Sets of addresses and ports that comprise a service.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EndpointSubset"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.EndpointSubset", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_EndpointsList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EndpointsList is a list of endpoints.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of endpoints.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Endpoints"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Endpoints", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_EnvFromSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EnvFromSource represents the source of a set of ConfigMaps", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "prefix": { - SchemaProps: spec.SchemaProps{ - Description: "An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.", - Type: []string{"string"}, - Format: "", - }, - }, - "configMapRef": { - SchemaProps: spec.SchemaProps{ - Description: "The ConfigMap to select from", - Ref: ref("k8s.io/api/core/v1.ConfigMapEnvSource"), - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "The Secret to select from", - Ref: ref("k8s.io/api/core/v1.SecretEnvSource"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMapEnvSource", "k8s.io/api/core/v1.SecretEnvSource"}, - } -} - -func schema_k8sio_api_core_v1_EnvVar(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EnvVar represents an environment variable present in a Container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the environment variable. Must be a C_IDENTIFIER.", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Description: "Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", - Type: []string{"string"}, - Format: "", - }, - }, - "valueFrom": { - SchemaProps: spec.SchemaProps{ - Description: "Source for the environment variable's value. Cannot be used if value is not empty.", - Ref: ref("k8s.io/api/core/v1.EnvVarSource"), - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.EnvVarSource"}, - } -} - -func schema_k8sio_api_core_v1_EnvVarSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EnvVarSource represents a source for the value of an EnvVar.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "fieldRef": { - SchemaProps: spec.SchemaProps{ - Description: "Selects a field of the pod: supports metadata.name, metadata.namespace, metadata.labels, metadata.annotations, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP.", - Ref: ref("k8s.io/api/core/v1.ObjectFieldSelector"), - }, - }, - "resourceFieldRef": { - SchemaProps: spec.SchemaProps{ - Description: "Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.", - Ref: ref("k8s.io/api/core/v1.ResourceFieldSelector"), - }, - }, - "configMapKeyRef": { - SchemaProps: spec.SchemaProps{ - Description: "Selects a key of a ConfigMap.", - Ref: ref("k8s.io/api/core/v1.ConfigMapKeySelector"), - }, - }, - "secretKeyRef": { - SchemaProps: spec.SchemaProps{ - Description: "Selects a key of a secret in the pod's namespace", - Ref: ref("k8s.io/api/core/v1.SecretKeySelector"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMapKeySelector", "k8s.io/api/core/v1.ObjectFieldSelector", "k8s.io/api/core/v1.ResourceFieldSelector", "k8s.io/api/core/v1.SecretKeySelector"}, - } -} - -func schema_k8sio_api_core_v1_EphemeralContainer(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "An EphemeralContainer is a container that may be added temporarily to an existing pod for user-initiated activities such as debugging. Ephemeral containers have no resource or scheduling guarantees, and they will not be restarted when they exit or when a pod is removed or restarted. If an ephemeral container causes a pod to exceed its resource allocation, the pod may be evicted. Ephemeral containers may not be added by directly updating the pod spec. They must be added via the pod's ephemeralcontainers subresource, and they will appear in the pod spec once added. This is an alpha feature enabled by the EphemeralContainers feature flag.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", - Type: []string{"string"}, - Format: "", - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", - Type: []string{"string"}, - Format: "", - }, - }, - "command": { - SchemaProps: spec.SchemaProps{ - Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "args": { - SchemaProps: spec.SchemaProps{ - Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "workingDir": { - SchemaProps: spec.SchemaProps{ - Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "Ports are not allowed for ephemeral containers.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerPort"), - }, - }, - }, - }, - }, - "envFrom": { - SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvFromSource"), - }, - }, - }, - }, - }, - "env": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of environment variables to set in the container. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvVar"), - }, - }, - }, - }, - }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", - Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), - }, - }, - "volumeMounts": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "mountPath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeMount"), - }, - }, - }, - }, - }, - "volumeDevices": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "devicePath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeDevice"), - }, - }, - }, - }, - }, - "livenessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "readinessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "startupProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "lifecycle": { - SchemaProps: spec.SchemaProps{ - Description: "Lifecycle is not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Lifecycle"), - }, - }, - "terminationMessagePath": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "terminationMessagePolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "imagePullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", - Type: []string{"string"}, - Format: "", - }, - }, - "securityContext": { - SchemaProps: spec.SchemaProps{ - Description: "SecurityContext is not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.SecurityContext"), - }, - }, - "stdin": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stdinOnce": { - SchemaProps: spec.SchemaProps{ - Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tty": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "targetContainerName": { - SchemaProps: spec.SchemaProps{ - Description: "If set, the name of the container from PodSpec that this ephemeral container targets. The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container. If not set then the ephemeral container is run in whatever namespaces are shared for the pod. Note that the container runtime must support this feature.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, - } -} - -func schema_k8sio_api_core_v1_EphemeralContainerCommon(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EphemeralContainerCommon is a copy of all fields in Container to be inlined in EphemeralContainer. This separate type allows easy conversion from EphemeralContainer to Container and allows separate documentation for the fields of EphemeralContainer. When a new field is added to Container it must be added here as well.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", - Type: []string{"string"}, - Format: "", - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", - Type: []string{"string"}, - Format: "", - }, - }, - "command": { - SchemaProps: spec.SchemaProps{ - Description: "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "args": { - SchemaProps: spec.SchemaProps{ - Description: "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "workingDir": { - SchemaProps: spec.SchemaProps{ - Description: "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "Ports are not allowed for ephemeral containers.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerPort"), - }, - }, - }, - }, - }, - "envFrom": { - SchemaProps: spec.SchemaProps{ - Description: "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvFromSource"), - }, - }, - }, - }, - }, - "env": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of environment variables to set in the container. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EnvVar"), - }, - }, - }, - }, - }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "Resources are not allowed for ephemeral containers. Ephemeral containers use spare resources already allocated to the pod.", - Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), - }, - }, - "volumeMounts": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "mountPath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Pod volumes to mount into the container's filesystem. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeMount"), - }, - }, - }, - }, - }, - "volumeDevices": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "devicePath", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "volumeDevices is the list of block devices to be used by the container. This is a beta feature.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeDevice"), - }, - }, - }, - }, - }, - "livenessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "readinessProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "startupProbe": { - SchemaProps: spec.SchemaProps{ - Description: "Probes are not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Probe"), - }, - }, - "lifecycle": { - SchemaProps: spec.SchemaProps{ - Description: "Lifecycle is not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.Lifecycle"), - }, - }, - "terminationMessagePath": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "terminationMessagePolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", - Type: []string{"string"}, - Format: "", - }, - }, - "imagePullPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", - Type: []string{"string"}, - Format: "", - }, - }, - "securityContext": { - SchemaProps: spec.SchemaProps{ - Description: "SecurityContext is not allowed for ephemeral containers.", - Ref: ref("k8s.io/api/core/v1.SecurityContext"), - }, - }, - "stdin": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stdinOnce": { - SchemaProps: spec.SchemaProps{ - Description: "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tty": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerPort", "k8s.io/api/core/v1.EnvFromSource", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.Lifecycle", "k8s.io/api/core/v1.Probe", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.SecurityContext", "k8s.io/api/core/v1.VolumeDevice", "k8s.io/api/core/v1.VolumeMount"}, - } -} - -func schema_k8sio_api_core_v1_EphemeralContainers(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "ephemeralContainers": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "A list of ephemeral containers associated with this pod. New ephemeral containers may be appended to this list, but existing ephemeral containers may not be removed or modified.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), - }, - }, - }, - }, - }, - }, - Required: []string{"ephemeralContainers"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_Event(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Event is a report of an event somewhere in the cluster.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "involvedObject": { - SchemaProps: spec.SchemaProps{ - Description: "The object that this event is about.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "This should be a short, machine understandable string that gives the reason for the transition into the object's current status.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human-readable description of the status of this operation.", - Type: []string{"string"}, - Format: "", - }, - }, - "source": { - SchemaProps: spec.SchemaProps{ - Description: "The component reporting this event. Should be a short machine understandable string.", - Ref: ref("k8s.io/api/core/v1.EventSource"), - }, - }, - "firstTimestamp": { - SchemaProps: spec.SchemaProps{ - Description: "The time at which the event was first recorded. (Time of server receipt is in TypeMeta.)", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "lastTimestamp": { - SchemaProps: spec.SchemaProps{ - Description: "The time at which the most recent occurrence of this event was recorded.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "count": { - SchemaProps: spec.SchemaProps{ - Description: "The number of times this event has occurred.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of this event (Normal, Warning), new types could be added in the future", - Type: []string{"string"}, - Format: "", - }, - }, - "eventTime": { - SchemaProps: spec.SchemaProps{ - Description: "Time when this Event was first observed.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), - }, - }, - "series": { - SchemaProps: spec.SchemaProps{ - Description: "Data about the Event series this event represents or nil if it's a singleton Event.", - Ref: ref("k8s.io/api/core/v1.EventSeries"), - }, - }, - "action": { - SchemaProps: spec.SchemaProps{ - Description: "What action was taken/failed regarding to the Regarding object.", - Type: []string{"string"}, - Format: "", - }, - }, - "related": { - SchemaProps: spec.SchemaProps{ - Description: "Optional secondary object for more complex actions.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - "reportingComponent": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the controller that emitted this Event, e.g. `kubernetes.io/kubelet`.", - Type: []string{"string"}, - Format: "", - }, - }, - "reportingInstance": { - SchemaProps: spec.SchemaProps{ - Description: "ID of the controller instance, e.g. `kubelet-xyzf`.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"metadata", "involvedObject"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.EventSeries", "k8s.io/api/core/v1.EventSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_EventList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EventList is a list of events.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of events", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Event"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Event", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_EventSeries(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EventSeries contain information on series of events, i.e. thing that was/is happening continuously for some time.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "count": { - SchemaProps: spec.SchemaProps{ - Description: "Number of occurrences in this series up to the last heartbeat time", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "lastObservedTime": { - SchemaProps: spec.SchemaProps{ - Description: "Time of the last occurrence observed", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"), - }, - }, - "state": { - SchemaProps: spec.SchemaProps{ - Description: "State of this Series: Ongoing or Finished Deprecated. Planned removal for 1.18", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.MicroTime"}, - } -} - -func schema_k8sio_api_core_v1_EventSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "EventSource contains information for an event.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "component": { - SchemaProps: spec.SchemaProps{ - Description: "Component from which the event is generated.", - Type: []string{"string"}, - Format: "", - }, - }, - "host": { - SchemaProps: spec.SchemaProps{ - Description: "Node name on which the event is generated.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ExecAction(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ExecAction describes a \"run in container\" action.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "command": { - SchemaProps: spec.SchemaProps{ - Description: "Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem. The command is simply exec'd, it is not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use a shell, you need to explicitly call out to that shell. Exit status of 0 is treated as live/healthy and non-zero is unhealthy.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_FCVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Fibre Channel volume. Fibre Channel volumes can only be mounted as read/write once. Fibre Channel volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "targetWWNs": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: FC target worldwide names (WWNs)", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "lun": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: FC target lun number", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "wwids": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_FlexPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "FlexPersistentVolumeSource represents a generic persistent volume resource that is provisioned/attached using an exec based plugin.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "driver": { - SchemaProps: spec.SchemaProps{ - Description: "Driver is the name of the driver to use for this volume.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "options": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Extra command options if any.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"driver"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_FlexVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "driver": { - SchemaProps: spec.SchemaProps{ - Description: "Driver is the name of the driver to use for this volume.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default filesystem depends on FlexVolume script.", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "options": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Extra command options if any.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"driver"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_FlockerVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Flocker volume mounted by the Flocker agent. One and only one of datasetName and datasetUUID should be set. Flocker volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "datasetName": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated", - Type: []string{"string"}, - Format: "", - }, - }, - "datasetUUID": { - SchemaProps: spec.SchemaProps{ - Description: "UUID of the dataset. This is unique identifier of a Flocker dataset", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_GCEPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Persistent Disk resource in Google Compute Engine.\n\nA GCE PD must exist before mounting to a container. The disk must also be in the same GCE project and zone as the kubelet. A GCE PD can only be mounted as read/write once or read-only many times. GCE PDs support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "pdName": { - SchemaProps: spec.SchemaProps{ - Description: "Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Type: []string{"string"}, - Format: "", - }, - }, - "partition": { - SchemaProps: spec.SchemaProps{ - Description: "The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as \"1\". Similarly, the volume partition for /dev/sda is \"0\" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"pdName"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_GitRepoVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a volume that is populated with the contents of a git repository. Git repo volumes do not support ownership management. Git repo volumes support SELinux relabeling.\n\nDEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "repository": { - SchemaProps: spec.SchemaProps{ - Description: "Repository URL", - Type: []string{"string"}, - Format: "", - }, - }, - "revision": { - SchemaProps: spec.SchemaProps{ - Description: "Commit hash for the specified revision.", - Type: []string{"string"}, - Format: "", - }, - }, - "directory": { - SchemaProps: spec.SchemaProps{ - Description: "Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"repository"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_GlusterfsPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "endpoints": { - SchemaProps: spec.SchemaProps{ - Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"boolean"}, - Format: "", - }, - }, - "endpointsNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "EndpointsNamespace is the namespace that contains Glusterfs endpoint. If this field is empty, the EndpointNamespace defaults to the same namespace as the bound PVC. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"endpoints", "path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_GlusterfsVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Glusterfs mount that lasts the lifetime of a pod. Glusterfs volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "endpoints": { - SchemaProps: spec.SchemaProps{ - Description: "EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"endpoints", "path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_HTTPGetAction(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "HTTPGetAction describes an action based on HTTP Get requests.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path to access on the HTTP server.", - Type: []string{"string"}, - Format: "", - }, - }, - "port": { - SchemaProps: spec.SchemaProps{ - Description: "Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", - Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), - }, - }, - "host": { - SchemaProps: spec.SchemaProps{ - Description: "Host name to connect to, defaults to the pod IP. You probably want to set \"Host\" in httpHeaders instead.", - Type: []string{"string"}, - Format: "", - }, - }, - "scheme": { - SchemaProps: spec.SchemaProps{ - Description: "Scheme to use for connecting to the host. Defaults to HTTP.", - Type: []string{"string"}, - Format: "", - }, - }, - "httpHeaders": { - SchemaProps: spec.SchemaProps{ - Description: "Custom headers to set in the request. HTTP allows repeated headers.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.HTTPHeader"), - }, - }, - }, - }, - }, - }, - Required: []string{"port"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.HTTPHeader", "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, - } -} - -func schema_k8sio_api_core_v1_HTTPHeader(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "HTTPHeader describes a custom header to be used in HTTP probes", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "The header field name", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Description: "The header field value", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "value"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Handler(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Handler defines a specific action that should be taken", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "exec": { - SchemaProps: spec.SchemaProps{ - Description: "One and only one of the following should be specified. Exec specifies the action to take.", - Ref: ref("k8s.io/api/core/v1.ExecAction"), - }, - }, - "httpGet": { - SchemaProps: spec.SchemaProps{ - Description: "HTTPGet specifies the http request to perform.", - Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), - }, - }, - "tcpSocket": { - SchemaProps: spec.SchemaProps{ - Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", - Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, - } -} - -func schema_k8sio_api_core_v1_HostAlias(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "HostAlias holds the mapping between IP and hostnames that will be injected as an entry in the pod's hosts file.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ip": { - SchemaProps: spec.SchemaProps{ - Description: "IP address of the host file entry.", - Type: []string{"string"}, - Format: "", - }, - }, - "hostnames": { - SchemaProps: spec.SchemaProps{ - Description: "Hostnames for the above IP address.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_HostPathVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a host path mapped into a pod. Host path volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type for HostPath Volume Defaults to \"\" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ISCSIPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ISCSIPersistentVolumeSource represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "targetPortal": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", - Type: []string{"string"}, - Format: "", - }, - }, - "iqn": { - SchemaProps: spec.SchemaProps{ - Description: "Target iSCSI Qualified Name.", - Type: []string{"string"}, - Format: "", - }, - }, - "lun": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Lun number.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "iscsiInterface": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "portals": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Portal List. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "chapAuthDiscovery": { - SchemaProps: spec.SchemaProps{ - Description: "whether support iSCSI Discovery CHAP authentication", - Type: []string{"boolean"}, - Format: "", - }, - }, - "chapAuthSession": { - SchemaProps: spec.SchemaProps{ - Description: "whether support iSCSI Session CHAP authentication", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "CHAP Secret for iSCSI target and initiator authentication", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "initiatorName": { - SchemaProps: spec.SchemaProps{ - Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"targetPortal", "iqn", "lun"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_ISCSIVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents an ISCSI disk. ISCSI volumes can only be mounted as read/write once. ISCSI volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "targetPortal": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", - Type: []string{"string"}, - Format: "", - }, - }, - "iqn": { - SchemaProps: spec.SchemaProps{ - Description: "Target iSCSI Qualified Name.", - Type: []string{"string"}, - Format: "", - }, - }, - "lun": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Lun number.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "iscsiInterface": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "portals": { - SchemaProps: spec.SchemaProps{ - Description: "iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "chapAuthDiscovery": { - SchemaProps: spec.SchemaProps{ - Description: "whether support iSCSI Discovery CHAP authentication", - Type: []string{"boolean"}, - Format: "", - }, - }, - "chapAuthSession": { - SchemaProps: spec.SchemaProps{ - Description: "whether support iSCSI Session CHAP authentication", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "CHAP Secret for iSCSI target and initiator authentication", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "initiatorName": { - SchemaProps: spec.SchemaProps{ - Description: "Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"targetPortal", "iqn", "lun"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_KeyToPath(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Maps a string key to a path within a volume.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Description: "The key to project.", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.", - Type: []string{"string"}, - Format: "", - }, - }, - "mode": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: mode bits to use on this file, must be a value between 0 and 0777. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"key", "path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Lifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Lifecycle describes actions that the management system should take in response to container lifecycle events. For the PostStart and PreStop lifecycle handlers, management of the container blocks until the action is complete, unless the container process fails, in which case the handler is aborted.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "postStart": { - SchemaProps: spec.SchemaProps{ - Description: "PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. Other management of the container blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", - Ref: ref("k8s.io/api/core/v1.Handler"), - }, - }, - "preStop": { - SchemaProps: spec.SchemaProps{ - Description: "PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. The handler is not called if the container crashes or exits. The reason for termination is passed to the handler. The Pod's termination grace period countdown begins before the PreStop hooked is executed. Regardless of the outcome of the handler, the container will eventually terminate within the Pod's termination grace period. Other management of the container blocks until the hook completes or until the termination grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks", - Ref: ref("k8s.io/api/core/v1.Handler"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Handler"}, - } -} - -func schema_k8sio_api_core_v1_LimitRange(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LimitRange sets resource usage limits for each kind of resource in a Namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the limits enforced. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.LimitRangeSpec"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LimitRangeSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_LimitRangeItem(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of resource that this limit applies to.", - Type: []string{"string"}, - Format: "", - }, - }, - "max": { - SchemaProps: spec.SchemaProps{ - Description: "Max usage constraints on this kind by resource name.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "min": { - SchemaProps: spec.SchemaProps{ - Description: "Min usage constraints on this kind by resource name.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "default": { - SchemaProps: spec.SchemaProps{ - Description: "Default resource requirement limit value by resource name if resource limit is omitted.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "defaultRequest": { - SchemaProps: spec.SchemaProps{ - Description: "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "maxLimitRequestRatio": { - SchemaProps: spec.SchemaProps{ - Description: "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_LimitRangeList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LimitRangeList is a list of LimitRange items.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is a list of LimitRange objects. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LimitRange"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LimitRange", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_LimitRangeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LimitRangeSpec defines a min/max usage limit for resources that match on kind.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "limits": { - SchemaProps: spec.SchemaProps{ - Description: "Limits is the list of LimitRangeItem objects that are enforced.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LimitRangeItem"), - }, - }, - }, - }, - }, - }, - Required: []string{"limits"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LimitRangeItem"}, - } -} - -func schema_k8sio_api_core_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "List holds a list of objects, which may not be known by the server.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of objects", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, - } -} - -func schema_k8sio_api_core_v1_LoadBalancerIngress(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LoadBalancerIngress represents the status of a load-balancer ingress point: traffic intended for the service should be sent to an ingress point.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ip": { - SchemaProps: spec.SchemaProps{ - Description: "IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)", - Type: []string{"string"}, - Format: "", - }, - }, - "hostname": { - SchemaProps: spec.SchemaProps{ - Description: "Hostname is set for load-balancer ingress points that are DNS based (typically AWS load-balancers)", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_LoadBalancerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LoadBalancerStatus represents the status of a load-balancer.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ingress": { - SchemaProps: spec.SchemaProps{ - Description: "Ingress is a list containing ingress points for the load-balancer. Traffic intended for the service should be sent to these ingress points.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LoadBalancerIngress"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LoadBalancerIngress"}, - } -} - -func schema_k8sio_api_core_v1_LocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_LocalVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Local represents directly-attached storage with node affinity (Beta feature)", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "path": { - SchemaProps: spec.SchemaProps{ - Description: "The full path to the volume on the node. It can be either a directory or block device (disk, partition, ...).", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. It applies only when the Path is a block device. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". The default value is to auto-select a fileystem if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_NFSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents an NFS mount that lasts the lifetime of a pod. NFS volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "server": { - SchemaProps: spec.SchemaProps{ - Description: "Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"server", "path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Namespace(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Namespace provides a scope for Names. Use of multiple namespaces is optional.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the behavior of the Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.NamespaceSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status describes the current status of a Namespace. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.NamespaceStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NamespaceSpec", "k8s.io/api/core/v1.NamespaceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_NamespaceCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NamespaceCondition contains details about state of namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of namespace controller condition.", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the condition, one of True, False, Unknown.", - Type: []string{"string"}, - Format: "", - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_NamespaceList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NamespaceList is a list of Namespaces.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is the list of Namespace objects in the list. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Namespace"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Namespace", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_NamespaceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NamespaceSpec describes the attributes on a Namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "finalizers": { - SchemaProps: spec.SchemaProps{ - Description: "Finalizers is an opaque list of values that must be empty to permanently remove object from storage. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_NamespaceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NamespaceStatus is information about the current status of a Namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "phase": { - SchemaProps: spec.SchemaProps{ - Description: "Phase is the current lifecycle phase of the namespace. More info: https://kubernetes.io/docs/tasks/administer-cluster/namespaces/", - Type: []string{"string"}, - Format: "", - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Represents the latest available observations of a namespace's current state.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NamespaceCondition"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NamespaceCondition"}, - } -} - -func schema_k8sio_api_core_v1_Node(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Node is a worker node in Kubernetes. Each node will have a unique identifier in the cache (i.e. in etcd).", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the behavior of a node. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.NodeSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Most recently observed status of the node. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.NodeStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSpec", "k8s.io/api/core/v1.NodeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_NodeAddress(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeAddress contains information for the node's address.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Node address type, one of Hostname, ExternalIP or InternalIP.", - Type: []string{"string"}, - Format: "", - }, - }, - "address": { - SchemaProps: spec.SchemaProps{ - Description: "The node address.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "address"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_NodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Node affinity is a group of node affinity scheduling rules.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "requiredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node.", - Ref: ref("k8s.io/api/core/v1.NodeSelector"), - }, - }, - "preferredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PreferredSchedulingTerm"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSelector", "k8s.io/api/core/v1.PreferredSchedulingTerm"}, - } -} - -func schema_k8sio_api_core_v1_NodeCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeCondition contains condition information for a node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of node condition.", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the condition, one of True, False, Unknown.", - Type: []string{"string"}, - Format: "", - }, - }, - "lastHeartbeatTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time we got an update on a given condition.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time the condition transit from one status to another.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "(brief) reason for the condition's last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Human readable message indicating details about last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_NodeConfigSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "configMap": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap is a reference to a Node's ConfigMap", - Ref: ref("k8s.io/api/core/v1.ConfigMapNodeConfigSource"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMapNodeConfigSource"}, - } -} - -func schema_k8sio_api_core_v1_NodeConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeConfigStatus describes the status of the config assigned by Node.Spec.ConfigSource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "assigned": { - SchemaProps: spec.SchemaProps{ - Description: "Assigned reports the checkpointed config the node will try to use. When Node.Spec.ConfigSource is updated, the node checkpoints the associated config payload to local disk, along with a record indicating intended config. The node refers to this record to choose its config checkpoint, and reports this record in Assigned. Assigned only updates in the status after the record has been checkpointed to disk. When the Kubelet is restarted, it tries to make the Assigned config the Active config by loading and validating the checkpointed payload identified by Assigned.", - Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), - }, - }, - "active": { - SchemaProps: spec.SchemaProps{ - Description: "Active reports the checkpointed config the node is actively using. Active will represent either the current version of the Assigned config, or the current LastKnownGood config, depending on whether attempting to use the Assigned config results in an error.", - Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), - }, - }, - "lastKnownGood": { - SchemaProps: spec.SchemaProps{ - Description: "LastKnownGood reports the checkpointed config the node will fall back to when it encounters an error attempting to use the Assigned config. The Assigned config becomes the LastKnownGood config when the node determines that the Assigned config is stable and correct. This is currently implemented as a 10-minute soak period starting when the local record of Assigned config is updated. If the Assigned config is Active at the end of this period, it becomes the LastKnownGood. Note that if Spec.ConfigSource is reset to nil (use local defaults), the LastKnownGood is also immediately reset to nil, because the local default config is always assumed good. You should not make assumptions about the node's method of determining config stability and correctness, as this may change or become configurable in the future.", - Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), - }, - }, - "error": { - SchemaProps: spec.SchemaProps{ - Description: "Error describes any problems reconciling the Spec.ConfigSource to the Active config. Errors may occur, for example, attempting to checkpoint Spec.ConfigSource to the local Assigned record, attempting to checkpoint the payload associated with Spec.ConfigSource, attempting to load or validate the Assigned config, etc. Errors may occur at different points while syncing config. Earlier errors (e.g. download or checkpointing errors) will not result in a rollback to LastKnownGood, and may resolve across Kubelet retries. Later errors (e.g. loading or validating a checkpointed config) will result in a rollback to LastKnownGood. In the latter case, it is usually possible to resolve the error by fixing the config assigned in Spec.ConfigSource. You can find additional information for debugging by searching the error message in the Kubelet log. Error is a human-readable description of the error state; machines can check whether or not Error is empty, but should not rely on the stability of the Error text across Kubelet versions.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeConfigSource"}, - } -} - -func schema_k8sio_api_core_v1_NodeDaemonEndpoints(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeDaemonEndpoints lists ports opened by daemons running on the Node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kubeletEndpoint": { - SchemaProps: spec.SchemaProps{ - Description: "Endpoint on which Kubelet is listening.", - Ref: ref("k8s.io/api/core/v1.DaemonEndpoint"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.DaemonEndpoint"}, - } -} - -func schema_k8sio_api_core_v1_NodeList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeList is the whole list of all Nodes which have been registered with master.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of nodes", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Node"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Node", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_NodeProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeProxyOptions is the query options to a Node's proxy call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the URL path to use for the current proxy request to node.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_NodeResources(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeResources is an object for conveying resource information about a node. see http://releases.k8s.io/HEAD/docs/design/resources.md for more details.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "Capacity": { - SchemaProps: spec.SchemaProps{ - Description: "Capacity represents the available resources of a node", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - }, - Required: []string{"Capacity"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_NodeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "nodeSelectorTerms": { - SchemaProps: spec.SchemaProps{ - Description: "Required. A list of node selector terms. The terms are ORed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), - }, - }, - }, - }, - }, - }, - Required: []string{"nodeSelectorTerms"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSelectorTerm"}, - } -} - -func schema_k8sio_api_core_v1_NodeSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Description: "The label key that the selector applies to.", - Type: []string{"string"}, - Format: "", - }, - }, - "operator": { - SchemaProps: spec.SchemaProps{ - Description: "Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.", - Type: []string{"string"}, - Format: "", - }, - }, - "values": { - SchemaProps: spec.SchemaProps{ - Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"key", "operator"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_NodeSelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "matchExpressions": { - SchemaProps: spec.SchemaProps{ - Description: "A list of node selector requirements by node's labels.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), - }, - }, - }, - }, - }, - "matchFields": { - SchemaProps: spec.SchemaProps{ - Description: "A list of node selector requirements by node's fields.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NodeSelectorRequirement"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSelectorRequirement"}, - } -} - -func schema_k8sio_api_core_v1_NodeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeSpec describes the attributes that a node is created with.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "podCIDR": { - SchemaProps: spec.SchemaProps{ - Description: "PodCIDR represents the pod IP range assigned to the node.", - Type: []string{"string"}, - Format: "", - }, - }, - "podCIDRs": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "podCIDRs represents the IP ranges assigned to the node for usage by Pods on that node. If this field is specified, the 0th entry must match the podCIDR field. It may contain at most 1 value for each of IPv4 and IPv6.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "providerID": { - SchemaProps: spec.SchemaProps{ - Description: "ID of the node assigned by the cloud provider in the format: ://", - Type: []string{"string"}, - Format: "", - }, - }, - "unschedulable": { - SchemaProps: spec.SchemaProps{ - Description: "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", - Type: []string{"boolean"}, - Format: "", - }, - }, - "taints": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the node's taints.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Taint"), - }, - }, - }, - }, - }, - "configSource": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field", - Ref: ref("k8s.io/api/core/v1.NodeConfigSource"), - }, - }, - "externalID": { - SchemaProps: spec.SchemaProps{ - Description: "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeConfigSource", "k8s.io/api/core/v1.Taint"}, - } -} - -func schema_k8sio_api_core_v1_NodeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeStatus is information about the current status of a node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "capacity": { - SchemaProps: spec.SchemaProps{ - Description: "Capacity represents the total resources of a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "allocatable": { - SchemaProps: spec.SchemaProps{ - Description: "Allocatable represents the resources of a node that are available for scheduling. Defaults to Capacity.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "phase": { - SchemaProps: spec.SchemaProps{ - Description: "NodePhase is the recently observed lifecycle phase of the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#phase The field is never populated, and now is deprecated.", - Type: []string{"string"}, - Format: "", - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Conditions is an array of current observed node conditions. More info: https://kubernetes.io/docs/concepts/nodes/node/#condition", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NodeCondition"), - }, - }, - }, - }, - }, - "addresses": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of addresses reachable to the node. Queried from cloud provider, if available. More info: https://kubernetes.io/docs/concepts/nodes/node/#addresses Note: This field is declared as mergeable, but the merge key is not sufficiently unique, which can cause data corruption when it is merged. Callers should instead use a full-replacement patch. See http://pr.k8s.io/79391 for an example.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.NodeAddress"), - }, - }, - }, - }, - }, - "daemonEndpoints": { - SchemaProps: spec.SchemaProps{ - Description: "Endpoints of daemons running on the Node.", - Ref: ref("k8s.io/api/core/v1.NodeDaemonEndpoints"), - }, - }, - "nodeInfo": { - SchemaProps: spec.SchemaProps{ - Description: "Set of ids/uuids to uniquely identify the node. More info: https://kubernetes.io/docs/concepts/nodes/node/#info", - Ref: ref("k8s.io/api/core/v1.NodeSystemInfo"), - }, - }, - "images": { - SchemaProps: spec.SchemaProps{ - Description: "List of container images on this node", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerImage"), - }, - }, - }, - }, - }, - "volumesInUse": { - SchemaProps: spec.SchemaProps{ - Description: "List of attachable volumes in use (mounted) by the node.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "volumesAttached": { - SchemaProps: spec.SchemaProps{ - Description: "List of volumes that are attached to the node.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.AttachedVolume"), - }, - }, - }, - }, - }, - "config": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the config assigned to the node via the dynamic Kubelet config feature.", - Ref: ref("k8s.io/api/core/v1.NodeConfigStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.AttachedVolume", "k8s.io/api/core/v1.ContainerImage", "k8s.io/api/core/v1.NodeAddress", "k8s.io/api/core/v1.NodeCondition", "k8s.io/api/core/v1.NodeConfigStatus", "k8s.io/api/core/v1.NodeDaemonEndpoints", "k8s.io/api/core/v1.NodeSystemInfo", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_NodeSystemInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "NodeSystemInfo is a set of ids/uuids to uniquely identify the node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "machineID": { - SchemaProps: spec.SchemaProps{ - Description: "MachineID reported by the node. For unique machine identification in the cluster this field is preferred. Learn more from man(5) machine-id: http://man7.org/linux/man-pages/man5/machine-id.5.html", - Type: []string{"string"}, - Format: "", - }, - }, - "systemUUID": { - SchemaProps: spec.SchemaProps{ - Description: "SystemUUID reported by the node. For unique machine identification MachineID is preferred. This field is specific to Red Hat hosts https://access.redhat.com/documentation/en-US/Red_Hat_Subscription_Management/1/html/RHSM/getting-system-uuid.html", - Type: []string{"string"}, - Format: "", - }, - }, - "bootID": { - SchemaProps: spec.SchemaProps{ - Description: "Boot ID reported by the node.", - Type: []string{"string"}, - Format: "", - }, - }, - "kernelVersion": { - SchemaProps: spec.SchemaProps{ - Description: "Kernel Version reported by the node from 'uname -r' (e.g. 3.16.0-0.bpo.4-amd64).", - Type: []string{"string"}, - Format: "", - }, - }, - "osImage": { - SchemaProps: spec.SchemaProps{ - Description: "OS Image reported by the node from /etc/os-release (e.g. Debian GNU/Linux 7 (wheezy)).", - Type: []string{"string"}, - Format: "", - }, - }, - "containerRuntimeVersion": { - SchemaProps: spec.SchemaProps{ - Description: "ContainerRuntime Version reported by the node through runtime remote API (e.g. docker://1.5.0).", - Type: []string{"string"}, - Format: "", - }, - }, - "kubeletVersion": { - SchemaProps: spec.SchemaProps{ - Description: "Kubelet Version reported by the node.", - Type: []string{"string"}, - Format: "", - }, - }, - "kubeProxyVersion": { - SchemaProps: spec.SchemaProps{ - Description: "KubeProxy Version reported by the node.", - Type: []string{"string"}, - Format: "", - }, - }, - "operatingSystem": { - SchemaProps: spec.SchemaProps{ - Description: "The Operating System reported by the node", - Type: []string{"string"}, - Format: "", - }, - }, - "architecture": { - SchemaProps: spec.SchemaProps{ - Description: "The Architecture reported by the node", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"machineID", "systemUUID", "bootID", "kernelVersion", "osImage", "containerRuntimeVersion", "kubeletVersion", "kubeProxyVersion", "operatingSystem", "architecture"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ObjectFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ObjectFieldSelector selects an APIVersioned field of an object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "Version of the schema the FieldPath is written in terms of, defaults to \"v1\".", - Type: []string{"string"}, - Format: "", - }, - }, - "fieldPath": { - SchemaProps: spec.SchemaProps{ - Description: "Path of the field to select in the specified API version.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"fieldPath"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ObjectReference contains enough information to let you inspect or modify the referred object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "API version of the referent.", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "Specific resourceVersion to which this reference is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - Type: []string{"string"}, - Format: "", - }, - }, - "fieldPath": { - SchemaProps: spec.SchemaProps{ - Description: "If referring to a piece of an object instead of an entire object, this string should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. For example, if the object reference is to a container within a pod, this would take on a value like: \"spec.containers{name}\" (where \"name\" refers to the name of the container that triggered the event) or if no container name is specified \"spec.containers[2]\" (container with index 2 in this pod). This syntax is chosen only to have some well-defined way of referencing a part of an object.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PersistentVolume(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolume (PV) is a storage resource provisioned by an administrator. It is analogous to a node. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines a specification of a persistent volume owned by the cluster. Provisioned by an administrator. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status represents the current information/status for the persistent volume. Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistent-volumes", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PersistentVolumeSpec", "k8s.io/api/core/v1.PersistentVolumeStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaim(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaim is a user's request for and claim to a persistent volume", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PersistentVolumeClaimSpec", "k8s.io/api/core/v1.PersistentVolumeClaimStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaimCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimCondition contails details about state of pvc", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "lastProbeTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time we probed the condition.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time the condition transitioned from one status to another.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports \"ResizeStarted\" that means the underlying persistent volume is being resized.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Human-readable message indicating details about last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaimList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimList is a list of PersistentVolumeClaim items.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "A list of persistent volume claims. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaim"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimSpec describes the common attributes of storage devices and allows a Source for provider-specific attributes", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "accessModes": { - SchemaProps: spec.SchemaProps{ - Description: "AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "A label query over volumes to consider for binding.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "Resources represents the minimum resources the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources", - Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), - }, - }, - "volumeName": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeName is the binding reference to the PersistentVolume backing this claim.", - Type: []string{"string"}, - Format: "", - }, - }, - "storageClassName": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeMode": { - SchemaProps: spec.SchemaProps{ - Description: "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. This is a beta feature.", - Type: []string{"string"}, - Format: "", - }, - }, - "dataSource": { - SchemaProps: spec.SchemaProps{ - Description: "This field requires the VolumeSnapshotDataSource alpha feature gate to be enabled and currently VolumeSnapshot is the only supported data source. If the provisioner can support VolumeSnapshot data source, it will create a new volume and data will be restored to the volume at the same time. If the provisioner does not support VolumeSnapshot data source, volume will not be created and the failure will be reported as an event. In the future, we plan to support more data source types and the behavior of the provisioner may change.", - Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.TypedLocalObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaimStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimStatus is the current status of a persistent volume claim.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "phase": { - SchemaProps: spec.SchemaProps{ - Description: "Phase represents the current phase of PersistentVolumeClaim.", - Type: []string{"string"}, - Format: "", - }, - }, - "accessModes": { - SchemaProps: spec.SchemaProps{ - Description: "AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "capacity": { - SchemaProps: spec.SchemaProps{ - Description: "Represents the actual resources of the underlying volume.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimCondition"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PersistentVolumeClaimCondition", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeClaimVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace. This volume finds the bound PV and mounts that volume for the pod. A PersistentVolumeClaimVolumeSource is, essentially, a wrapper around another type of volume that is owned by someone else (the system).", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "claimName": { - SchemaProps: spec.SchemaProps{ - Description: "ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Will force the ReadOnly setting in VolumeMounts. Default false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"claimName"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeList is a list of PersistentVolume items.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of persistent volumes. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PersistentVolume"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PersistentVolume", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeSource is similar to VolumeSource but meant for the administrator who creates PVs. Exactly one of its members must be set.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "gcePersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), - }, - }, - "awsElasticBlockStore": { - SchemaProps: spec.SchemaProps{ - Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), - }, - }, - "hostPath": { - SchemaProps: spec.SchemaProps{ - Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), - }, - }, - "glusterfs": { - SchemaProps: spec.SchemaProps{ - Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", - Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), - }, - }, - "nfs": { - SchemaProps: spec.SchemaProps{ - Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), - }, - }, - "rbd": { - SchemaProps: spec.SchemaProps{ - Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", - Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), - }, - }, - "iscsi": { - SchemaProps: spec.SchemaProps{ - Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", - Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), - }, - }, - "cinder": { - SchemaProps: spec.SchemaProps{ - Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), - }, - }, - "cephfs": { - SchemaProps: spec.SchemaProps{ - Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), - }, - }, - "fc": { - SchemaProps: spec.SchemaProps{ - Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", - Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), - }, - }, - "flocker": { - SchemaProps: spec.SchemaProps{ - Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", - Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), - }, - }, - "flexVolume": { - SchemaProps: spec.SchemaProps{ - Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", - Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), - }, - }, - "azureFile": { - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), - }, - }, - "vsphereVolume": { - SchemaProps: spec.SchemaProps{ - Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), - }, - }, - "quobyte": { - SchemaProps: spec.SchemaProps{ - Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), - }, - }, - "azureDisk": { - SchemaProps: spec.SchemaProps{ - Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), - }, - }, - "photonPersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), - }, - }, - "portworxVolume": { - SchemaProps: spec.SchemaProps{ - Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), - }, - }, - "scaleIO": { - SchemaProps: spec.SchemaProps{ - Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), - }, - }, - "local": { - SchemaProps: spec.SchemaProps{ - Description: "Local represents directly-attached storage with node affinity", - Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), - }, - }, - "storageos": { - SchemaProps: spec.SchemaProps{ - Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", - Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), - }, - }, - "csi": { - SchemaProps: spec.SchemaProps{ - Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", - Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeSpec is the specification of a persistent volume.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "capacity": { - SchemaProps: spec.SchemaProps{ - Description: "A description of the persistent volume's resources and capacity. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#capacity", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "gcePersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), - }, - }, - "awsElasticBlockStore": { - SchemaProps: spec.SchemaProps{ - Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), - }, - }, - "hostPath": { - SchemaProps: spec.SchemaProps{ - Description: "HostPath represents a directory on the host. Provisioned by a developer or tester. This is useful for single-node development and testing only! On-host storage is not supported in any way and WILL NOT WORK in a multi-node cluster. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), - }, - }, - "glusterfs": { - SchemaProps: spec.SchemaProps{ - Description: "Glusterfs represents a Glusterfs volume that is attached to a host and exposed to the pod. Provisioned by an admin. More info: https://examples.k8s.io/volumes/glusterfs/README.md", - Ref: ref("k8s.io/api/core/v1.GlusterfsPersistentVolumeSource"), - }, - }, - "nfs": { - SchemaProps: spec.SchemaProps{ - Description: "NFS represents an NFS mount on the host. Provisioned by an admin. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), - }, - }, - "rbd": { - SchemaProps: spec.SchemaProps{ - Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", - Ref: ref("k8s.io/api/core/v1.RBDPersistentVolumeSource"), - }, - }, - "iscsi": { - SchemaProps: spec.SchemaProps{ - Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. Provisioned by an admin.", - Ref: ref("k8s.io/api/core/v1.ISCSIPersistentVolumeSource"), - }, - }, - "cinder": { - SchemaProps: spec.SchemaProps{ - Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Ref: ref("k8s.io/api/core/v1.CinderPersistentVolumeSource"), - }, - }, - "cephfs": { - SchemaProps: spec.SchemaProps{ - Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.CephFSPersistentVolumeSource"), - }, - }, - "fc": { - SchemaProps: spec.SchemaProps{ - Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", - Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), - }, - }, - "flocker": { - SchemaProps: spec.SchemaProps{ - Description: "Flocker represents a Flocker volume attached to a kubelet's host machine and exposed to the pod for its usage. This depends on the Flocker control service being running", - Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), - }, - }, - "flexVolume": { - SchemaProps: spec.SchemaProps{ - Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", - Ref: ref("k8s.io/api/core/v1.FlexPersistentVolumeSource"), - }, - }, - "azureFile": { - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureFilePersistentVolumeSource"), - }, - }, - "vsphereVolume": { - SchemaProps: spec.SchemaProps{ - Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), - }, - }, - "quobyte": { - SchemaProps: spec.SchemaProps{ - Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), - }, - }, - "azureDisk": { - SchemaProps: spec.SchemaProps{ - Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), - }, - }, - "photonPersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), - }, - }, - "portworxVolume": { - SchemaProps: spec.SchemaProps{ - Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), - }, - }, - "scaleIO": { - SchemaProps: spec.SchemaProps{ - Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.ScaleIOPersistentVolumeSource"), - }, - }, - "local": { - SchemaProps: spec.SchemaProps{ - Description: "Local represents directly-attached storage with node affinity", - Ref: ref("k8s.io/api/core/v1.LocalVolumeSource"), - }, - }, - "storageos": { - SchemaProps: spec.SchemaProps{ - Description: "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://examples.k8s.io/volumes/storageos/README.md", - Ref: ref("k8s.io/api/core/v1.StorageOSPersistentVolumeSource"), - }, - }, - "csi": { - SchemaProps: spec.SchemaProps{ - Description: "CSI represents storage that is handled by an external CSI driver (Beta feature).", - Ref: ref("k8s.io/api/core/v1.CSIPersistentVolumeSource"), - }, - }, - "accessModes": { - SchemaProps: spec.SchemaProps{ - Description: "AccessModes contains all ways the volume can be mounted. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "claimRef": { - SchemaProps: spec.SchemaProps{ - Description: "ClaimRef is part of a bi-directional binding between PersistentVolume and PersistentVolumeClaim. Expected to be non-nil when bound. claim.VolumeName is the authoritative bind between PV and PVC. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#binding", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - "persistentVolumeReclaimPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "What happens to a persistent volume when released from its claim. Valid options are Retain (default for manually created PersistentVolumes), Delete (default for dynamically provisioned PersistentVolumes), and Recycle (deprecated). Recycle must be supported by the volume plugin underlying this PersistentVolume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#reclaiming", - Type: []string{"string"}, - Format: "", - }, - }, - "storageClassName": { - SchemaProps: spec.SchemaProps{ - Description: "Name of StorageClass to which this persistent volume belongs. Empty value means that this volume does not belong to any StorageClass.", - Type: []string{"string"}, - Format: "", - }, - }, - "mountOptions": { - SchemaProps: spec.SchemaProps{ - Description: "A list of mount options, e.g. [\"ro\", \"soft\"]. Not validated - mount will simply fail if one is invalid. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "volumeMode": { - SchemaProps: spec.SchemaProps{ - Description: "volumeMode defines if a volume is intended to be used with a formatted filesystem or to remain in raw block state. Value of Filesystem is implied when not included in spec. This is a beta feature.", - Type: []string{"string"}, - Format: "", - }, - }, - "nodeAffinity": { - SchemaProps: spec.SchemaProps{ - Description: "NodeAffinity defines constraints that limit what nodes this volume can be accessed from. This field influences the scheduling of pods that use this volume.", - Ref: ref("k8s.io/api/core/v1.VolumeNodeAffinity"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFilePersistentVolumeSource", "k8s.io/api/core/v1.CSIPersistentVolumeSource", "k8s.io/api/core/v1.CephFSPersistentVolumeSource", "k8s.io/api/core/v1.CinderPersistentVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexPersistentVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GlusterfsPersistentVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIPersistentVolumeSource", "k8s.io/api/core/v1.LocalVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.ObjectReference", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDPersistentVolumeSource", "k8s.io/api/core/v1.ScaleIOPersistentVolumeSource", "k8s.io/api/core/v1.StorageOSPersistentVolumeSource", "k8s.io/api/core/v1.VolumeNodeAffinity", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_PersistentVolumeStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeStatus is the current status of a persistent volume.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "phase": { - SchemaProps: spec.SchemaProps{ - Description: "Phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human-readable message indicating details about why the volume is in this state.", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "Reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PhotonPersistentDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Photon Controller persistent disk resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "pdID": { - SchemaProps: spec.SchemaProps{ - Description: "ID that identifies Photon Controller persistent disk", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"pdID"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Pod(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Pod is a collection of containers that can run on a host. This resource is created by clients and scheduled onto hosts.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.PodSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.PodStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodSpec", "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PodAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Pod affinity is a group of inter pod affinity scheduling rules.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "requiredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), - }, - }, - }, - }, - }, - "preferredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, - } -} - -func schema_k8sio_api_core_v1_PodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "labelSelector": { - SchemaProps: spec.SchemaProps{ - Description: "A label query over a set of resources, in this case pods.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - "namespaces": { - SchemaProps: spec.SchemaProps{ - Description: "namespaces specifies which namespaces the labelSelector applies to (matches against); null or empty list means \"this pod's namespace\"", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "topologyKey": { - SchemaProps: spec.SchemaProps{ - Description: "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"topologyKey"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, - } -} - -func schema_k8sio_api_core_v1_PodAntiAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "requiredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), - }, - }, - }, - }, - }, - "preferredDuringSchedulingIgnoredDuringExecution": { - SchemaProps: spec.SchemaProps{ - Description: "The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.WeightedPodAffinityTerm"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodAffinityTerm", "k8s.io/api/core/v1.WeightedPodAffinityTerm"}, - } -} - -func schema_k8sio_api_core_v1_PodAttachOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodAttachOptions is the query options to a Pod's remote attach call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "stdin": { - SchemaProps: spec.SchemaProps{ - Description: "Stdin if true, redirects the standard input stream of the pod for this call. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stdout": { - SchemaProps: spec.SchemaProps{ - Description: "Stdout if true indicates that stdout is to be redirected for the attach call. Defaults to true.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stderr": { - SchemaProps: spec.SchemaProps{ - Description: "Stderr if true indicates that stderr is to be redirected for the attach call. Defaults to true.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tty": { - SchemaProps: spec.SchemaProps{ - Description: "TTY if true indicates that a tty will be allocated for the attach call. This is passed through the container runtime so the tty is allocated on the worker node by the container runtime. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "container": { - SchemaProps: spec.SchemaProps{ - Description: "The container in which to execute the command. Defaults to only container if there is only one container in the pod.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodCondition contains details for the current condition of this pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type is the type of the condition. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status is the status of the condition. Can be True, False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", - Type: []string{"string"}, - Format: "", - }, - }, - "lastProbeTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time we probed the condition.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Description: "Last time the condition transitioned from one status to another.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "Unique, one-word, CamelCase reason for the condition's last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Human-readable message indicating details about last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_PodDNSConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodDNSConfig defines the DNS parameters of a pod in addition to those generated from DNSPolicy.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "nameservers": { - SchemaProps: spec.SchemaProps{ - Description: "A list of DNS name server IP addresses. This will be appended to the base nameservers generated from DNSPolicy. Duplicated nameservers will be removed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "searches": { - SchemaProps: spec.SchemaProps{ - Description: "A list of DNS search domains for host-name lookup. This will be appended to the base search paths generated from DNSPolicy. Duplicated search paths will be removed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "options": { - SchemaProps: spec.SchemaProps{ - Description: "A list of DNS resolver options. This will be merged with the base options generated from DNSPolicy. Duplicated entries will be removed. Resolution options given in Options will override those that appear in the base DNSPolicy.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodDNSConfigOption"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodDNSConfigOption"}, - } -} - -func schema_k8sio_api_core_v1_PodDNSConfigOption(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodDNSConfigOption defines DNS resolver options of a pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Required.", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodExecOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodExecOptions is the query options to a Pod's remote exec call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "stdin": { - SchemaProps: spec.SchemaProps{ - Description: "Redirect the standard input stream of the pod for this call. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stdout": { - SchemaProps: spec.SchemaProps{ - Description: "Redirect the standard output stream of the pod for this call. Defaults to true.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "stderr": { - SchemaProps: spec.SchemaProps{ - Description: "Redirect the standard error stream of the pod for this call. Defaults to true.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tty": { - SchemaProps: spec.SchemaProps{ - Description: "TTY if true indicates that a tty will be allocated for the exec call. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "container": { - SchemaProps: spec.SchemaProps{ - Description: "Container in which to execute the command. Defaults to only container if there is only one container in the pod.", - Type: []string{"string"}, - Format: "", - }, - }, - "command": { - SchemaProps: spec.SchemaProps{ - Description: "Command is the remote command to execute. argv array. Not executed within a shell.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"command"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodIP(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "IP address information for entries in the (plural) PodIPs field. Each entry includes:\n IP: An IP address allocated to the pod. Routable at least within the cluster.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ip": { - SchemaProps: spec.SchemaProps{ - Description: "ip is an IP address (IPv4 or IPv6) assigned to the pod", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodList is a list of Pods.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of pods. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Pod"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Pod", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_PodLogOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodLogOptions is the query options for a Pod's logs REST call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "container": { - SchemaProps: spec.SchemaProps{ - Description: "The container for which to stream logs. Defaults to only container if there is one container in the pod.", - Type: []string{"string"}, - Format: "", - }, - }, - "follow": { - SchemaProps: spec.SchemaProps{ - Description: "Follow the log stream of the pod. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "previous": { - SchemaProps: spec.SchemaProps{ - Description: "Return previous terminated container logs. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "sinceSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "A relative time in seconds before the current time from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "sinceTime": { - SchemaProps: spec.SchemaProps{ - Description: "An RFC3339 timestamp from which to show logs. If this value precedes the time a pod was started, only logs since the pod start will be returned. If this value is in the future, no logs will be returned. Only one of sinceSeconds or sinceTime may be specified.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "timestamps": { - SchemaProps: spec.SchemaProps{ - Description: "If true, add an RFC3339 or RFC3339Nano timestamp at the beginning of every line of log output. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "tailLines": { - SchemaProps: spec.SchemaProps{ - Description: "If set, the number of lines from the end of the logs to show. If not specified, logs are shown from the creation of the container or sinceSeconds or sinceTime", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "limitBytes": { - SchemaProps: spec.SchemaProps{ - Description: "If set, the number of bytes to read from the server before terminating the log output. This may not display a complete final line of logging, and may return slightly more or slightly less than the specified limit.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_PodPortForwardOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodPortForwardOptions is the query options to a Pod's port forward call when using WebSockets. The `port` query parameter must specify the port or ports (comma separated) to forward over. Port forwarding over SPDY does not use these options. It requires the port to be passed in the `port` header as part of request.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "ports": { - SchemaProps: spec.SchemaProps{ - Description: "List of ports to forward Required when using WebSockets", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodProxyOptions is the query options to a Pod's proxy call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the URL path to use for the current proxy request to pod.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodReadinessGate(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodReadinessGate contains the reference to a pod condition", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "conditionType": { - SchemaProps: spec.SchemaProps{ - Description: "ConditionType refers to a condition in the pod's condition list with matching type.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"conditionType"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PodSecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "seLinuxOptions": { - SchemaProps: spec.SchemaProps{ - Description: "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", - Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), - }, - }, - "windowsOptions": { - SchemaProps: spec.SchemaProps{ - Description: "The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), - }, - }, - "runAsUser": { - SchemaProps: spec.SchemaProps{ - Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "runAsGroup": { - SchemaProps: spec.SchemaProps{ - Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "runAsNonRoot": { - SchemaProps: spec.SchemaProps{ - Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "supplementalGroups": { - SchemaProps: spec.SchemaProps{ - Description: "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - }, - }, - }, - "fsGroup": { - SchemaProps: spec.SchemaProps{ - Description: "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw----\n\nIf unset, the Kubelet will not modify the ownership and permissions of any volume.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "sysctls": { - SchemaProps: spec.SchemaProps{ - Description: "Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Sysctl"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.Sysctl", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, - } -} - -func schema_k8sio_api_core_v1_PodSignature(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Describes the class of pods that should avoid this node. Exactly one field should be set.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "podController": { - SchemaProps: spec.SchemaProps{ - Description: "Reference to controller whose pods should avoid this node.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"}, - } -} - -func schema_k8sio_api_core_v1_PodSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodSpec is a description of a pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumes": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge,retainKeys", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of volumes that can be mounted by containers belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Volume"), - }, - }, - }, - }, - }, - "initContainers": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of initialization containers belonging to the pod. Init containers are executed in order prior to containers being started. If any init container fails, the pod is considered to have failed and is handled according to its restartPolicy. The name for an init container or normal container must be unique among all containers. Init containers may not have Lifecycle actions, Readiness probes, Liveness probes, or Startup probes. The resourceRequirements of an init container are taken into account during scheduling by finding the highest request/limit for each resource type, and then using the max of of that value or the sum of the normal containers. Limits are applied to init containers in a similar fashion. Init containers cannot currently be added or removed. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Container"), - }, - }, - }, - }, - }, - "containers": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of containers belonging to the pod. Containers cannot currently be added or removed. There must be at least one container in a Pod. Cannot be updated.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Container"), - }, - }, - }, - }, - }, - "ephemeralContainers": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of ephemeral containers run in this pod. Ephemeral containers may be run in an existing pod to perform user-initiated actions such as debugging. This list cannot be specified when creating a pod, and it cannot be modified by updating the pod spec. In order to add an ephemeral container to an existing pod, use the pod's ephemeralcontainers subresource. This field is alpha-level and is only honored by servers that enable the EphemeralContainers feature.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.EphemeralContainer"), - }, - }, - }, - }, - }, - "restartPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Restart policy for all containers within the pod. One of Always, OnFailure, Never. Default to Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy", - Type: []string{"string"}, - Format: "", - }, - }, - "terminationGracePeriodSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period will be used instead. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. Defaults to 30 seconds.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "activeDeadlineSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Optional duration in seconds the pod may be active on the node relative to StartTime before the system will actively try to mark it failed and kill associated containers. Value must be a positive integer.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "dnsPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Set DNS policy for the pod. Defaults to \"ClusterFirst\". Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'. DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy. To have DNS options set along with hostNetwork, you have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'.", - Type: []string{"string"}, - Format: "", - }, - }, - "nodeSelector": { - SchemaProps: spec.SchemaProps{ - 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: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "serviceAccountName": { - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", - Type: []string{"string"}, - Format: "", - }, - }, - "serviceAccount": { - SchemaProps: spec.SchemaProps{ - Description: "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.", - Type: []string{"string"}, - Format: "", - }, - }, - "automountServiceAccountToken": { - SchemaProps: spec.SchemaProps{ - Description: "AutomountServiceAccountToken indicates whether a service account token should be automatically mounted.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "nodeName": { - SchemaProps: spec.SchemaProps{ - Description: "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.", - Type: []string{"string"}, - Format: "", - }, - }, - "hostNetwork": { - SchemaProps: spec.SchemaProps{ - Description: "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "hostPID": { - SchemaProps: spec.SchemaProps{ - Description: "Use the host's pid namespace. Optional: Default to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "hostIPC": { - SchemaProps: spec.SchemaProps{ - Description: "Use the host's ipc namespace. Optional: Default to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "shareProcessNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false. This field is beta-level and may be disabled with the PodShareProcessNamespace feature.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "securityContext": { - SchemaProps: spec.SchemaProps{ - Description: "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.", - Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), - }, - }, - "imagePullSecrets": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - 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", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - }, - }, - "hostname": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", - Type: []string{"string"}, - Format: "", - }, - }, - "subdomain": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the fully qualified Pod hostname will be \"...svc.\". If not specified, the pod will not have a domainname at all.", - Type: []string{"string"}, - Format: "", - }, - }, - "affinity": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the pod's scheduling constraints", - Ref: ref("k8s.io/api/core/v1.Affinity"), - }, - }, - "schedulerName": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the pod will be dispatched by specified scheduler. If not specified, the pod will be dispatched by default scheduler.", - Type: []string{"string"}, - Format: "", - }, - }, - "tolerations": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the pod's tolerations.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Toleration"), - }, - }, - }, - }, - }, - "hostAliases": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "ip", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "HostAliases is an optional list of hosts and IPs that will be injected into the pod's hosts file if specified. This is only valid for non-hostNetwork pods.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.HostAlias"), - }, - }, - }, - }, - }, - "priorityClassName": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", - Type: []string{"string"}, - Format: "", - }, - }, - "priority": { - SchemaProps: spec.SchemaProps{ - Description: "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "dnsConfig": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", - Ref: ref("k8s.io/api/core/v1.PodDNSConfig"), - }, - }, - "readinessGates": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodReadinessGate"), - }, - }, - }, - }, - }, - "runtimeClassName": { - SchemaProps: spec.SchemaProps{ - Description: "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", - Type: []string{"string"}, - Format: "", - }, - }, - "enableServiceLinks": { - SchemaProps: spec.SchemaProps{ - Description: "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "preemptionPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is alpha-level and is only honored by servers that enable the NonPreemptingPriority feature.", - Type: []string{"string"}, - Format: "", - }, - }, - "overhead": { - SchemaProps: spec.SchemaProps{ - Description: "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "topologySpreadConstraints": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-map-keys": []interface{}{ - "topologyKey", - "whenUnsatisfiable", - }, - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "topologyKey", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. This field is alpha-level and is only honored by clusters that enables the EvenPodsSpread feature. All topologySpreadConstraints are ANDed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.TopologySpreadConstraint"), - }, - }, - }, - }, - }, - }, - Required: []string{"containers"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.EphemeralContainer", "k8s.io/api/core/v1.HostAlias", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodDNSConfig", "k8s.io/api/core/v1.PodReadinessGate", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.TopologySpreadConstraint", "k8s.io/api/core/v1.Volume", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_PodStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodStatus represents information about the status of a pod. Status may trail the actual state of a system, especially if the node that hosts the pod cannot contact the control plane.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "phase": { - SchemaProps: spec.SchemaProps{ - Description: "The phase of a Pod is a simple, high-level summary of where the Pod is in its lifecycle. The conditions array, the reason and message fields, and the individual container status arrays contain more detail about the pod's status. There are five possible phase values:\n\nPending: The pod has been accepted by the Kubernetes system, but one or more of the container images has not been created. This includes time before being scheduled as well as time spent downloading images over the network, which could take a while. Running: The pod has been bound to a node, and all of the containers have been created. At least one container is still running, or is in the process of starting or restarting. Succeeded: All containers in the pod have terminated in success, and will not be restarted. Failed: All containers in the pod have terminated, and at least one container has terminated in failure. The container either exited with non-zero status or was terminated by the system. Unknown: For some reason the state of the pod could not be obtained, typically due to an error in communicating with the host of the pod.\n\nMore info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-phase", - Type: []string{"string"}, - Format: "", - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Current service state of pod. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodCondition"), - }, - }, - }, - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human readable message indicating details about why the pod is in this condition.", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "A brief CamelCase message indicating details about why the pod is in this state. e.g. 'Evicted'", - Type: []string{"string"}, - Format: "", - }, - }, - "nominatedNodeName": { - SchemaProps: spec.SchemaProps{ - Description: "nominatedNodeName is set only when this pod preempts other pods on the node, but it cannot be scheduled right away as preemption victims receive their graceful termination periods. This field does not guarantee that the pod will be scheduled on this node. Scheduler may decide to place the pod elsewhere if other nodes become available sooner. Scheduler may also decide to give the resources on this node to a higher priority pod that is created after preemption. As a result, this field may be different than PodSpec.nodeName when the pod is scheduled.", - Type: []string{"string"}, - Format: "", - }, - }, - "hostIP": { - SchemaProps: spec.SchemaProps{ - Description: "IP address of the host to which the pod is assigned. Empty if not yet scheduled.", - Type: []string{"string"}, - Format: "", - }, - }, - "podIP": { - SchemaProps: spec.SchemaProps{ - Description: "IP address allocated to the pod. Routable at least within the cluster. Empty if not yet allocated.", - Type: []string{"string"}, - Format: "", - }, - }, - "podIPs": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "ip", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "podIPs holds the IP addresses allocated to the pod. If this field is specified, the 0th entry must match the podIP field. Pods may be allocated at most 1 value for each of IPv4 and IPv6. This list is empty if no IPs have been allocated yet.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodIP"), - }, - }, - }, - }, - }, - "startTime": { - SchemaProps: spec.SchemaProps{ - Description: "RFC 3339 date and time at which the object was acknowledged by the Kubelet. This is before the Kubelet pulled the container image(s) for the pod.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "initContainerStatuses": { - SchemaProps: spec.SchemaProps{ - Description: "The list has one entry per init container in the manifest. The most recent successful init container will have ready = true, the most recently started container will have startTime set. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerStatus"), - }, - }, - }, - }, - }, - "containerStatuses": { - SchemaProps: spec.SchemaProps{ - Description: "The list has one entry per container in the manifest. Each entry is currently the output of `docker inspect`. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerStatus"), - }, - }, - }, - }, - }, - "qosClass": { - SchemaProps: spec.SchemaProps{ - Description: "The Quality of Service (QOS) classification assigned to the pod based on resource requirements See PodQOSClass type for available QOS classes More info: https://git.k8s.io/community/contributors/design-proposals/node/resource-qos.md", - Type: []string{"string"}, - Format: "", - }, - }, - "ephemeralContainerStatuses": { - SchemaProps: spec.SchemaProps{ - Description: "Status for any ephemeral containers that have run in this pod. This field is alpha-level and is only populated by servers that enable the EphemeralContainers feature.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ContainerStatus"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ContainerStatus", "k8s.io/api/core/v1.PodCondition", "k8s.io/api/core/v1.PodIP", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_PodStatusResult(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Most recently observed status of the pod. This data may not be up to date. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.PodStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PodTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodTemplate describes a template for creating copies of a predefined pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "template": { - SchemaProps: spec.SchemaProps{ - Description: "Template defines the pods that will be created from this pod template. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodTemplateSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PodTemplateList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodTemplateList is a list of PodTemplates.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of pod templates", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.PodTemplate"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodTemplate", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_PodTemplateSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PodTemplateSpec describes the data a pod should have when created from a template", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Specification of the desired behavior of the pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.PodSpec"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_PortworxVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PortworxVolumeSource represents a Portworx volume resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeID": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeID uniquely identifies a Portworx volume", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"volumeID"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_PreferAvoidPodsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Describes a class of pods that should avoid this node.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "podSignature": { - SchemaProps: spec.SchemaProps{ - Description: "The class of pods.", - Ref: ref("k8s.io/api/core/v1.PodSignature"), - }, - }, - "evictionTime": { - SchemaProps: spec.SchemaProps{ - Description: "Time at which this entry was added to the list.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "(brief) reason why this entry was added to the list.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Human readable message indicating why this entry was added to the list.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"podSignature"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodSignature", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_PreferredSchedulingTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "weight": { - SchemaProps: spec.SchemaProps{ - Description: "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "preference": { - SchemaProps: spec.SchemaProps{ - Description: "A node selector term, associated with the corresponding weight.", - Ref: ref("k8s.io/api/core/v1.NodeSelectorTerm"), - }, - }, - }, - Required: []string{"weight", "preference"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSelectorTerm"}, - } -} - -func schema_k8sio_api_core_v1_Probe(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Probe describes a health check to be performed against a container to determine whether it is alive or ready to receive traffic.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "exec": { - SchemaProps: spec.SchemaProps{ - Description: "One and only one of the following should be specified. Exec specifies the action to take.", - Ref: ref("k8s.io/api/core/v1.ExecAction"), - }, - }, - "httpGet": { - SchemaProps: spec.SchemaProps{ - Description: "HTTPGet specifies the http request to perform.", - Ref: ref("k8s.io/api/core/v1.HTTPGetAction"), - }, - }, - "tcpSocket": { - SchemaProps: spec.SchemaProps{ - Description: "TCPSocket specifies an action involving a TCP port. TCP hooks not yet supported", - Ref: ref("k8s.io/api/core/v1.TCPSocketAction"), - }, - }, - "initialDelaySeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "timeoutSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "periodSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "successThreshold": { - SchemaProps: spec.SchemaProps{ - Description: "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "failureThreshold": { - SchemaProps: spec.SchemaProps{ - Description: "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ExecAction", "k8s.io/api/core/v1.HTTPGetAction", "k8s.io/api/core/v1.TCPSocketAction"}, - } -} - -func schema_k8sio_api_core_v1_ProjectedVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a projected volume source", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "sources": { - SchemaProps: spec.SchemaProps{ - Description: "list of volume projections", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.VolumeProjection"), - }, - }, - }, - }, - }, - "defaultMode": { - SchemaProps: spec.SchemaProps{ - Description: "Mode bits to use on created files by default. Must be a value between 0 and 0777. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"sources"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.VolumeProjection"}, - } -} - -func schema_k8sio_api_core_v1_QuobyteVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Quobyte mount that lasts the lifetime of a pod. Quobyte volumes do not support ownership management or SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "registry": { - SchemaProps: spec.SchemaProps{ - Description: "Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes", - Type: []string{"string"}, - Format: "", - }, - }, - "volume": { - SchemaProps: spec.SchemaProps{ - Description: "Volume is a string that references an already created Quobyte volume by name.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "user": { - SchemaProps: spec.SchemaProps{ - Description: "User to map volume access to Defaults to serivceaccount user", - Type: []string{"string"}, - Format: "", - }, - }, - "group": { - SchemaProps: spec.SchemaProps{ - Description: "Group to map volume access to Default is no group", - Type: []string{"string"}, - Format: "", - }, - }, - "tenant": { - SchemaProps: spec.SchemaProps{ - Description: "Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"registry", "volume"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_RBDPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "monitors": { - SchemaProps: spec.SchemaProps{ - Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", - Type: []string{"string"}, - Format: "", - }, - }, - "pool": { - SchemaProps: spec.SchemaProps{ - Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "user": { - SchemaProps: spec.SchemaProps{ - Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "keyring": { - SchemaProps: spec.SchemaProps{ - Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"monitors", "image"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_RBDVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a Rados Block Device mount that lasts the lifetime of a pod. RBD volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "monitors": { - SchemaProps: spec.SchemaProps{ - Description: "A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "image": { - SchemaProps: spec.SchemaProps{ - Description: "The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd", - Type: []string{"string"}, - Format: "", - }, - }, - "pool": { - SchemaProps: spec.SchemaProps{ - Description: "The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "user": { - SchemaProps: spec.SchemaProps{ - Description: "The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "keyring": { - SchemaProps: spec.SchemaProps{ - Description: "Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"monitors", "image"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_RangeAllocation(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "RangeAllocation is not a public type.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "range": { - SchemaProps: spec.SchemaProps{ - Description: "Range is string that identifies the range represented by 'data'.", - Type: []string{"string"}, - Format: "", - }, - }, - "data": { - SchemaProps: spec.SchemaProps{ - Description: "Data is a bit array containing all allocated addresses in the previous segment.", - Type: []string{"string"}, - Format: "byte", - }, - }, - }, - Required: []string{"range", "data"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ReplicationController(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ReplicationController represents the configuration of a replication controller.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ReplicationControllerSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ReplicationControllerStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ReplicationControllerSpec", "k8s.io/api/core/v1.ReplicationControllerStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ReplicationControllerCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ReplicationControllerCondition describes the state of a replication controller at a certain point.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of replication controller condition.", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the condition, one of True, False, Unknown.", - Type: []string{"string"}, - Format: "", - }, - }, - "lastTransitionTime": { - SchemaProps: spec.SchemaProps{ - Description: "The last time the condition transitioned from one status to another.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "The reason for the condition's last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human readable message indicating details about the transition.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_ReplicationControllerList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ReplicationControllerList is a collection of replication controllers.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of replication controllers. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ReplicationController"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ReplicationController", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ReplicationControllerSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ReplicationControllerSpec is the specification of a replication controller.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "replicas": { - SchemaProps: spec.SchemaProps{ - Description: "Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified. Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "minReadySeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready)", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template. Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "template": { - SchemaProps: spec.SchemaProps{ - Description: "Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", - Ref: ref("k8s.io/api/core/v1.PodTemplateSpec"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodTemplateSpec"}, - } -} - -func schema_k8sio_api_core_v1_ReplicationControllerStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ReplicationControllerStatus represents the current status of a replication controller.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "replicas": { - SchemaProps: spec.SchemaProps{ - Description: "Replicas is the most recently oberved number of replicas. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "fullyLabeledReplicas": { - SchemaProps: spec.SchemaProps{ - Description: "The number of pods that have labels matching the labels of the pod template of the replication controller.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "readyReplicas": { - SchemaProps: spec.SchemaProps{ - Description: "The number of ready replicas for this replication controller.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "availableReplicas": { - SchemaProps: spec.SchemaProps{ - Description: "The number of available replicas (ready for at least minReadySeconds) for this replication controller.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "observedGeneration": { - SchemaProps: spec.SchemaProps{ - Description: "ObservedGeneration reflects the generation of the most recently observed replication controller.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "conditions": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "type", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Represents the latest available observations of a replication controller's current state.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ReplicationControllerCondition"), - }, - }, - }, - }, - }, - }, - Required: []string{"replicas"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ReplicationControllerCondition"}, - } -} - -func schema_k8sio_api_core_v1_ResourceFieldSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceFieldSelector represents container resources (cpu, memory) and their output format", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "containerName": { - SchemaProps: spec.SchemaProps{ - Description: "Container name: required for volumes, optional for env vars", - Type: []string{"string"}, - Format: "", - }, - }, - "resource": { - SchemaProps: spec.SchemaProps{ - Description: "Required: resource to select", - Type: []string{"string"}, - Format: "", - }, - }, - "divisor": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies the output format of the exposed resources, defaults to \"1\"", - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - Required: []string{"resource"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_ResourceQuota(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceQuota sets aggregate quota restrictions enforced per namespace", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the desired quota. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ResourceQuotaSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status defines the actual enforced quota and its current usage. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ResourceQuotaStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ResourceQuotaSpec", "k8s.io/api/core/v1.ResourceQuotaStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ResourceQuotaList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceQuotaList is a list of ResourceQuota items.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is a list of ResourceQuota objects. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ResourceQuota"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ResourceQuota", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ResourceQuotaSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceQuotaSpec defines the desired hard limits to enforce for Quota.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "hard": { - SchemaProps: spec.SchemaProps{ - Description: "hard is the set of desired hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "scopes": { - SchemaProps: spec.SchemaProps{ - Description: "A collection of filters that must match each object tracked by a quota. If not specified, the quota matches all objects.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "scopeSelector": { - SchemaProps: spec.SchemaProps{ - Description: "scopeSelector is also a collection of filters like scopes that must match each object tracked by a quota but expressed using ScopeSelectorOperator in combination with possible values. For a resource to match, both scopes AND scopeSelector (if specified in spec), must be matched.", - Ref: ref("k8s.io/api/core/v1.ScopeSelector"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ScopeSelector", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_ResourceQuotaStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceQuotaStatus defines the enforced hard limits and observed use.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "hard": { - SchemaProps: spec.SchemaProps{ - Description: "Hard is the set of enforced hard limits for each named resource. More info: https://kubernetes.io/docs/concepts/policy/resource-quotas/", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "used": { - SchemaProps: spec.SchemaProps{ - Description: "Used is the current observed total usage of the resource in the namespace.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_ResourceRequirements(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ResourceRequirements describes the compute resource requirements.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "limits": { - SchemaProps: spec.SchemaProps{ - Description: "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - "requests": { - SchemaProps: spec.SchemaProps{ - Description: "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/api/resource.Quantity"}, - } -} - -func schema_k8sio_api_core_v1_SELinuxOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SELinuxOptions are the labels to be applied to the container", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "user": { - SchemaProps: spec.SchemaProps{ - Description: "User is a SELinux user label that applies to the container.", - Type: []string{"string"}, - Format: "", - }, - }, - "role": { - SchemaProps: spec.SchemaProps{ - Description: "Role is a SELinux role label that applies to the container.", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type is a SELinux type label that applies to the container.", - Type: []string{"string"}, - Format: "", - }, - }, - "level": { - SchemaProps: spec.SchemaProps{ - Description: "Level is SELinux level label that applies to the container.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ScaleIOPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "gateway": { - SchemaProps: spec.SchemaProps{ - Description: "The host address of the ScaleIO API Gateway.", - Type: []string{"string"}, - Format: "", - }, - }, - "system": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the storage system as configured in ScaleIO.", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", - Ref: ref("k8s.io/api/core/v1.SecretReference"), - }, - }, - "sslEnabled": { - SchemaProps: spec.SchemaProps{ - Description: "Flag to enable/disable SSL communication with Gateway, default false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "protectionDomain": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the ScaleIO Protection Domain for the configured storage.", - Type: []string{"string"}, - Format: "", - }, - }, - "storagePool": { - SchemaProps: spec.SchemaProps{ - Description: "The ScaleIO Storage Pool associated with the protection domain.", - Type: []string{"string"}, - Format: "", - }, - }, - "storageMode": { - SchemaProps: spec.SchemaProps{ - Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeName": { - SchemaProps: spec.SchemaProps{ - Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\"", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"gateway", "system", "secretRef"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.SecretReference"}, - } -} - -func schema_k8sio_api_core_v1_ScaleIOVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ScaleIOVolumeSource represents a persistent ScaleIO volume", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "gateway": { - SchemaProps: spec.SchemaProps{ - Description: "The host address of the ScaleIO API Gateway.", - Type: []string{"string"}, - Format: "", - }, - }, - "system": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the storage system as configured in ScaleIO.", - Type: []string{"string"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - "sslEnabled": { - SchemaProps: spec.SchemaProps{ - Description: "Flag to enable/disable SSL communication with Gateway, default false", - Type: []string{"boolean"}, - Format: "", - }, - }, - "protectionDomain": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the ScaleIO Protection Domain for the configured storage.", - Type: []string{"string"}, - Format: "", - }, - }, - "storagePool": { - SchemaProps: spec.SchemaProps{ - Description: "The ScaleIO Storage Pool associated with the protection domain.", - Type: []string{"string"}, - Format: "", - }, - }, - "storageMode": { - SchemaProps: spec.SchemaProps{ - Description: "Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeName": { - SchemaProps: spec.SchemaProps{ - Description: "The name of a volume already created in the ScaleIO system that is associated with this volume source.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Default is \"xfs\".", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"gateway", "system", "secretRef"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_ScopeSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A scope selector represents the AND of the selectors represented by the scoped-resource selector requirements.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "matchExpressions": { - SchemaProps: spec.SchemaProps{ - Description: "A list of scope selector requirements by scope of the resources.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ScopedResourceSelectorRequirement"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ScopedResourceSelectorRequirement"}, - } -} - -func schema_k8sio_api_core_v1_ScopedResourceSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A scoped-resource selector requirement is a selector that contains values, a scope name, and an operator that relates the scope name and values.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "scopeName": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the scope that the selector applies to.", - Type: []string{"string"}, - Format: "", - }, - }, - "operator": { - SchemaProps: spec.SchemaProps{ - Description: "Represents a scope's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist.", - Type: []string{"string"}, - Format: "", - }, - }, - "values": { - SchemaProps: spec.SchemaProps{ - Description: "An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"scopeName", "operator"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Secret(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Secret holds secret data of a certain type. The total bytes of the values in the Data field must be less than MaxSecretSize bytes.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "data": { - SchemaProps: spec.SchemaProps{ - Description: "Data contains the secret data. Each key must consist of alphanumeric characters, '-', '_' or '.'. The serialized form of the secret data is a base64 encoded string, representing the arbitrary (possibly non-string) data value here. Described in https://tools.ietf.org/html/rfc4648#section-4", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "byte", - }, - }, - }, - }, - }, - "stringData": { - SchemaProps: spec.SchemaProps{ - Description: "stringData allows specifying non-binary secret data in string form. It is provided as a write-only convenience method. All keys and values are merged into the data field on write, overwriting any existing values. It is never output when reading from the API.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Used to facilitate programmatic handling of secret data.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_SecretEnvSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SecretEnvSource selects a Secret to populate the environment variables with.\n\nThe contents of the target Secret's Data field will represent the key-value pairs as environment variables.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the Secret must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_SecretKeySelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SecretKeySelector selects a key of a Secret.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "key": { - SchemaProps: spec.SchemaProps{ - Description: "The key of the secret to select from. Must be a valid secret key.", - Type: []string{"string"}, - Format: "", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the Secret or its key must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"key"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_SecretList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SecretList is a list of Secret.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "Items is a list of secret objects. More info: https://kubernetes.io/docs/concepts/configuration/secret", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Secret"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Secret", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_SecretProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Adapts a secret into a projected volume.\n\nThe contents of the target Secret's Data field will be presented in a projected volume as files using the keys in the Data field as the file names. Note that this is identical to a secret volume source without the default mode.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.KeyToPath"), - }, - }, - }, - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the Secret or its key must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.KeyToPath"}, - } -} - -func schema_k8sio_api_core_v1_SecretReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SecretReference represents a Secret Reference. It has enough information to retrieve secret in any namespace", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name is unique within a namespace to reference a secret resource.", - Type: []string{"string"}, - Format: "", - }, - }, - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace defines the space within which the secret name must be unique.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_SecretVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Adapts a Secret into a volume.\n\nThe contents of the target Secret's Data field will be presented in a volume as files using the keys in the Data field as the file names. Secret volumes support ownership management and SELinux relabeling.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "secretName": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", - Type: []string{"string"}, - Format: "", - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.KeyToPath"), - }, - }, - }, - }, - }, - "defaultMode": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: mode bits to use on created files by default. Must be a value between 0 and 0777. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "optional": { - SchemaProps: spec.SchemaProps{ - Description: "Specify whether the Secret or its keys must be defined", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.KeyToPath"}, - } -} - -func schema_k8sio_api_core_v1_SecurityContext(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "capabilities": { - SchemaProps: spec.SchemaProps{ - Description: "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.", - Ref: ref("k8s.io/api/core/v1.Capabilities"), - }, - }, - "privileged": { - SchemaProps: spec.SchemaProps{ - Description: "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "seLinuxOptions": { - SchemaProps: spec.SchemaProps{ - Description: "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Ref: ref("k8s.io/api/core/v1.SELinuxOptions"), - }, - }, - "windowsOptions": { - SchemaProps: spec.SchemaProps{ - Description: "The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Ref: ref("k8s.io/api/core/v1.WindowsSecurityContextOptions"), - }, - }, - "runAsUser": { - SchemaProps: spec.SchemaProps{ - Description: "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "runAsGroup": { - SchemaProps: spec.SchemaProps{ - Description: "The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "runAsNonRoot": { - SchemaProps: spec.SchemaProps{ - Description: "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "readOnlyRootFilesystem": { - SchemaProps: spec.SchemaProps{ - Description: "Whether this container has a read-only root filesystem. Default is false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "allowPrivilegeEscalation": { - SchemaProps: spec.SchemaProps{ - Description: "AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN", - Type: []string{"boolean"}, - Format: "", - }, - }, - "procMount": { - SchemaProps: spec.SchemaProps{ - Description: "procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Capabilities", "k8s.io/api/core/v1.SELinuxOptions", "k8s.io/api/core/v1.WindowsSecurityContextOptions"}, - } -} - -func schema_k8sio_api_core_v1_SerializedReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SerializedReference is a reference to serialized object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "reference": { - SchemaProps: spec.SchemaProps{ - Description: "The reference to an object in the system.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_Service(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Service is a named abstraction of software service (for example, mysql) consisting of local port (for example 3306) that the proxy listens on, and the selector that determines which pods will answer requests sent through the proxy.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "spec": { - SchemaProps: spec.SchemaProps{ - Description: "Spec defines the behavior of a service. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ServiceSpec"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Most recently observed status of the service. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Ref: ref("k8s.io/api/core/v1.ServiceStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ServiceSpec", "k8s.io/api/core/v1.ServiceStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ServiceAccount(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - "secrets": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "name", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Secrets is the list of secrets allowed to be used by pods running using this ServiceAccount. More info: https://kubernetes.io/docs/concepts/configuration/secret", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - }, - }, - }, - "imagePullSecrets": { - SchemaProps: spec.SchemaProps{ - Description: "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - }, - }, - "automountServiceAccountToken": { - SchemaProps: spec.SchemaProps{ - Description: "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.ObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_k8sio_api_core_v1_ServiceAccountList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccountList is a list of ServiceAccount objects", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of ServiceAccounts. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ServiceAccount"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ServiceAccount", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ServiceAccountTokenProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceAccountTokenProjection represents a projected service account token volume. This projection can be used to insert a service account token into the pods runtime filesystem for use against APIs (Kubernetes API Server or otherwise).", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "audience": { - SchemaProps: spec.SchemaProps{ - Description: "Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.", - Type: []string{"string"}, - Format: "", - }, - }, - "expirationSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the path relative to the mount point of the file to project the token into.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"path"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ServiceList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceList holds a list of services.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of services", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.Service"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.Service", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, - } -} - -func schema_k8sio_api_core_v1_ServicePort(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServicePort contains information on service's port.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", - Type: []string{"string"}, - Format: "", - }, - }, - "protocol": { - SchemaProps: spec.SchemaProps{ - Description: "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", - Type: []string{"string"}, - Format: "", - }, - }, - "port": { - SchemaProps: spec.SchemaProps{ - Description: "The port that will be exposed by this service.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "targetPort": { - SchemaProps: spec.SchemaProps{ - Description: "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service", - Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), - }, - }, - "nodePort": { - SchemaProps: spec.SchemaProps{ - Description: "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"port"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, - } -} - -func schema_k8sio_api_core_v1_ServiceProxyOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceProxyOptions is the query options to a Service's proxy call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "path": { - SchemaProps: spec.SchemaProps{ - Description: "Path is the part of URLs that include service endpoints, suffixes, and parameters to use for the current proxy request to service. For example, the whole request URL is http://localhost/api/v1/namespaces/kube-system/services/elasticsearch-logging/_search?q=user:kimchy. Path is _search?q=user:kimchy.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_ServiceSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceSpec describes the attributes that a user creates on a service.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "ports": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-list-map-keys": []interface{}{ - "port", - "protocol", - }, - "x-kubernetes-list-type": "map", - "x-kubernetes-patch-merge-key": "port", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.ServicePort"), - }, - }, - }, - }, - }, - "selector": { - SchemaProps: spec.SchemaProps{ - Description: "Route service traffic to pods with label keys and values matching this selector. If empty or not present, the service is assumed to have an external process managing its endpoints, which Kubernetes will not modify. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "clusterIP": { - SchemaProps: spec.SchemaProps{ - Description: "clusterIP is the IP address of the service and is usually assigned randomly by the master. If an address is specified manually and is not in use by others, it will be allocated to the service; otherwise, creation of the service will fail. This field can not be changed through updates. Valid values are \"None\", empty string (\"\"), or a valid IP address. \"None\" can be specified for headless services when proxying is not required. Only applies to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is ExternalName. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. \"ExternalName\" maps to the specified externalName. \"ClusterIP\" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object. If clusterIP is \"None\", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a stable IP. \"NodePort\" builds on ClusterIP and allocates a port on every node which routes to the clusterIP. \"LoadBalancer\" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the clusterIP. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types", - Type: []string{"string"}, - Format: "", - }, - }, - "externalIPs": { - SchemaProps: spec.SchemaProps{ - 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.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "sessionAffinity": { - SchemaProps: spec.SchemaProps{ - Description: "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", - Type: []string{"string"}, - Format: "", - }, - }, - "loadBalancerIP": { - SchemaProps: spec.SchemaProps{ - Description: "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", - Type: []string{"string"}, - Format: "", - }, - }, - "loadBalancerSourceRanges": { - SchemaProps: spec.SchemaProps{ - Description: "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.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "externalName": { - SchemaProps: spec.SchemaProps{ - Description: "externalName is the external reference that kubedns or equivalent will return as a CNAME record for this service. No proxying will be involved. Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires Type to be ExternalName.", - Type: []string{"string"}, - Format: "", - }, - }, - "externalTrafficPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \"Local\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \"Cluster\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.", - Type: []string{"string"}, - Format: "", - }, - }, - "healthCheckNodePort": { - SchemaProps: spec.SchemaProps{ - Description: "healthCheckNodePort specifies the healthcheck nodePort for the service. If not specified, HealthCheckNodePort is created by the service api backend with the allocated nodePort. Will use user-specified nodePort value if specified by the client. Only effects when Type is set to LoadBalancer and ExternalTrafficPolicy is set to Local.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "publishNotReadyAddresses": { - SchemaProps: spec.SchemaProps{ - Description: "publishNotReadyAddresses, when set to true, indicates that DNS implementations must publish the notReadyAddresses of subsets for the Endpoints associated with the Service. The default value is false. The primary use case for setting this field is to use a StatefulSet's Headless Service to propagate SRV records for its Pods without respect to their readiness for purpose of peer discovery.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "sessionAffinityConfig": { - SchemaProps: spec.SchemaProps{ - Description: "sessionAffinityConfig contains the configurations of session affinity.", - Ref: ref("k8s.io/api/core/v1.SessionAffinityConfig"), - }, - }, - "ipFamily": { - SchemaProps: spec.SchemaProps{ - Description: "ipFamily specifies whether this Service has a preference for a particular IP family (e.g. IPv4 vs. IPv6). If a specific IP family is requested, the clusterIP field will be allocated from that family, if it is available in the cluster. If no IP family is requested, the cluster's primary IP family will be used. Other IP fields (loadBalancerIP, loadBalancerSourceRanges, externalIPs) and controllers which allocate external load-balancers should use the same IP family. Endpoints for this Service will be of this family. This field is immutable after creation. Assigning a ServiceIPFamily not available in the cluster (e.g. IPv6 in IPv4 only cluster) is an error condition and will fail during clusterIP assignment.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ServicePort", "k8s.io/api/core/v1.SessionAffinityConfig"}, - } -} - -func schema_k8sio_api_core_v1_ServiceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServiceStatus represents the current status of a service.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "loadBalancer": { - SchemaProps: spec.SchemaProps{ - Description: "LoadBalancer contains the current status of the load-balancer, if one is present.", - Ref: ref("k8s.io/api/core/v1.LoadBalancerStatus"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LoadBalancerStatus"}, - } -} - -func schema_k8sio_api_core_v1_SessionAffinityConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "SessionAffinityConfig represents the configurations of session affinity.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "clientIP": { - SchemaProps: spec.SchemaProps{ - Description: "clientIP contains the configurations of Client IP based session affinity.", - Ref: ref("k8s.io/api/core/v1.ClientIPConfig"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ClientIPConfig"}, - } -} - -func schema_k8sio_api_core_v1_StorageOSPersistentVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a StorageOS persistent volume resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeName": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", - Ref: ref("k8s.io/api/core/v1.ObjectReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_StorageOSVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a StorageOS persistent volume resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumeName": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.", - Type: []string{"string"}, - Format: "", - }, - }, - "volumeNamespace": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to \"default\" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "secretRef": { - SchemaProps: spec.SchemaProps{ - Description: "SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.", - Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.LocalObjectReference"}, - } -} - -func schema_k8sio_api_core_v1_Sysctl(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Sysctl defines a kernel parameter to be set", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of a property to set", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Description: "Value of a property to set", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "value"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_TCPSocketAction(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TCPSocketAction describes an action based on opening a socket", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "port": { - SchemaProps: spec.SchemaProps{ - Description: "Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.", - Ref: ref("k8s.io/apimachinery/pkg/util/intstr.IntOrString"), - }, - }, - "host": { - SchemaProps: spec.SchemaProps{ - Description: "Optional: Host name to connect to, defaults to the pod IP.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"port"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/util/intstr.IntOrString"}, - } -} - -func schema_k8sio_api_core_v1_Taint(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "The node this Taint is attached to has the \"effect\" on any pod that does not tolerate the Taint.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Description: "Required. The taint key to be applied to a node.", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Description: "Required. The taint value corresponding to the taint key.", - Type: []string{"string"}, - Format: "", - }, - }, - "effect": { - SchemaProps: spec.SchemaProps{ - Description: "Required. The effect of the taint on pods that do not tolerate the taint. Valid effects are NoSchedule, PreferNoSchedule and NoExecute.", - Type: []string{"string"}, - Format: "", - }, - }, - "timeAdded": { - SchemaProps: spec.SchemaProps{ - Description: "TimeAdded represents the time at which the taint was added. It is only written for NoExecute taints.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - }, - Required: []string{"key", "effect"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_k8sio_api_core_v1_Toleration(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator .", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Description: "Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.", - Type: []string{"string"}, - Format: "", - }, - }, - "operator": { - SchemaProps: spec.SchemaProps{ - Description: "Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.", - Type: []string{"string"}, - Format: "", - }, - }, - "value": { - SchemaProps: spec.SchemaProps{ - Description: "Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.", - Type: []string{"string"}, - Format: "", - }, - }, - "effect": { - SchemaProps: spec.SchemaProps{ - Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.", - Type: []string{"string"}, - Format: "", - }, - }, - "tolerationSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_api_core_v1_TopologySelectorLabelRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A topology selector requirement is a selector that matches given label. This is an alpha feature and may change in the future.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - SchemaProps: spec.SchemaProps{ - Description: "The label key that the selector applies to.", - Type: []string{"string"}, - Format: "", - }, - }, - "values": { - SchemaProps: spec.SchemaProps{ - Description: "An array of string values. One value must match the label to be selected. Each entry in Values is ORed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"key", "values"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_TopologySelectorTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A topology selector term represents the result of label queries. A null or empty topology selector term matches no objects. The requirements of them are ANDed. It provides a subset of functionality as NodeSelectorTerm. This is an alpha feature and may change in the future.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "matchLabelExpressions": { - SchemaProps: spec.SchemaProps{ - Description: "A list of topology selector requirements by labels.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/api/core/v1.TopologySelectorLabelRequirement"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.TopologySelectorLabelRequirement"}, - } -} - -func schema_k8sio_api_core_v1_TopologySpreadConstraint(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TopologySpreadConstraint specifies how to spread matching pods among the given topology.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "maxSkew": { - SchemaProps: spec.SchemaProps{ - Description: "MaxSkew describes the degree to which pods may be unevenly distributed. It's the maximum permitted difference between the number of matching pods in any two topology domains of a given topology type. For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 1/1/1; scheduling it onto zone1(zone2) would make the ActualSkew(2-0) on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming pod can be scheduled onto any zone. It's a required field. Default value is 1 and 0 is not allowed.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "topologyKey": { - SchemaProps: spec.SchemaProps{ - Description: "TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. We consider each as a \"bucket\", and try to put balanced number of pods into each bucket. It's a required field.", - Type: []string{"string"}, - Format: "", - }, - }, - "whenUnsatisfiable": { - SchemaProps: spec.SchemaProps{ - Description: "WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it - ScheduleAnyway tells the scheduler to still schedule it It's considered as \"Unsatisfiable\" if and only if placing incoming pod on any topology violates \"MaxSkew\". For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same labelSelector spread as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler won't make it *more* imbalanced. It's a required field.", - Type: []string{"string"}, - Format: "", - }, - }, - "labelSelector": { - SchemaProps: spec.SchemaProps{ - Description: "LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), - }, - }, - }, - Required: []string{"maxSkew", "topologyKey", "whenUnsatisfiable"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, - } -} - -func schema_k8sio_api_core_v1_TypedLocalObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TypedLocalObjectReference contains enough information to let you locate the typed referenced object inside the same namespace.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiGroup": { - SchemaProps: spec.SchemaProps{ - Description: "APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.", - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind is the type of resource being referenced", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name is the name of resource being referenced", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"kind", "name"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Volume represents a named volume in a pod that may be accessed by any container in the pod.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Volume's name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", - Type: []string{"string"}, - Format: "", - }, - }, - "hostPath": { - SchemaProps: spec.SchemaProps{ - Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), - }, - }, - "emptyDir": { - SchemaProps: spec.SchemaProps{ - Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", - Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), - }, - }, - "gcePersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), - }, - }, - "awsElasticBlockStore": { - SchemaProps: spec.SchemaProps{ - Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), - }, - }, - "gitRepo": { - SchemaProps: spec.SchemaProps{ - Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", - Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), - }, - }, - "secret": { - SchemaProps: spec.SchemaProps{ - Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", - Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), - }, - }, - "nfs": { - SchemaProps: spec.SchemaProps{ - Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), - }, - }, - "iscsi": { - SchemaProps: spec.SchemaProps{ - Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", - Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), - }, - }, - "glusterfs": { - SchemaProps: spec.SchemaProps{ - Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", - Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), - }, - }, - "persistentVolumeClaim": { - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), - }, - }, - "rbd": { - SchemaProps: spec.SchemaProps{ - Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", - Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), - }, - }, - "flexVolume": { - SchemaProps: spec.SchemaProps{ - Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", - Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), - }, - }, - "cinder": { - SchemaProps: spec.SchemaProps{ - Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), - }, - }, - "cephfs": { - SchemaProps: spec.SchemaProps{ - Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), - }, - }, - "flocker": { - SchemaProps: spec.SchemaProps{ - Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", - Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), - }, - }, - "downwardAPI": { - SchemaProps: spec.SchemaProps{ - Description: "DownwardAPI represents downward API about the pod that should populate this volume", - Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), - }, - }, - "fc": { - SchemaProps: spec.SchemaProps{ - Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", - Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), - }, - }, - "azureFile": { - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), - }, - }, - "configMap": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap represents a configMap that should populate this volume", - Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), - }, - }, - "vsphereVolume": { - SchemaProps: spec.SchemaProps{ - Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), - }, - }, - "quobyte": { - SchemaProps: spec.SchemaProps{ - Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), - }, - }, - "azureDisk": { - SchemaProps: spec.SchemaProps{ - Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), - }, - }, - "photonPersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), - }, - }, - "projected": { - SchemaProps: spec.SchemaProps{ - Description: "Items for all in one resources secrets, configmaps, and downward API", - Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), - }, - }, - "portworxVolume": { - SchemaProps: spec.SchemaProps{ - Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), - }, - }, - "scaleIO": { - SchemaProps: spec.SchemaProps{ - Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), - }, - }, - "storageos": { - SchemaProps: spec.SchemaProps{ - Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), - }, - }, - "csi": { - SchemaProps: spec.SchemaProps{ - Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", - Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), - }, - }, - }, - Required: []string{"name"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, - } -} - -func schema_k8sio_api_core_v1_VolumeDevice(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "volumeDevice describes a mapping of a raw block device within a container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "name must match the name of a persistentVolumeClaim in the pod", - Type: []string{"string"}, - Format: "", - }, - }, - "devicePath": { - SchemaProps: spec.SchemaProps{ - Description: "devicePath is the path inside of the container that the device will be mapped to.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "devicePath"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_VolumeMount(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "VolumeMount describes a mounting of a Volume within a container.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "This must match the Name of a Volume.", - Type: []string{"string"}, - Format: "", - }, - }, - "readOnly": { - SchemaProps: spec.SchemaProps{ - Description: "Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "mountPath": { - SchemaProps: spec.SchemaProps{ - Description: "Path within the container at which the volume should be mounted. Must not contain ':'.", - Type: []string{"string"}, - Format: "", - }, - }, - "subPath": { - SchemaProps: spec.SchemaProps{ - Description: "Path within the volume from which the container's volume should be mounted. Defaults to \"\" (volume's root).", - Type: []string{"string"}, - Format: "", - }, - }, - "mountPropagation": { - SchemaProps: spec.SchemaProps{ - Description: "mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.", - Type: []string{"string"}, - Format: "", - }, - }, - "subPathExpr": { - SchemaProps: spec.SchemaProps{ - Description: "Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to \"\" (volume's root). SubPathExpr and SubPath are mutually exclusive. This field is beta in 1.15.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "mountPath"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_VolumeNodeAffinity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "VolumeNodeAffinity defines constraints that limit what nodes this volume can be accessed from.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "required": { - SchemaProps: spec.SchemaProps{ - Description: "Required specifies hard node constraints that must be met.", - Ref: ref("k8s.io/api/core/v1.NodeSelector"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.NodeSelector"}, - } -} - -func schema_k8sio_api_core_v1_VolumeProjection(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Projection that may be projected along with other supported volume types", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "secret": { - SchemaProps: spec.SchemaProps{ - Description: "information about the secret data to project", - Ref: ref("k8s.io/api/core/v1.SecretProjection"), - }, - }, - "downwardAPI": { - SchemaProps: spec.SchemaProps{ - Description: "information about the downwardAPI data to project", - Ref: ref("k8s.io/api/core/v1.DownwardAPIProjection"), - }, - }, - "configMap": { - SchemaProps: spec.SchemaProps{ - Description: "information about the configMap data to project", - Ref: ref("k8s.io/api/core/v1.ConfigMapProjection"), - }, - }, - "serviceAccountToken": { - SchemaProps: spec.SchemaProps{ - Description: "information about the serviceAccountToken data to project", - Ref: ref("k8s.io/api/core/v1.ServiceAccountTokenProjection"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.ConfigMapProjection", "k8s.io/api/core/v1.DownwardAPIProjection", "k8s.io/api/core/v1.SecretProjection", "k8s.io/api/core/v1.ServiceAccountTokenProjection"}, - } -} - -func schema_k8sio_api_core_v1_VolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents the source of a volume to mount. Only one of its members may be specified.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "hostPath": { - SchemaProps: spec.SchemaProps{ - Description: "HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath", - Ref: ref("k8s.io/api/core/v1.HostPathVolumeSource"), - }, - }, - "emptyDir": { - SchemaProps: spec.SchemaProps{ - Description: "EmptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir", - Ref: ref("k8s.io/api/core/v1.EmptyDirVolumeSource"), - }, - }, - "gcePersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk", - Ref: ref("k8s.io/api/core/v1.GCEPersistentDiskVolumeSource"), - }, - }, - "awsElasticBlockStore": { - SchemaProps: spec.SchemaProps{ - Description: "AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore", - Ref: ref("k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource"), - }, - }, - "gitRepo": { - SchemaProps: spec.SchemaProps{ - Description: "GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.", - Ref: ref("k8s.io/api/core/v1.GitRepoVolumeSource"), - }, - }, - "secret": { - SchemaProps: spec.SchemaProps{ - Description: "Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret", - Ref: ref("k8s.io/api/core/v1.SecretVolumeSource"), - }, - }, - "nfs": { - SchemaProps: spec.SchemaProps{ - Description: "NFS represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs", - Ref: ref("k8s.io/api/core/v1.NFSVolumeSource"), - }, - }, - "iscsi": { - SchemaProps: spec.SchemaProps{ - Description: "ISCSI represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md", - Ref: ref("k8s.io/api/core/v1.ISCSIVolumeSource"), - }, - }, - "glusterfs": { - SchemaProps: spec.SchemaProps{ - Description: "Glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md", - Ref: ref("k8s.io/api/core/v1.GlusterfsVolumeSource"), - }, - }, - "persistentVolumeClaim": { - SchemaProps: spec.SchemaProps{ - Description: "PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims", - Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource"), - }, - }, - "rbd": { - SchemaProps: spec.SchemaProps{ - Description: "RBD represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md", - Ref: ref("k8s.io/api/core/v1.RBDVolumeSource"), - }, - }, - "flexVolume": { - SchemaProps: spec.SchemaProps{ - Description: "FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.", - Ref: ref("k8s.io/api/core/v1.FlexVolumeSource"), - }, - }, - "cinder": { - SchemaProps: spec.SchemaProps{ - Description: "Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md", - Ref: ref("k8s.io/api/core/v1.CinderVolumeSource"), - }, - }, - "cephfs": { - SchemaProps: spec.SchemaProps{ - Description: "CephFS represents a Ceph FS mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.CephFSVolumeSource"), - }, - }, - "flocker": { - SchemaProps: spec.SchemaProps{ - Description: "Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running", - Ref: ref("k8s.io/api/core/v1.FlockerVolumeSource"), - }, - }, - "downwardAPI": { - SchemaProps: spec.SchemaProps{ - Description: "DownwardAPI represents downward API about the pod that should populate this volume", - Ref: ref("k8s.io/api/core/v1.DownwardAPIVolumeSource"), - }, - }, - "fc": { - SchemaProps: spec.SchemaProps{ - Description: "FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.", - Ref: ref("k8s.io/api/core/v1.FCVolumeSource"), - }, - }, - "azureFile": { - SchemaProps: spec.SchemaProps{ - Description: "AzureFile represents an Azure File Service mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureFileVolumeSource"), - }, - }, - "configMap": { - SchemaProps: spec.SchemaProps{ - Description: "ConfigMap represents a configMap that should populate this volume", - Ref: ref("k8s.io/api/core/v1.ConfigMapVolumeSource"), - }, - }, - "vsphereVolume": { - SchemaProps: spec.SchemaProps{ - Description: "VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"), - }, - }, - "quobyte": { - SchemaProps: spec.SchemaProps{ - Description: "Quobyte represents a Quobyte mount on the host that shares a pod's lifetime", - Ref: ref("k8s.io/api/core/v1.QuobyteVolumeSource"), - }, - }, - "azureDisk": { - SchemaProps: spec.SchemaProps{ - Description: "AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.", - Ref: ref("k8s.io/api/core/v1.AzureDiskVolumeSource"), - }, - }, - "photonPersistentDisk": { - SchemaProps: spec.SchemaProps{ - Description: "PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource"), - }, - }, - "projected": { - SchemaProps: spec.SchemaProps{ - Description: "Items for all in one resources secrets, configmaps, and downward API", - Ref: ref("k8s.io/api/core/v1.ProjectedVolumeSource"), - }, - }, - "portworxVolume": { - SchemaProps: spec.SchemaProps{ - Description: "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine", - Ref: ref("k8s.io/api/core/v1.PortworxVolumeSource"), - }, - }, - "scaleIO": { - SchemaProps: spec.SchemaProps{ - Description: "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.ScaleIOVolumeSource"), - }, - }, - "storageos": { - SchemaProps: spec.SchemaProps{ - Description: "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.", - Ref: ref("k8s.io/api/core/v1.StorageOSVolumeSource"), - }, - }, - "csi": { - SchemaProps: spec.SchemaProps{ - Description: "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).", - Ref: ref("k8s.io/api/core/v1.CSIVolumeSource"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.AWSElasticBlockStoreVolumeSource", "k8s.io/api/core/v1.AzureDiskVolumeSource", "k8s.io/api/core/v1.AzureFileVolumeSource", "k8s.io/api/core/v1.CSIVolumeSource", "k8s.io/api/core/v1.CephFSVolumeSource", "k8s.io/api/core/v1.CinderVolumeSource", "k8s.io/api/core/v1.ConfigMapVolumeSource", "k8s.io/api/core/v1.DownwardAPIVolumeSource", "k8s.io/api/core/v1.EmptyDirVolumeSource", "k8s.io/api/core/v1.FCVolumeSource", "k8s.io/api/core/v1.FlexVolumeSource", "k8s.io/api/core/v1.FlockerVolumeSource", "k8s.io/api/core/v1.GCEPersistentDiskVolumeSource", "k8s.io/api/core/v1.GitRepoVolumeSource", "k8s.io/api/core/v1.GlusterfsVolumeSource", "k8s.io/api/core/v1.HostPathVolumeSource", "k8s.io/api/core/v1.ISCSIVolumeSource", "k8s.io/api/core/v1.NFSVolumeSource", "k8s.io/api/core/v1.PersistentVolumeClaimVolumeSource", "k8s.io/api/core/v1.PhotonPersistentDiskVolumeSource", "k8s.io/api/core/v1.PortworxVolumeSource", "k8s.io/api/core/v1.ProjectedVolumeSource", "k8s.io/api/core/v1.QuobyteVolumeSource", "k8s.io/api/core/v1.RBDVolumeSource", "k8s.io/api/core/v1.ScaleIOVolumeSource", "k8s.io/api/core/v1.SecretVolumeSource", "k8s.io/api/core/v1.StorageOSVolumeSource", "k8s.io/api/core/v1.VsphereVirtualDiskVolumeSource"}, - } -} - -func schema_k8sio_api_core_v1_VsphereVirtualDiskVolumeSource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Represents a vSphere volume resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "volumePath": { - SchemaProps: spec.SchemaProps{ - Description: "Path that identifies vSphere volume vmdk", - Type: []string{"string"}, - Format: "", - }, - }, - "fsType": { - SchemaProps: spec.SchemaProps{ - Description: "Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. \"ext4\", \"xfs\", \"ntfs\". Implicitly inferred to be \"ext4\" if unspecified.", - Type: []string{"string"}, - Format: "", - }, - }, - "storagePolicyName": { - SchemaProps: spec.SchemaProps{ - Description: "Storage Policy Based Management (SPBM) profile name.", - Type: []string{"string"}, - Format: "", - }, - }, - "storagePolicyID": { - SchemaProps: spec.SchemaProps{ - Description: "Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"volumePath"}, - }, - }, - } -} - -func schema_k8sio_api_core_v1_WeightedPodAffinityTerm(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s)", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "weight": { - SchemaProps: spec.SchemaProps{ - Description: "weight associated with matching the corresponding podAffinityTerm, in the range 1-100.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - "podAffinityTerm": { - SchemaProps: spec.SchemaProps{ - Description: "Required. A pod affinity term, associated with the corresponding weight.", - Ref: ref("k8s.io/api/core/v1.PodAffinityTerm"), - }, - }, - }, - Required: []string{"weight", "podAffinityTerm"}, - }, - }, - Dependencies: []string{ - "k8s.io/api/core/v1.PodAffinityTerm"}, - } -} - -func schema_k8sio_api_core_v1_WindowsSecurityContextOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "WindowsSecurityContextOptions contain Windows-specific options and credentials.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "gmsaCredentialSpecName": { - SchemaProps: spec.SchemaProps{ - Description: "GMSACredentialSpecName is the name of the GMSA credential spec to use. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", - Type: []string{"string"}, - Format: "", - }, - }, - "gmsaCredentialSpec": { - SchemaProps: spec.SchemaProps{ - Description: "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. This field is alpha-level and is only honored by servers that enable the WindowsGMSA feature flag.", - Type: []string{"string"}, - Format: "", - }, - }, - "runAsUserName": { - SchemaProps: spec.SchemaProps{ - Description: "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. This field is alpha-level and it is only honored by servers that enable the WindowsRunAsUserName feature flag.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_apimachinery_pkg_api_resource_Quantity(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n ::= \n (Note that may be empty, from the \"\" case in .)\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n ::= m | \"\" | k | M | G | T | P | E\n (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n ::= \"e\" | \"E\" \n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n a. No precision is lost\n b. No fractional digits will be emitted\n c. The exponent (or suffix) is as large as possible.\nThe sign will be omitted unless the number is negative.\n\nExamples:\n 1.5 will be serialized as \"1500m\"\n 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation.", - Type: resource.Quantity{}.OpenAPISchemaType(), - Format: resource.Quantity{}.OpenAPISchemaFormat(), - }, - }, - } -} - -func schema_apimachinery_pkg_api_resource_int64Amount(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster than operations on inf.Dec for values that can be represented as int64.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "value": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int64", - }, - }, - "scale": { - SchemaProps: spec.SchemaProps{ - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"value", "scale"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_APIGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "APIGroup contains the name, the supported versions, and the preferred version of a group.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "name is the name of the group.", - Type: []string{"string"}, - Format: "", - }, - }, - "versions": { - SchemaProps: spec.SchemaProps{ - Description: "versions are the versions supported in this group.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), - }, - }, - }, - }, - }, - "preferredVersion": { - SchemaProps: spec.SchemaProps{ - Description: "preferredVersion is the version preferred by the API server, which probably is the storage version.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery"), - }, - }, - "serverAddressByClientCIDRs": { - SchemaProps: spec.SchemaProps{ - Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), - }, - }, - }, - }, - }, - }, - Required: []string{"name", "versions"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery", "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, - } -} - -func schema_pkg_apis_meta_v1_APIGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "groups": { - SchemaProps: spec.SchemaProps{ - Description: "groups is a list of APIGroup.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"), - }, - }, - }, - }, - }, - }, - Required: []string{"groups"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup"}, - } -} - -func schema_pkg_apis_meta_v1_APIResource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "APIResource specifies the name of a resource and whether it is namespaced.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "name is the plural name of the resource.", - Type: []string{"string"}, - Format: "", - }, - }, - "singularName": { - SchemaProps: spec.SchemaProps{ - Description: "singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface.", - Type: []string{"string"}, - Format: "", - }, - }, - "namespaced": { - SchemaProps: spec.SchemaProps{ - Description: "namespaced indicates if a resource is namespaced or not.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "group": { - SchemaProps: spec.SchemaProps{ - Description: "group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale\".", - Type: []string{"string"}, - Format: "", - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Description: "version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)\".", - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", - Type: []string{"string"}, - Format: "", - }, - }, - "verbs": { - SchemaProps: spec.SchemaProps{ - Description: "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "shortNames": { - SchemaProps: spec.SchemaProps{ - Description: "shortNames is a list of suggested short names of the resource.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "categories": { - SchemaProps: spec.SchemaProps{ - Description: "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "storageVersionHash": { - SchemaProps: spec.SchemaProps{ - Description: "The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"name", "singularName", "namespaced", "kind", "verbs"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_APIResourceList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "groupVersion": { - SchemaProps: spec.SchemaProps{ - Description: "groupVersion is the group and version this APIResourceList is for.", - Type: []string{"string"}, - Format: "", - }, - }, - "resources": { - SchemaProps: spec.SchemaProps{ - Description: "resources contains the name of the resources and if they are namespaced.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"), - }, - }, - }, - }, - }, - }, - Required: []string{"groupVersion", "resources"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource"}, - } -} - -func schema_pkg_apis_meta_v1_APIVersions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "versions": { - SchemaProps: spec.SchemaProps{ - Description: "versions are the api versions that are available.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "serverAddressByClientCIDRs": { - SchemaProps: spec.SchemaProps{ - Description: "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"), - }, - }, - }, - }, - }, - }, - Required: []string{"versions", "serverAddressByClientCIDRs"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR"}, - } -} - -func schema_pkg_apis_meta_v1_CreateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "CreateOptions may be provided when creating an API object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "dryRun": { - SchemaProps: spec.SchemaProps{ - Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "fieldManager": { - SchemaProps: spec.SchemaProps{ - Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "DeleteOptions may be provided when deleting an API object.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "gracePeriodSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "preconditions": { - SchemaProps: spec.SchemaProps{ - Description: "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"), - }, - }, - "orphanDependents": { - SchemaProps: spec.SchemaProps{ - Description: "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "propagationPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground.", - Type: []string{"string"}, - Format: "", - }, - }, - "dryRun": { - SchemaProps: spec.SchemaProps{ - Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.Preconditions"}, - } -} - -func schema_pkg_apis_meta_v1_Duration(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Duration is a wrapper around time.Duration which supports correct marshaling to YAML and JSON. In particular, it marshals into strings, which can be used as map keys in json.", - Type: v1.Duration{}.OpenAPISchemaType(), - Format: v1.Duration{}.OpenAPISchemaFormat(), - }, - }, - } -} - -func schema_pkg_apis_meta_v1_ExportOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ExportOptions is the query options to the standard REST get call. Deprecated. Planned for removal in 1.18.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "export": { - SchemaProps: spec.SchemaProps{ - Description: "Should this value be exported. Export strips fields that a user can not specify. Deprecated. Planned for removal in 1.18.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "exact": { - SchemaProps: spec.SchemaProps{ - Description: "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'. Deprecated. Planned for removal in 1.18.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"export", "exact"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_FieldsV1(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff", - Type: []string{"object"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GetOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GetOptions is the standard query options to the standard REST get call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupKind(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"group", "kind"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupResource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying concepts during lookup stages without having partially valid types", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "resource": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"group", "resource"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupVersion(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupVersion contains the \"group\" and the \"version\", which uniquely identifies the API.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"group", "version"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupVersionForDiscovery(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "groupVersion": { - SchemaProps: spec.SchemaProps{ - Description: "groupVersion specifies the API group and version in the form \"group/version\"", - Type: []string{"string"}, - Format: "", - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Description: "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"groupVersion", "version"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupVersionKind(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"group", "version", "kind"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_GroupVersionResource(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "group": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "version": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "resource": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"group", "version", "resource"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_InternalEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "InternalEvent makes watch.Event versioned", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "Type": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "Object": { - SchemaProps: spec.SchemaProps{ - Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Bookmark: the object (instance of a type being watched) where\n only ResourceVersion field is set. On successful restart of watch from a\n bookmark resourceVersion, client is guaranteed to not get repeat event\n nor miss any events.\n * If Type is Error: *api.Status is recommended; other types may make sense\n depending on context.", - Ref: ref("k8s.io/apimachinery/pkg/runtime.Object"), - }, - }, - }, - Required: []string{"Type", "Object"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.Object"}, - } -} - -func schema_pkg_apis_meta_v1_LabelSelector(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "matchLabels": { - SchemaProps: spec.SchemaProps{ - Description: "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "matchExpressions": { - SchemaProps: spec.SchemaProps{ - Description: "matchExpressions is a list of label selector requirements. The requirements are ANDed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement"}, - } -} - -func schema_pkg_apis_meta_v1_LabelSelectorRequirement(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "key": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "key", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "key is the label key that the selector applies to.", - Type: []string{"string"}, - Format: "", - }, - }, - "operator": { - SchemaProps: spec.SchemaProps{ - Description: "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", - Type: []string{"string"}, - Format: "", - }, - }, - "values": { - SchemaProps: spec.SchemaProps{ - Description: "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"key", "operator"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_List(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "List holds a list of objects, which may not be known by the server.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "List of objects", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, - } -} - -func schema_pkg_apis_meta_v1_ListMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "selfLink": { - SchemaProps: spec.SchemaProps{ - Description: "selfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - Type: []string{"string"}, - Format: "", - }, - }, - "continue": { - SchemaProps: spec.SchemaProps{ - Description: "continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message.", - Type: []string{"string"}, - Format: "", - }, - }, - "remainingItemCount": { - SchemaProps: spec.SchemaProps{ - Description: "remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_ListOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ListOptions is the query options to a standard REST list call.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "labelSelector": { - SchemaProps: spec.SchemaProps{ - Description: "A selector to restrict the list of returned objects by their labels. Defaults to everything.", - Type: []string{"string"}, - Format: "", - }, - }, - "fieldSelector": { - SchemaProps: spec.SchemaProps{ - Description: "A selector to restrict the list of returned objects by their fields. Defaults to everything.", - Type: []string{"string"}, - Format: "", - }, - }, - "watch": { - SchemaProps: spec.SchemaProps{ - Description: "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "allowWatchBookmarks": { - SchemaProps: spec.SchemaProps{ - Description: "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.\n\nThis field is beta.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.", - Type: []string{"string"}, - Format: "", - }, - }, - "timeoutSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "limit": { - SchemaProps: spec.SchemaProps{ - Description: "limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.\n\nThe server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "continue": { - SchemaProps: spec.SchemaProps{ - Description: "The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the \"next key\".\n\nThis field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_ManagedFieldsEntry(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "manager": { - SchemaProps: spec.SchemaProps{ - Description: "Manager is an identifier of the workflow managing these fields.", - Type: []string{"string"}, - Format: "", - }, - }, - "operation": { - SchemaProps: spec.SchemaProps{ - Description: "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", - Type: []string{"string"}, - Format: "", - }, - }, - "time": { - SchemaProps: spec.SchemaProps{ - Description: "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "fieldsType": { - SchemaProps: spec.SchemaProps{ - Description: "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", - Type: []string{"string"}, - Format: "", - }, - }, - "fieldsV1": { - SchemaProps: spec.SchemaProps{ - Description: "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.FieldsV1", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_pkg_apis_meta_v1_MicroTime(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "MicroTime is version of Time with microsecond level precision.", - Type: v1.MicroTime{}.OpenAPISchemaType(), - Format: v1.MicroTime{}.OpenAPISchemaFormat(), - }, - }, - } -} - -func schema_pkg_apis_meta_v1_ObjectMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names", - Type: []string{"string"}, - Format: "", - }, - }, - "generateName": { - SchemaProps: spec.SchemaProps{ - Description: "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", - Type: []string{"string"}, - Format: "", - }, - }, - "namespace": { - SchemaProps: spec.SchemaProps{ - Description: "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces", - Type: []string{"string"}, - Format: "", - }, - }, - "selfLink": { - SchemaProps: spec.SchemaProps{ - Description: "SelfLink is a URL representing this object. Populated by the system. Read-only.\n\nDEPRECATED Kubernetes will stop propagating this field in 1.20 release and the field is planned to be removed in 1.21 release.", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency", - Type: []string{"string"}, - Format: "", - }, - }, - "generation": { - SchemaProps: spec.SchemaProps{ - Description: "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "creationTimestamp": { - SchemaProps: spec.SchemaProps{ - Description: "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "deletionTimestamp": { - SchemaProps: spec.SchemaProps{ - Description: "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), - }, - }, - "deletionGracePeriodSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "labels": { - SchemaProps: spec.SchemaProps{ - Description: "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "annotations": { - SchemaProps: spec.SchemaProps{ - Description: "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations", - Type: []string{"object"}, - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "ownerReferences": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-merge-key": "uid", - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference"), - }, - }, - }, - }, - }, - "finalizers": { - VendorExtensible: spec.VendorExtensible{ - Extensions: spec.Extensions{ - "x-kubernetes-patch-strategy": "merge", - }, - }, - SchemaProps: spec.SchemaProps{ - Description: "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "clusterName": { - SchemaProps: spec.SchemaProps{ - Description: "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.", - Type: []string{"string"}, - Format: "", - }, - }, - "managedFields": { - SchemaProps: spec.SchemaProps{ - Description: "ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like \"ci-cd\". The set of fields is always in the version that the workflow used when modifying the object.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry"), - }, - }, - }, - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ManagedFieldsEntry", "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, - } -} - -func schema_pkg_apis_meta_v1_OwnerReference(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Description: "API version of the referent.", - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "name": { - SchemaProps: spec.SchemaProps{ - Description: "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids", - Type: []string{"string"}, - Format: "", - }, - }, - "controller": { - SchemaProps: spec.SchemaProps{ - Description: "If true, this reference points to the managing controller.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "blockOwnerDeletion": { - SchemaProps: spec.SchemaProps{ - Description: "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.", - Type: []string{"boolean"}, - Format: "", - }, - }, - }, - Required: []string{"apiVersion", "kind", "name", "uid"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_PartialObjectMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PartialObjectMetadata is a generic representation of any object with ObjectMeta. It allows clients to get access to a particular ObjectMeta schema without knowing the details of the version.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, - } -} - -func schema_pkg_apis_meta_v1_PartialObjectMetadataList(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PartialObjectMetadataList contains a list of objects containing only their metadata", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "items": { - SchemaProps: spec.SchemaProps{ - Description: "items contains each of the included items.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"), - }, - }, - }, - }, - }, - }, - Required: []string{"items"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.PartialObjectMetadata"}, - } -} - -func schema_pkg_apis_meta_v1_Patch(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", - Type: []string{"object"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_PatchOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "PatchOptions may be provided when patching an API object. PatchOptions is meant to be a superset of UpdateOptions.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "dryRun": { - SchemaProps: spec.SchemaProps{ - Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "force": { - SchemaProps: spec.SchemaProps{ - Description: "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "fieldManager": { - SchemaProps: spec.SchemaProps{ - Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_Preconditions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies the target UID.", - Type: []string{"string"}, - Format: "", - }, - }, - "resourceVersion": { - SchemaProps: spec.SchemaProps{ - Description: "Specifies the target ResourceVersion", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_RootPaths(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "RootPaths lists the paths available at root. For example: \"/healthz\", \"/apis\".", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "paths": { - SchemaProps: spec.SchemaProps{ - Description: "paths are the paths available at root.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - }, - Required: []string{"paths"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_ServerAddressByClientCIDR(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "clientCIDR": { - SchemaProps: spec.SchemaProps{ - Description: "The CIDR with which clients can match their IP to figure out the server address that they should use.", - Type: []string{"string"}, - Format: "", - }, - }, - "serverAddress": { - SchemaProps: spec.SchemaProps{ - Description: "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"clientCIDR", "serverAddress"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_Status(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Status is a return value for calls that don't return other objects.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the operation. One of: \"Success\" or \"Failure\". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human-readable description of the status of this operation.", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.", - Type: []string{"string"}, - Format: "", - }, - }, - "details": { - SchemaProps: spec.SchemaProps{ - Description: "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"), - }, - }, - "code": { - SchemaProps: spec.SchemaProps{ - Description: "Suggested HTTP return code for this status, 0 if not set.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails"}, - } -} - -func schema_pkg_apis_meta_v1_StatusCause(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "A machine-readable description of the cause of the error. If this value is empty there is no information available.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", - Type: []string{"string"}, - Format: "", - }, - }, - "field": { - SchemaProps: spec.SchemaProps{ - Description: "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_StatusDetails(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", - Type: []string{"string"}, - Format: "", - }, - }, - "group": { - SchemaProps: spec.SchemaProps{ - Description: "The group attribute of the resource associated with the status StatusReason.", - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Description: "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "uid": { - SchemaProps: spec.SchemaProps{ - Description: "UID of the resource. (when there is a single resource which can be described). More info: http://kubernetes.io/docs/user-guide/identifiers#uids", - Type: []string{"string"}, - Format: "", - }, - }, - "causes": { - SchemaProps: spec.SchemaProps{ - Description: "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"), - }, - }, - }, - }, - }, - "retryAfterSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause"}, - } -} - -func schema_pkg_apis_meta_v1_Table(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Table is a tabular representation of a set of API resources. The server transforms the object into a set of preferred columns for quickly reviewing the objects.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "metadata": { - SchemaProps: spec.SchemaProps{ - Description: "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), - }, - }, - "columnDefinitions": { - SchemaProps: spec.SchemaProps{ - Description: "columnDefinitions describes each column in the returned items array. The number of cells per row will always match the number of column definitions.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition"), - }, - }, - }, - }, - }, - "rows": { - SchemaProps: spec.SchemaProps{ - Description: "rows is the list of items in the table.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"), - }, - }, - }, - }, - }, - }, - Required: []string{"columnDefinitions", "rows"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.TableColumnDefinition", "k8s.io/apimachinery/pkg/apis/meta/v1.TableRow"}, - } -} - -func schema_pkg_apis_meta_v1_TableColumnDefinition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TableColumnDefinition contains information about a column returned in the Table.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "name": { - SchemaProps: spec.SchemaProps{ - Description: "name is a human readable name for the column.", - Type: []string{"string"}, - Format: "", - }, - }, - "type": { - SchemaProps: spec.SchemaProps{ - Description: "type is an OpenAPI type definition for this column, such as number, integer, string, or array. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", - Type: []string{"string"}, - Format: "", - }, - }, - "format": { - SchemaProps: spec.SchemaProps{ - Description: "format is an optional OpenAPI type modifier for this column. A format modifies the type and imposes additional rules, like date or time formatting for a string. The 'name' format is applied to the primary identifier column which has type 'string' to assist in clients identifying column is the resource name. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types for more.", - Type: []string{"string"}, - Format: "", - }, - }, - "description": { - SchemaProps: spec.SchemaProps{ - Description: "description is a human readable description of this column.", - Type: []string{"string"}, - Format: "", - }, - }, - "priority": { - SchemaProps: spec.SchemaProps{ - Description: "priority is an integer defining the relative importance of this column compared to others. Lower numbers are considered higher priority. Columns that may be omitted in limited space scenarios should be given a higher priority.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"name", "type", "format", "description", "priority"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_TableOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TableOptions are used when a Table is requested by the caller.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "includeObject": { - SchemaProps: spec.SchemaProps{ - Description: "includeObject decides whether to include each object along with its columnar information. Specifying \"None\" will return no object, specifying \"Object\" will return the full object contents, and specifying \"Metadata\" (the default) will return the object's metadata in the PartialObjectMetadata kind in version v1beta1 of the meta.k8s.io API group.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_TableRow(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TableRow is an individual row in a table.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "cells": { - SchemaProps: spec.SchemaProps{ - Description: "cells will be as wide as the column definitions array and may contain strings, numbers (float64 or int64), booleans, simple maps, lists, or null. See the type field of the column definition for a more detailed description.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Format: "", - }, - }, - }, - }, - }, - "conditions": { - SchemaProps: spec.SchemaProps{ - Description: "conditions describe additional status of a row that are relevant for a human user. These conditions apply to the row, not to the object, and will be specific to table output. The only defined condition type is 'Completed', for a row that indicates a resource that has run to completion and can be given less visual priority.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition"), - }, - }, - }, - }, - }, - "object": { - SchemaProps: spec.SchemaProps{ - Description: "This field contains the requested additional information about each object based on the includeObject policy when requesting the Table. If \"None\", this field is empty, if \"Object\" this will be the default serialization of the object for the current API version, and if \"Metadata\" (the default) will contain the object metadata. Check the returned kind and apiVersion of the object before parsing. The media type of the object will always match the enclosing list - if this as a JSON table, these will be JSON encoded objects.", - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), - }, - }, - }, - Required: []string{"cells"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/apis/meta/v1.TableRowCondition", "k8s.io/apimachinery/pkg/runtime.RawExtension"}, - } -} - -func schema_pkg_apis_meta_v1_TableRowCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TableRowCondition allows a row to be marked with additional information.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Description: "Type of row condition. The only defined value is 'Completed' indicating that the object this row represents has reached a completed state and may be given less visual priority than other rows. Clients are not required to honor any conditions but should be consistent where possible about handling the conditions.", - Type: []string{"string"}, - Format: "", - }, - }, - "status": { - SchemaProps: spec.SchemaProps{ - Description: "Status of the condition, one of True, False, Unknown.", - Type: []string{"string"}, - Format: "", - }, - }, - "reason": { - SchemaProps: spec.SchemaProps{ - Description: "(brief) machine readable reason for the condition's last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - "message": { - SchemaProps: spec.SchemaProps{ - Description: "Human readable message indicating details about last transition.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"type", "status"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_Time(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", - Type: v1.Time{}.OpenAPISchemaType(), - Format: v1.Time{}.OpenAPISchemaFormat(), - }, - }, - } -} - -func schema_pkg_apis_meta_v1_Timestamp(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Timestamp is a struct that is equivalent to Time, but intended for protobuf marshalling/unmarshalling. It is generated into a serialization that matches Time. Do not use in Go structs.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "seconds": { - SchemaProps: spec.SchemaProps{ - Description: "Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.", - Type: []string{"integer"}, - Format: "int64", - }, - }, - "nanos": { - SchemaProps: spec.SchemaProps{ - Description: "Non-negative fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values that count forward in time. Must be from 0 to 999,999,999 inclusive. This field may be limited in precision depending on context.", - Type: []string{"integer"}, - Format: "int32", - }, - }, - }, - Required: []string{"seconds", "nanos"}, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TypeMeta describes an individual object in an API response or request with strings representing the type of the object and its API schema version. Structures that are versioned or persisted should inline TypeMeta.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_UpdateOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "UpdateOptions may be provided when updating an API object. All fields in UpdateOptions should also be present in PatchOptions.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "kind": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#types-kinds", - Type: []string{"string"}, - Format: "", - }, - }, - "apiVersion": { - SchemaProps: spec.SchemaProps{ - 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/sig-architecture/api-conventions.md#resources", - Type: []string{"string"}, - Format: "", - }, - }, - "dryRun": { - SchemaProps: spec.SchemaProps{ - Description: "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - "fieldManager": { - SchemaProps: spec.SchemaProps{ - Description: "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_pkg_apis_meta_v1_WatchEvent(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Event represents a single event to a watched resource.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "type": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "object": { - SchemaProps: spec.SchemaProps{ - Description: "Object is:\n * If Type is Added or Modified: the new state of the object.\n * If Type is Deleted: the state of the object immediately before deletion.\n * If Type is Error: *Status is recommended; other types may make sense\n depending on context.", - Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"), - }, - }, - }, - Required: []string{"type", "object"}, - }, - }, - Dependencies: []string{ - "k8s.io/apimachinery/pkg/runtime.RawExtension"}, - } -} - -func schema_k8sio_apimachinery_pkg_runtime_RawExtension(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "RawExtension is used to hold extensions in external versions.\n\nTo use this, make a field which has RawExtension as its type in your external, versioned struct, and Object in your internal struct. You also need to register your various plugin types.\n\n// Internal package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.Object `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// External package: type MyAPIObject struct {\n\truntime.TypeMeta `json:\",inline\"`\n\tMyPlugin runtime.RawExtension `json:\"myPlugin\"`\n} type PluginA struct {\n\tAOption string `json:\"aOption\"`\n}\n\n// On the wire, the JSON will look something like this: {\n\t\"kind\":\"MyAPIObject\",\n\t\"apiVersion\":\"v1\",\n\t\"myPlugin\": {\n\t\t\"kind\":\"PluginA\",\n\t\t\"aOption\":\"foo\",\n\t},\n}\n\nSo what happens? Decode first uses json or yaml to unmarshal the serialized data into your external MyAPIObject. That causes the raw JSON to be stored, but not unpacked. The next step is to copy (using pkg/conversion) into the internal struct. The runtime package's DefaultScheme has conversion functions installed which will unpack the JSON stored in RawExtension, turning it into the correct object type, and storing it in the Object. (TODO: In the case where the object is of an unknown type, a runtime.Unknown object will be created and stored.)", - Type: []string{"object"}, - }, - }, - } -} - -func schema_k8sio_apimachinery_pkg_runtime_TypeMeta(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "TypeMeta is shared by all top level objects. The proper way to use it is to inline it in your type, like this: type MyAwesomeAPIObject struct {\n runtime.TypeMeta `json:\",inline\"`\n ... // other fields\n} func (obj *MyAwesomeAPIObject) SetGroupVersionKind(gvk *metav1.GroupVersionKind) { metav1.UpdateTypeMeta(obj,gvk) }; GroupVersionKind() *GroupVersionKind\n\nTypeMeta is provided here for convenience. You may use it directly from this package or define your own with the same fields.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, - } -} - -func schema_k8sio_apimachinery_pkg_runtime_Unknown(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Unknown allows api objects with unknown types to be passed-through. This can be used to deal with the API objects from a plug-in. Unknown objects still have functioning TypeMeta features-- kind, version, etc. metadata and field mutatation.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "apiVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "kind": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "Raw": { - SchemaProps: spec.SchemaProps{ - Description: "Raw will hold the complete serialized object which couldn't be matched with a registered type. Most likely, nothing should be done with this except for passing it through the system.", - Type: []string{"string"}, - Format: "byte", - }, - }, - "ContentEncoding": { - SchemaProps: spec.SchemaProps{ - Description: "ContentEncoding is encoding used to encode 'Raw' data. Unspecified means no encoding.", - Type: []string{"string"}, - Format: "", - }, - }, - "ContentType": { - SchemaProps: spec.SchemaProps{ - Description: "ContentType is serialization method used to serialize 'Raw'. Unspecified means ContentTypeJSON.", - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"Raw", "ContentEncoding", "ContentType"}, - }, - }, - } -} - -func schema_apimachinery_pkg_util_intstr_IntOrString(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "IntOrString is a type that can hold an int32 or a string. When used in JSON or YAML marshalling and unmarshalling, it produces or consumes the inner type. This allows you to have, for example, a JSON field that can accept a name or number.", - Type: intstr.IntOrString{}.OpenAPISchemaType(), - Format: intstr.IntOrString{}.OpenAPISchemaFormat(), - }, - }, - } -} - -func schema_k8sio_apimachinery_pkg_version_Info(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Description: "Info contains versioning information. how we'll want to distribute that information.", - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "major": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "minor": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "gitVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "gitCommit": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "gitTreeState": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "buildDate": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "goVersion": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "compiler": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - "platform": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, - }, - Required: []string{"major", "minor", "gitVersion", "gitCommit", "gitTreeState", "buildDate", "goVersion", "compiler", "platform"}, - }, - }, - } -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/register.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/register.go deleted file mode 100644 index eb65d4f4a..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/register.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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. -*/ - -// NOTE: Boilerplate only. Ignore this file. - -// Package v1alpha1 contains API Schema definitions for the devops v1alpha1 API group -// +k8s:openapi-gen=true -// +k8s:deepcopy-gen=package,register -// +k8s:conversion-gen=github.com/kubesphere/s2ioperator/pkg/apis/devops -// +k8s:defaulter-gen=TypeMeta -// +groupName=devops.kubesphere.io -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" -) - -var ( - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: "devops.kubesphere.io", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} - - // AddToScheme is required by pkg/client/... - AddToScheme = SchemeBuilder.AddToScheme -) - -// Resource is required by pkg/client/listers/... -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_webhook.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_webhook.go deleted file mode 100644 index 3f1bd7001..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuilder_webhook.go +++ /dev/null @@ -1,284 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 v1alpha1 - -import ( - "context" - "encoding/json" - "fmt" - "github.com/kubesphere/s2ioperator/pkg/errors" - "github.com/kubesphere/s2ioperator/pkg/util/reflectutils" - k8serror "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - errorutil "k8s.io/apimachinery/pkg/util/errors" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - "strings" -) - -const ( - DefaultRevisionId = "master" - DefaultTag = "latest" -) - -// log is for logging in this package. -var s2ibuilderlog = logf.Log.WithName("s2ibuilder-resource") - -func (r *S2iBuilder) SetupWebhookWithManager(mgr ctrl.Manager) error { - kclient = mgr.GetClient() - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// +kubebuilder:webhook:path=/mutate-devops-kubesphere-io-v1alpha1-s2ibuilder,mutating=true,failurePolicy=fail,groups=devops.kubesphere.io,resources=s2ibuilders,verbs=create;update,versions=v1alpha1,name=s2ibuilder.kb.io - -var _ webhook.Defaulter = &S2iBuilder{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type -func (r *S2iBuilder) Default() { - s2ibuilderlog.Info("default", "name", r.Name) - - if r.Spec.Config.RevisionId == "" { - r.Spec.Config.RevisionId = DefaultRevisionId - } - - if r.Spec.Config.Tag == "" { - r.Spec.Config.Tag = DefaultTag - } - - // TODO(user): fill in your defaulting logic. -} - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// +kubebuilder:webhook:verbs=create;update,path=/validate-devops-kubesphere-io-v1alpha1-s2ibuilder,mutating=false,failurePolicy=fail,groups=devops.kubesphere.io,resources=s2ibuilders,versions=v1alpha1,name=vs2ibuilder.kb.io - -var _ webhook.Validator = &S2iBuilder{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilder) ValidateCreate() error { - s2ibuilderlog.Info("validate create", "name", r.Name) - - fromTemplate := false - if r.Spec.FromTemplate != nil { - t := &S2iBuilderTemplate{} - err := kclient.Get(context.TODO(), types.NamespacedName{Name: r.Spec.FromTemplate.Name}, t) - if err != nil { - if k8serror.IsNotFound(err) { - return fmt.Errorf("Template not found, pls check the template name [%s] or create a template", r.Spec.FromTemplate.Name) - } - return err - } - - errs := validateParameter(r.Spec.FromTemplate.Parameters, t.Spec.Parameters) - if len(errs) != 0 { - return errorutil.NewAggregate(errs) - } - var BaseImages []string - for _, ImageInfo := range t.Spec.ContainerInfo { - BaseImages = append(BaseImages, ImageInfo.BuilderImage) - } - if r.Spec.FromTemplate.BuilderImage != "" { - if !reflectutils.Contains(r.Spec.FromTemplate.BuilderImage, BaseImages) { - return fmt.Errorf("builder's baseImage [%s] not in builder baseImages [%v]", - r.Spec.FromTemplate.BuilderImage, BaseImages) - } - } - fromTemplate = true - } - if anno, ok := r.Annotations[AutoScaleAnnotations]; ok { - if err := validatingS2iBuilderAutoScale(anno); err != nil { - return errors.NewFieldInvalidValueWithReason(AutoScaleAnnotations, err.Error()) - } - } - if errs := validateConfig(r.Spec.Config, fromTemplate); len(errs) == 0 { - return nil - } else { - return errorutil.NewAggregate(errs) - } -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilder) ValidateUpdate(old runtime.Object) error { - s2ibuilderlog.Info("validate update", "name", r.Name) - - s2ibuilderlog.Info("validate create", "name", r.Name) - - fromTemplate := false - if r.Spec.FromTemplate != nil { - t := &S2iBuilderTemplate{} - err := kclient.Get(context.TODO(), types.NamespacedName{Name: r.Spec.FromTemplate.Name}, t) - if err != nil { - if k8serror.IsNotFound(err) { - return fmt.Errorf("Template not found, pls check the template name [%s] or create a template", r.Spec.FromTemplate.Name) - } - return err - } - - errs := validateParameter(r.Spec.FromTemplate.Parameters, t.Spec.Parameters) - if len(errs) != 0 { - return errorutil.NewAggregate(errs) - } - var BaseImages []string - for _, ImageInfo := range t.Spec.ContainerInfo { - BaseImages = append(BaseImages, ImageInfo.BuilderImage) - } - if r.Spec.FromTemplate.BuilderImage != "" { - if !reflectutils.Contains(r.Spec.FromTemplate.BuilderImage, BaseImages) { - return fmt.Errorf("builder's baseImage [%s] not in builder baseImages [%v]", - r.Spec.FromTemplate.BuilderImage, BaseImages) - } - } - fromTemplate = true - } - if anno, ok := r.Annotations[AutoScaleAnnotations]; ok { - if err := validatingS2iBuilderAutoScale(anno); err != nil { - return errors.NewFieldInvalidValueWithReason(AutoScaleAnnotations, err.Error()) - } - } - if errs := validateConfig(r.Spec.Config, fromTemplate); len(errs) == 0 { - return nil - } else { - return errorutil.NewAggregate(errs) - } -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilder) ValidateDelete() error { - s2ibuilderlog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil -} - -// ValidateConfig returns a list of error from validation. -func validateConfig(config *S2iConfig, fromTemplate bool) []error { - allErrs := make([]error, 0) - if !config.IsBinaryURL && len(config.SourceURL) == 0 { - allErrs = append(allErrs, errors.NewFieldRequired("sourceUrl")) - } - if !fromTemplate && len(config.BuilderImage) == 0 { - allErrs = append(allErrs, errors.NewFieldRequired("builderImage")) - } - switch config.BuilderPullPolicy { - case PullNever, PullAlways, PullIfNotPresent: - default: - allErrs = append(allErrs, errors.NewFieldInvalidValue("builderPullPolicy")) - } - if config.DockerNetworkMode != "" && !validateDockerNetworkMode(config.DockerNetworkMode) { - allErrs = append(allErrs, errors.NewFieldInvalidValue("dockerNetworkMode")) - } - if config.Labels != nil { - for k := range config.Labels { - if len(k) == 0 { - allErrs = append(allErrs, errors.NewFieldInvalidValue("labels")) - } - } - } - if config.BuilderImage != "" { - if err := validateDockerReference(config.BuilderImage); err != nil { - allErrs = append(allErrs, errors.NewFieldInvalidValueWithReason("builderImage", err.Error())) - } - } - if config.RuntimeAuthentication != nil { - if config.RuntimeAuthentication.SecretRef == nil { - if config.RuntimeAuthentication.Username == "" && config.RuntimeAuthentication.Password == "" { - allErrs = append(allErrs, errors.NewFieldRequired("RuntimeAuthentication username|password / secretRef")) - } - } - } - if config.IncrementalAuthentication != nil { - if config.IncrementalAuthentication.SecretRef == nil { - if config.IncrementalAuthentication.Username == "" && config.IncrementalAuthentication.Password == "" { - allErrs = append(allErrs, errors.NewFieldRequired("IncrementalAuthentication username|password / secretRef")) - } - } - } - if config.PullAuthentication != nil { - if config.PullAuthentication.SecretRef == nil { - if config.PullAuthentication.Username == "" && config.PullAuthentication.Password == "" { - allErrs = append(allErrs, errors.NewFieldRequired("PullAuthentication username|password / secretRef")) - } - } - } - if config.PushAuthentication != nil { - if config.PushAuthentication.SecretRef == nil { - if config.PushAuthentication.Username == "" && config.PushAuthentication.Password == "" { - allErrs = append(allErrs, errors.NewFieldRequired("PushAuthentication username|password / secretRef")) - } - } - } - return allErrs -} - -// validateDockerNetworkMode checks wether the network mode conforms to the docker remote API specification (v1.19) -// Supported values are: bridge, host, container:, and netns:/proc//ns/net -func validateDockerNetworkMode(mode DockerNetworkMode) bool { - switch mode { - case DockerNetworkModeBridge, DockerNetworkModeHost: - return true - } - if strings.HasPrefix(string(mode), DockerNetworkModeContainerPrefix) { - return true - } - if strings.HasPrefix(string(mode), DockerNetworkModeNetworkNamespacePrefix) { - return true - } - return false -} - -func validateParameter(user, tmpt []Parameter) []error { - findParameter := func(name string, ps []Parameter) int { - for index, v := range ps { - if v.Key == name { - return index - } - } - return -1 - } - allErrs := make([]error, 0) - for _, v := range tmpt { - index := findParameter(v.Key, user) - if v.Required && (index == -1 || user[index].Value == "") { - allErrs = append(allErrs, errors.NewFieldRequired("Parameter:"+v.Key)) - } - } - return allErrs -} - -func validatingS2iBuilderAutoScale(anno string) error { - - s2iAutoScale := make([]S2iAutoScale, 0) - if err := json.Unmarshal([]byte(anno), &s2iAutoScale); err != nil { - return err - } - for _, scale := range s2iAutoScale { - switch scale.Kind { - case KindStatefulSet: - return nil - case KindDeployment: - return nil - default: - return fmt.Errorf("unsupport workload type [%s], name [%s]", scale.Kind, scale.Name) - } - } - return nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_webhook.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_webhook.go deleted file mode 100644 index 9ae5e7b64..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2ibuildertemplate_webhook.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 v1alpha1 - -import ( - "fmt" - "github.com/docker/distribution/reference" - "github.com/kubesphere/s2ioperator/pkg/errors" - "github.com/kubesphere/s2ioperator/pkg/util/reflectutils" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" -) - -// log is for logging in this package. -var s2ibuildertemplatelog = logf.Log.WithName("s2ibuildertemplate-resource") - -func (r *S2iBuilderTemplate) SetupWebhookWithManager(mgr ctrl.Manager) error { - kclient = mgr.GetClient() - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// +kubebuilder:webhook:verbs=create;update,path=/validate-devops-kubesphere-io-v1alpha1-s2ibuildertemplate,mutating=false,failurePolicy=fail,groups=devops.kubesphere.io,resources=s2ibuildertemplates,versions=v1alpha1,name=s2ibuildertemplate.kb.io - -var _ webhook.Validator = &S2iBuilderTemplate{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilderTemplate) ValidateCreate() error { - s2ibuildertemplatelog.Info("validate create", "name", r.Name) - - if len(r.Spec.ContainerInfo) == 0 { - return errors.NewFieldRequired("baseImages") - } - - if r.Spec.DefaultBaseImage == "" { - return errors.NewFieldRequired("defaultBaseImage") - } - var builderImages []string - for _, ImageInfo := range r.Spec.ContainerInfo { - builderImages = append(builderImages, ImageInfo.BuilderImage) - } - if !reflectutils.Contains(r.Spec.DefaultBaseImage, builderImages) { - return errors.NewFieldInvalidValueWithReason("defaultBaseImage", - fmt.Sprintf("defaultBaseImage [%s] should in [%v]", r.Spec.DefaultBaseImage, builderImages)) - } - - for _, ImageInfo := range r.Spec.ContainerInfo { - if err := validateDockerReference(ImageInfo.BuilderImage); err != nil { - return errors.NewFieldInvalidValueWithReason("builderImage", err.Error()) - } - } - if err := validateDockerReference(r.Spec.DefaultBaseImage); err != nil { - return errors.NewFieldInvalidValueWithReason("defaultBaseImage", err.Error()) - } - return nil -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilderTemplate) ValidateUpdate(old runtime.Object) error { - s2ibuildertemplatelog.Info("validate update", "name", r.Name) - - if len(r.Spec.ContainerInfo) == 0 { - return errors.NewFieldRequired("baseImages") - } - - if r.Spec.DefaultBaseImage == "" { - return errors.NewFieldRequired("defaultBaseImage") - } - var builderImages []string - for _, ImageInfo := range r.Spec.ContainerInfo { - builderImages = append(builderImages, ImageInfo.BuilderImage) - } - if !reflectutils.Contains(r.Spec.DefaultBaseImage, builderImages) { - return errors.NewFieldInvalidValueWithReason("defaultBaseImage", - fmt.Sprintf("defaultBaseImage [%s] should in [%v]", r.Spec.DefaultBaseImage, builderImages)) - } - - for _, ImageInfo := range r.Spec.ContainerInfo { - if err := validateDockerReference(ImageInfo.BuilderImage); err != nil { - return errors.NewFieldInvalidValueWithReason("builderImage", err.Error()) - } - } - if err := validateDockerReference(r.Spec.DefaultBaseImage); err != nil { - return errors.NewFieldInvalidValueWithReason("defaultBaseImage", err.Error()) - } - return nil -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *S2iBuilderTemplate) ValidateDelete() error { - s2ibuildertemplatelog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil -} - -func validateDockerReference(ref string) error { - _, err := reference.Parse(ref) - return err -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2irun_webhook.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2irun_webhook.go deleted file mode 100644 index 20f717846..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/s2irun_webhook.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 v1alpha1 - -import ( - "context" - "fmt" - "github.com/kubesphere/s2ioperator/pkg/errors" - k8serror "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "reflect" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" -) - -// log is for logging in this package. -var ( - s2irunlog = logf.Log.WithName("s2irun-resource") - kclient client.Client -) - -func (r *S2iRun) SetupWebhookWithManager(mgr ctrl.Manager) error { - kclient = mgr.GetClient() - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - -// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -// +kubebuilder:webhook:verbs=create;update,path=/validate-devops-kubesphere-io-v1alpha1-s2irun,mutating=false,failurePolicy=fail,groups=devops.kubesphere.io,resources=s2iruns,versions=v1alpha1,name=vs2irun.kb.io - -var _ webhook.Validator = &S2iRun{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iRun) ValidateCreate() error { - s2irunlog.Info("validate create", "name", r.Name) - - origin := &S2iRun{} - - builder := &S2iBuilder{} - - err := kclient.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Spec.BuilderName}, builder) - if err != nil && !k8serror.IsNotFound(err) { - return errors.NewFieldInvalidValueWithReason("no", "could not call k8s api") - } - if !k8serror.IsNotFound(err) { - if r.Spec.NewSourceURL != "" && !builder.Spec.Config.IsBinaryURL { - return errors.NewFieldInvalidValueWithReason("newSourceURL", "only b2i could set newSourceURL") - } - } - - err = kclient.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Name}, origin) - if !k8serror.IsNotFound(err) && origin.Status.RunState != "" && !reflect.DeepEqual(origin.Spec, r.Spec) { - return errors.NewFieldInvalidValueWithReason("spec", "should not change s2i run spec when job started") - } - - if r.Spec.NewTag != "" { - validateImageName := fmt.Sprintf("validate:%s", r.Spec.NewTag) - if err := validateDockerReference(validateImageName); err != nil { - return err - } - } - - return nil -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type -func (r *S2iRun) ValidateUpdate(old runtime.Object) error { - s2irunlog.Info("validate update", "name", r.Name) - - origin := &S2iRun{} - - builder := &S2iBuilder{} - - err := kclient.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Spec.BuilderName}, builder) - if err != nil && !k8serror.IsNotFound(err) { - return errors.NewFieldInvalidValueWithReason("no", "could not call k8s api") - } - if !k8serror.IsNotFound(err) { - if r.Spec.NewSourceURL != "" && !builder.Spec.Config.IsBinaryURL { - return errors.NewFieldInvalidValueWithReason("newSourceURL", "only b2i could set newSourceURL") - } - } - - err = kclient.Get(context.TODO(), types.NamespacedName{Namespace: r.Namespace, Name: r.Name}, origin) - if !k8serror.IsNotFound(err) && origin.Status.RunState != "" && !reflect.DeepEqual(origin.Spec, r.Spec) { - return errors.NewFieldInvalidValueWithReason("spec", "should not change s2i run spec when job started") - } - - if r.Spec.NewTag != "" { - validateImageName := fmt.Sprintf("validate:%s", r.Spec.NewTag) - if err := validateDockerReference(validateImageName); err != nil { - return err - } - } - - return nil -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type -func (r *S2iRun) ValidateDelete() error { - s2irunlog.Info("validate delete", "name", r.Name) - - // TODO(user): fill in your validation logic upon object deletion. - return nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 86fcbe5ed..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,753 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2019 The Kubesphere 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 controller-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AuthConfig) DeepCopyInto(out *AuthConfig) { - *out = *in - if in.SecretRef != nil { - in, out := &in.SecretRef, &out.SecretRef - *out = new(v1.LocalObjectReference) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthConfig. -func (in *AuthConfig) DeepCopy() *AuthConfig { - if in == nil { - return nil - } - out := new(AuthConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CGroupLimits) DeepCopyInto(out *CGroupLimits) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CGroupLimits. -func (in *CGroupLimits) DeepCopy() *CGroupLimits { - if in == nil { - return nil - } - out := new(CGroupLimits) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ContainerConfig) DeepCopyInto(out *ContainerConfig) { - *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerConfig. -func (in *ContainerConfig) DeepCopy() *ContainerConfig { - if in == nil { - return nil - } - out := new(ContainerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ContainerInfo) DeepCopyInto(out *ContainerInfo) { - *out = *in - if in.RuntimeArtifacts != nil { - in, out := &in.RuntimeArtifacts, &out.RuntimeArtifacts - *out = make([]VolumeSpec, len(*in)) - copy(*out, *in) - } - if in.BuildVolumes != nil { - in, out := &in.BuildVolumes, &out.BuildVolumes - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerInfo. -func (in *ContainerInfo) DeepCopy() *ContainerInfo { - if in == nil { - return nil - } - out := new(ContainerInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DockerConfig) DeepCopyInto(out *DockerConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfig. -func (in *DockerConfig) DeepCopy() *DockerConfig { - if in == nil { - return nil - } - out := new(DockerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DockerConfigEntry) DeepCopyInto(out *DockerConfigEntry) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigEntry. -func (in *DockerConfigEntry) DeepCopy() *DockerConfigEntry { - if in == nil { - return nil - } - out := new(DockerConfigEntry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DockerConfigJson) DeepCopyInto(out *DockerConfigJson) { - *out = *in - if in.Auths != nil { - in, out := &in.Auths, &out.Auths - *out = make(DockerConfigMap, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigJson. -func (in *DockerConfigJson) DeepCopy() *DockerConfigJson { - if in == nil { - return nil - } - out := new(DockerConfigJson) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in DockerConfigMap) DeepCopyInto(out *DockerConfigMap) { - { - in := &in - *out = make(DockerConfigMap, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DockerConfigMap. -func (in DockerConfigMap) DeepCopy() DockerConfigMap { - if in == nil { - return nil - } - out := new(DockerConfigMap) - in.DeepCopyInto(out) - return *out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvironmentSpec) DeepCopyInto(out *EnvironmentSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentSpec. -func (in *EnvironmentSpec) DeepCopy() *EnvironmentSpec { - if in == nil { - return nil - } - out := new(EnvironmentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Parameter) DeepCopyInto(out *Parameter) { - *out = *in - if in.OptValues != nil { - in, out := &in.OptValues, &out.OptValues - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. -func (in *Parameter) DeepCopy() *Parameter { - if in == nil { - return nil - } - out := new(Parameter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig. -func (in *ProxyConfig) DeepCopy() *ProxyConfig { - if in == nil { - return nil - } - out := new(ProxyConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iAutoScale) DeepCopyInto(out *S2iAutoScale) { - *out = *in - if in.InitReplicas != nil { - in, out := &in.InitReplicas, &out.InitReplicas - *out = new(int32) - **out = **in - } - if in.Containers != nil { - in, out := &in.Containers, &out.Containers - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iAutoScale. -func (in *S2iAutoScale) DeepCopy() *S2iAutoScale { - if in == nil { - return nil - } - out := new(S2iAutoScale) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuildResult) DeepCopyInto(out *S2iBuildResult) { - *out = *in - if in.ImageRepoTags != nil { - in, out := &in.ImageRepoTags, &out.ImageRepoTags - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildResult. -func (in *S2iBuildResult) DeepCopy() *S2iBuildResult { - if in == nil { - return nil - } - out := new(S2iBuildResult) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuildSource) DeepCopyInto(out *S2iBuildSource) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuildSource. -func (in *S2iBuildSource) DeepCopy() *S2iBuildSource { - if in == nil { - return nil - } - out := new(S2iBuildSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuilder) DeepCopyInto(out *S2iBuilder) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilder. -func (in *S2iBuilder) DeepCopy() *S2iBuilder { - if in == nil { - return nil - } - out := new(S2iBuilder) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iBuilder) 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 *S2iBuilderList) DeepCopyInto(out *S2iBuilderList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]S2iBuilder, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderList. -func (in *S2iBuilderList) DeepCopy() *S2iBuilderList { - if in == nil { - return nil - } - out := new(S2iBuilderList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iBuilderList) 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 *S2iBuilderSpec) DeepCopyInto(out *S2iBuilderSpec) { - *out = *in - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = new(S2iConfig) - (*in).DeepCopyInto(*out) - } - if in.FromTemplate != nil { - in, out := &in.FromTemplate, &out.FromTemplate - *out = new(UserDefineTemplate) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderSpec. -func (in *S2iBuilderSpec) DeepCopy() *S2iBuilderSpec { - if in == nil { - return nil - } - out := new(S2iBuilderSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuilderStatus) DeepCopyInto(out *S2iBuilderStatus) { - *out = *in - if in.LastRunName != nil { - in, out := &in.LastRunName, &out.LastRunName - *out = new(string) - **out = **in - } - if in.LastRunStartTime != nil { - in, out := &in.LastRunStartTime, &out.LastRunStartTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderStatus. -func (in *S2iBuilderStatus) DeepCopy() *S2iBuilderStatus { - if in == nil { - return nil - } - out := new(S2iBuilderStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuilderTemplate) DeepCopyInto(out *S2iBuilderTemplate) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplate. -func (in *S2iBuilderTemplate) DeepCopy() *S2iBuilderTemplate { - if in == nil { - return nil - } - out := new(S2iBuilderTemplate) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iBuilderTemplate) 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 *S2iBuilderTemplateList) DeepCopyInto(out *S2iBuilderTemplateList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]S2iBuilderTemplate, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateList. -func (in *S2iBuilderTemplateList) DeepCopy() *S2iBuilderTemplateList { - if in == nil { - return nil - } - out := new(S2iBuilderTemplateList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iBuilderTemplateList) 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 *S2iBuilderTemplateSpec) DeepCopyInto(out *S2iBuilderTemplateSpec) { - *out = *in - if in.ContainerInfo != nil { - in, out := &in.ContainerInfo, &out.ContainerInfo - *out = make([]ContainerInfo, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make([]Parameter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateSpec. -func (in *S2iBuilderTemplateSpec) DeepCopy() *S2iBuilderTemplateSpec { - if in == nil { - return nil - } - out := new(S2iBuilderTemplateSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iBuilderTemplateStatus) DeepCopyInto(out *S2iBuilderTemplateStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iBuilderTemplateStatus. -func (in *S2iBuilderTemplateStatus) DeepCopy() *S2iBuilderTemplateStatus { - if in == nil { - return nil - } - out := new(S2iBuilderTemplateStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iConfig) DeepCopyInto(out *S2iConfig) { - *out = *in - if in.RuntimeAuthentication != nil { - in, out := &in.RuntimeAuthentication, &out.RuntimeAuthentication - *out = new(AuthConfig) - (*in).DeepCopyInto(*out) - } - if in.RuntimeArtifacts != nil { - in, out := &in.RuntimeArtifacts, &out.RuntimeArtifacts - *out = make([]VolumeSpec, len(*in)) - copy(*out, *in) - } - if in.DockerConfig != nil { - in, out := &in.DockerConfig, &out.DockerConfig - *out = new(DockerConfig) - **out = **in - } - if in.PullAuthentication != nil { - in, out := &in.PullAuthentication, &out.PullAuthentication - *out = new(AuthConfig) - (*in).DeepCopyInto(*out) - } - if in.PushAuthentication != nil { - in, out := &in.PushAuthentication, &out.PushAuthentication - *out = new(AuthConfig) - (*in).DeepCopyInto(*out) - } - if in.IncrementalAuthentication != nil { - in, out := &in.IncrementalAuthentication, &out.IncrementalAuthentication - *out = new(AuthConfig) - (*in).DeepCopyInto(*out) - } - if in.Environment != nil { - in, out := &in.Environment, &out.Environment - *out = make([]EnvironmentSpec, len(*in)) - copy(*out, *in) - } - if in.Injections != nil { - in, out := &in.Injections, &out.Injections - *out = make([]VolumeSpec, len(*in)) - copy(*out, *in) - } - if in.CGroupLimits != nil { - in, out := &in.CGroupLimits, &out.CGroupLimits - *out = new(CGroupLimits) - **out = **in - } - if in.DropCapabilities != nil { - in, out := &in.DropCapabilities, &out.DropCapabilities - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ScriptDownloadProxyConfig != nil { - in, out := &in.ScriptDownloadProxyConfig, &out.ScriptDownloadProxyConfig - *out = new(ProxyConfig) - **out = **in - } - if in.BuildVolumes != nil { - in, out := &in.BuildVolumes, &out.BuildVolumes - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.SecurityOpt != nil { - in, out := &in.SecurityOpt, &out.SecurityOpt - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AddHost != nil { - in, out := &in.AddHost, &out.AddHost - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.GitSecretRef != nil { - in, out := &in.GitSecretRef, &out.GitSecretRef - *out = new(v1.LocalObjectReference) - **out = **in - } - if in.NodeAffinityValues != nil { - in, out := &in.NodeAffinityValues, &out.NodeAffinityValues - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iConfig. -func (in *S2iConfig) DeepCopy() *S2iConfig { - if in == nil { - return nil - } - out := new(S2iConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iRun) DeepCopyInto(out *S2iRun) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRun. -func (in *S2iRun) DeepCopy() *S2iRun { - if in == nil { - return nil - } - out := new(S2iRun) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iRun) 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 *S2iRunList) DeepCopyInto(out *S2iRunList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]S2iRun, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunList. -func (in *S2iRunList) DeepCopy() *S2iRunList { - if in == nil { - return nil - } - out := new(S2iRunList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *S2iRunList) 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 *S2iRunSpec) DeepCopyInto(out *S2iRunSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunSpec. -func (in *S2iRunSpec) DeepCopy() *S2iRunSpec { - if in == nil { - return nil - } - out := new(S2iRunSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *S2iRunStatus) DeepCopyInto(out *S2iRunStatus) { - *out = *in - if in.StartTime != nil { - in, out := &in.StartTime, &out.StartTime - *out = (*in).DeepCopy() - } - if in.CompletionTime != nil { - in, out := &in.CompletionTime, &out.CompletionTime - *out = (*in).DeepCopy() - } - if in.S2iBuildResult != nil { - in, out := &in.S2iBuildResult, &out.S2iBuildResult - *out = new(S2iBuildResult) - (*in).DeepCopyInto(*out) - } - if in.S2iBuildSource != nil { - in, out := &in.S2iBuildSource, &out.S2iBuildSource - *out = new(S2iBuildSource) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S2iRunStatus. -func (in *S2iRunStatus) DeepCopy() *S2iRunStatus { - if in == nil { - return nil - } - out := new(S2iRunStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *UserDefineTemplate) DeepCopyInto(out *UserDefineTemplate) { - *out = *in - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make([]Parameter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UserDefineTemplate. -func (in *UserDefineTemplate) DeepCopy() *UserDefineTemplate { - if in == nil { - return nil - } - out := new(UserDefineTemplate) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSpec. -func (in *VolumeSpec) DeepCopy() *VolumeSpec { - if in == nil { - return nil - } - out := new(VolumeSpec) - in.DeepCopyInto(out) - return out -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/clientset.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/clientset.go deleted file mode 100644 index 2ef2ee21f..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/clientset.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package versioned - -import ( - "fmt" - - devopsv1alpha1 "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/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 - DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - devopsV1alpha1 *devopsv1alpha1.DevopsV1alpha1Client -} - -// DevopsV1alpha1 retrieves the DevopsV1alpha1Client -func (c *Clientset) DevopsV1alpha1() devopsv1alpha1.DevopsV1alpha1Interface { - return c.devopsV1alpha1 -} - -// 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. -// If config's RateLimiter is not set and QPS and Burst are acceptable, -// NewForConfig will generate a rate-limiter in configShallowCopy. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - if configShallowCopy.Burst <= 0 { - return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") - } - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.devopsV1alpha1, err = devopsv1alpha1.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.devopsV1alpha1 = devopsv1alpha1.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.devopsV1alpha1 = devopsv1alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/doc.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/doc.go deleted file mode 100644 index c94f528e1..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/doc.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/doc.go deleted file mode 100644 index 33a447aff..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/register.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/register.go deleted file mode 100644 index 8f00896e2..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package scheme - -import ( - devopsv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/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{ - devopsv1alpha1.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/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go deleted file mode 100644 index a592aea33..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/devops_client.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type DevopsV1alpha1Interface interface { - RESTClient() rest.Interface - S2iBuildersGetter - S2iBuilderTemplatesGetter - S2iRunsGetter -} - -// DevopsV1alpha1Client is used to interact with features provided by the devops.kubesphere.io group. -type DevopsV1alpha1Client struct { - restClient rest.Interface -} - -func (c *DevopsV1alpha1Client) S2iBuilders(namespace string) S2iBuilderInterface { - return newS2iBuilders(c, namespace) -} - -func (c *DevopsV1alpha1Client) S2iBuilderTemplates() S2iBuilderTemplateInterface { - return newS2iBuilderTemplates(c) -} - -func (c *DevopsV1alpha1Client) S2iRuns(namespace string) S2iRunInterface { - return newS2iRuns(c, namespace) -} - -// NewForConfig creates a new DevopsV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*DevopsV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &DevopsV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new DevopsV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *DevopsV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new DevopsV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *DevopsV1alpha1Client { - return &DevopsV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - 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 *DevopsV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/doc.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/doc.go deleted file mode 100644 index a86048def..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go deleted file mode 100644 index ce8107965..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -type S2iBuilderExpansion interface{} - -type S2iBuilderTemplateExpansion interface{} - -type S2iRunExpansion interface{} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go deleted file mode 100644 index de5d83383..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuilder.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - scheme "github.com/kubesphere/s2ioperator/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" -) - -// S2iBuildersGetter has a method to return a S2iBuilderInterface. -// A group's client should implement this interface. -type S2iBuildersGetter interface { - S2iBuilders(namespace string) S2iBuilderInterface -} - -// S2iBuilderInterface has methods to work with S2iBuilder resources. -type S2iBuilderInterface interface { - Create(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) - Update(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) - UpdateStatus(*v1alpha1.S2iBuilder) (*v1alpha1.S2iBuilder, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.S2iBuilder, error) - List(opts v1.ListOptions) (*v1alpha1.S2iBuilderList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilder, err error) - S2iBuilderExpansion -} - -// s2iBuilders implements S2iBuilderInterface -type s2iBuilders struct { - client rest.Interface - ns string -} - -// newS2iBuilders returns a S2iBuilders -func newS2iBuilders(c *DevopsV1alpha1Client, namespace string) *s2iBuilders { - return &s2iBuilders{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the s2iBuilder, and returns the corresponding s2iBuilder object, and an error if there is any. -func (c *s2iBuilders) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilder, err error) { - result = &v1alpha1.S2iBuilder{} - err = c.client.Get(). - Namespace(c.ns). - Resource("s2ibuilders"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of S2iBuilders that match those selectors. -func (c *s2iBuilders) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.S2iBuilderList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("s2ibuilders"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested s2iBuilders. -func (c *s2iBuilders) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("s2ibuilders"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a s2iBuilder and creates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. -func (c *s2iBuilders) Create(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { - result = &v1alpha1.S2iBuilder{} - err = c.client.Post(). - Namespace(c.ns). - Resource("s2ibuilders"). - Body(s2iBuilder). - Do(). - Into(result) - return -} - -// Update takes the representation of a s2iBuilder and updates it. Returns the server's representation of the s2iBuilder, and an error, if there is any. -func (c *s2iBuilders) Update(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { - result = &v1alpha1.S2iBuilder{} - err = c.client.Put(). - Namespace(c.ns). - Resource("s2ibuilders"). - Name(s2iBuilder.Name). - Body(s2iBuilder). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *s2iBuilders) UpdateStatus(s2iBuilder *v1alpha1.S2iBuilder) (result *v1alpha1.S2iBuilder, err error) { - result = &v1alpha1.S2iBuilder{} - err = c.client.Put(). - Namespace(c.ns). - Resource("s2ibuilders"). - Name(s2iBuilder.Name). - SubResource("status"). - Body(s2iBuilder). - Do(). - Into(result) - return -} - -// Delete takes name of the s2iBuilder and deletes it. Returns an error if one occurs. -func (c *s2iBuilders) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("s2ibuilders"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *s2iBuilders) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("s2ibuilders"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched s2iBuilder. -func (c *s2iBuilders) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilder, err error) { - result = &v1alpha1.S2iBuilder{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("s2ibuilders"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go deleted file mode 100644 index f2fc9ef6f..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2ibuildertemplate.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - scheme "github.com/kubesphere/s2ioperator/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" -) - -// S2iBuilderTemplatesGetter has a method to return a S2iBuilderTemplateInterface. -// A group's client should implement this interface. -type S2iBuilderTemplatesGetter interface { - S2iBuilderTemplates() S2iBuilderTemplateInterface -} - -// S2iBuilderTemplateInterface has methods to work with S2iBuilderTemplate resources. -type S2iBuilderTemplateInterface interface { - Create(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) - Update(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) - UpdateStatus(*v1alpha1.S2iBuilderTemplate) (*v1alpha1.S2iBuilderTemplate, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.S2iBuilderTemplate, error) - List(opts v1.ListOptions) (*v1alpha1.S2iBuilderTemplateList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilderTemplate, err error) - S2iBuilderTemplateExpansion -} - -// s2iBuilderTemplates implements S2iBuilderTemplateInterface -type s2iBuilderTemplates struct { - client rest.Interface -} - -// newS2iBuilderTemplates returns a S2iBuilderTemplates -func newS2iBuilderTemplates(c *DevopsV1alpha1Client) *s2iBuilderTemplates { - return &s2iBuilderTemplates{ - client: c.RESTClient(), - } -} - -// Get takes name of the s2iBuilderTemplate, and returns the corresponding s2iBuilderTemplate object, and an error if there is any. -func (c *s2iBuilderTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iBuilderTemplate, err error) { - result = &v1alpha1.S2iBuilderTemplate{} - err = c.client.Get(). - Resource("s2ibuildertemplates"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of S2iBuilderTemplates that match those selectors. -func (c *s2iBuilderTemplates) List(opts v1.ListOptions) (result *v1alpha1.S2iBuilderTemplateList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.S2iBuilderTemplateList{} - err = c.client.Get(). - Resource("s2ibuildertemplates"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested s2iBuilderTemplates. -func (c *s2iBuilderTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Resource("s2ibuildertemplates"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a s2iBuilderTemplate and creates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. -func (c *s2iBuilderTemplates) Create(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { - result = &v1alpha1.S2iBuilderTemplate{} - err = c.client.Post(). - Resource("s2ibuildertemplates"). - Body(s2iBuilderTemplate). - Do(). - Into(result) - return -} - -// Update takes the representation of a s2iBuilderTemplate and updates it. Returns the server's representation of the s2iBuilderTemplate, and an error, if there is any. -func (c *s2iBuilderTemplates) Update(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { - result = &v1alpha1.S2iBuilderTemplate{} - err = c.client.Put(). - Resource("s2ibuildertemplates"). - Name(s2iBuilderTemplate.Name). - Body(s2iBuilderTemplate). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *s2iBuilderTemplates) UpdateStatus(s2iBuilderTemplate *v1alpha1.S2iBuilderTemplate) (result *v1alpha1.S2iBuilderTemplate, err error) { - result = &v1alpha1.S2iBuilderTemplate{} - err = c.client.Put(). - Resource("s2ibuildertemplates"). - Name(s2iBuilderTemplate.Name). - SubResource("status"). - Body(s2iBuilderTemplate). - Do(). - Into(result) - return -} - -// Delete takes name of the s2iBuilderTemplate and deletes it. Returns an error if one occurs. -func (c *s2iBuilderTemplates) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("s2ibuildertemplates"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *s2iBuilderTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Resource("s2ibuildertemplates"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched s2iBuilderTemplate. -func (c *s2iBuilderTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iBuilderTemplate, err error) { - result = &v1alpha1.S2iBuilderTemplate{} - err = c.client.Patch(pt). - Resource("s2ibuildertemplates"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go deleted file mode 100644 index f104d1da6..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned/typed/devops/v1alpha1/s2irun.go +++ /dev/null @@ -1,190 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - "time" - - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - scheme "github.com/kubesphere/s2ioperator/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" -) - -// S2iRunsGetter has a method to return a S2iRunInterface. -// A group's client should implement this interface. -type S2iRunsGetter interface { - S2iRuns(namespace string) S2iRunInterface -} - -// S2iRunInterface has methods to work with S2iRun resources. -type S2iRunInterface interface { - Create(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) - Update(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) - UpdateStatus(*v1alpha1.S2iRun) (*v1alpha1.S2iRun, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.S2iRun, error) - List(opts v1.ListOptions) (*v1alpha1.S2iRunList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iRun, err error) - S2iRunExpansion -} - -// s2iRuns implements S2iRunInterface -type s2iRuns struct { - client rest.Interface - ns string -} - -// newS2iRuns returns a S2iRuns -func newS2iRuns(c *DevopsV1alpha1Client, namespace string) *s2iRuns { - return &s2iRuns{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the s2iRun, and returns the corresponding s2iRun object, and an error if there is any. -func (c *s2iRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.S2iRun, err error) { - result = &v1alpha1.S2iRun{} - err = c.client.Get(). - Namespace(c.ns). - Resource("s2iruns"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of S2iRuns that match those selectors. -func (c *s2iRuns) List(opts v1.ListOptions) (result *v1alpha1.S2iRunList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.S2iRunList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("s2iruns"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested s2iRuns. -func (c *s2iRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("s2iruns"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch() -} - -// Create takes the representation of a s2iRun and creates it. Returns the server's representation of the s2iRun, and an error, if there is any. -func (c *s2iRuns) Create(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { - result = &v1alpha1.S2iRun{} - err = c.client.Post(). - Namespace(c.ns). - Resource("s2iruns"). - Body(s2iRun). - Do(). - Into(result) - return -} - -// Update takes the representation of a s2iRun and updates it. Returns the server's representation of the s2iRun, and an error, if there is any. -func (c *s2iRuns) Update(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { - result = &v1alpha1.S2iRun{} - err = c.client.Put(). - Namespace(c.ns). - Resource("s2iruns"). - Name(s2iRun.Name). - Body(s2iRun). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *s2iRuns) UpdateStatus(s2iRun *v1alpha1.S2iRun) (result *v1alpha1.S2iRun, err error) { - result = &v1alpha1.S2iRun{} - err = c.client.Put(). - Namespace(c.ns). - Resource("s2iruns"). - Name(s2iRun.Name). - SubResource("status"). - Body(s2iRun). - Do(). - Into(result) - return -} - -// Delete takes name of the s2iRun and deletes it. Returns an error if one occurs. -func (c *s2iRuns) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("s2iruns"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *s2iRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - var timeout time.Duration - if listOptions.TimeoutSeconds != nil { - timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("s2iruns"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Timeout(timeout). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched s2iRun. -func (c *s2iRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.S2iRun, err error) { - result = &v1alpha1.S2iRun{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("s2iruns"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/interface.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/interface.go deleted file mode 100644 index 295d64a6e..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/interface.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package devops - -import ( - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1" - internalinterfaces "github.com/kubesphere/s2ioperator/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/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/interface.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/interface.go deleted file mode 100644 index 5ecf79601..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/interface.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // S2iBuilders returns a S2iBuilderInformer. - S2iBuilders() S2iBuilderInformer - // S2iBuilderTemplates returns a S2iBuilderTemplateInformer. - S2iBuilderTemplates() S2iBuilderTemplateInformer - // S2iRuns returns a S2iRunInformer. - S2iRuns() S2iRunInformer -} - -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} -} - -// S2iBuilders returns a S2iBuilderInformer. -func (v *version) S2iBuilders() S2iBuilderInformer { - return &s2iBuilderInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// S2iBuilderTemplates returns a S2iBuilderTemplateInformer. -func (v *version) S2iBuilderTemplates() S2iBuilderTemplateInformer { - return &s2iBuilderTemplateInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// S2iRuns returns a S2iRunInformer. -func (v *version) S2iRuns() S2iRunInformer { - return &s2iRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go deleted file mode 100644 index e052d62a4..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuilder.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - devopsv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - versioned "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - internalinterfaces "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/client/listers/devops/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" -) - -// S2iBuilderInformer provides access to a shared informer and lister for -// S2iBuilders. -type S2iBuilderInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.S2iBuilderLister -} - -type s2iBuilderInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewS2iBuilderInformer constructs a new informer for S2iBuilder 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 NewS2iBuilderInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredS2iBuilderInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredS2iBuilderInformer constructs a new informer for S2iBuilder 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 NewFilteredS2iBuilderInformer(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.DevopsV1alpha1().S2iBuilders(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.DevopsV1alpha1().S2iBuilders(namespace).Watch(options) - }, - }, - &devopsv1alpha1.S2iBuilder{}, - resyncPeriod, - indexers, - ) -} - -func (f *s2iBuilderInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredS2iBuilderInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *s2iBuilderInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&devopsv1alpha1.S2iBuilder{}, f.defaultInformer) -} - -func (f *s2iBuilderInformer) Lister() v1alpha1.S2iBuilderLister { - return v1alpha1.NewS2iBuilderLister(f.Informer().GetIndexer()) -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go deleted file mode 100644 index ef15735aa..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2ibuildertemplate.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - devopsv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - versioned "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - internalinterfaces "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/client/listers/devops/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" -) - -// S2iBuilderTemplateInformer provides access to a shared informer and lister for -// S2iBuilderTemplates. -type S2iBuilderTemplateInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.S2iBuilderTemplateLister -} - -type s2iBuilderTemplateInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewS2iBuilderTemplateInformer constructs a new informer for S2iBuilderTemplate 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 NewS2iBuilderTemplateInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredS2iBuilderTemplateInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredS2iBuilderTemplateInformer constructs a new informer for S2iBuilderTemplate 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 NewFilteredS2iBuilderTemplateInformer(client versioned.Interface, 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.DevopsV1alpha1().S2iBuilderTemplates().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.DevopsV1alpha1().S2iBuilderTemplates().Watch(options) - }, - }, - &devopsv1alpha1.S2iBuilderTemplate{}, - resyncPeriod, - indexers, - ) -} - -func (f *s2iBuilderTemplateInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredS2iBuilderTemplateInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *s2iBuilderTemplateInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&devopsv1alpha1.S2iBuilderTemplate{}, f.defaultInformer) -} - -func (f *s2iBuilderTemplateInformer) Lister() v1alpha1.S2iBuilderTemplateLister { - return v1alpha1.NewS2iBuilderTemplateLister(f.Informer().GetIndexer()) -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go deleted file mode 100644 index 87e97cde8..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops/v1alpha1/s2irun.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - devopsv1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - versioned "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - internalinterfaces "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/client/listers/devops/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" -) - -// S2iRunInformer provides access to a shared informer and lister for -// S2iRuns. -type S2iRunInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.S2iRunLister -} - -type s2iRunInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewS2iRunInformer constructs a new informer for S2iRun 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 NewS2iRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredS2iRunInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredS2iRunInformer constructs a new informer for S2iRun 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 NewFilteredS2iRunInformer(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.DevopsV1alpha1().S2iRuns(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.DevopsV1alpha1().S2iRuns(namespace).Watch(options) - }, - }, - &devopsv1alpha1.S2iRun{}, - resyncPeriod, - indexers, - ) -} - -func (f *s2iRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredS2iRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *s2iRunInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&devopsv1alpha1.S2iRun{}, f.defaultInformer) -} - -func (f *s2iRunInformer) Lister() v1alpha1.S2iRunLister { - return v1alpha1.NewS2iRunLister(f.Informer().GetIndexer()) -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/factory.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/factory.go deleted file mode 100644 index d268df3b8..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/factory.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/kubesphere/s2ioperator/pkg/client/clientset/versioned" - devops "github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/devops" - internalinterfaces "github.com/kubesphere/s2ioperator/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 - - Devops() devops.Interface -} - -func (f *sharedInformerFactory) Devops() devops.Interface { - return devops.New(f, f.namespace, f.tweakListOptions) -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/generic.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/generic.go deleted file mode 100644 index 9f74cf506..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/generic.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/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=devops.kubesphere.io, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("s2ibuilders"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBuilders().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("s2ibuildertemplates"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iBuilderTemplates().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("s2iruns"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Devops().V1alpha1().S2iRuns().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 4271b20df..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/kubesphere/s2ioperator/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" -) - -// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. -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 -} - -// TweakListOptionsFunc is a function that transforms a v1.ListOptions. -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/expansion_generated.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/expansion_generated.go deleted file mode 100644 index e977e7f62..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 lister-gen. DO NOT EDIT. - -package v1alpha1 - -// S2iBuilderListerExpansion allows custom methods to be added to -// S2iBuilderLister. -type S2iBuilderListerExpansion interface{} - -// S2iBuilderNamespaceListerExpansion allows custom methods to be added to -// S2iBuilderNamespaceLister. -type S2iBuilderNamespaceListerExpansion interface{} - -// S2iBuilderTemplateListerExpansion allows custom methods to be added to -// S2iBuilderTemplateLister. -type S2iBuilderTemplateListerExpansion interface{} - -// S2iRunListerExpansion allows custom methods to be added to -// S2iRunLister. -type S2iRunListerExpansion interface{} - -// S2iRunNamespaceListerExpansion allows custom methods to be added to -// S2iRunNamespaceLister. -type S2iRunNamespaceListerExpansion interface{} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuilder.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuilder.go deleted file mode 100644 index b8b94a064..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuilder.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// S2iBuilderLister helps list S2iBuilders. -type S2iBuilderLister interface { - // List lists all S2iBuilders in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) - // S2iBuilders returns an object that can list and get S2iBuilders. - S2iBuilders(namespace string) S2iBuilderNamespaceLister - S2iBuilderListerExpansion -} - -// s2iBuilderLister implements the S2iBuilderLister interface. -type s2iBuilderLister struct { - indexer cache.Indexer -} - -// NewS2iBuilderLister returns a new S2iBuilderLister. -func NewS2iBuilderLister(indexer cache.Indexer) S2iBuilderLister { - return &s2iBuilderLister{indexer: indexer} -} - -// List lists all S2iBuilders in the indexer. -func (s *s2iBuilderLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.S2iBuilder)) - }) - return ret, err -} - -// S2iBuilders returns an object that can list and get S2iBuilders. -func (s *s2iBuilderLister) S2iBuilders(namespace string) S2iBuilderNamespaceLister { - return s2iBuilderNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// S2iBuilderNamespaceLister helps list and get S2iBuilders. -type S2iBuilderNamespaceLister interface { - // List lists all S2iBuilders in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) - // Get retrieves the S2iBuilder from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.S2iBuilder, error) - S2iBuilderNamespaceListerExpansion -} - -// s2iBuilderNamespaceLister implements the S2iBuilderNamespaceLister -// interface. -type s2iBuilderNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all S2iBuilders in the indexer for a given namespace. -func (s s2iBuilderNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilder, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.S2iBuilder)) - }) - return ret, err -} - -// Get retrieves the S2iBuilder from the indexer for a given namespace and name. -func (s s2iBuilderNamespaceLister) Get(name string) (*v1alpha1.S2iBuilder, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("s2ibuilder"), name) - } - return obj.(*v1alpha1.S2iBuilder), nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go deleted file mode 100644 index f7609f3d3..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2ibuildertemplate.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// S2iBuilderTemplateLister helps list S2iBuilderTemplates. -type S2iBuilderTemplateLister interface { - // List lists all S2iBuilderTemplates in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.S2iBuilderTemplate, err error) - // Get retrieves the S2iBuilderTemplate from the index for a given name. - Get(name string) (*v1alpha1.S2iBuilderTemplate, error) - S2iBuilderTemplateListerExpansion -} - -// s2iBuilderTemplateLister implements the S2iBuilderTemplateLister interface. -type s2iBuilderTemplateLister struct { - indexer cache.Indexer -} - -// NewS2iBuilderTemplateLister returns a new S2iBuilderTemplateLister. -func NewS2iBuilderTemplateLister(indexer cache.Indexer) S2iBuilderTemplateLister { - return &s2iBuilderTemplateLister{indexer: indexer} -} - -// List lists all S2iBuilderTemplates in the indexer. -func (s *s2iBuilderTemplateLister) List(selector labels.Selector) (ret []*v1alpha1.S2iBuilderTemplate, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.S2iBuilderTemplate)) - }) - return ret, err -} - -// Get retrieves the S2iBuilderTemplate from the index for a given name. -func (s *s2iBuilderTemplateLister) Get(name string) (*v1alpha1.S2iBuilderTemplate, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("s2ibuildertemplate"), name) - } - return obj.(*v1alpha1.S2iBuilderTemplate), nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2irun.go b/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2irun.go deleted file mode 100644 index 6ea33fc9e..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/client/listers/devops/v1alpha1/s2irun.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2019 The Kubesphere 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 lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// S2iRunLister helps list S2iRuns. -type S2iRunLister interface { - // List lists all S2iRuns in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) - // S2iRuns returns an object that can list and get S2iRuns. - S2iRuns(namespace string) S2iRunNamespaceLister - S2iRunListerExpansion -} - -// s2iRunLister implements the S2iRunLister interface. -type s2iRunLister struct { - indexer cache.Indexer -} - -// NewS2iRunLister returns a new S2iRunLister. -func NewS2iRunLister(indexer cache.Indexer) S2iRunLister { - return &s2iRunLister{indexer: indexer} -} - -// List lists all S2iRuns in the indexer. -func (s *s2iRunLister) List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.S2iRun)) - }) - return ret, err -} - -// S2iRuns returns an object that can list and get S2iRuns. -func (s *s2iRunLister) S2iRuns(namespace string) S2iRunNamespaceLister { - return s2iRunNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// S2iRunNamespaceLister helps list and get S2iRuns. -type S2iRunNamespaceLister interface { - // List lists all S2iRuns in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) - // Get retrieves the S2iRun from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.S2iRun, error) - S2iRunNamespaceListerExpansion -} - -// s2iRunNamespaceLister implements the S2iRunNamespaceLister -// interface. -type s2iRunNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all S2iRuns in the indexer for a given namespace. -func (s s2iRunNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.S2iRun, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.S2iRun)) - }) - return ret, err -} - -// Get retrieves the S2iRun from the indexer for a given namespace and name. -func (s s2iRunNamespaceLister) Get(name string) (*v1alpha1.S2iRun, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("s2irun"), name) - } - return obj.(*v1alpha1.S2iRun), nil -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/errors/error.go b/vendor/github.com/kubesphere/s2ioperator/pkg/errors/error.go deleted file mode 100644 index bdce9fe10..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/errors/error.go +++ /dev/null @@ -1,56 +0,0 @@ -package errors - -import "fmt" - -// NewFieldRequired returns a *ValidationError indicating "value required" -func NewFieldRequired(field string) Error { - return Error{Type: ErrorTypeRequired, Field: field} -} - -// NewFieldInvalidValue returns a ValidationError indicating "invalid value" -func NewFieldInvalidValue(field string) Error { - return Error{Type: ErrorInvalidValue, Field: field} -} - -// NewFieldInvalidValueWithReason returns a ValidationError indicating "invalid value" and a reason for the error -func NewFieldInvalidValueWithReason(field, reason string) Error { - return Error{Type: ErrorInvalidValue, Field: field, Reason: reason} -} - -// ErrorType is a machine readable value providing more detail about why a field -// is invalid. -type ErrorType string - -const ( - // ErrorTypeRequired is used to report required values that are not provided - // (e.g. empty strings, null values, or empty arrays). - ErrorTypeRequired ErrorType = "FieldValueRequired" - - // ErrorInvalidValue is used to report values that do not conform to the - // expected schema. - ErrorInvalidValue ErrorType = "InvalidValue" -) - -// Error is an implementation of the 'error' interface, which represents an -// error of validation. -type Error struct { - Type ErrorType - Field string - Reason string -} - -func (v Error) Error() string { - var msg string - switch v.Type { - case ErrorInvalidValue: - msg = fmt.Sprintf("Invalid value specified for %q", v.Field) - case ErrorTypeRequired: - msg = fmt.Sprintf("Required value not specified for %q", v.Field) - default: - msg = fmt.Sprintf("%s: %s", v.Type, v.Field) - } - if len(v.Reason) > 0 { - msg = fmt.Sprintf("%s: %s", msg, v.Reason) - } - return msg -} diff --git a/vendor/github.com/kubesphere/s2ioperator/pkg/util/reflectutils/reflect.go b/vendor/github.com/kubesphere/s2ioperator/pkg/util/reflectutils/reflect.go deleted file mode 100644 index 6ee960002..000000000 --- a/vendor/github.com/kubesphere/s2ioperator/pkg/util/reflectutils/reflect.go +++ /dev/null @@ -1,23 +0,0 @@ -package reflectutils - -import "reflect" - -// ContainsString checks if a given slice contains the provided value. -func Contains(value interface{}, container interface{}) bool { - containerValue := reflect.ValueOf(container) - switch reflect.TypeOf(container).Kind() { - case reflect.Slice, reflect.Array: - for i := 0; i < containerValue.Len(); i++ { - if containerValue.Index(i).Interface() == value { - return true - } - } - case reflect.Map: - if containerValue.MapIndex(reflect.ValueOf(value)).IsValid() { - return true - } - default: - return false - } - return false -} diff --git a/vendor/github.com/leodido/go-urn/.gitignore b/vendor/github.com/leodido/go-urn/.gitignore deleted file mode 100644 index a30b5ab04..000000000 --- a/vendor/github.com/leodido/go-urn/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -*.exe -*.dll -*.so -*.dylib - -*.test - -*.out -*.txt \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/.travis.yml b/vendor/github.com/leodido/go-urn/.travis.yml deleted file mode 100644 index 913b6418b..000000000 --- a/vendor/github.com/leodido/go-urn/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: go - -go: - - 1.9.x - - 1.10.x - - tip - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/README.md b/vendor/github.com/leodido/go-urn/README.md deleted file mode 100644 index cc902ec0e..000000000 --- a/vendor/github.com/leodido/go-urn/README.md +++ /dev/null @@ -1,55 +0,0 @@ -[![Build](https://img.shields.io/travis/leodido/go-urn/master.svg?style=for-the-badge)](https://travis-ci.org/leodido/go-urn) [![Coverage](https://img.shields.io/codecov/c/github/leodido/go-urn.svg?style=for-the-badge)](https://codecov.io/gh/leodido/go-urn) [![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](https://godoc.org/github.com/leodido/go-urn) - -**A parser for URNs**. - -> As seen on [RFC 2141](https://tools.ietf.org/html/rfc2141#ref-1). - -[API documentation](https://godoc.org/github.com/leodido/go-urn). - -## Installation - -``` -go get github.com/leodido/go-urn -``` - -## Performances - -This implementation results to be really fast. - -Usually below ½ microsecond on my machine[1](#mymachine). - -Notice it also performs, while parsing: - -1. fine-grained and informative erroring -2. specific-string normalization - -``` -ok/00/urn:a:b______________________________________/-4 20000000 265 ns/op 182 B/op 6 allocs/op -ok/01/URN:foo:a123,456_____________________________/-4 30000000 296 ns/op 200 B/op 6 allocs/op -ok/02/urn:foo:a123%2c456___________________________/-4 20000000 331 ns/op 208 B/op 6 allocs/op -ok/03/urn:ietf:params:scim:schemas:core:2.0:User___/-4 20000000 430 ns/op 280 B/op 6 allocs/op -ok/04/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 411 ns/op 312 B/op 6 allocs/op -ok/05/urn:ietf:params:scim:schemas:extension:enterp/-4 20000000 472 ns/op 344 B/op 6 allocs/op -ok/06/urn:burnout:nss______________________________/-4 30000000 257 ns/op 192 B/op 6 allocs/op -ok/07/urn:abcdefghilmnopqrstuvzabcdefghilm:x_______/-4 20000000 375 ns/op 213 B/op 6 allocs/op -ok/08/urn:urnurnurn:urn____________________________/-4 30000000 265 ns/op 197 B/op 6 allocs/op -ok/09/urn:ciao:@!=%2c(xyz)+a,b.*@g=$_'_____________/-4 20000000 307 ns/op 248 B/op 6 allocs/op -ok/10/URN:x:abc%1dz%2f%3az_________________________/-4 30000000 259 ns/op 212 B/op 6 allocs/op -no/11/URN:-xxx:x___________________________________/-4 20000000 445 ns/op 320 B/op 6 allocs/op -no/12/urn::colon:nss_______________________________/-4 20000000 461 ns/op 320 B/op 6 allocs/op -no/13/urn:abcdefghilmnopqrstuvzabcdefghilmn:specifi/-4 10000000 660 ns/op 320 B/op 6 allocs/op -no/14/URN:a!?:x____________________________________/-4 20000000 507 ns/op 320 B/op 6 allocs/op -no/15/urn:urn:NSS__________________________________/-4 20000000 429 ns/op 288 B/op 6 allocs/op -no/16/urn:white_space:NSS__________________________/-4 20000000 482 ns/op 320 B/op 6 allocs/op -no/17/urn:concat:no_spaces_________________________/-4 20000000 539 ns/op 328 B/op 7 allocs/op -no/18/urn:a:/______________________________________/-4 20000000 470 ns/op 320 B/op 7 allocs/op -no/19/urn:UrN:NSS__________________________________/-4 20000000 399 ns/op 288 B/op 6 allocs/op -``` - ---- - -* [1]: Intel Core i7-7600U CPU @ 2.80GHz - ---- - -[![Analytics](https://ga-beacon.appspot.com/UA-49657176-1/go-urn?flat)](https://github.com/igrigorik/ga-beacon) \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/machine.go b/vendor/github.com/leodido/go-urn/machine.go deleted file mode 100644 index d621ea6e4..000000000 --- a/vendor/github.com/leodido/go-urn/machine.go +++ /dev/null @@ -1,1670 +0,0 @@ -package urn - -import ( - "fmt" -) - -var ( - errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" - errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" - errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" - errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" - errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" - errParse = "parsing error [col %d]" -) - - -const start int = 1 -const first_final int = 44 - -const en_fail int = 46 -const en_main int = 1 - - -// Machine is the interface representing the FSM -type Machine interface { - Error() error - Parse(input []byte) (*URN, error) -} - -type machine struct { - data []byte - cs int - p, pe, eof, pb int - err error - tolower []int -} - -// NewMachine creates a new FSM able to parse RFC 2141 strings. -func NewMachine() Machine { - m := &machine{} - - return m -} - -// Err returns the error that occurred on the last call to Parse. -// -// If the result is nil, then the line was parsed successfully. -func (m *machine) Error() error { - return m.err -} - -func (m *machine) text() []byte { - return m.data[m.pb:m.p] -} - -// Parse parses the input byte array as a RFC 2141 string. -func (m *machine) Parse(input []byte) (*URN, error) { - m.data = input - m.p = 0 - m.pb = 0 - m.pe = len(input) - m.eof = len(input) - m.err = nil - m.tolower = []int{} - output := &URN{} - - { - m.cs = start - } - - - { - if (m.p) == (m.pe) { - goto _test_eof - } - switch m.cs { - case 1: - goto st_case_1 - case 0: - goto st_case_0 - case 2: - goto st_case_2 - case 3: - goto st_case_3 - case 4: - goto st_case_4 - case 5: - goto st_case_5 - case 6: - goto st_case_6 - case 7: - goto st_case_7 - case 8: - goto st_case_8 - case 9: - goto st_case_9 - case 10: - goto st_case_10 - case 11: - goto st_case_11 - case 12: - goto st_case_12 - case 13: - goto st_case_13 - case 14: - goto st_case_14 - case 15: - goto st_case_15 - case 16: - goto st_case_16 - case 17: - goto st_case_17 - case 18: - goto st_case_18 - case 19: - goto st_case_19 - case 20: - goto st_case_20 - case 21: - goto st_case_21 - case 22: - goto st_case_22 - case 23: - goto st_case_23 - case 24: - goto st_case_24 - case 25: - goto st_case_25 - case 26: - goto st_case_26 - case 27: - goto st_case_27 - case 28: - goto st_case_28 - case 29: - goto st_case_29 - case 30: - goto st_case_30 - case 31: - goto st_case_31 - case 32: - goto st_case_32 - case 33: - goto st_case_33 - case 34: - goto st_case_34 - case 35: - goto st_case_35 - case 36: - goto st_case_36 - case 37: - goto st_case_37 - case 38: - goto st_case_38 - case 44: - goto st_case_44 - case 39: - goto st_case_39 - case 40: - goto st_case_40 - case 45: - goto st_case_45 - case 41: - goto st_case_41 - case 42: - goto st_case_42 - case 43: - goto st_case_43 - case 46: - goto st_case_46 - } - goto st_out - st_case_1: - switch (m.data)[(m.p)] { - case 85: - goto tr1 - case 117: - goto tr1 - } - goto tr0 - tr0: - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr3: - m.err = fmt.Errorf(errPrefix, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr6: - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr41: - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr44: - m.err = fmt.Errorf(errHex, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr50: - m.err = fmt.Errorf(errPrefix, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - tr52: - m.err = fmt.Errorf(errNoUrnWithinID, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - goto st0 - st_case_0: - st0: - m.cs = 0 - goto _out - tr1: - m.pb = m.p - - goto st2 - st2: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof2 - } - st_case_2: - switch (m.data)[(m.p)] { - case 82: - goto st3 - case 114: - goto st3 - } - goto tr0 - st3: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof3 - } - st_case_3: - switch (m.data)[(m.p)] { - case 78: - goto st4 - case 110: - goto st4 - } - goto tr3 - st4: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof4 - } - st_case_4: - if (m.data)[(m.p)] == 58 { - goto tr5 - } - goto tr0 - tr5: - output.prefix = string(m.text()) - - goto st5 - st5: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof5 - } - st_case_5: - switch (m.data)[(m.p)] { - case 85: - goto tr8 - case 117: - goto tr8 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto tr7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto tr7 - } - default: - goto tr7 - } - goto tr6 - tr7: - m.pb = m.p - - goto st6 - st6: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof6 - } - st_case_6: - switch (m.data)[(m.p)] { - case 45: - goto st7 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st7 - } - default: - goto st7 - } - goto tr6 - st7: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof7 - } - st_case_7: - switch (m.data)[(m.p)] { - case 45: - goto st8 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st8 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st8 - } - default: - goto st8 - } - goto tr6 - st8: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof8 - } - st_case_8: - switch (m.data)[(m.p)] { - case 45: - goto st9 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st9 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st9 - } - default: - goto st9 - } - goto tr6 - st9: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof9 - } - st_case_9: - switch (m.data)[(m.p)] { - case 45: - goto st10 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st10 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st10 - } - default: - goto st10 - } - goto tr6 - st10: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof10 - } - st_case_10: - switch (m.data)[(m.p)] { - case 45: - goto st11 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st11 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st11 - } - default: - goto st11 - } - goto tr6 - st11: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof11 - } - st_case_11: - switch (m.data)[(m.p)] { - case 45: - goto st12 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st12 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st12 - } - default: - goto st12 - } - goto tr6 - st12: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof12 - } - st_case_12: - switch (m.data)[(m.p)] { - case 45: - goto st13 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st13 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st13 - } - default: - goto st13 - } - goto tr6 - st13: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof13 - } - st_case_13: - switch (m.data)[(m.p)] { - case 45: - goto st14 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st14 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st14 - } - default: - goto st14 - } - goto tr6 - st14: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof14 - } - st_case_14: - switch (m.data)[(m.p)] { - case 45: - goto st15 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st15 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st15 - } - default: - goto st15 - } - goto tr6 - st15: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof15 - } - st_case_15: - switch (m.data)[(m.p)] { - case 45: - goto st16 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st16 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st16 - } - default: - goto st16 - } - goto tr6 - st16: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof16 - } - st_case_16: - switch (m.data)[(m.p)] { - case 45: - goto st17 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st17 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st17 - } - default: - goto st17 - } - goto tr6 - st17: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof17 - } - st_case_17: - switch (m.data)[(m.p)] { - case 45: - goto st18 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st18 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st18 - } - default: - goto st18 - } - goto tr6 - st18: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof18 - } - st_case_18: - switch (m.data)[(m.p)] { - case 45: - goto st19 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st19 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st19 - } - default: - goto st19 - } - goto tr6 - st19: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof19 - } - st_case_19: - switch (m.data)[(m.p)] { - case 45: - goto st20 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st20 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st20 - } - default: - goto st20 - } - goto tr6 - st20: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof20 - } - st_case_20: - switch (m.data)[(m.p)] { - case 45: - goto st21 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st21 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st21 - } - default: - goto st21 - } - goto tr6 - st21: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof21 - } - st_case_21: - switch (m.data)[(m.p)] { - case 45: - goto st22 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st22 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st22 - } - default: - goto st22 - } - goto tr6 - st22: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof22 - } - st_case_22: - switch (m.data)[(m.p)] { - case 45: - goto st23 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st23 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st23 - } - default: - goto st23 - } - goto tr6 - st23: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof23 - } - st_case_23: - switch (m.data)[(m.p)] { - case 45: - goto st24 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st24 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st24 - } - default: - goto st24 - } - goto tr6 - st24: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof24 - } - st_case_24: - switch (m.data)[(m.p)] { - case 45: - goto st25 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st25 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st25 - } - default: - goto st25 - } - goto tr6 - st25: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof25 - } - st_case_25: - switch (m.data)[(m.p)] { - case 45: - goto st26 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st26 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st26 - } - default: - goto st26 - } - goto tr6 - st26: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof26 - } - st_case_26: - switch (m.data)[(m.p)] { - case 45: - goto st27 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st27 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st27 - } - default: - goto st27 - } - goto tr6 - st27: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof27 - } - st_case_27: - switch (m.data)[(m.p)] { - case 45: - goto st28 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st28 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st28 - } - default: - goto st28 - } - goto tr6 - st28: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof28 - } - st_case_28: - switch (m.data)[(m.p)] { - case 45: - goto st29 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st29 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st29 - } - default: - goto st29 - } - goto tr6 - st29: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof29 - } - st_case_29: - switch (m.data)[(m.p)] { - case 45: - goto st30 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st30 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st30 - } - default: - goto st30 - } - goto tr6 - st30: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof30 - } - st_case_30: - switch (m.data)[(m.p)] { - case 45: - goto st31 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st31 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st31 - } - default: - goto st31 - } - goto tr6 - st31: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof31 - } - st_case_31: - switch (m.data)[(m.p)] { - case 45: - goto st32 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st32 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st32 - } - default: - goto st32 - } - goto tr6 - st32: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof32 - } - st_case_32: - switch (m.data)[(m.p)] { - case 45: - goto st33 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st33 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st33 - } - default: - goto st33 - } - goto tr6 - st33: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof33 - } - st_case_33: - switch (m.data)[(m.p)] { - case 45: - goto st34 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st34 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st34 - } - default: - goto st34 - } - goto tr6 - st34: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof34 - } - st_case_34: - switch (m.data)[(m.p)] { - case 45: - goto st35 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st35 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st35 - } - default: - goto st35 - } - goto tr6 - st35: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof35 - } - st_case_35: - switch (m.data)[(m.p)] { - case 45: - goto st36 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st36 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st36 - } - default: - goto st36 - } - goto tr6 - st36: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof36 - } - st_case_36: - switch (m.data)[(m.p)] { - case 45: - goto st37 - case 58: - goto tr10 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st37 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st37 - } - default: - goto st37 - } - goto tr6 - st37: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof37 - } - st_case_37: - if (m.data)[(m.p)] == 58 { - goto tr10 - } - goto tr6 - tr10: - output.ID = string(m.text()) - - goto st38 - st38: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof38 - } - st_case_38: - switch (m.data)[(m.p)] { - case 33: - goto tr42 - case 36: - goto tr42 - case 37: - goto tr43 - case 61: - goto tr42 - case 95: - goto tr42 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto tr42 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto tr42 - } - case (m.data)[(m.p)] >= 64: - goto tr42 - } - default: - goto tr42 - } - goto tr41 - tr42: - m.pb = m.p - - goto st44 - st44: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof44 - } - st_case_44: - switch (m.data)[(m.p)] { - case 33: - goto st44 - case 36: - goto st44 - case 37: - goto st39 - case 61: - goto st44 - case 95: - goto st44 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto st44 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st44 - } - case (m.data)[(m.p)] >= 64: - goto st44 - } - default: - goto st44 - } - goto tr41 - tr43: - m.pb = m.p - - goto st39 - st39: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof39 - } - st_case_39: - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st40 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st40 - } - default: - goto tr46 - } - goto tr44 - tr46: - m.tolower = append(m.tolower, m.p-m.pb) - - goto st40 - st40: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof40 - } - st_case_40: - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st45 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st45 - } - default: - goto tr48 - } - goto tr44 - tr48: - m.tolower = append(m.tolower, m.p-m.pb) - - goto st45 - st45: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof45 - } - st_case_45: - switch (m.data)[(m.p)] { - case 33: - goto st44 - case 36: - goto st44 - case 37: - goto st39 - case 61: - goto st44 - case 95: - goto st44 - } - switch { - case (m.data)[(m.p)] < 48: - if 39 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 46 { - goto st44 - } - case (m.data)[(m.p)] > 59: - switch { - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st44 - } - case (m.data)[(m.p)] >= 64: - goto st44 - } - default: - goto st44 - } - goto tr44 - tr8: - m.pb = m.p - - goto st41 - st41: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof41 - } - st_case_41: - switch (m.data)[(m.p)] { - case 45: - goto st7 - case 58: - goto tr10 - case 82: - goto st42 - case 114: - goto st42 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st7 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st7 - } - default: - goto st7 - } - goto tr6 - st42: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof42 - } - st_case_42: - switch (m.data)[(m.p)] { - case 45: - goto st8 - case 58: - goto tr10 - case 78: - goto st43 - case 110: - goto st43 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st8 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st8 - } - default: - goto st8 - } - goto tr50 - st43: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof43 - } - st_case_43: - if (m.data)[(m.p)] == 45 { - goto st9 - } - switch { - case (m.data)[(m.p)] < 65: - if 48 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 57 { - goto st9 - } - case (m.data)[(m.p)] > 90: - if 97 <= (m.data)[(m.p)] && (m.data)[(m.p)] <= 122 { - goto st9 - } - default: - goto st9 - } - goto tr52 - st46: - if (m.p)++; (m.p) == (m.pe) { - goto _test_eof46 - } - st_case_46: - switch (m.data)[(m.p)] { - case 10: - goto st0 - case 13: - goto st0 - } - goto st46 - st_out: - _test_eof2: - m.cs = 2 - goto _test_eof - _test_eof3: - m.cs = 3 - goto _test_eof - _test_eof4: - m.cs = 4 - goto _test_eof - _test_eof5: - m.cs = 5 - goto _test_eof - _test_eof6: - m.cs = 6 - goto _test_eof - _test_eof7: - m.cs = 7 - goto _test_eof - _test_eof8: - m.cs = 8 - goto _test_eof - _test_eof9: - m.cs = 9 - goto _test_eof - _test_eof10: - m.cs = 10 - goto _test_eof - _test_eof11: - m.cs = 11 - goto _test_eof - _test_eof12: - m.cs = 12 - goto _test_eof - _test_eof13: - m.cs = 13 - goto _test_eof - _test_eof14: - m.cs = 14 - goto _test_eof - _test_eof15: - m.cs = 15 - goto _test_eof - _test_eof16: - m.cs = 16 - goto _test_eof - _test_eof17: - m.cs = 17 - goto _test_eof - _test_eof18: - m.cs = 18 - goto _test_eof - _test_eof19: - m.cs = 19 - goto _test_eof - _test_eof20: - m.cs = 20 - goto _test_eof - _test_eof21: - m.cs = 21 - goto _test_eof - _test_eof22: - m.cs = 22 - goto _test_eof - _test_eof23: - m.cs = 23 - goto _test_eof - _test_eof24: - m.cs = 24 - goto _test_eof - _test_eof25: - m.cs = 25 - goto _test_eof - _test_eof26: - m.cs = 26 - goto _test_eof - _test_eof27: - m.cs = 27 - goto _test_eof - _test_eof28: - m.cs = 28 - goto _test_eof - _test_eof29: - m.cs = 29 - goto _test_eof - _test_eof30: - m.cs = 30 - goto _test_eof - _test_eof31: - m.cs = 31 - goto _test_eof - _test_eof32: - m.cs = 32 - goto _test_eof - _test_eof33: - m.cs = 33 - goto _test_eof - _test_eof34: - m.cs = 34 - goto _test_eof - _test_eof35: - m.cs = 35 - goto _test_eof - _test_eof36: - m.cs = 36 - goto _test_eof - _test_eof37: - m.cs = 37 - goto _test_eof - _test_eof38: - m.cs = 38 - goto _test_eof - _test_eof44: - m.cs = 44 - goto _test_eof - _test_eof39: - m.cs = 39 - goto _test_eof - _test_eof40: - m.cs = 40 - goto _test_eof - _test_eof45: - m.cs = 45 - goto _test_eof - _test_eof41: - m.cs = 41 - goto _test_eof - _test_eof42: - m.cs = 42 - goto _test_eof - _test_eof43: - m.cs = 43 - goto _test_eof - _test_eof46: - m.cs = 46 - goto _test_eof - - _test_eof: - { - } - if (m.p) == (m.eof) { - switch m.cs { - case 44, 45: - raw := m.text() - output.SS = string(raw) - // Iterate upper letters lowering them - for _, i := range m.tolower { - raw[i] = raw[i] + 32 - } - output.norm = string(raw) - - case 1, 2, 4: - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 3: - m.err = fmt.Errorf(errPrefix, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 41: - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 38: - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 42: - m.err = fmt.Errorf(errPrefix, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 43: - m.err = fmt.Errorf(errNoUrnWithinID, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errIdentifier, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - case 39, 40: - m.err = fmt.Errorf(errHex, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errSpecificString, m.p) - (m.p)-- - - { - goto st46 - } - - m.err = fmt.Errorf(errParse, m.p) - (m.p)-- - - { - goto st46 - } - - } - } - - _out: - { - } - } - - if m.cs < first_final || m.cs == en_fail { - return nil, m.err - } - - return output, nil -} diff --git a/vendor/github.com/leodido/go-urn/machine.go.rl b/vendor/github.com/leodido/go-urn/machine.go.rl deleted file mode 100644 index 3bc05a651..000000000 --- a/vendor/github.com/leodido/go-urn/machine.go.rl +++ /dev/null @@ -1,159 +0,0 @@ -package urn - -import ( - "fmt" -) - -var ( - errPrefix = "expecting the prefix to be the \"urn\" string (whatever case) [col %d]" - errIdentifier = "expecting the identifier to be string (1..31 alnum chars, also containing dashes but not at its start) [col %d]" - errSpecificString = "expecting the specific string to be a string containing alnum, hex, or others ([()+,-.:=@;$_!*']) chars [col %d]" - errNoUrnWithinID = "expecting the identifier to not contain the \"urn\" reserved string [col %d]" - errHex = "expecting the specific string hex chars to be well-formed (%%alnum{2}) [col %d]" - errParse = "parsing error [col %d]" -) - -%%{ -machine urn; - -# unsigned alphabet -alphtype uint8; - -action mark { - m.pb = m.p -} - -action tolower { - m.tolower = append(m.tolower, m.p - m.pb) -} - -action set_pre { - output.prefix = string(m.text()) -} - -action set_nid { - output.ID = string(m.text()) -} - -action set_nss { - raw := m.text() - output.SS = string(raw) - // Iterate upper letters lowering them - for _, i := range m.tolower { - raw[i] = raw[i] + 32 - } - output.norm = string(raw) -} - -action err_pre { - m.err = fmt.Errorf(errPrefix, m.p) - fhold; - fgoto fail; -} - -action err_nid { - m.err = fmt.Errorf(errIdentifier, m.p) - fhold; - fgoto fail; -} - -action err_nss { - m.err = fmt.Errorf(errSpecificString, m.p) - fhold; - fgoto fail; -} - -action err_urn { - m.err = fmt.Errorf(errNoUrnWithinID, m.p) - fhold; - fgoto fail; -} - -action err_hex { - m.err = fmt.Errorf(errHex, m.p) - fhold; - fgoto fail; -} - -action err_parse { - m.err = fmt.Errorf(errParse, m.p) - fhold; - fgoto fail; -} - -pre = ([uU][rR][nN] @err(err_pre)) >mark %set_pre; - -nid = (alnum >mark (alnum | '-'){0,31}) %set_nid; - -hex = '%' (digit | lower | upper >tolower){2} $err(err_hex); - -sss = (alnum | [()+,\-.:=@;$_!*']); - -nss = (sss | hex)+ $err(err_nss); - -fail := (any - [\n\r])* @err{ fgoto main; }; - -main := (pre ':' (nid - pre %err(err_urn)) $err(err_nid) ':' nss >mark %set_nss) $err(err_parse); - -}%% - -%% write data noerror noprefix; - -// Machine is the interface representing the FSM -type Machine interface { - Error() error - Parse(input []byte) (*URN, error) -} - -type machine struct { - data []byte - cs int - p, pe, eof, pb int - err error - tolower []int -} - -// NewMachine creates a new FSM able to parse RFC 2141 strings. -func NewMachine() Machine { - m := &machine{} - - %% access m.; - %% variable p m.p; - %% variable pe m.pe; - %% variable eof m.eof; - %% variable data m.data; - - return m -} - -// Err returns the error that occurred on the last call to Parse. -// -// If the result is nil, then the line was parsed successfully. -func (m *machine) Error() error { - return m.err -} - -func (m *machine) text() []byte { - return m.data[m.pb:m.p] -} - -// Parse parses the input byte array as a RFC 2141 string. -func (m *machine) Parse(input []byte) (*URN, error) { - m.data = input - m.p = 0 - m.pb = 0 - m.pe = len(input) - m.eof = len(input) - m.err = nil - m.tolower = []int{} - output := &URN{} - - %% write init; - %% write exec; - - if m.cs < first_final || m.cs == en_fail { - return nil, m.err - } - - return output, nil -} diff --git a/vendor/github.com/leodido/go-urn/makefile b/vendor/github.com/leodido/go-urn/makefile deleted file mode 100644 index 362137ad2..000000000 --- a/vendor/github.com/leodido/go-urn/makefile +++ /dev/null @@ -1,17 +0,0 @@ -SHELL := /bin/bash - -machine.go: machine.go.rl - ragel -Z -G2 -e -o $@ $< - @gofmt -w -s $@ - @sed -i '/^\/\/line/d' $@ - -.PHONY: build -build: machine.go - -.PHONY: bench -bench: *_test.go machine.go - go test -bench=. -benchmem -benchtime=5s ./... - -.PHONY: tests -tests: *_test.go machine.go - go test -race -timeout 10s -coverprofile=coverage.out -covermode=atomic -v ./... \ No newline at end of file diff --git a/vendor/github.com/leodido/go-urn/urn.go b/vendor/github.com/leodido/go-urn/urn.go deleted file mode 100644 index b903b7b3c..000000000 --- a/vendor/github.com/leodido/go-urn/urn.go +++ /dev/null @@ -1,63 +0,0 @@ -package urn - -import ( - "strings" -) - -// URN represents an Uniform Resource Name. -// -// The general form represented is: -// -// urn:: -// -// Details at https://tools.ietf.org/html/rfc2141. -type URN struct { - prefix string // Static prefix. Equal to "urn" when empty. - ID string // Namespace identifier - SS string // Namespace specific string - norm string // Normalized namespace specific string -} - -// Normalize turns the receiving URN into its norm version. -// -// Which means: lowercase prefix, lowercase namespace identifier, and immutate namespace specific string chars (except tokens which are lowercased). -func (u *URN) Normalize() *URN { - return &URN{ - prefix: "urn", - ID: strings.ToLower(u.ID), - SS: u.norm, - } -} - -// Equal checks the lexical equivalence of the current URN with another one. -func (u *URN) Equal(x *URN) bool { - return *u.Normalize() == *x.Normalize() -} - -// String reassembles the URN into a valid URN string. -// -// This requires both ID and SS fields to be non-empty. -// Otherwise it returns an empty string. -// -// Default URN prefix is "urn". -func (u *URN) String() string { - var res string - if u.ID != "" && u.SS != "" { - if u.prefix == "" { - res += "urn" - } - res += u.prefix + ":" + u.ID + ":" + u.SS - } - - return res -} - -// Parse is responsible to create an URN instance from a byte array matching the correct URN syntax. -func Parse(u []byte) (*URN, bool) { - urn, err := NewMachine().Parse(u) - if err != nil { - return nil, false - } - - return urn, true -} diff --git a/vendor/github.com/lucas-clemente/quic-go/.editorconfig b/vendor/github.com/lucas-clemente/quic-go/.editorconfig deleted file mode 100644 index 538ba2b24..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/.editorconfig +++ /dev/null @@ -1,5 +0,0 @@ -root = true - -[*] -indent_style = tab -indent_size = 2 diff --git a/vendor/github.com/lucas-clemente/quic-go/.gitignore b/vendor/github.com/lucas-clemente/quic-go/.gitignore deleted file mode 100644 index 040b55ac0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -debug -debug.test -main diff --git a/vendor/github.com/lucas-clemente/quic-go/.golangci.yml b/vendor/github.com/lucas-clemente/quic-go/.golangci.yml deleted file mode 100644 index eb9de2f3b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/.golangci.yml +++ /dev/null @@ -1,25 +0,0 @@ -run: - skip-files: - - h2quic/response_writer_closenotifier.go - - internal/handshake/unsafe_test.go - -linters-settings: - misspell: - ignore-words: - - ect - -linters: - disable-all: true - enable: - - deadcode - - goconst - - goimports - - gosimple - - ineffassign - - misspell - - staticcheck - - structcheck - - unconvert - - unused - - varcheck - - vet diff --git a/vendor/github.com/lucas-clemente/quic-go/.travis.yml b/vendor/github.com/lucas-clemente/quic-go/.travis.yml deleted file mode 100644 index a57287e66..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -dist: trusty -group: travis_latest - -language: go - -go: - - "1.12.x" - -# first part of the GOARCH workaround -# setting the GOARCH directly doesn't work, since the value will be overwritten later -# so set it to a temporary environment variable first -env: - global: - - TIMESCALE_FACTOR=20 - - GO111MODULE=on - matrix: - - TRAVIS_GOARCH=amd64 TESTMODE=lint - - TRAVIS_GOARCH=amd64 TESTMODE=unit - - TRAVIS_GOARCH=amd64 TESTMODE=integration - - TRAVIS_GOARCH=386 TESTMODE=unit - - TRAVIS_GOARCH=386 TESTMODE=integration - - -# second part of the GOARCH workaround -# now actually set the GOARCH env variable to the value of the temporary variable set earlier -before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/onsi/ginkgo/ginkgo - - go get github.com/onsi/gomega - - export GOARCH=$TRAVIS_GOARCH - - go env # for debugging - - "export DISPLAY=:99.0" - - "Xvfb $DISPLAY &> /dev/null &" - -script: - - .travis/script.sh - -after_success: - - .travis/after_success.sh diff --git a/vendor/github.com/lucas-clemente/quic-go/Changelog.md b/vendor/github.com/lucas-clemente/quic-go/Changelog.md deleted file mode 100644 index 47b115dcb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/Changelog.md +++ /dev/null @@ -1,52 +0,0 @@ -# Changelog - -## v0.11.0 (2019-04-05) - -- Drop support for gQUIC. For qQUIC support, please switch to the *gquic* branch. -- Implement QUIC WG draft-19. -- Use [qtls](https://github.com/marten-seemann/qtls) for TLS 1.3. -- Return a `tls.ConnectionState` from `quic.Session.ConnectionState()`. -- Remove the error return values from `quic.Stream.CancelRead()` and `quic.Stream.CancelWrite()` - -## v0.10.0 (2018-08-28) - -- Add support for QUIC 44, drop support for QUIC 42. - -## v0.9.0 (2018-08-15) - -- Add a `quic.Config` option for the length of the connection ID (for IETF QUIC). -- Split Session.Close into one method for regular closing and one for closing with an error. - -## v0.8.0 (2018-06-26) - -- Add support for unidirectional streams (for IETF QUIC). -- Add a `quic.Config` option for the maximum number of incoming streams. -- Add support for QUIC 42 and 43. -- Add dial functions that use a context. -- Multiplex clients on a net.PacketConn, when using Dial(conn). - -## v0.7.0 (2018-02-03) - -- The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored. -- Remove `DialNonFWSecure` and `DialAddrNonFWSecure`. -- Expose the `ConnectionState` in the `Session` (experimental API). -- Implement packet pacing. - -## v0.6.0 (2017-12-12) - -- Add support for QUIC 39, drop support for QUIC 35 - 37 -- Added `quic.Config` options for maximal flow control windows -- Add a `quic.Config` option for QUIC versions -- Add a `quic.Config` option to request omission of the connection ID from a server -- Add a `quic.Config` option to configure the source address validation -- Add a `quic.Config` option to configure the handshake timeout -- Add a `quic.Config` option to configure the idle timeout -- Add a `quic.Config` option to configure keep-alive -- Rename the STK to Cookie -- Implement `net.Conn`-style deadlines for streams -- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/lucas-clemente/quic-go) for details. -- Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/lucas-clemente/quic-go/wiki/Logging) for more details. -- Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper` -- Changed `h2quic.Server.Serve()` to accept a `net.PacketConn` -- Drop support for Go 1.7 and 1.8. -- Various bugfixes diff --git a/vendor/github.com/lucas-clemente/quic-go/LICENSE b/vendor/github.com/lucas-clemente/quic-go/LICENSE deleted file mode 100644 index 51378befb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016 the quic-go authors & Google, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/lucas-clemente/quic-go/README.md b/vendor/github.com/lucas-clemente/quic-go/README.md deleted file mode 100644 index 19f987a4d..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# A QUIC implementation in pure Go - - - -[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/lucas-clemente/quic-go) -[![Travis Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=Travis+build)](https://travis-ci.org/lucas-clemente/quic-go) -[![CircleCI Build Status](https://img.shields.io/circleci/project/github/lucas-clemente/quic-go.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/lucas-clemente/quic-go) -[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master) -[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/) - -quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. It roughly implements the [IETF QUIC draft](https://github.com/quicwg/base-drafts), although we don't fully support any of the draft versions at the moment. - -## Version compatibility - -Since quic-go is under active development, there's no guarantee that two builds of different commits are interoperable. The QUIC version used in the *master* branch is just a placeholder, and should not be considered stable. - -If you want to use quic-go as a library in other projects, please consider using a [tagged release](https://github.com/lucas-clemente/quic-go/releases). These releases expose [experimental QUIC versions](https://github.com/quicwg/base-drafts/wiki/QUIC-Versions), which are guaranteed to be stable. - -## Google QUIC - -quic-go used to support both the QUIC versions supported by Google Chrome and QUIC as deployed on Google's servers, as well as IETF QUIC. Due to the divergence of the two protocols, we decided to not support both versions any more. - -The *master* branch **only** supports IETF QUIC. For Google QUIC support, please refer to the [gquic branch](https://github.com/lucas-clemente/quic-go/tree/gquic). - -## Guides - -We currently support Go 1.12+. - -Installing and updating dependencies: - - go get -t -u ./... - -Running tests: - - go test ./... - -### HTTP mapping - -We're currently not implementing the HTTP mapping as described in the [QUIC over HTTP draft](https://quicwg.org/base-drafts/draft-ietf-quic-http.html). The HTTP mapping here is a leftover from Google QUIC. - -### QUIC without HTTP/2 - -Take a look at [this echo example](example/echo/echo.go). - -## Usage - -### As a server - -See the [example server](example/main.go). Starting a QUIC server is very similar to the standard lib http in go: - -```go -http.Handle("/", http.FileServer(http.Dir(wwwDir))) -h2quic.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil) -``` - -### As a client - -See the [example client](example/client/main.go). Use a `h2quic.RoundTripper` as a `Transport` in a `http.Client`. - -```go -http.Client{ - Transport: &h2quic.RoundTripper{}, -} -``` - -## Contributing - -We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with [help wanted](https://github.com/lucas-clemente/quic-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). If you have any questions, please feel free to reach out by opening an issue or leaving a comment. diff --git a/vendor/github.com/lucas-clemente/quic-go/appveyor.yml b/vendor/github.com/lucas-clemente/quic-go/appveyor.yml deleted file mode 100644 index 9ecd2d04b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/appveyor.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: "{build}" - -os: Windows Server 2012 R2 - -environment: - GOPATH: c:\gopath - CGO_ENABLED: 0 - TIMESCALE_FACTOR: 20 - matrix: - - GOARCH: 386 - - GOARCH: amd64 - -clone_folder: c:\gopath\src\github.com\lucas-clemente\quic-go - -install: - - rmdir c:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go1.12.windows-amd64.zip - - 7z x go1.12.windows-amd64.zip -y -oC:\ > NUL - - set PATH=%PATH%;%GOPATH%\bin\windows_%GOARCH%;%GOPATH%\bin - - set GO111MODULE=on - - echo %PATH% - - echo %GOPATH% - - go get github.com/onsi/ginkgo/ginkgo - - go get github.com/onsi/gomega - - go version - - go env - -build_script: - - ginkgo -r -v -randomizeAllSpecs -randomizeSuites -trace -skipPackage benchmark,integrationtests - - ginkgo -randomizeAllSpecs -randomizeSuites -trace benchmark -- -samples=1 - -test: off - -deploy: off diff --git a/vendor/github.com/lucas-clemente/quic-go/buffer_pool.go b/vendor/github.com/lucas-clemente/quic-go/buffer_pool.go deleted file mode 100644 index d6fb76730..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/buffer_pool.go +++ /dev/null @@ -1,75 +0,0 @@ -package quic - -import ( - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -type packetBuffer struct { - Slice []byte - - // refCount counts how many packets the Slice is used in. - // It doesn't support concurrent use. - // It is > 1 when used for coalesced packet. - refCount int -} - -// Split increases the refCount. -// It must be called when a packet buffer is used for more than one packet, -// e.g. when splitting coalesced packets. -func (b *packetBuffer) Split() { - b.refCount++ -} - -// Decrement decrements the reference counter. -// It doesn't put the buffer back into the pool. -func (b *packetBuffer) Decrement() { - b.refCount-- - if b.refCount < 0 { - panic("negative packetBuffer refCount") - } -} - -// MaybeRelease puts the packet buffer back into the pool, -// if the reference counter already reached 0. -func (b *packetBuffer) MaybeRelease() { - // only put the packetBuffer back if it's not used any more - if b.refCount == 0 { - b.putBack() - } -} - -// Release puts back the packet buffer into the pool. -// It should be called when processing is definitely finished. -func (b *packetBuffer) Release() { - b.Decrement() - if b.refCount != 0 { - panic("packetBuffer refCount not zero") - } - b.putBack() -} - -func (b *packetBuffer) putBack() { - if cap(b.Slice) != int(protocol.MaxReceivePacketSize) { - panic("putPacketBuffer called with packet of wrong size!") - } - bufferPool.Put(b) -} - -var bufferPool sync.Pool - -func getPacketBuffer() *packetBuffer { - buf := bufferPool.Get().(*packetBuffer) - buf.refCount = 1 - buf.Slice = buf.Slice[:protocol.MaxReceivePacketSize] - return buf -} - -func init() { - bufferPool.New = func() interface{} { - return &packetBuffer{ - Slice: make([]byte, 0, protocol.MaxReceivePacketSize), - } - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/client.go b/vendor/github.com/lucas-clemente/quic-go/client.go deleted file mode 100644 index 9ede391fe..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/client.go +++ /dev/null @@ -1,413 +0,0 @@ -package quic - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "sync" - - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type client struct { - mutex sync.Mutex - - conn connection - // If the client is created with DialAddr, we create a packet conn. - // If it is started with Dial, we take a packet conn as a parameter. - createdPacketConn bool - - packetHandlers packetHandlerManager - - versionNegotiated utils.AtomicBool // has the server accepted our version - receivedVersionNegotiationPacket bool - negotiatedVersions []protocol.VersionNumber // the list of versions from the version negotiation packet - - tlsConf *tls.Config - config *Config - - srcConnID protocol.ConnectionID - destConnID protocol.ConnectionID - - initialPacketNumber protocol.PacketNumber - - initialVersion protocol.VersionNumber - version protocol.VersionNumber - - handshakeChan chan struct{} - - session quicSession - - logger utils.Logger -} - -var _ packetHandler = &client{} - -var ( - // make it possible to mock connection ID generation in the tests - generateConnectionID = protocol.GenerateConnectionID - generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial -) - -// DialAddr establishes a new QUIC connection to a server. -// It uses a new UDP connection and closes this connection when the QUIC session is closed. -// The hostname for SNI is taken from the given address. -func DialAddr( - addr string, - tlsConf *tls.Config, - config *Config, -) (Session, error) { - return DialAddrContext(context.Background(), addr, tlsConf, config) -} - -// DialAddrContext establishes a new QUIC connection to a server using the provided context. -// See DialAddr for details. -func DialAddrContext( - ctx context.Context, - addr string, - tlsConf *tls.Config, - config *Config, -) (Session, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } - udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) - if err != nil { - return nil, err - } - return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, true) -} - -// Dial establishes a new QUIC connection to a server using a net.PacketConn. -// The same PacketConn can be used for multiple calls to Dial and Listen, -// QUIC connection IDs are used for demultiplexing the different connections. -// The host parameter is used for SNI. -func Dial( - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (Session, error) { - return DialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config) -} - -// DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context. -// See Dial for details. -func DialContext( - ctx context.Context, - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, -) (Session, error) { - return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false) -} - -func dialContext( - ctx context.Context, - pconn net.PacketConn, - remoteAddr net.Addr, - host string, - tlsConf *tls.Config, - config *Config, - createdPacketConn bool, -) (Session, error) { - config = populateClientConfig(config, createdPacketConn) - packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey) - if err != nil { - return nil, err - } - c, err := newClient(pconn, remoteAddr, config, tlsConf, host, createdPacketConn) - if err != nil { - return nil, err - } - c.packetHandlers = packetHandlers - if err := c.dial(ctx); err != nil { - return nil, err - } - return c.session, nil -} - -func newClient( - pconn net.PacketConn, - remoteAddr net.Addr, - config *Config, - tlsConf *tls.Config, - host string, - createdPacketConn bool, -) (*client, error) { - if tlsConf == nil { - tlsConf = &tls.Config{} - } - if tlsConf.ServerName == "" { - var err error - tlsConf.ServerName, _, err = net.SplitHostPort(host) - if err != nil { - return nil, err - } - } - - // check that all versions are actually supported - if config != nil { - for _, v := range config.Versions { - if !protocol.IsValidVersion(v) { - return nil, fmt.Errorf("%s is not a valid QUIC version", v) - } - } - } - - srcConnID, err := generateConnectionID(config.ConnectionIDLength) - if err != nil { - return nil, err - } - destConnID, err := generateConnectionIDForInitial() - if err != nil { - return nil, err - } - c := &client{ - srcConnID: srcConnID, - destConnID: destConnID, - conn: &conn{pconn: pconn, currentAddr: remoteAddr}, - createdPacketConn: createdPacketConn, - tlsConf: tlsConf, - config: config, - version: config.Versions[0], - handshakeChan: make(chan struct{}), - logger: utils.DefaultLogger.WithPrefix("client"), - } - return c, nil -} - -// populateClientConfig populates fields in the quic.Config with their default values, if none are set -// it may be called with nil -func populateClientConfig(config *Config, createdPacketConn bool) *Config { - if config == nil { - config = &Config{} - } - versions := config.Versions - if len(versions) == 0 { - versions = protocol.SupportedVersions - } - - handshakeTimeout := protocol.DefaultHandshakeTimeout - if config.HandshakeTimeout != 0 { - handshakeTimeout = config.HandshakeTimeout - } - idleTimeout := protocol.DefaultIdleTimeout - if config.IdleTimeout != 0 { - idleTimeout = config.IdleTimeout - } - - maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow - if maxReceiveStreamFlowControlWindow == 0 { - maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow - } - maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow - if maxReceiveConnectionFlowControlWindow == 0 { - maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow - } - maxIncomingStreams := config.MaxIncomingStreams - if maxIncomingStreams == 0 { - maxIncomingStreams = protocol.DefaultMaxIncomingStreams - } else if maxIncomingStreams < 0 { - maxIncomingStreams = 0 - } - maxIncomingUniStreams := config.MaxIncomingUniStreams - if maxIncomingUniStreams == 0 { - maxIncomingUniStreams = protocol.DefaultMaxIncomingUniStreams - } else if maxIncomingUniStreams < 0 { - maxIncomingUniStreams = 0 - } - connIDLen := config.ConnectionIDLength - if connIDLen == 0 && !createdPacketConn { - connIDLen = protocol.DefaultConnectionIDLength - } - - return &Config{ - Versions: versions, - HandshakeTimeout: handshakeTimeout, - IdleTimeout: idleTimeout, - ConnectionIDLength: connIDLen, - MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow, - MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow, - MaxIncomingStreams: maxIncomingStreams, - MaxIncomingUniStreams: maxIncomingUniStreams, - KeepAlive: config.KeepAlive, - StatelessResetKey: config.StatelessResetKey, - } -} - -func (c *client) dial(ctx context.Context) error { - c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) - - if err := c.createNewTLSSession(c.version); err != nil { - return err - } - err := c.establishSecureConnection(ctx) - if err == errCloseForRecreating { - return c.dial(ctx) - } - return err -} - -// establishSecureConnection runs the session, and tries to establish a secure connection -// It returns: -// - errCloseForRecreating when the server sends a version negotiation packet -// - any other error that might occur -// - when the connection is forward-secure -func (c *client) establishSecureConnection(ctx context.Context) error { - errorChan := make(chan error, 1) - - go func() { - err := c.session.run() // returns as soon as the session is closed - if err != errCloseForRecreating && c.createdPacketConn { - c.packetHandlers.Close() - } - errorChan <- err - }() - - select { - case <-ctx.Done(): - // The session will send a PeerGoingAway error to the server. - c.session.Close() - return ctx.Err() - case err := <-errorChan: - return err - case <-c.handshakeChan: - // handshake successfully completed - return nil - } -} - -func (c *client) handlePacket(p *receivedPacket) { - if wire.IsVersionNegotiationPacket(p.data) { - go c.handleVersionNegotiationPacket(p) - return - } - - // this is the first packet we are receiving - // since it is not a Version Negotiation Packet, this means the server supports the suggested version - if !c.versionNegotiated.Get() { - c.versionNegotiated.Set(true) - } - - c.session.handlePacket(p) -} - -func (c *client) handleVersionNegotiationPacket(p *receivedPacket) { - c.mutex.Lock() - defer c.mutex.Unlock() - - hdr, _, _, err := wire.ParsePacket(p.data, 0) - if err != nil { - c.logger.Debugf("Error parsing Version Negotiation packet: %s", err) - return - } - - // ignore delayed / duplicated version negotiation packets - if c.receivedVersionNegotiationPacket || c.versionNegotiated.Get() { - c.logger.Debugf("Received a delayed Version Negotiation packet.") - return - } - - for _, v := range hdr.SupportedVersions { - if v == c.version { - // The Version Negotiation packet contains the version that we offered. - // This might be a packet sent by an attacker (or by a terribly broken server implementation). - return - } - } - - c.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", hdr.SupportedVersions) - newVersion, ok := protocol.ChooseSupportedVersion(c.config.Versions, hdr.SupportedVersions) - if !ok { - c.session.destroy(fmt.Errorf("No compatible QUIC version found. We support %s, server offered %s", c.config.Versions, hdr.SupportedVersions)) - c.logger.Debugf("No compatible QUIC version found.") - return - } - c.receivedVersionNegotiationPacket = true - c.negotiatedVersions = hdr.SupportedVersions - - // switch to negotiated version - c.initialVersion = c.version - c.version = newVersion - - c.logger.Infof("Switching to QUIC version %s. New connection ID: %s", newVersion, c.destConnID) - c.initialPacketNumber = c.session.closeForRecreating() -} - -func (c *client) createNewTLSSession(version protocol.VersionNumber) error { - params := &handshake.TransportParameters{ - InitialMaxStreamDataBidiRemote: protocol.InitialMaxStreamData, - InitialMaxStreamDataBidiLocal: protocol.InitialMaxStreamData, - InitialMaxStreamDataUni: protocol.InitialMaxStreamData, - InitialMaxData: protocol.InitialMaxData, - IdleTimeout: c.config.IdleTimeout, - MaxBidiStreams: uint64(c.config.MaxIncomingStreams), - MaxUniStreams: uint64(c.config.MaxIncomingUniStreams), - AckDelayExponent: protocol.AckDelayExponent, - DisableMigration: true, - } - - c.mutex.Lock() - defer c.mutex.Unlock() - runner := &runner{ - packetHandlerManager: c.packetHandlers, - onHandshakeCompleteImpl: func(_ Session) { close(c.handshakeChan) }, - } - sess, err := newClientSession( - c.conn, - runner, - c.destConnID, - c.srcConnID, - c.config, - c.tlsConf, - c.initialPacketNumber, - params, - c.initialVersion, - c.logger, - c.version, - ) - if err != nil { - return err - } - c.session = sess - c.packetHandlers.Add(c.srcConnID, c) - return nil -} - -func (c *client) Close() error { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.session == nil { - return nil - } - return c.session.Close() -} - -func (c *client) destroy(e error) { - c.mutex.Lock() - defer c.mutex.Unlock() - if c.session == nil { - return - } - c.session.destroy(e) -} - -func (c *client) GetVersion() protocol.VersionNumber { - c.mutex.Lock() - v := c.version - c.mutex.Unlock() - return v -} - -func (c *client) getPerspective() protocol.Perspective { - return protocol.PerspectiveClient -} diff --git a/vendor/github.com/lucas-clemente/quic-go/codecov.yml b/vendor/github.com/lucas-clemente/quic-go/codecov.yml deleted file mode 100644 index f077c1ad0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/codecov.yml +++ /dev/null @@ -1,18 +0,0 @@ -coverage: - round: nearest - ignore: - - streams_map_incoming_bidi.go - - streams_map_incoming_uni.go - - streams_map_outgoing_bidi.go - - streams_map_outgoing_uni.go - - h2quic/gzipreader.go - - h2quic/response.go - - internal/ackhandler/packet_linkedlist.go - - internal/utils/byteinterval_linkedlist.go - - internal/utils/packetinterval_linkedlist.go - - internal/utils/linkedlist/linkedlist.go - status: - project: - default: - threshold: 0.5 - patch: false diff --git a/vendor/github.com/lucas-clemente/quic-go/conn.go b/vendor/github.com/lucas-clemente/quic-go/conn.go deleted file mode 100644 index 700c1471c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/conn.go +++ /dev/null @@ -1,54 +0,0 @@ -package quic - -import ( - "net" - "sync" -) - -type connection interface { - Write([]byte) error - Read([]byte) (int, net.Addr, error) - Close() error - LocalAddr() net.Addr - RemoteAddr() net.Addr - SetCurrentRemoteAddr(net.Addr) -} - -type conn struct { - mutex sync.RWMutex - - pconn net.PacketConn - currentAddr net.Addr -} - -var _ connection = &conn{} - -func (c *conn) Write(p []byte) error { - _, err := c.pconn.WriteTo(p, c.currentAddr) - return err -} - -func (c *conn) Read(p []byte) (int, net.Addr, error) { - return c.pconn.ReadFrom(p) -} - -func (c *conn) SetCurrentRemoteAddr(addr net.Addr) { - c.mutex.Lock() - c.currentAddr = addr - c.mutex.Unlock() -} - -func (c *conn) LocalAddr() net.Addr { - return c.pconn.LocalAddr() -} - -func (c *conn) RemoteAddr() net.Addr { - c.mutex.RLock() - addr := c.currentAddr - c.mutex.RUnlock() - return addr -} - -func (c *conn) Close() error { - return c.pconn.Close() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/crypto_stream.go b/vendor/github.com/lucas-clemente/quic-go/crypto_stream.go deleted file mode 100644 index 095feb1d1..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/crypto_stream.go +++ /dev/null @@ -1,135 +0,0 @@ -package quic - -import ( - "errors" - "fmt" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type cryptoStream interface { - // for receiving data - HandleCryptoFrame(*wire.CryptoFrame) error - GetCryptoData() []byte - Finish() error - // for sending data - io.Writer - HasData() bool - PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame -} - -type postHandshakeCryptoStream struct { - cryptoStream - - framer framer -} - -func newPostHandshakeCryptoStream(framer framer) cryptoStream { - return &postHandshakeCryptoStream{ - cryptoStream: newCryptoStream(), - framer: framer, - } -} - -// Write writes post-handshake messages. -// For simplicity, post-handshake crypto messages are treated as control frames. -// The framer functions as a stack (LIFO), so if there are multiple writes, -// they will be returned in the opposite order. -// This is acceptable, since post-handshake crypto messages are very rare. -func (s *postHandshakeCryptoStream) Write(p []byte) (int, error) { - n, err := s.cryptoStream.Write(p) - if err != nil { - return n, err - } - for s.cryptoStream.HasData() { - s.framer.QueueControlFrame(s.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize)) - } - return n, nil -} - -type cryptoStreamImpl struct { - queue *frameSorter - msgBuf []byte - - highestOffset protocol.ByteCount - finished bool - - writeOffset protocol.ByteCount - writeBuf []byte -} - -func newCryptoStream() cryptoStream { - return &cryptoStreamImpl{queue: newFrameSorter()} -} - -func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error { - highestOffset := f.Offset + protocol.ByteCount(len(f.Data)) - if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset { - return fmt.Errorf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset) - } - if s.finished { - if highestOffset > s.highestOffset { - // reject crypto data received after this stream was already finished - return errors.New("received crypto data after change of encryption level") - } - // ignore data with a smaller offset than the highest received - // could e.g. be a retransmission - return nil - } - s.highestOffset = utils.MaxByteCount(s.highestOffset, highestOffset) - if err := s.queue.Push(f.Data, f.Offset); err != nil { - return err - } - for { - _, data := s.queue.Pop() - if data == nil { - return nil - } - s.msgBuf = append(s.msgBuf, data...) - } -} - -// GetCryptoData retrieves data that was received in CRYPTO frames -func (s *cryptoStreamImpl) GetCryptoData() []byte { - if len(s.msgBuf) < 4 { - return nil - } - msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3]) - if len(s.msgBuf) < msgLen { - return nil - } - msg := make([]byte, msgLen) - copy(msg, s.msgBuf[:msgLen]) - s.msgBuf = s.msgBuf[msgLen:] - return msg -} - -func (s *cryptoStreamImpl) Finish() error { - if s.queue.HasMoreData() { - return errors.New("encryption level changed, but crypto stream has more data to read") - } - s.finished = true - return nil -} - -// Writes writes data that should be sent out in CRYPTO frames -func (s *cryptoStreamImpl) Write(p []byte) (int, error) { - s.writeBuf = append(s.writeBuf, p...) - return len(p), nil -} - -func (s *cryptoStreamImpl) HasData() bool { - return len(s.writeBuf) > 0 -} - -func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { - f := &wire.CryptoFrame{Offset: s.writeOffset} - n := utils.MinByteCount(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf))) - f.Data = s.writeBuf[:n] - s.writeBuf = s.writeBuf[n:] - s.writeOffset += n - return f -} diff --git a/vendor/github.com/lucas-clemente/quic-go/crypto_stream_manager.go b/vendor/github.com/lucas-clemente/quic-go/crypto_stream_manager.go deleted file mode 100644 index 489b306a7..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/crypto_stream_manager.go +++ /dev/null @@ -1,60 +0,0 @@ -package quic - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type cryptoDataHandler interface { - HandleMessage([]byte, protocol.EncryptionLevel) bool -} - -type cryptoStreamManager struct { - cryptoHandler cryptoDataHandler - - initialStream cryptoStream - handshakeStream cryptoStream - oneRTTStream cryptoStream -} - -func newCryptoStreamManager( - cryptoHandler cryptoDataHandler, - initialStream cryptoStream, - handshakeStream cryptoStream, - oneRTTStream cryptoStream, -) *cryptoStreamManager { - return &cryptoStreamManager{ - cryptoHandler: cryptoHandler, - initialStream: initialStream, - handshakeStream: handshakeStream, - oneRTTStream: oneRTTStream, - } -} - -func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, error) { - var str cryptoStream - switch encLevel { - case protocol.EncryptionInitial: - str = m.initialStream - case protocol.EncryptionHandshake: - str = m.handshakeStream - case protocol.Encryption1RTT: - str = m.oneRTTStream - default: - return false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) - } - if err := str.HandleCryptoFrame(frame); err != nil { - return false, err - } - for { - data := str.GetCryptoData() - if data == nil { - return false, nil - } - if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished { - return true, str.Finish() - } - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/frame_sorter.go b/vendor/github.com/lucas-clemente/quic-go/frame_sorter.go deleted file mode 100644 index 609c35dd7..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/frame_sorter.go +++ /dev/null @@ -1,159 +0,0 @@ -package quic - -import ( - "errors" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type frameSorter struct { - queue map[protocol.ByteCount][]byte - readPos protocol.ByteCount - gaps *utils.ByteIntervalList -} - -var errDuplicateStreamData = errors.New("Duplicate Stream Data") - -func newFrameSorter() *frameSorter { - s := frameSorter{ - gaps: utils.NewByteIntervalList(), - queue: make(map[protocol.ByteCount][]byte), - } - s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount}) - return &s -} - -func (s *frameSorter) Push(data []byte, offset protocol.ByteCount) error { - err := s.push(data, offset) - if err == errDuplicateStreamData { - return nil - } - return err -} - -func (s *frameSorter) push(data []byte, offset protocol.ByteCount) error { - if len(data) == 0 { - return nil - } - - var wasCut bool - if oldData, ok := s.queue[offset]; ok { - if len(data) <= len(oldData) { - return errDuplicateStreamData - } - data = data[len(oldData):] - offset += protocol.ByteCount(len(oldData)) - wasCut = true - } - - start := offset - end := offset + protocol.ByteCount(len(data)) - - // skip all gaps that are before this stream frame - var gap *utils.ByteIntervalElement - for gap = s.gaps.Front(); gap != nil; gap = gap.Next() { - // the frame is a duplicate. Ignore it - if end <= gap.Value.Start { - return errDuplicateStreamData - } - if end > gap.Value.Start && start <= gap.Value.End { - break - } - } - - if gap == nil { - return errors.New("StreamFrameSorter BUG: no gap found") - } - - if start < gap.Value.Start { - add := gap.Value.Start - start - offset += add - start += add - data = data[add:] - wasCut = true - } - - // find the highest gaps whose Start lies before the end of the frame - endGap := gap - for end >= endGap.Value.End { - nextEndGap := endGap.Next() - if nextEndGap == nil { - return errors.New("StreamFrameSorter BUG: no end gap found") - } - if endGap != gap { - s.gaps.Remove(endGap) - } - if end <= nextEndGap.Value.Start { - break - } - // delete queued frames completely covered by the current frame - delete(s.queue, endGap.Value.End) - endGap = nextEndGap - } - - if end > endGap.Value.End { - cutLen := end - endGap.Value.End - len := protocol.ByteCount(len(data)) - cutLen - end -= cutLen - data = data[:len] - wasCut = true - } - - if start == gap.Value.Start { - if end >= gap.Value.End { - // the frame completely fills this gap - // delete the gap - s.gaps.Remove(gap) - } - if end < endGap.Value.End { - // the frame covers the beginning of the gap - // adjust the Start value to shrink the gap - endGap.Value.Start = end - } - } else if end == endGap.Value.End { - // the frame covers the end of the gap - // adjust the End value to shrink the gap - gap.Value.End = start - } else { - if gap == endGap { - // the frame lies within the current gap, splitting it into two - // insert a new gap and adjust the current one - intv := utils.ByteInterval{Start: end, End: gap.Value.End} - s.gaps.InsertAfter(intv, gap) - gap.Value.End = start - } else { - gap.Value.End = start - endGap.Value.Start = end - } - } - - if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { - return errors.New("Too many gaps in received data") - } - - if wasCut { - newData := make([]byte, len(data)) - copy(newData, data) - data = newData - } - - s.queue[offset] = data - return nil -} - -func (s *frameSorter) Pop() (protocol.ByteCount, []byte) { - data, ok := s.queue[s.readPos] - if !ok { - return s.readPos, nil - } - delete(s.queue, s.readPos) - offset := s.readPos - s.readPos += protocol.ByteCount(len(data)) - return offset, data -} - -// HasMoreData says if there is any more data queued at *any* offset. -func (s *frameSorter) HasMoreData() bool { - return len(s.queue) > 0 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/framer.go b/vendor/github.com/lucas-clemente/quic-go/framer.go deleted file mode 100644 index fbfe9bb76..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/framer.go +++ /dev/null @@ -1,109 +0,0 @@ -package quic - -import ( - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type framer interface { - QueueControlFrame(wire.Frame) - AppendControlFrames([]wire.Frame, protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) - - AddActiveStream(protocol.StreamID) - AppendStreamFrames([]wire.Frame, protocol.ByteCount) []wire.Frame -} - -type framerI struct { - mutex sync.Mutex - - streamGetter streamGetter - version protocol.VersionNumber - - activeStreams map[protocol.StreamID]struct{} - streamQueue []protocol.StreamID - - controlFrameMutex sync.Mutex - controlFrames []wire.Frame -} - -var _ framer = &framerI{} - -func newFramer( - streamGetter streamGetter, - v protocol.VersionNumber, -) framer { - return &framerI{ - streamGetter: streamGetter, - activeStreams: make(map[protocol.StreamID]struct{}), - version: v, - } -} - -func (f *framerI) QueueControlFrame(frame wire.Frame) { - f.controlFrameMutex.Lock() - f.controlFrames = append(f.controlFrames, frame) - f.controlFrameMutex.Unlock() -} - -func (f *framerI) AppendControlFrames(frames []wire.Frame, maxLen protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) { - var length protocol.ByteCount - f.controlFrameMutex.Lock() - for len(f.controlFrames) > 0 { - frame := f.controlFrames[len(f.controlFrames)-1] - frameLen := frame.Length(f.version) - if length+frameLen > maxLen { - break - } - frames = append(frames, frame) - length += frameLen - f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] - } - f.controlFrameMutex.Unlock() - return frames, length -} - -func (f *framerI) AddActiveStream(id protocol.StreamID) { - f.mutex.Lock() - if _, ok := f.activeStreams[id]; !ok { - f.streamQueue = append(f.streamQueue, id) - f.activeStreams[id] = struct{}{} - } - f.mutex.Unlock() -} - -func (f *framerI) AppendStreamFrames(frames []wire.Frame, maxLen protocol.ByteCount) []wire.Frame { - var length protocol.ByteCount - f.mutex.Lock() - // pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet - numActiveStreams := len(f.streamQueue) - for i := 0; i < numActiveStreams; i++ { - if maxLen-length < protocol.MinStreamFrameSize { - break - } - id := f.streamQueue[0] - f.streamQueue = f.streamQueue[1:] - // This should never return an error. Better check it anyway. - // The stream will only be in the streamQueue, if it enqueued itself there. - str, err := f.streamGetter.GetOrOpenSendStream(id) - // The stream can be nil if it completed after it said it had data. - if str == nil || err != nil { - delete(f.activeStreams, id) - continue - } - frame, hasMoreData := str.popStreamFrame(maxLen - length) - if hasMoreData { // put the stream back in the queue (at the end) - f.streamQueue = append(f.streamQueue, id) - } else { // no more data to send. Stream is not active any more - delete(f.activeStreams, id) - } - if frame == nil { // can happen if the receiveStream was canceled after it said it had data - continue - } - frames = append(frames, frame) - length += frame.Length(f.version) - } - f.mutex.Unlock() - return frames -} diff --git a/vendor/github.com/lucas-clemente/quic-go/go.mod b/vendor/github.com/lucas-clemente/quic-go/go.mod deleted file mode 100644 index 63d9d89b3..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/go.mod +++ /dev/null @@ -1,13 +0,0 @@ -module github.com/lucas-clemente/quic-go - -go 1.12 - -require ( - github.com/cheekybits/genny v1.0.0 - github.com/golang/mock v1.2.0 - github.com/marten-seemann/qtls v0.2.3 - github.com/onsi/ginkgo v1.7.0 - github.com/onsi/gomega v1.4.3 - golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 - golang.org/x/net v0.0.0-20180906233101-161cd47e91fd -) diff --git a/vendor/github.com/lucas-clemente/quic-go/go.sum b/vendor/github.com/lucas-clemente/quic-go/go.sum deleted file mode 100644 index c5efe8535..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/go.sum +++ /dev/null @@ -1,37 +0,0 @@ -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/client.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/client.go deleted file mode 100644 index 3ffa4829a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/client.go +++ /dev/null @@ -1,311 +0,0 @@ -package h2quic - -import ( - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "net/http" - "strings" - "sync" - - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" - "golang.org/x/net/idna" - - quic "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type roundTripperOpts struct { - DisableCompression bool -} - -var dialAddr = quic.DialAddr - -// client is a HTTP2 client doing QUIC requests -type client struct { - mutex sync.RWMutex - - tlsConf *tls.Config - config *quic.Config - opts *roundTripperOpts - - hostname string - handshakeErr error - dialOnce sync.Once - dialer func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.Session, error) - - session quic.Session - headerStream quic.Stream - headerErr *qerr.QuicError - headerErrored chan struct{} // this channel is closed if an error occurs on the header stream - requestWriter *requestWriter - - responses map[protocol.StreamID]chan *http.Response - - logger utils.Logger -} - -var _ http.RoundTripper = &client{} - -var defaultQuicConfig = &quic.Config{KeepAlive: true} - -// newClient creates a new client -func newClient( - hostname string, - tlsConfig *tls.Config, - opts *roundTripperOpts, - quicConfig *quic.Config, - dialer func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.Session, error), -) *client { - config := defaultQuicConfig - if quicConfig != nil { - config = quicConfig - } - return &client{ - hostname: authorityAddr("https", hostname), - responses: make(map[protocol.StreamID]chan *http.Response), - tlsConf: tlsConfig, - config: config, - opts: opts, - headerErrored: make(chan struct{}), - dialer: dialer, - logger: utils.DefaultLogger.WithPrefix("client"), - } -} - -// dial dials the connection -func (c *client) dial() error { - var err error - if c.dialer != nil { - c.session, err = c.dialer("udp", c.hostname, c.tlsConf, c.config) - } else { - c.session, err = dialAddr(c.hostname, c.tlsConf, c.config) - } - if err != nil { - return err - } - - // once the version has been negotiated, open the header stream - c.headerStream, err = c.session.OpenStreamSync() - if err != nil { - return err - } - c.requestWriter = newRequestWriter(c.headerStream, c.logger) - go c.handleHeaderStream() - return nil -} - -func (c *client) handleHeaderStream() { - decoder := hpack.NewDecoder(4096, func(hf hpack.HeaderField) {}) - h2framer := http2.NewFramer(nil, c.headerStream) - - var err error - for err == nil { - err = c.readResponse(h2framer, decoder) - } - if quicErr, ok := err.(*qerr.QuicError); !ok || quicErr.ErrorCode != qerr.NoError { - c.logger.Debugf("Error handling header stream: %s", err) - } - c.headerErr = qerr.Error(qerr.InternalError, err.Error()) - // stop all running request - close(c.headerErrored) -} - -func (c *client) readResponse(h2framer *http2.Framer, decoder *hpack.Decoder) error { - frame, err := h2framer.ReadFrame() - if err != nil { - return err - } - hframe, ok := frame.(*http2.HeadersFrame) - if !ok { - return errors.New("not a headers frame") - } - mhframe := &http2.MetaHeadersFrame{HeadersFrame: hframe} - mhframe.Fields, err = decoder.DecodeFull(hframe.HeaderBlockFragment()) - if err != nil { - return fmt.Errorf("cannot read header fields: %s", err.Error()) - } - - c.mutex.RLock() - responseChan, ok := c.responses[protocol.StreamID(hframe.StreamID)] - c.mutex.RUnlock() - if !ok { - return fmt.Errorf("response channel for stream %d not found", hframe.StreamID) - } - - rsp, err := responseFromHeaders(mhframe) - if err != nil { - return err - } - responseChan <- rsp - return nil -} - -// Roundtrip executes a request and returns a response -func (c *client) RoundTrip(req *http.Request) (*http.Response, error) { - // TODO: add port to address, if it doesn't have one - if req.URL.Scheme != "https" { - return nil, errors.New("quic http2: unsupported scheme") - } - if authorityAddr("https", hostnameFromRequest(req)) != c.hostname { - return nil, fmt.Errorf("h2quic Client BUG: RoundTrip called for the wrong client (expected %s, got %s)", c.hostname, req.Host) - } - - c.dialOnce.Do(func() { - c.handshakeErr = c.dial() - }) - - if c.handshakeErr != nil { - return nil, c.handshakeErr - } - - hasBody := (req.Body != nil) - - responseChan := make(chan *http.Response) - dataStream, err := c.session.OpenStreamSync() - if err != nil { - _ = c.closeWithError(err) - return nil, err - } - c.mutex.Lock() - c.responses[dataStream.StreamID()] = responseChan - c.mutex.Unlock() - - var requestedGzip bool - if !c.opts.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" && req.Method != "HEAD" { - requestedGzip = true - } - // TODO: add support for trailers - endStream := !hasBody - err = c.requestWriter.WriteRequest(req, dataStream.StreamID(), endStream, requestedGzip) - if err != nil { - _ = c.closeWithError(err) - return nil, err - } - - resc := make(chan error, 1) - if hasBody { - go func() { - resc <- c.writeRequestBody(dataStream, req.Body) - }() - } - - var res *http.Response - - var receivedResponse bool - var bodySent bool - - if !hasBody { - bodySent = true - } - - ctx := req.Context() - for !(bodySent && receivedResponse) { - select { - case res = <-responseChan: - receivedResponse = true - c.mutex.Lock() - delete(c.responses, dataStream.StreamID()) - c.mutex.Unlock() - case err := <-resc: - bodySent = true - if err != nil { - return nil, err - } - case <-ctx.Done(): - // error code 6 signals that stream was canceled - dataStream.CancelRead(6) - dataStream.CancelWrite(6) - c.mutex.Lock() - delete(c.responses, dataStream.StreamID()) - c.mutex.Unlock() - return nil, ctx.Err() - case <-c.headerErrored: - // an error occurred on the header stream - _ = c.closeWithError(c.headerErr) - return nil, c.headerErr - } - } - - // TODO: correctly set this variable - var streamEnded bool - isHead := (req.Method == "HEAD") - - res = setLength(res, isHead, streamEnded) - - if streamEnded || isHead { - res.Body = noBody - } else { - res.Body = &responseBody{dataStream} - if requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { - res.Header.Del("Content-Encoding") - res.Header.Del("Content-Length") - res.ContentLength = -1 - res.Body = &gzipReader{body: res.Body} - res.Uncompressed = true - } - } - - res.Request = req - return res, nil -} - -func (c *client) writeRequestBody(dataStream quic.Stream, body io.ReadCloser) (err error) { - defer func() { - cerr := body.Close() - if err == nil { - // TODO: what to do with dataStream here? Maybe reset it? - err = cerr - } - }() - - _, err = io.Copy(dataStream, body) - if err != nil { - // TODO: what to do with dataStream here? Maybe reset it? - return err - } - return dataStream.Close() -} - -func (c *client) closeWithError(e error) error { - if c.session == nil { - return nil - } - return c.session.CloseWithError(quic.ErrorCode(qerr.InternalError), e) -} - -// Close closes the client -func (c *client) Close() error { - if c.session == nil { - return nil - } - return c.session.Close() -} - -// copied from net/transport.go - -// authorityAddr returns a given authority (a host/IP, or host:port / ip:port) -// and returns a host:port. The port 443 is added if needed. -func authorityAddr(scheme string, authority string) (addr string) { - host, port, err := net.SplitHostPort(authority) - if err != nil { // authority didn't have a port - port = "443" - if scheme == "http" { - port = "80" - } - host = authority - } - if a, err := idna.ToASCII(host); err == nil { - host = a - } - // IPv6 address literal, without a port: - if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { - return host + ":" + port - } - return net.JoinHostPort(host, port) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/gzipreader.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/gzipreader.go deleted file mode 100644 index 91c226b1e..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/gzipreader.go +++ /dev/null @@ -1,35 +0,0 @@ -package h2quic - -// copied from net/transport.go - -// gzipReader wraps a response body so it can lazily -// call gzip.NewReader on the first call to Read -import ( - "compress/gzip" - "io" -) - -// call gzip.NewReader on the first call to Read -type gzipReader struct { - body io.ReadCloser // underlying Response.Body - zr *gzip.Reader // lazily-initialized gzip reader - zerr error // sticky error -} - -func (gz *gzipReader) Read(p []byte) (n int, err error) { - if gz.zerr != nil { - return 0, gz.zerr - } - if gz.zr == nil { - gz.zr, err = gzip.NewReader(gz.body) - if err != nil { - gz.zerr = err - return 0, err - } - } - return gz.zr.Read(p) -} - -func (gz *gzipReader) Close() error { - return gz.body.Close() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/request.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/request.go deleted file mode 100644 index b27e37e2e..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/request.go +++ /dev/null @@ -1,77 +0,0 @@ -package h2quic - -import ( - "crypto/tls" - "errors" - "net/http" - "net/url" - "strconv" - "strings" - - "golang.org/x/net/http2/hpack" -) - -func requestFromHeaders(headers []hpack.HeaderField) (*http.Request, error) { - var path, authority, method, contentLengthStr string - httpHeaders := http.Header{} - - for _, h := range headers { - switch h.Name { - case ":path": - path = h.Value - case ":method": - method = h.Value - case ":authority": - authority = h.Value - case "content-length": - contentLengthStr = h.Value - default: - if !h.IsPseudo() { - httpHeaders.Add(h.Name, h.Value) - } - } - } - - // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4 - if len(httpHeaders["Cookie"]) > 0 { - httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; ")) - } - - if len(path) == 0 || len(authority) == 0 || len(method) == 0 { - return nil, errors.New(":path, :authority and :method must not be empty") - } - - u, err := url.Parse(path) - if err != nil { - return nil, err - } - - var contentLength int64 - if len(contentLengthStr) > 0 { - contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64) - if err != nil { - return nil, err - } - } - - return &http.Request{ - Method: method, - URL: u, - Proto: "HTTP/2.0", - ProtoMajor: 2, - ProtoMinor: 0, - Header: httpHeaders, - Body: nil, - ContentLength: contentLength, - Host: authority, - RequestURI: path, - TLS: &tls.ConnectionState{}, - }, nil -} - -func hostnameFromRequest(req *http.Request) string { - if req.URL != nil { - return req.URL.Host - } - return "" -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/request_body.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/request_body.go deleted file mode 100644 index 2d4d59543..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/request_body.go +++ /dev/null @@ -1,29 +0,0 @@ -package h2quic - -import ( - "io" - - quic "github.com/lucas-clemente/quic-go" -) - -type requestBody struct { - requestRead bool - dataStream quic.Stream -} - -// make sure the requestBody can be used as a http.Request.Body -var _ io.ReadCloser = &requestBody{} - -func newRequestBody(stream quic.Stream) *requestBody { - return &requestBody{dataStream: stream} -} - -func (b *requestBody) Read(p []byte) (int, error) { - b.requestRead = true - return b.dataStream.Read(p) -} - -func (b *requestBody) Close() error { - // stream's Close() closes the write side, not the read side - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/request_writer.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/request_writer.go deleted file mode 100644 index b5ee97513..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/request_writer.go +++ /dev/null @@ -1,203 +0,0 @@ -package h2quic - -import ( - "bytes" - "fmt" - "net/http" - "strconv" - "strings" - "sync" - - "golang.org/x/net/http/httpguts" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" - - quic "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type requestWriter struct { - mutex sync.Mutex - headerStream quic.Stream - - henc *hpack.Encoder - hbuf bytes.Buffer // HPACK encoder writes into this - - logger utils.Logger -} - -const defaultUserAgent = "quic-go" - -func newRequestWriter(headerStream quic.Stream, logger utils.Logger) *requestWriter { - rw := &requestWriter{ - headerStream: headerStream, - logger: logger, - } - rw.henc = hpack.NewEncoder(&rw.hbuf) - return rw -} - -func (w *requestWriter) WriteRequest(req *http.Request, dataStreamID protocol.StreamID, endStream, requestGzip bool) error { - // TODO: add support for trailers - // TODO: add support for gzip compression - // TODO: write continuation frames, if the header frame is too long - - w.mutex.Lock() - defer w.mutex.Unlock() - - w.encodeHeaders(req, requestGzip, "", actualContentLength(req)) - h2framer := http2.NewFramer(w.headerStream, nil) - return h2framer.WriteHeaders(http2.HeadersFrameParam{ - StreamID: uint32(dataStreamID), - EndHeaders: true, - EndStream: endStream, - BlockFragment: w.hbuf.Bytes(), - Priority: http2.PriorityParam{Weight: 0xff}, - }) -} - -// the rest of this files is copied from http2.Transport -func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { - w.hbuf.Reset() - - host := req.Host - if host == "" { - host = req.URL.Host - } - host, err := httpguts.PunycodeHostPort(host) - if err != nil { - return nil, err - } - - var path string - if req.Method != "CONNECT" { - path = req.URL.RequestURI() - if !validPseudoPath(path) { - orig := path - path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) - if !validPseudoPath(path) { - if req.URL.Opaque != "" { - return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) - } - return nil, fmt.Errorf("invalid request :path %q", orig) - } - } - } - - // Check for any invalid headers and return an error before we - // potentially pollute our hpack state. (We want to be able to - // continue to reuse the hpack encoder for future requests) - for k, vv := range req.Header { - if !httpguts.ValidHeaderFieldName(k) { - return nil, fmt.Errorf("invalid HTTP header name %q", k) - } - for _, v := range vv { - if !httpguts.ValidHeaderFieldValue(v) { - return nil, fmt.Errorf("invalid HTTP header value %q for header %q", v, k) - } - } - } - - // 8.1.2.3 Request Pseudo-Header Fields - // The :path pseudo-header field includes the path and query parts of the - // target URI (the path-absolute production and optionally a '?' character - // followed by the query production (see Sections 3.3 and 3.4 of - // [RFC3986]). - w.writeHeader(":authority", host) - w.writeHeader(":method", req.Method) - if req.Method != "CONNECT" { - w.writeHeader(":path", path) - w.writeHeader(":scheme", req.URL.Scheme) - } - if trailers != "" { - w.writeHeader("trailer", trailers) - } - - var didUA bool - for k, vv := range req.Header { - lowKey := strings.ToLower(k) - switch lowKey { - case "host", "content-length": - // Host is :authority, already sent. - // Content-Length is automatic, set below. - continue - case "connection", "proxy-connection", "transfer-encoding", "upgrade", "keep-alive": - // Per 8.1.2.2 Connection-Specific Header - // Fields, don't send connection-specific - // fields. We have already checked if any - // are error-worthy so just ignore the rest. - continue - case "user-agent": - // Match Go's http1 behavior: at most one - // User-Agent. If set to nil or empty string, - // then omit it. Otherwise if not mentioned, - // include the default (below). - didUA = true - if len(vv) < 1 { - continue - } - vv = vv[:1] - if vv[0] == "" { - continue - } - } - for _, v := range vv { - w.writeHeader(lowKey, v) - } - } - if shouldSendReqContentLength(req.Method, contentLength) { - w.writeHeader("content-length", strconv.FormatInt(contentLength, 10)) - } - if addGzipHeader { - w.writeHeader("accept-encoding", "gzip") - } - if !didUA { - w.writeHeader("user-agent", defaultUserAgent) - } - return w.hbuf.Bytes(), nil -} - -func (w *requestWriter) writeHeader(name, value string) { - w.logger.Debugf("http2: Transport encoding header %q = %q", name, value) - w.henc.WriteField(hpack.HeaderField{Name: name, Value: value}) -} - -// shouldSendReqContentLength reports whether the http2.Transport should send -// a "content-length" request header. This logic is basically a copy of the net/http -// transferWriter.shouldSendContentLength. -// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). -// -1 means unknown. -func shouldSendReqContentLength(method string, contentLength int64) bool { - if contentLength > 0 { - return true - } - if contentLength < 0 { - return false - } - // For zero bodies, whether we send a content-length depends on the method. - // It also kinda doesn't matter for http2 either way, with END_STREAM. - switch method { - case "POST", "PUT", "PATCH": - return true - default: - return false - } -} - -func validPseudoPath(v string) bool { - return (len(v) > 0 && v[0] == '/' && (len(v) == 1 || v[1] != '/')) || v == "*" -} - -// actualContentLength returns a sanitized version of -// req.ContentLength, where 0 actually means zero (not unknown) and -1 -// means unknown. -func actualContentLength(req *http.Request) int64 { - if req.Body == nil { - return 0 - } - if req.ContentLength != 0 { - return req.ContentLength - } - return -1 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/response.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/response.go deleted file mode 100644 index d5dd21941..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/response.go +++ /dev/null @@ -1,95 +0,0 @@ -package h2quic - -import ( - "bytes" - "errors" - "io/ioutil" - "net/http" - "net/textproto" - "strconv" - "strings" - - "golang.org/x/net/http2" -) - -// copied from net/http2/transport.go - -var errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") -var noBody = ioutil.NopCloser(bytes.NewReader(nil)) - -// from the handleResponse function -func responseFromHeaders(f *http2.MetaHeadersFrame) (*http.Response, error) { - if f.Truncated { - return nil, errResponseHeaderListSize - } - - status := f.PseudoValue("status") - if status == "" { - return nil, errors.New("missing status pseudo header") - } - statusCode, err := strconv.Atoi(status) - if err != nil { - return nil, errors.New("malformed non-numeric status pseudo header") - } - - // TODO: handle statusCode == 100 - - header := make(http.Header) - res := &http.Response{ - Proto: "HTTP/2.0", - ProtoMajor: 2, - Header: header, - StatusCode: statusCode, - Status: status + " " + http.StatusText(statusCode), - } - for _, hf := range f.RegularFields() { - key := http.CanonicalHeaderKey(hf.Name) - if key == "Trailer" { - t := res.Trailer - if t == nil { - t = make(http.Header) - res.Trailer = t - } - foreachHeaderElement(hf.Value, func(v string) { - t[http.CanonicalHeaderKey(v)] = nil - }) - } else { - header[key] = append(header[key], hf.Value) - } - } - - return res, nil -} - -// continuation of the handleResponse function -func setLength(res *http.Response, isHead, streamEnded bool) *http.Response { - if !streamEnded || isHead { - res.ContentLength = -1 - if clens := res.Header["Content-Length"]; len(clens) == 1 { - if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil { - res.ContentLength = clen64 - } - } - } - return res -} - -// copied from net/http/server.go - -// foreachHeaderElement splits v according to the "#rule" construction -// in RFC 2616 section 2.1 and calls fn for each non-empty element. -func foreachHeaderElement(v string, fn func(string)) { - v = textproto.TrimString(v) - if v == "" { - return - } - if !strings.Contains(v, ",") { - fn(v) - return - } - for _, f := range strings.Split(v, ",") { - if f = textproto.TrimString(f); f != "" { - fn(f) - } - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_body.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/response_body.go deleted file mode 100644 index 11d79e803..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_body.go +++ /dev/null @@ -1,18 +0,0 @@ -package h2quic - -import ( - "io" - - quic "github.com/lucas-clemente/quic-go" -) - -type responseBody struct { - quic.Stream -} - -var _ io.ReadCloser = &responseBody{} - -func (rb *responseBody) Close() error { - rb.Stream.CancelRead(0) - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer.go deleted file mode 100644 index 02841227e..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer.go +++ /dev/null @@ -1,114 +0,0 @@ -package h2quic - -import ( - "bytes" - "net/http" - "strconv" - "strings" - "sync" - - quic "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" -) - -type responseWriter struct { - dataStreamID protocol.StreamID - dataStream quic.Stream - - headerStream quic.Stream - headerStreamMutex *sync.Mutex - - header http.Header - status int // status code passed to WriteHeader - headerWritten bool - - logger utils.Logger -} - -func newResponseWriter( - headerStream quic.Stream, - headerStreamMutex *sync.Mutex, - dataStream quic.Stream, - dataStreamID protocol.StreamID, - logger utils.Logger, -) *responseWriter { - return &responseWriter{ - header: http.Header{}, - headerStream: headerStream, - headerStreamMutex: headerStreamMutex, - dataStream: dataStream, - dataStreamID: dataStreamID, - logger: logger, - } -} - -func (w *responseWriter) Header() http.Header { - return w.header -} - -func (w *responseWriter) WriteHeader(status int) { - if w.headerWritten { - return - } - w.headerWritten = true - w.status = status - - var headers bytes.Buffer - enc := hpack.NewEncoder(&headers) - enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)}) - - for k, v := range w.header { - for index := range v { - enc.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}) - } - } - - w.logger.Infof("Responding with %d", status) - w.headerStreamMutex.Lock() - defer w.headerStreamMutex.Unlock() - h2framer := http2.NewFramer(w.headerStream, nil) - err := h2framer.WriteHeaders(http2.HeadersFrameParam{ - StreamID: uint32(w.dataStreamID), - EndHeaders: true, - BlockFragment: headers.Bytes(), - }) - if err != nil { - w.logger.Errorf("could not write h2 header: %s", err.Error()) - } -} - -func (w *responseWriter) Write(p []byte) (int, error) { - if !w.headerWritten { - w.WriteHeader(200) - } - if !bodyAllowedForStatus(w.status) { - return 0, http.ErrBodyNotAllowed - } - return w.dataStream.Write(p) -} - -func (w *responseWriter) Flush() {} - -// This is a NOP. Use http.Request.Context -func (w *responseWriter) CloseNotify() <-chan bool { return make(<-chan bool) } - -// test that we implement http.Flusher -var _ http.Flusher = &responseWriter{} - -// copied from http2/http2.go -// bodyAllowedForStatus reports whether a given response status code -// permits a body. See RFC 2616, section 4.4. -func bodyAllowedForStatus(status int) bool { - switch { - case status >= 100 && status <= 199: - return false - case status == 204: - return false - case status == 304: - return false - } - return true -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer_closenotifier.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer_closenotifier.go deleted file mode 100644 index b26f91c1b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/response_writer_closenotifier.go +++ /dev/null @@ -1,9 +0,0 @@ -package h2quic - -import "net/http" - -// The CloseNotifier is a deprecated interface, and staticcheck will report that from Go 1.11. -// By defining it in a separate file, we can exclude this file from staticcheck. - -// test that we implement http.CloseNotifier -var _ http.CloseNotifier = &responseWriter{} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/roundtrip.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/roundtrip.go deleted file mode 100644 index 27732b5e4..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/roundtrip.go +++ /dev/null @@ -1,179 +0,0 @@ -package h2quic - -import ( - "crypto/tls" - "errors" - "fmt" - "io" - "net/http" - "strings" - "sync" - - quic "github.com/lucas-clemente/quic-go" - - "golang.org/x/net/http/httpguts" -) - -type roundTripCloser interface { - http.RoundTripper - io.Closer -} - -// RoundTripper implements the http.RoundTripper interface -type RoundTripper struct { - mutex sync.Mutex - - // DisableCompression, if true, prevents the Transport from - // requesting compression with an "Accept-Encoding: gzip" - // request header when the Request contains no existing - // Accept-Encoding value. If the Transport requests gzip on - // its own and gets a gzipped response, it's transparently - // decoded in the Response.Body. However, if the user - // explicitly requested gzip it is not automatically - // uncompressed. - DisableCompression bool - - // TLSClientConfig specifies the TLS configuration to use with - // tls.Client. If nil, the default configuration is used. - TLSClientConfig *tls.Config - - // QuicConfig is the quic.Config used for dialing new connections. - // If nil, reasonable default values will be used. - QuicConfig *quic.Config - - // Dial specifies an optional dial function for creating QUIC - // connections for requests. - // If Dial is nil, quic.DialAddr will be used. - Dial func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.Session, error) - - clients map[string]roundTripCloser -} - -// RoundTripOpt are options for the Transport.RoundTripOpt method. -type RoundTripOpt struct { - // OnlyCachedConn controls whether the RoundTripper may - // create a new QUIC connection. If set true and - // no cached connection is available, RoundTrip - // will return ErrNoCachedConn. - OnlyCachedConn bool -} - -var _ roundTripCloser = &RoundTripper{} - -// ErrNoCachedConn is returned when RoundTripper.OnlyCachedConn is set -var ErrNoCachedConn = errors.New("h2quic: no cached connection was available") - -// RoundTripOpt is like RoundTrip, but takes options. -func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { - if req.URL == nil { - closeRequestBody(req) - return nil, errors.New("quic: nil Request.URL") - } - if req.URL.Host == "" { - closeRequestBody(req) - return nil, errors.New("quic: no Host in request URL") - } - if req.Header == nil { - closeRequestBody(req) - return nil, errors.New("quic: nil Request.Header") - } - - if req.URL.Scheme == "https" { - for k, vv := range req.Header { - if !httpguts.ValidHeaderFieldName(k) { - return nil, fmt.Errorf("quic: invalid http header field name %q", k) - } - for _, v := range vv { - if !httpguts.ValidHeaderFieldValue(v) { - return nil, fmt.Errorf("quic: invalid http header field value %q for key %v", v, k) - } - } - } - } else { - closeRequestBody(req) - return nil, fmt.Errorf("quic: unsupported protocol scheme: %s", req.URL.Scheme) - } - - if req.Method != "" && !validMethod(req.Method) { - closeRequestBody(req) - return nil, fmt.Errorf("quic: invalid method %q", req.Method) - } - - hostname := authorityAddr("https", hostnameFromRequest(req)) - cl, err := r.getClient(hostname, opt.OnlyCachedConn) - if err != nil { - return nil, err - } - return cl.RoundTrip(req) -} - -// RoundTrip does a round trip. -func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - return r.RoundTripOpt(req, RoundTripOpt{}) -} - -func (r *RoundTripper) getClient(hostname string, onlyCached bool) (http.RoundTripper, error) { - r.mutex.Lock() - defer r.mutex.Unlock() - - if r.clients == nil { - r.clients = make(map[string]roundTripCloser) - } - - client, ok := r.clients[hostname] - if !ok { - if onlyCached { - return nil, ErrNoCachedConn - } - client = newClient( - hostname, - r.TLSClientConfig, - &roundTripperOpts{DisableCompression: r.DisableCompression}, - r.QuicConfig, - r.Dial, - ) - r.clients[hostname] = client - } - return client, nil -} - -// Close closes the QUIC connections that this RoundTripper has used -func (r *RoundTripper) Close() error { - r.mutex.Lock() - defer r.mutex.Unlock() - for _, client := range r.clients { - if err := client.Close(); err != nil { - return err - } - } - r.clients = nil - return nil -} - -func closeRequestBody(req *http.Request) { - if req.Body != nil { - req.Body.Close() - } -} - -func validMethod(method string) bool { - /* - Method = "OPTIONS" ; Section 9.2 - | "GET" ; Section 9.3 - | "HEAD" ; Section 9.4 - | "POST" ; Section 9.5 - | "PUT" ; Section 9.6 - | "DELETE" ; Section 9.7 - | "TRACE" ; Section 9.8 - | "CONNECT" ; Section 9.9 - | extension-method - extension-method = token - token = 1* - */ - return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1 -} - -// copied from net/http/http.go -func isNotToken(r rune) bool { - return !httpguts.IsTokenRune(r) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/h2quic/server.go b/vendor/github.com/lucas-clemente/quic-go/h2quic/server.go deleted file mode 100644 index 327499198..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/h2quic/server.go +++ /dev/null @@ -1,402 +0,0 @@ -package h2quic - -import ( - "crypto/tls" - "errors" - "fmt" - "net" - "net/http" - "runtime" - "strings" - "sync" - "sync/atomic" - "time" - - quic "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" -) - -type streamCreator interface { - quic.Session - GetOrOpenStream(protocol.StreamID) (quic.Stream, error) -} - -type remoteCloser interface { - CloseRemote(protocol.ByteCount) -} - -// allows mocking of quic.Listen and quic.ListenAddr -var ( - quicListen = quic.Listen - quicListenAddr = quic.ListenAddr -) - -// Server is a HTTP2 server listening for QUIC connections. -type Server struct { - *http.Server - - // By providing a quic.Config, it is possible to set parameters of the QUIC connection. - // If nil, it uses reasonable default values. - QuicConfig *quic.Config - - // Private flag for demo, do not use - CloseAfterFirstRequest bool - - port uint32 // used atomically - - listenerMutex sync.Mutex - listener quic.Listener - closed bool - - supportedVersionsAsString string - - logger utils.Logger // will be set by Server.serveImpl() -} - -// ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections. -func (s *Server) ListenAndServe() error { - if s.Server == nil { - return errors.New("use of h2quic.Server without http.Server") - } - return s.serveImpl(s.TLSConfig, nil) -} - -// ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/2 requests on incoming connections. -func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { - var err error - certs := make([]tls.Certificate, 1) - certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return err - } - // We currently only use the cert-related stuff from tls.Config, - // so we don't need to make a full copy. - config := &tls.Config{ - Certificates: certs, - } - return s.serveImpl(config, nil) -} - -// Serve an existing UDP connection. -func (s *Server) Serve(conn net.PacketConn) error { - return s.serveImpl(s.TLSConfig, conn) -} - -func (s *Server) serveImpl(tlsConfig *tls.Config, conn net.PacketConn) error { - if s.Server == nil { - return errors.New("use of h2quic.Server without http.Server") - } - s.logger = utils.DefaultLogger.WithPrefix("server") - s.listenerMutex.Lock() - if s.closed { - s.listenerMutex.Unlock() - return errors.New("Server is already closed") - } - if s.listener != nil { - s.listenerMutex.Unlock() - return errors.New("ListenAndServe may only be called once") - } - - var ln quic.Listener - var err error - if conn == nil { - ln, err = quicListenAddr(s.Addr, tlsConfig, s.QuicConfig) - } else { - ln, err = quicListen(conn, tlsConfig, s.QuicConfig) - } - if err != nil { - s.listenerMutex.Unlock() - return err - } - s.listener = ln - s.listenerMutex.Unlock() - - for { - sess, err := ln.Accept() - if err != nil { - return err - } - go s.handleHeaderStream(sess.(streamCreator)) - } -} - -func (s *Server) handleHeaderStream(session streamCreator) { - stream, err := session.AcceptStream() - if err != nil { - session.CloseWithError(quic.ErrorCode(qerr.InternalError), err) - return - } - - hpackDecoder := hpack.NewDecoder(4096, nil) - h2framer := http2.NewFramer(nil, stream) - - var headerStreamMutex sync.Mutex // Protects concurrent calls to Write() - for { - if err := s.handleRequest(session, stream, &headerStreamMutex, hpackDecoder, h2framer); err != nil { - // QuicErrors must originate from stream.Read() returning an error. - // In this case, the session has already logged the error, so we don't - // need to log it again. - errorCode := qerr.InternalError - if qerr, ok := err.(*qerr.QuicError); ok { - errorCode = qerr.ErrorCode - s.logger.Errorf("error handling h2 request: %s", err.Error()) - } - session.CloseWithError(quic.ErrorCode(errorCode), err) - return - } - } -} - -func (s *Server) handleRequest(session streamCreator, headerStream quic.Stream, headerStreamMutex *sync.Mutex, hpackDecoder *hpack.Decoder, h2framer *http2.Framer) error { - h2frame, err := h2framer.ReadFrame() - if err != nil { - return qerr.Error(qerr.InternalError, "cannot read frame") - } - var h2headersFrame *http2.HeadersFrame - switch f := h2frame.(type) { - case *http2.PriorityFrame: - // ignore PRIORITY frames - s.logger.Debugf("Ignoring H2 PRIORITY frame: %#v", f) - return nil - case *http2.HeadersFrame: - h2headersFrame = f - default: - return qerr.Error(qerr.ProtocolViolation, "expected a header frame") - } - - if !h2headersFrame.HeadersEnded() { - return errors.New("http2 header continuation not implemented") - } - headers, err := hpackDecoder.DecodeFull(h2headersFrame.HeaderBlockFragment()) - if err != nil { - s.logger.Errorf("invalid http2 headers encoding: %s", err.Error()) - return err - } - - req, err := requestFromHeaders(headers) - if err != nil { - return err - } - - if s.logger.Debug() { - s.logger.Infof("%s %s%s, on data stream %d", req.Method, req.Host, req.RequestURI, h2headersFrame.StreamID) - } else { - s.logger.Infof("%s %s%s", req.Method, req.Host, req.RequestURI) - } - - dataStream, err := session.GetOrOpenStream(protocol.StreamID(h2headersFrame.StreamID)) - if err != nil { - return err - } - // this can happen if the client immediately closes the data stream after sending the request and the runtime processes the reset before the request - if dataStream == nil { - return nil - } - - // handleRequest should be as non-blocking as possible to minimize - // head-of-line blocking. Potentially blocking code is run in a separate - // goroutine, enabling handleRequest to return before the code is executed. - go func() { - streamEnded := h2headersFrame.StreamEnded() - if streamEnded { - dataStream.(remoteCloser).CloseRemote(0) - streamEnded = true - _, _ = dataStream.Read([]byte{0}) // read the eof - } - - req = req.WithContext(dataStream.Context()) - reqBody := newRequestBody(dataStream) - req.Body = reqBody - - req.RemoteAddr = session.RemoteAddr().String() - - responseWriter := newResponseWriter(headerStream, headerStreamMutex, dataStream, protocol.StreamID(h2headersFrame.StreamID), s.logger) - - handler := s.Handler - if handler == nil { - handler = http.DefaultServeMux - } - panicked := false - func() { - defer func() { - if p := recover(); p != nil { - // Copied from net/http/server.go - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - s.logger.Errorf("http: panic serving: %v\n%s", p, buf) - panicked = true - } - }() - handler.ServeHTTP(responseWriter, req) - }() - if panicked { - responseWriter.WriteHeader(500) - } else { - responseWriter.WriteHeader(200) - } - if responseWriter.dataStream != nil { - if !streamEnded && !reqBody.requestRead { - // in gQUIC, the error code doesn't matter, so just use 0 here - responseWriter.dataStream.CancelRead(0) - } - responseWriter.dataStream.Close() - } - if s.CloseAfterFirstRequest { - time.Sleep(100 * time.Millisecond) - session.Close() - } - }() - - return nil -} - -// Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients. -// Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. -func (s *Server) Close() error { - s.listenerMutex.Lock() - defer s.listenerMutex.Unlock() - s.closed = true - if s.listener != nil { - err := s.listener.Close() - s.listener = nil - return err - } - return nil -} - -// CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete. -// CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. -func (s *Server) CloseGracefully(timeout time.Duration) error { - // TODO: implement - return nil -} - -// SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC. -// The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443): -// Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30" -func (s *Server) SetQuicHeaders(hdr http.Header) error { - port := atomic.LoadUint32(&s.port) - - if port == 0 { - // Extract port from s.Server.Addr - _, portStr, err := net.SplitHostPort(s.Server.Addr) - if err != nil { - return err - } - portInt, err := net.LookupPort("tcp", portStr) - if err != nil { - return err - } - port = uint32(portInt) - atomic.StoreUint32(&s.port, port) - } - - if s.supportedVersionsAsString == "" { - var versions []string - for _, v := range protocol.SupportedVersions { - versions = append(versions, v.ToAltSvc()) - } - s.supportedVersionsAsString = strings.Join(versions, ",") - } - - hdr.Add("Alt-Svc", fmt.Sprintf(`quic=":%d"; ma=2592000; v="%s"`, port, s.supportedVersionsAsString)) - - return nil -} - -// ListenAndServeQUIC listens on the UDP network address addr and calls the -// handler for HTTP/2 requests on incoming connections. http.DefaultServeMux is -// used when handler is nil. -func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error { - server := &Server{ - Server: &http.Server{ - Addr: addr, - Handler: handler, - }, - } - return server.ListenAndServeTLS(certFile, keyFile) -} - -// ListenAndServe listens on the given network address for both, TLS and QUIC -// connetions in parallel. It returns if one of the two returns an error. -// http.DefaultServeMux is used when handler is nil. -// The correct Alt-Svc headers for QUIC are set. -func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error { - // Load certs - var err error - certs := make([]tls.Certificate, 1) - certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return err - } - // We currently only use the cert-related stuff from tls.Config, - // so we don't need to make a full copy. - config := &tls.Config{ - Certificates: certs, - } - - // Open the listeners - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return err - } - udpConn, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return err - } - defer udpConn.Close() - - tcpAddr, err := net.ResolveTCPAddr("tcp", addr) - if err != nil { - return err - } - tcpConn, err := net.ListenTCP("tcp", tcpAddr) - if err != nil { - return err - } - defer tcpConn.Close() - - tlsConn := tls.NewListener(tcpConn, config) - defer tlsConn.Close() - - // Start the servers - httpServer := &http.Server{ - Addr: addr, - TLSConfig: config, - } - - quicServer := &Server{ - Server: httpServer, - } - - if handler == nil { - handler = http.DefaultServeMux - } - httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - quicServer.SetQuicHeaders(w.Header()) - handler.ServeHTTP(w, r) - }) - - hErr := make(chan error) - qErr := make(chan error) - go func() { - hErr <- httpServer.Serve(tlsConn) - }() - go func() { - qErr <- quicServer.Serve(udpConn) - }() - - select { - case err := <-hErr: - quicServer.Close() - return err - case err := <-qErr: - // Cannot close the HTTP server or wait for requests to complete properly :/ - return err - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/interface.go b/vendor/github.com/lucas-clemente/quic-go/interface.go deleted file mode 100644 index a83d09b84..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/interface.go +++ /dev/null @@ -1,224 +0,0 @@ -package quic - -import ( - "context" - "crypto/tls" - "io" - "net" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// The StreamID is the ID of a QUIC stream. -type StreamID = protocol.StreamID - -// A VersionNumber is a QUIC version number. -type VersionNumber = protocol.VersionNumber - -// A Cookie can be used to verify the ownership of the client address. -type Cookie struct { - RemoteAddr string - SentTime time.Time -} - -// An ErrorCode is an application-defined error code. -type ErrorCode = protocol.ApplicationErrorCode - -// Stream is the interface implemented by QUIC streams -type Stream interface { - // StreamID returns the stream ID. - StreamID() StreamID - // Read reads data from the stream. - // Read can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetReadDeadline. - // If the stream was canceled by the peer, the error implements the StreamError - // interface, and Canceled() == true. - // If the session was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - io.Reader - // Write writes data to the stream. - // Write can be made to time out and return a net.Error with Timeout() == true - // after a fixed time limit; see SetDeadline and SetWriteDeadline. - // If the stream was canceled by the peer, the error implements the StreamError - // interface, and Canceled() == true. - // If the session was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - io.Writer - // Close closes the write-direction of the stream. - // Future calls to Write are not permitted after calling Close. - // It must not be called concurrently with Write. - // It must not be called after calling CancelWrite. - io.Closer - // CancelWrite aborts sending on this stream. - // Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably. - // Write will unblock immediately, and future calls to Write will fail. - // When called multiple times or after closing the stream it is a no-op. - CancelWrite(ErrorCode) - // CancelRead aborts receiving on this stream. - // It will ask the peer to stop transmitting stream data. - // Read will unblock immediately, and future Read calls will fail. - // When called multiple times or after reading the io.EOF it is a no-op. - CancelRead(ErrorCode) - // The context is canceled as soon as the write-side of the stream is closed. - // This happens when Close() or CancelWrite() is called, or when the peer - // cancels the read-side of their stream. - // Warning: This API should not be considered stable and might change soon. - Context() context.Context - // SetReadDeadline sets the deadline for future Read calls and - // any currently-blocked Read call. - // A zero value for t means Read will not time out. - SetReadDeadline(t time.Time) error - // SetWriteDeadline sets the deadline for future Write calls - // and any currently-blocked Write call. - // Even if write times out, it may return n > 0, indicating that - // some of the data was successfully written. - // A zero value for t means Write will not time out. - SetWriteDeadline(t time.Time) error - // SetDeadline sets the read and write deadlines associated - // with the connection. It is equivalent to calling both - // SetReadDeadline and SetWriteDeadline. - SetDeadline(t time.Time) error -} - -// A ReceiveStream is a unidirectional Receive Stream. -type ReceiveStream interface { - // see Stream.StreamID - StreamID() StreamID - // see Stream.Read - io.Reader - // see Stream.CancelRead - CancelRead(ErrorCode) - // see Stream.SetReadDealine - SetReadDeadline(t time.Time) error -} - -// A SendStream is a unidirectional Send Stream. -type SendStream interface { - // see Stream.StreamID - StreamID() StreamID - // see Stream.Write - io.Writer - // see Stream.Close - io.Closer - // see Stream.CancelWrite - CancelWrite(ErrorCode) - // see Stream.Context - Context() context.Context - // see Stream.SetWriteDeadline - SetWriteDeadline(t time.Time) error -} - -// StreamError is returned by Read and Write when the peer cancels the stream. -type StreamError interface { - error - Canceled() bool - ErrorCode() ErrorCode -} - -// A Session is a QUIC connection between two peers. -type Session interface { - // AcceptStream returns the next stream opened by the peer, blocking until one is available. - // If the session was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - AcceptStream() (Stream, error) - // AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available. - // If the session was closed due to a timeout, the error satisfies - // the net.Error interface, and Timeout() will be true. - AcceptUniStream() (ReceiveStream, error) - // OpenStream opens a new bidirectional QUIC stream. - // There is no signaling to the peer about new streams: - // The peer can only accept the stream after data has been sent on the stream. - // If the error is non-nil, it satisfies the net.Error interface. - // When reaching the peer's stream limit, err.Temporary() will be true. - // If the session was closed due to a timeout, Timeout() will be true. - OpenStream() (Stream, error) - // OpenStreamSync opens a new bidirectional QUIC stream. - // It blocks until a new stream can be opened. - // If the error is non-nil, it satisfies the net.Error interface. - // If the session was closed due to a timeout, Timeout() will be true. - OpenStreamSync() (Stream, error) - // OpenUniStream opens a new outgoing unidirectional QUIC stream. - // If the error is non-nil, it satisfies the net.Error interface. - // When reaching the peer's stream limit, Temporary() will be true. - // If the session was closed due to a timeout, Timeout() will be true. - OpenUniStream() (SendStream, error) - // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. - // It blocks until a new stream can be opened. - // If the error is non-nil, it satisfies the net.Error interface. - // If the session was closed due to a timeout, Timeout() will be true. - OpenUniStreamSync() (SendStream, error) - // LocalAddr returns the local address. - LocalAddr() net.Addr - // RemoteAddr returns the address of the peer. - RemoteAddr() net.Addr - // Close the connection. - io.Closer - // Close the connection with an error. - // The error must not be nil. - CloseWithError(ErrorCode, error) error - // The context is cancelled when the session is closed. - // Warning: This API should not be considered stable and might change soon. - Context() context.Context - // ConnectionState returns basic details about the QUIC connection. - // Warning: This API should not be considered stable and might change soon. - ConnectionState() tls.ConnectionState -} - -// Config contains all configuration data needed for a QUIC server or client. -type Config struct { - // The QUIC versions that can be negotiated. - // If not set, it uses all versions available. - // Warning: This API should not be considered stable and will change soon. - Versions []VersionNumber - // The length of the connection ID in bytes. - // It can be 0, or any value between 4 and 18. - // If not set, the interpretation depends on where the Config is used: - // If used for dialing an address, a 0 byte connection ID will be used. - // If used for a server, or dialing on a packet conn, a 4 byte connection ID will be used. - // When dialing on a packet conn, the ConnectionIDLength value must be the same for every Dial call. - ConnectionIDLength int - // HandshakeTimeout is the maximum duration that the cryptographic handshake may take. - // If the timeout is exceeded, the connection is closed. - // If this value is zero, the timeout is set to 10 seconds. - HandshakeTimeout time.Duration - // IdleTimeout is the maximum duration that may pass without any incoming network activity. - // This value only applies after the handshake has completed. - // If the timeout is exceeded, the connection is closed. - // If this value is zero, the timeout is set to 30 seconds. - IdleTimeout time.Duration - // AcceptCookie determines if a Cookie is accepted. - // It is called with cookie = nil if the client didn't send an Cookie. - // If not set, it verifies that the address matches, and that the Cookie was issued within the last 24 hours. - // This option is only valid for the server. - AcceptCookie func(clientAddr net.Addr, cookie *Cookie) bool - // MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data. - // If this value is zero, it will default to 1 MB for the server and 6 MB for the client. - MaxReceiveStreamFlowControlWindow uint64 - // MaxReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data. - // If this value is zero, it will default to 1.5 MB for the server and 15 MB for the client. - MaxReceiveConnectionFlowControlWindow uint64 - // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. - // If not set, it will default to 100. - // If set to a negative value, it doesn't allow any bidirectional streams. - MaxIncomingStreams int - // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open. - // If not set, it will default to 100. - // If set to a negative value, it doesn't allow any unidirectional streams. - MaxIncomingUniStreams int - // The StatelessResetKey is used to generate stateless reset tokens. - // If no key is configured, sending of stateless resets is disabled. - StatelessResetKey []byte - // KeepAlive defines whether this peer will periodically send a packet to keep the connection alive. - KeepAlive bool -} - -// A Listener for incoming QUIC connections -type Listener interface { - // Close the server. All active sessions will be closed. - Close() error - // Addr returns the local network addr that the server is listening on. - Addr() net.Addr - // Accept returns new sessions. It should be called in a loop. - Accept() (Session, error) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/gen.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/gen.go deleted file mode 100644 index 32235f81a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/gen.go +++ /dev/null @@ -1,3 +0,0 @@ -package ackhandler - -//go:generate genny -pkg ackhandler -in ../utils/linkedlist/linkedlist.go -out packet_linkedlist.go gen Item=Packet diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go deleted file mode 100644 index 362b9fbe8..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/interfaces.go +++ /dev/null @@ -1,50 +0,0 @@ -package ackhandler - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// SentPacketHandler handles ACKs received for outgoing packets -type SentPacketHandler interface { - // SentPacket may modify the packet - SentPacket(packet *Packet) - SentPacketsAsRetransmission(packets []*Packet, retransmissionOf protocol.PacketNumber) - ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, recvTime time.Time) error - SetHandshakeComplete() - ResetForRetry() error - - // The SendMode determines if and what kind of packets can be sent. - SendMode() SendMode - // TimeUntilSend is the time when the next packet should be sent. - // It is used for pacing packets. - TimeUntilSend() time.Time - // ShouldSendNumPackets returns the number of packets that should be sent immediately. - // It always returns a number greater or equal than 1. - // A number greater than 1 is returned when the pacing delay is smaller than the minimum pacing delay. - // Note that the number of packets is only calculated based on the pacing algorithm. - // Before sending any packet, SendingAllowed() must be called to learn if we can actually send it. - ShouldSendNumPackets() int - - // only to be called once the handshake is complete - GetLowestPacketNotConfirmedAcked() protocol.PacketNumber - DequeuePacketForRetransmission() *Packet - DequeueProbePacket() (*Packet, error) - - PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) - PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber - - GetAlarmTimeout() time.Time - OnAlarm() error -} - -// ReceivedPacketHandler handles ACKs needed to send for incoming packets -type ReceivedPacketHandler interface { - ReceivedPacket(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel, rcvTime time.Time, shouldInstigateAck bool) error - IgnoreBelow(protocol.PacketNumber) - - GetAlarmTimeout() time.Time - GetAckFrame(protocol.EncryptionLevel) *wire.AckFrame -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet.go deleted file mode 100644 index 9673a85c7..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet.go +++ /dev/null @@ -1,29 +0,0 @@ -package ackhandler - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// A Packet is a packet -type Packet struct { - PacketNumber protocol.PacketNumber - PacketType protocol.PacketType - Frames []wire.Frame - Length protocol.ByteCount - EncryptionLevel protocol.EncryptionLevel - SendTime time.Time - - largestAcked protocol.PacketNumber // if the packet contains an ACK, the LargestAcked value of that ACK - - // There are two reasons why a packet cannot be retransmitted: - // * it was already retransmitted - // * this packet is a retransmission, and we already received an ACK for the original packet - canBeRetransmitted bool - includedInBytesInFlight bool - retransmittedAs []protocol.PacketNumber - isRetransmission bool // we need a separate bool here because 0 is a valid packet number - retransmissionOf protocol.PacketNumber -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_linkedlist.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_linkedlist.go deleted file mode 100644 index bb74f4ef9..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_linkedlist.go +++ /dev/null @@ -1,217 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package ackhandler - -// Linked list implementation from the Go standard library. - -// PacketElement is an element of a linked list. -type PacketElement struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *PacketElement - - // The list to which this element belongs. - list *PacketList - - // The value stored with this element. - Value Packet -} - -// Next returns the next list element or nil. -func (e *PacketElement) Next() *PacketElement { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// Prev returns the previous list element or nil. -func (e *PacketElement) Prev() *PacketElement { - if p := e.prev; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// PacketList is a linked list of Packets. -type PacketList struct { - root PacketElement // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element -} - -// Init initializes or clears list l. -func (l *PacketList) Init() *PacketList { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// NewPacketList returns an initialized list. -func NewPacketList() *PacketList { return new(PacketList).Init() } - -// Len returns the number of elements of list l. -// The complexity is O(1). -func (l *PacketList) Len() int { return l.len } - -// Front returns the first element of list l or nil if the list is empty. -func (l *PacketList) Front() *PacketElement { - if l.len == 0 { - return nil - } - return l.root.next -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *PacketList) Back() *PacketElement { - if l.len == 0 { - return nil - } - return l.root.prev -} - -// lazyInit lazily initializes a zero List value. -func (l *PacketList) lazyInit() { - if l.root.next == nil { - l.Init() - } -} - -// insert inserts e after at, increments l.len, and returns e. -func (l *PacketList) insert(e, at *PacketElement) *PacketElement { - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - e.list = l - l.len++ - return e -} - -// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). -func (l *PacketList) insertValue(v Packet, at *PacketElement) *PacketElement { - return l.insert(&PacketElement{Value: v}, at) -} - -// remove removes e from its list, decrements l.len, and returns e. -func (l *PacketList) remove(e *PacketElement) *PacketElement { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - e.list = nil - l.len-- - return e -} - -// Remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *PacketList) Remove(e *PacketElement) Packet { - if e.list == l { - // if e.list == l, l must have been initialized when e was inserted - // in l or l == nil (e is a zero Element) and l.remove will crash - l.remove(e) - } - return e.Value -} - -// PushFront inserts a new element e with value v at the front of list l and returns e. -func (l *PacketList) PushFront(v Packet) *PacketElement { - l.lazyInit() - return l.insertValue(v, &l.root) -} - -// PushBack inserts a new element e with value v at the back of list l and returns e. -func (l *PacketList) PushBack(v Packet) *PacketElement { - l.lazyInit() - return l.insertValue(v, l.root.prev) -} - -// InsertBefore inserts a new element e with value v immediately before mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *PacketList) InsertBefore(v Packet, mark *PacketElement) *PacketElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark.prev) -} - -// InsertAfter inserts a new element e with value v immediately after mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *PacketList) InsertAfter(v Packet, mark *PacketElement) *PacketElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark) -} - -// MoveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *PacketList) MoveToFront(e *PacketElement) { - if e.list != l || l.root.next == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), &l.root) -} - -// MoveToBack moves element e to the back of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *PacketList) MoveToBack(e *PacketElement) { - if e.list != l || l.root.prev == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), l.root.prev) -} - -// MoveBefore moves element e to its new position before mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *PacketList) MoveBefore(e, mark *PacketElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark.prev) -} - -// MoveAfter moves element e to its new position after mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *PacketList) MoveAfter(e, mark *PacketElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark) -} - -// PushBackList inserts a copy of an other list at the back of list l. -// The lists l and other may be the same. They must not be nil. -func (l *PacketList) PushBackList(other *PacketList) { - l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { - l.insertValue(e.Value, l.root.prev) - } -} - -// PushFrontList inserts a copy of an other list at the front of list l. -// The lists l and other may be the same. They must not be nil. -func (l *PacketList) PushFrontList(other *PacketList) { - l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { - l.insertValue(e.Value, &l.root) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_number_generator.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_number_generator.go deleted file mode 100644 index 56fbf3d80..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/packet_number_generator.go +++ /dev/null @@ -1,78 +0,0 @@ -package ackhandler - -import ( - "crypto/rand" - "math" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// The packetNumberGenerator generates the packet number for the next packet -// it randomly skips a packet number every averagePeriod packets (on average) -// it is guarantued to never skip two consecutive packet numbers -type packetNumberGenerator struct { - averagePeriod protocol.PacketNumber - - next protocol.PacketNumber - nextToSkip protocol.PacketNumber - - history []protocol.PacketNumber -} - -func newPacketNumberGenerator(initial, averagePeriod protocol.PacketNumber) *packetNumberGenerator { - g := &packetNumberGenerator{ - next: initial, - averagePeriod: averagePeriod, - } - g.generateNewSkip() - return g -} - -func (p *packetNumberGenerator) Peek() protocol.PacketNumber { - return p.next -} - -func (p *packetNumberGenerator) Pop() protocol.PacketNumber { - next := p.next - - // generate a new packet number for the next packet - p.next++ - - if p.next == p.nextToSkip { - if len(p.history)+1 > protocol.MaxTrackedSkippedPackets { - p.history = p.history[1:] - } - p.history = append(p.history, p.next) - p.next++ - p.generateNewSkip() - } - - return next -} - -func (p *packetNumberGenerator) generateNewSkip() { - num := p.getRandomNumber() - skip := protocol.PacketNumber(num) * (p.averagePeriod - 1) / (math.MaxUint16 / 2) - // make sure that there are never two consecutive packet numbers that are skipped - p.nextToSkip = p.next + 2 + skip -} - -// getRandomNumber() generates a cryptographically secure random number between 0 and MaxUint16 (= 65535) -// The expectation value is 65535/2 -func (p *packetNumberGenerator) getRandomNumber() uint16 { - b := make([]byte, 2) - rand.Read(b) // ignore the error here - - num := uint16(b[0])<<8 + uint16(b[1]) - return num -} - -func (p *packetNumberGenerator) Validate(ack *wire.AckFrame) bool { - for _, pn := range p.history { - if ack.AcksPacket(pn) { - return false - } - } - return true -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_handler.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_handler.go deleted file mode 100644 index 1df64ea87..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_handler.go +++ /dev/null @@ -1,98 +0,0 @@ -package ackhandler - -import ( - "fmt" - "time" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -const ( - // maximum delay that can be applied to an ACK for a retransmittable packet - ackSendDelay = 25 * time.Millisecond - // initial maximum number of retransmittable packets received before sending an ack. - initialRetransmittablePacketsBeforeAck = 2 - // number of retransmittable that an ACK is sent for - retransmittablePacketsBeforeAck = 10 - // 1/5 RTT delay when doing ack decimation - ackDecimationDelay = 1.0 / 4 - // 1/8 RTT delay when doing ack decimation - shortAckDecimationDelay = 1.0 / 8 - // Minimum number of packets received before ack decimation is enabled. - // This intends to avoid the beginning of slow start, when CWNDs may be - // rapidly increasing. - minReceivedBeforeAckDecimation = 100 - // Maximum number of packets to ack immediately after a missing packet for - // fast retransmission to kick in at the sender. This limit is created to - // reduce the number of acks sent that have no benefit for fast retransmission. - // Set to the number of nacks needed for fast retransmit plus one for protection - // against an ack loss - maxPacketsAfterNewMissing = 4 -) - -type receivedPacketHandler struct { - initialPackets *receivedPacketTracker - handshakePackets *receivedPacketTracker - oneRTTPackets *receivedPacketTracker -} - -var _ ReceivedPacketHandler = &receivedPacketHandler{} - -// NewReceivedPacketHandler creates a new receivedPacketHandler -func NewReceivedPacketHandler( - rttStats *congestion.RTTStats, - logger utils.Logger, - version protocol.VersionNumber, -) ReceivedPacketHandler { - return &receivedPacketHandler{ - initialPackets: newReceivedPacketTracker(rttStats, logger, version), - handshakePackets: newReceivedPacketTracker(rttStats, logger, version), - oneRTTPackets: newReceivedPacketTracker(rttStats, logger, version), - } -} - -func (h *receivedPacketHandler) ReceivedPacket( - pn protocol.PacketNumber, - encLevel protocol.EncryptionLevel, - rcvTime time.Time, - shouldInstigateAck bool, -) error { - switch encLevel { - case protocol.EncryptionInitial: - return h.initialPackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck) - case protocol.EncryptionHandshake: - return h.handshakePackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck) - case protocol.Encryption1RTT: - return h.oneRTTPackets.ReceivedPacket(pn, rcvTime, shouldInstigateAck) - default: - return fmt.Errorf("received packet with unknown encryption level: %s", encLevel) - } -} - -// only to be used with 1-RTT packets -func (h *receivedPacketHandler) IgnoreBelow(pn protocol.PacketNumber) { - h.oneRTTPackets.IgnoreBelow(pn) -} - -func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { - initialAlarm := h.initialPackets.GetAlarmTimeout() - handshakeAlarm := h.handshakePackets.GetAlarmTimeout() - oneRTTAlarm := h.oneRTTPackets.GetAlarmTimeout() - return utils.MinNonZeroTime(utils.MinNonZeroTime(initialAlarm, handshakeAlarm), oneRTTAlarm) -} - -func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel) *wire.AckFrame { - switch encLevel { - case protocol.EncryptionInitial: - return h.initialPackets.GetAckFrame() - case protocol.EncryptionHandshake: - return h.handshakePackets.GetAckFrame() - case protocol.Encryption1RTT: - return h.oneRTTPackets.GetAckFrame() - default: - return nil - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_history.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_history.go deleted file mode 100644 index 0daba413d..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_history.go +++ /dev/null @@ -1,122 +0,0 @@ -package ackhandler - -import ( - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// The receivedPacketHistory stores if a packet number has already been received. -// It generates ACK ranges which can be used to assemble an ACK frame. -// It does not store packet contents. -type receivedPacketHistory struct { - ranges *utils.PacketIntervalList - - lowestInReceivedPacketNumbers protocol.PacketNumber -} - -var errTooManyOutstandingReceivedAckRanges = qerr.Error(qerr.InternalError, "Too many outstanding received ACK ranges") - -// newReceivedPacketHistory creates a new received packet history -func newReceivedPacketHistory() *receivedPacketHistory { - return &receivedPacketHistory{ - ranges: utils.NewPacketIntervalList(), - } -} - -// ReceivedPacket registers a packet with PacketNumber p and updates the ranges -func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) error { - if h.ranges.Len() >= protocol.MaxTrackedReceivedAckRanges { - return errTooManyOutstandingReceivedAckRanges - } - - if h.ranges.Len() == 0 { - h.ranges.PushBack(utils.PacketInterval{Start: p, End: p}) - return nil - } - - for el := h.ranges.Back(); el != nil; el = el.Prev() { - // p already included in an existing range. Nothing to do here - if p >= el.Value.Start && p <= el.Value.End { - return nil - } - - var rangeExtended bool - if el.Value.End == p-1 { // extend a range at the end - rangeExtended = true - el.Value.End = p - } else if el.Value.Start == p+1 { // extend a range at the beginning - rangeExtended = true - el.Value.Start = p - } - - // if a range was extended (either at the beginning or at the end, maybe it is possible to merge two ranges into one) - if rangeExtended { - prev := el.Prev() - if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges - prev.Value.End = el.Value.End - h.ranges.Remove(el) - return nil - } - return nil // if the two ranges were not merge, we're done here - } - - // create a new range at the end - if p > el.Value.End { - h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el) - return nil - } - } - - // create a new range at the beginning - h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front()) - - return nil -} - -// DeleteBelow deletes all entries below (but not including) p -func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) { - if p <= h.lowestInReceivedPacketNumbers { - return - } - h.lowestInReceivedPacketNumbers = p - - nextEl := h.ranges.Front() - for el := h.ranges.Front(); nextEl != nil; el = nextEl { - nextEl = el.Next() - - if p > el.Value.Start && p <= el.Value.End { - el.Value.Start = p - } else if el.Value.End < p { // delete a whole range - h.ranges.Remove(el) - } else { // no ranges affected. Nothing to do - return - } - } -} - -// GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame -func (h *receivedPacketHistory) GetAckRanges() []wire.AckRange { - if h.ranges.Len() == 0 { - return nil - } - - ackRanges := make([]wire.AckRange, h.ranges.Len()) - i := 0 - for el := h.ranges.Back(); el != nil; el = el.Prev() { - ackRanges[i] = wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End} - i++ - } - return ackRanges -} - -func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange { - ackRange := wire.AckRange{} - if h.ranges.Len() > 0 { - r := h.ranges.Back().Value - ackRange.Smallest = r.Start - ackRange.Largest = r.End - } - return ackRange -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_tracker.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_tracker.go deleted file mode 100644 index b7840490f..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/received_packet_tracker.go +++ /dev/null @@ -1,191 +0,0 @@ -package ackhandler - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type receivedPacketTracker struct { - largestObserved protocol.PacketNumber - ignoreBelow protocol.PacketNumber - largestObservedReceivedTime time.Time - - packetHistory *receivedPacketHistory - - ackSendDelay time.Duration - rttStats *congestion.RTTStats - - packetsReceivedSinceLastAck int - retransmittablePacketsReceivedSinceLastAck int - ackQueued bool - ackAlarm time.Time - lastAck *wire.AckFrame - - logger utils.Logger - - version protocol.VersionNumber -} - -func newReceivedPacketTracker( - rttStats *congestion.RTTStats, - logger utils.Logger, - version protocol.VersionNumber, -) *receivedPacketTracker { - return &receivedPacketTracker{ - packetHistory: newReceivedPacketHistory(), - ackSendDelay: ackSendDelay, - rttStats: rttStats, - logger: logger, - version: version, - } -} - -func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck bool) error { - if packetNumber < h.ignoreBelow { - return nil - } - - isMissing := h.isMissing(packetNumber) - if packetNumber >= h.largestObserved { - h.largestObserved = packetNumber - h.largestObservedReceivedTime = rcvTime - } - - if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil { - return err - } - h.maybeQueueAck(packetNumber, rcvTime, shouldInstigateAck, isMissing) - return nil -} - -// IgnoreBelow sets a lower limit for acking packets. -// Packets with packet numbers smaller than p will not be acked. -func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) { - if p <= h.ignoreBelow { - return - } - h.ignoreBelow = p - h.packetHistory.DeleteBelow(p) - if h.logger.Debug() { - h.logger.Debugf("\tIgnoring all packets below %#x.", p) - } -} - -// isMissing says if a packet was reported missing in the last ACK. -func (h *receivedPacketTracker) isMissing(p protocol.PacketNumber) bool { - if h.lastAck == nil || p < h.ignoreBelow { - return false - } - return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p) -} - -func (h *receivedPacketTracker) hasNewMissingPackets() bool { - if h.lastAck == nil { - return false - } - highestRange := h.packetHistory.GetHighestAckRange() - return highestRange.Smallest >= h.lastAck.LargestAcked() && highestRange.Len() <= maxPacketsAfterNewMissing -} - -// maybeQueueAck queues an ACK, if necessary. -// It is implemented analogously to Chrome's QuicConnection::MaybeQueueAck() -// in ACK_DECIMATION_WITH_REORDERING mode. -func (h *receivedPacketTracker) maybeQueueAck(packetNumber protocol.PacketNumber, rcvTime time.Time, shouldInstigateAck, wasMissing bool) { - h.packetsReceivedSinceLastAck++ - - // always ack the first packet - if h.lastAck == nil { - h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") - h.ackQueued = true - return - } - - // Send an ACK if this packet was reported missing in an ACK sent before. - // Ack decimation with reordering relies on the timer to send an ACK, but if - // missing packets we reported in the previous ack, send an ACK immediately. - if wasMissing { - if h.logger.Debug() { - h.logger.Debugf("\tQueueing ACK because packet %#x was missing before.", packetNumber) - } - h.ackQueued = true - } - - if !h.ackQueued && shouldInstigateAck { - h.retransmittablePacketsReceivedSinceLastAck++ - - if packetNumber > minReceivedBeforeAckDecimation { - // ack up to 10 packets at once - if h.retransmittablePacketsReceivedSinceLastAck >= retransmittablePacketsBeforeAck { - h.ackQueued = true - if h.logger.Debug() { - h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, retransmittablePacketsBeforeAck) - } - } else if h.ackAlarm.IsZero() { - // wait for the minimum of the ack decimation delay or the delayed ack time before sending an ack - ackDelay := utils.MinDuration(ackSendDelay, time.Duration(float64(h.rttStats.MinRTT())*float64(ackDecimationDelay))) - h.ackAlarm = rcvTime.Add(ackDelay) - if h.logger.Debug() { - h.logger.Debugf("\tSetting ACK timer to min(1/4 min-RTT, max ack delay): %s (%s from now)", ackDelay, time.Until(h.ackAlarm)) - } - } - } else { - // send an ACK every 2 retransmittable packets - if h.retransmittablePacketsReceivedSinceLastAck >= initialRetransmittablePacketsBeforeAck { - if h.logger.Debug() { - h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.retransmittablePacketsReceivedSinceLastAck, initialRetransmittablePacketsBeforeAck) - } - h.ackQueued = true - } else if h.ackAlarm.IsZero() { - if h.logger.Debug() { - h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", ackSendDelay) - } - h.ackAlarm = rcvTime.Add(ackSendDelay) - } - } - // If there are new missing packets to report, set a short timer to send an ACK. - if h.hasNewMissingPackets() { - // wait the minimum of 1/8 min RTT and the existing ack time - ackDelay := time.Duration(float64(h.rttStats.MinRTT()) * float64(shortAckDecimationDelay)) - ackTime := rcvTime.Add(ackDelay) - if h.ackAlarm.IsZero() || h.ackAlarm.After(ackTime) { - h.ackAlarm = ackTime - if h.logger.Debug() { - h.logger.Debugf("\tSetting ACK timer to 1/8 min-RTT: %s (%s from now)", ackDelay, time.Until(h.ackAlarm)) - } - } - } - } - - if h.ackQueued { - // cancel the ack alarm - h.ackAlarm = time.Time{} - } -} - -func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame { - now := time.Now() - if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) { - return nil - } - if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() { - h.logger.Debugf("Sending ACK because the ACK timer expired.") - } - - ack := &wire.AckFrame{ - AckRanges: h.packetHistory.GetAckRanges(), - DelayTime: now.Sub(h.largestObservedReceivedTime), - } - - h.lastAck = ack - h.ackAlarm = time.Time{} - h.ackQueued = false - h.packetsReceivedSinceLastAck = 0 - h.retransmittablePacketsReceivedSinceLastAck = 0 - return ack -} - -func (h *receivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm } diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/retransmittable.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/retransmittable.go deleted file mode 100644 index ae622afd5..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/retransmittable.go +++ /dev/null @@ -1,34 +0,0 @@ -package ackhandler - -import "github.com/lucas-clemente/quic-go/internal/wire" - -// Returns a new slice with all non-retransmittable frames deleted. -func stripNonRetransmittableFrames(fs []wire.Frame) []wire.Frame { - res := make([]wire.Frame, 0, len(fs)) - for _, f := range fs { - if IsFrameRetransmittable(f) { - res = append(res, f) - } - } - return res -} - -// IsFrameRetransmittable returns true if the frame should be retransmitted. -func IsFrameRetransmittable(f wire.Frame) bool { - switch f.(type) { - case *wire.AckFrame: - return false - default: - return true - } -} - -// HasRetransmittableFrames returns true if at least one frame is retransmittable. -func HasRetransmittableFrames(fs []wire.Frame) bool { - for _, f := range fs { - if IsFrameRetransmittable(f) { - return true - } - } - return false -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/send_mode.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/send_mode.go deleted file mode 100644 index 8cdaa7e6b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/send_mode.go +++ /dev/null @@ -1,36 +0,0 @@ -package ackhandler - -import "fmt" - -// The SendMode says what kind of packets can be sent. -type SendMode uint8 - -const ( - // SendNone means that no packets should be sent - SendNone SendMode = iota - // SendAck means an ACK-only packet should be sent - SendAck - // SendRetransmission means that retransmissions should be sent - SendRetransmission - // SendPTO means that a probe packet should be sent - SendPTO - // SendAny means that any packet should be sent - SendAny -) - -func (s SendMode) String() string { - switch s { - case SendNone: - return "none" - case SendAck: - return "ack" - case SendRetransmission: - return "retransmission" - case SendPTO: - return "pto" - case SendAny: - return "any" - default: - return fmt.Sprintf("invalid send mode: %d", s) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go deleted file mode 100644 index 51e28fc0f..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_handler.go +++ /dev/null @@ -1,647 +0,0 @@ -package ackhandler - -import ( - "errors" - "fmt" - "math" - "time" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -const ( - // Maximum reordering in time space before time based loss detection considers a packet lost. - // In fraction of an RTT. - timeReorderingFraction = 1.0 / 8 - // Timer granularity. The timer will not be set to a value smaller than granularity. - granularity = time.Millisecond -) - -type packetNumberSpace struct { - history *sentPacketHistory - pns *packetNumberGenerator - - largestAcked protocol.PacketNumber - largestSent protocol.PacketNumber -} - -func newPacketNumberSpace(initialPN protocol.PacketNumber) *packetNumberSpace { - return &packetNumberSpace{ - history: newSentPacketHistory(), - pns: newPacketNumberGenerator(initialPN, protocol.SkipPacketAveragePeriodLength), - } -} - -type sentPacketHandler struct { - lastSentRetransmittablePacketTime time.Time // only applies to the application-data packet number space - lastSentCryptoPacketTime time.Time - - nextSendTime time.Time - - initialPackets *packetNumberSpace - handshakePackets *packetNumberSpace - oneRTTPackets *packetNumberSpace - - // lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived - // example: we send an ACK for packets 90-100 with packet number 20 - // once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101 - // Only applies to the application-data packet number space. - lowestNotConfirmedAcked protocol.PacketNumber - - retransmissionQueue []*Packet - - bytesInFlight protocol.ByteCount - - congestion congestion.SendAlgorithm - rttStats *congestion.RTTStats - - handshakeComplete bool - - // The number of times the crypto packets have been retransmitted without receiving an ack. - cryptoCount uint32 - // The number of times a PTO has been sent without receiving an ack. - ptoCount uint32 - // The number of PTO probe packets that should be sent. - // Only applies to the application-data packet number space. - numProbesToSend int - - // The time at which the next packet will be considered lost based on early transmit or exceeding the reordering window in time. - lossTime time.Time - - // The alarm timeout - alarm time.Time - - logger utils.Logger -} - -// NewSentPacketHandler creates a new sentPacketHandler -func NewSentPacketHandler( - initialPacketNumber protocol.PacketNumber, - rttStats *congestion.RTTStats, - logger utils.Logger, -) SentPacketHandler { - congestion := congestion.NewCubicSender( - congestion.DefaultClock{}, - rttStats, - false, /* don't use reno since chromium doesn't (why?) */ - protocol.InitialCongestionWindow, - protocol.DefaultMaxCongestionWindow, - ) - - return &sentPacketHandler{ - initialPackets: newPacketNumberSpace(initialPacketNumber), - handshakePackets: newPacketNumberSpace(0), - oneRTTPackets: newPacketNumberSpace(0), - rttStats: rttStats, - congestion: congestion, - logger: logger, - } -} - -func (h *sentPacketHandler) SetHandshakeComplete() { - h.logger.Debugf("Handshake complete. Discarding all outstanding crypto packets.") - var queue []*Packet - for _, packet := range h.retransmissionQueue { - if packet.EncryptionLevel == protocol.Encryption1RTT { - queue = append(queue, packet) - } - } - for _, pnSpace := range []*packetNumberSpace{h.initialPackets, h.handshakePackets} { - var cryptoPackets []*Packet - pnSpace.history.Iterate(func(p *Packet) (bool, error) { - cryptoPackets = append(cryptoPackets, p) - return true, nil - }) - for _, p := range cryptoPackets { - pnSpace.history.Remove(p.PacketNumber) - } - } - h.retransmissionQueue = queue - h.handshakeComplete = true -} - -func (h *sentPacketHandler) SentPacket(packet *Packet) { - if isRetransmittable := h.sentPacketImpl(packet); isRetransmittable { - h.getPacketNumberSpace(packet.EncryptionLevel).history.SentPacket(packet) - h.updateLossDetectionAlarm() - } -} - -func (h *sentPacketHandler) SentPacketsAsRetransmission(packets []*Packet, retransmissionOf protocol.PacketNumber) { - var p []*Packet - for _, packet := range packets { - if isRetransmittable := h.sentPacketImpl(packet); isRetransmittable { - p = append(p, packet) - } - } - h.getPacketNumberSpace(p[0].EncryptionLevel).history.SentPacketsAsRetransmission(p, retransmissionOf) - h.updateLossDetectionAlarm() -} - -func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace { - switch encLevel { - case protocol.EncryptionInitial: - return h.initialPackets - case protocol.EncryptionHandshake: - return h.handshakePackets - case protocol.Encryption1RTT: - return h.oneRTTPackets - default: - panic("invalid packet number space") - } -} - -func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* isRetransmittable */ { - pnSpace := h.getPacketNumberSpace(packet.EncryptionLevel) - - if h.logger.Debug() && pnSpace.largestSent != 0 { - for p := pnSpace.largestSent + 1; p < packet.PacketNumber; p++ { - h.logger.Debugf("Skipping packet number %#x", p) - } - } - - pnSpace.largestSent = packet.PacketNumber - - if len(packet.Frames) > 0 { - if ackFrame, ok := packet.Frames[0].(*wire.AckFrame); ok { - packet.largestAcked = ackFrame.LargestAcked() - } - } - - packet.Frames = stripNonRetransmittableFrames(packet.Frames) - isRetransmittable := len(packet.Frames) != 0 - - if isRetransmittable { - if packet.EncryptionLevel != protocol.Encryption1RTT { - h.lastSentCryptoPacketTime = packet.SendTime - } - h.lastSentRetransmittablePacketTime = packet.SendTime - packet.includedInBytesInFlight = true - h.bytesInFlight += packet.Length - packet.canBeRetransmitted = true - if h.numProbesToSend > 0 { - h.numProbesToSend-- - } - } - h.congestion.OnPacketSent(packet.SendTime, h.bytesInFlight, packet.PacketNumber, packet.Length, isRetransmittable) - - h.nextSendTime = utils.MaxTime(h.nextSendTime, packet.SendTime).Add(h.congestion.TimeUntilSend(h.bytesInFlight)) - return isRetransmittable -} - -func (h *sentPacketHandler) ReceivedAck(ackFrame *wire.AckFrame, withPacketNumber protocol.PacketNumber, encLevel protocol.EncryptionLevel, rcvTime time.Time) error { - pnSpace := h.getPacketNumberSpace(encLevel) - - largestAcked := ackFrame.LargestAcked() - if largestAcked > pnSpace.largestSent { - return qerr.Error(qerr.ProtocolViolation, "Received ACK for an unsent packet") - } - - pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked) - - if !pnSpace.pns.Validate(ackFrame) { - return qerr.Error(qerr.ProtocolViolation, "Received an ACK for a skipped packet number") - } - - // maybe update the RTT - if p := pnSpace.history.GetPacket(ackFrame.LargestAcked()); p != nil { - h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackFrame.DelayTime, rcvTime) - if h.logger.Debug() { - h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) - } - h.congestion.MaybeExitSlowStart() - } - - ackedPackets, err := h.determineNewlyAckedPackets(ackFrame, encLevel) - if err != nil { - return err - } - if len(ackedPackets) == 0 { - return nil - } - - priorInFlight := h.bytesInFlight - for _, p := range ackedPackets { - // largestAcked == 0 either means that the packet didn't contain an ACK, or it just acked packet 0 - // It is safe to ignore the corner case of packets that just acked packet 0, because - // the lowestPacketNotConfirmedAcked is only used to limit the number of ACK ranges we will send. - if p.largestAcked != 0 && encLevel == protocol.Encryption1RTT { - h.lowestNotConfirmedAcked = utils.MaxPacketNumber(h.lowestNotConfirmedAcked, p.largestAcked+1) - } - if err := h.onPacketAcked(p, rcvTime); err != nil { - return err - } - if p.includedInBytesInFlight { - h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime) - } - } - - if err := h.detectLostPackets(rcvTime, encLevel, priorInFlight); err != nil { - return err - } - - h.ptoCount = 0 - h.cryptoCount = 0 - h.numProbesToSend = 0 - - h.updateLossDetectionAlarm() - return nil -} - -func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber { - return h.lowestNotConfirmedAcked -} - -func (h *sentPacketHandler) determineNewlyAckedPackets( - ackFrame *wire.AckFrame, - encLevel protocol.EncryptionLevel, -) ([]*Packet, error) { - pnSpace := h.getPacketNumberSpace(encLevel) - var ackedPackets []*Packet - ackRangeIndex := 0 - lowestAcked := ackFrame.LowestAcked() - largestAcked := ackFrame.LargestAcked() - err := pnSpace.history.Iterate(func(p *Packet) (bool, error) { - // Ignore packets below the lowest acked - if p.PacketNumber < lowestAcked { - return true, nil - } - // Break after largest acked is reached - if p.PacketNumber > largestAcked { - return false, nil - } - - if ackFrame.HasMissingRanges() { - ackRange := ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex] - - for p.PacketNumber > ackRange.Largest && ackRangeIndex < len(ackFrame.AckRanges)-1 { - ackRangeIndex++ - ackRange = ackFrame.AckRanges[len(ackFrame.AckRanges)-1-ackRangeIndex] - } - - if p.PacketNumber >= ackRange.Smallest { // packet i contained in ACK range - if p.PacketNumber > ackRange.Largest { - return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet 0x%x, while evaluating range 0x%x -> 0x%x", p.PacketNumber, ackRange.Smallest, ackRange.Largest) - } - ackedPackets = append(ackedPackets, p) - } - } else { - ackedPackets = append(ackedPackets, p) - } - return true, nil - }) - if h.logger.Debug() && len(ackedPackets) > 0 { - pns := make([]protocol.PacketNumber, len(ackedPackets)) - for i, p := range ackedPackets { - pns[i] = p.PacketNumber - } - h.logger.Debugf("\tnewly acked packets (%d): %#x", len(pns), pns) - } - return ackedPackets, err -} - -func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { - return h.initialPackets.history.HasOutstandingPackets() || h.handshakePackets.history.HasOutstandingPackets() -} - -func (h *sentPacketHandler) hasOutstandingPackets() bool { - return h.oneRTTPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets() -} - -func (h *sentPacketHandler) updateLossDetectionAlarm() { - // Cancel the alarm if no packets are outstanding - if !h.hasOutstandingPackets() { - h.alarm = time.Time{} - return - } - - if h.hasOutstandingCryptoPackets() { - h.alarm = h.lastSentCryptoPacketTime.Add(h.computeCryptoTimeout()) - } else if !h.lossTime.IsZero() { - // Early retransmit timer or time loss detection. - h.alarm = h.lossTime - } else { // PTO alarm - h.alarm = h.lastSentRetransmittablePacketTime.Add(h.computePTOTimeout()) - } -} - -func (h *sentPacketHandler) detectLostPackets( - now time.Time, - encLevel protocol.EncryptionLevel, - priorInFlight protocol.ByteCount, -) error { - if encLevel == protocol.Encryption1RTT { - h.lossTime = time.Time{} - } - pnSpace := h.getPacketNumberSpace(encLevel) - - maxRTT := float64(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) - delayUntilLost := time.Duration((1.0 + timeReorderingFraction) * maxRTT) - - var lostPackets []*Packet - pnSpace.history.Iterate(func(packet *Packet) (bool, error) { - if packet.PacketNumber > pnSpace.largestAcked { - return false, nil - } - - timeSinceSent := now.Sub(packet.SendTime) - if timeSinceSent > delayUntilLost { - lostPackets = append(lostPackets, packet) - } else if h.lossTime.IsZero() && encLevel == protocol.Encryption1RTT { - if h.logger.Debug() { - h.logger.Debugf("\tsetting loss timer for packet %#x to %s (in %s)", packet.PacketNumber, delayUntilLost, delayUntilLost-timeSinceSent) - } - // Note: This conditional is only entered once per call - h.lossTime = now.Add(delayUntilLost - timeSinceSent) - } - return true, nil - }) - - if h.logger.Debug() && len(lostPackets) > 0 { - pns := make([]protocol.PacketNumber, len(lostPackets)) - for i, p := range lostPackets { - pns[i] = p.PacketNumber - } - h.logger.Debugf("\tlost packets (%d): %#x", len(pns), pns) - } - - for _, p := range lostPackets { - // the bytes in flight need to be reduced no matter if this packet will be retransmitted - if p.includedInBytesInFlight { - h.bytesInFlight -= p.Length - h.congestion.OnPacketLost(p.PacketNumber, p.Length, priorInFlight) - } - if p.canBeRetransmitted { - // queue the packet for retransmission, and report the loss to the congestion controller - if err := h.queuePacketForRetransmission(p, pnSpace); err != nil { - return err - } - } - pnSpace.history.Remove(p.PacketNumber) - } - return nil -} - -func (h *sentPacketHandler) OnAlarm() error { - // When all outstanding are acknowledged, the alarm is canceled in - // updateLossDetectionAlarm. This doesn't reset the timer in the session though. - // When OnAlarm is called, we therefore need to make sure that there are - // actually packets outstanding. - if h.hasOutstandingPackets() { - if err := h.onVerifiedAlarm(); err != nil { - return err - } - } - h.updateLossDetectionAlarm() - return nil -} - -func (h *sentPacketHandler) onVerifiedAlarm() error { - var err error - if h.hasOutstandingCryptoPackets() { - if h.logger.Debug() { - h.logger.Debugf("Loss detection alarm fired in crypto mode. Crypto count: %d", h.cryptoCount) - } - h.cryptoCount++ - err = h.queueCryptoPacketsForRetransmission() - } else if !h.lossTime.IsZero() { - if h.logger.Debug() { - h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", h.lossTime) - } - // Early retransmit or time loss detection - err = h.detectLostPackets(time.Now(), protocol.Encryption1RTT, h.bytesInFlight) - } else { // PTO - if h.logger.Debug() { - h.logger.Debugf("Loss detection alarm fired in PTO mode. PTO count: %d", h.ptoCount) - } - h.ptoCount++ - h.numProbesToSend += 2 - } - return err -} - -func (h *sentPacketHandler) GetAlarmTimeout() time.Time { - return h.alarm -} - -func (h *sentPacketHandler) onPacketAcked(p *Packet, rcvTime time.Time) error { - pnSpace := h.getPacketNumberSpace(p.EncryptionLevel) - // This happens if a packet and its retransmissions is acked in the same ACK. - // As soon as we process the first one, this will remove all the retransmissions, - // so we won't find the retransmitted packet number later. - if packet := pnSpace.history.GetPacket(p.PacketNumber); packet == nil { - return nil - } - - // only report the acking of this packet to the congestion controller if: - // * it is a retransmittable packet - // * this packet wasn't retransmitted yet - if p.isRetransmission { - // that the parent doesn't exist is expected to happen every time the original packet was already acked - if parent := pnSpace.history.GetPacket(p.retransmissionOf); parent != nil { - if len(parent.retransmittedAs) == 1 { - parent.retransmittedAs = nil - } else { - // remove this packet from the slice of retransmission - retransmittedAs := make([]protocol.PacketNumber, 0, len(parent.retransmittedAs)-1) - for _, pn := range parent.retransmittedAs { - if pn != p.PacketNumber { - retransmittedAs = append(retransmittedAs, pn) - } - } - parent.retransmittedAs = retransmittedAs - } - } - } - // this also applies to packets that have been retransmitted as probe packets - if p.includedInBytesInFlight { - h.bytesInFlight -= p.Length - } - if err := h.stopRetransmissionsFor(p, pnSpace); err != nil { - return err - } - return pnSpace.history.Remove(p.PacketNumber) -} - -func (h *sentPacketHandler) stopRetransmissionsFor(p *Packet, pnSpace *packetNumberSpace) error { - if err := pnSpace.history.MarkCannotBeRetransmitted(p.PacketNumber); err != nil { - return err - } - for _, r := range p.retransmittedAs { - packet := pnSpace.history.GetPacket(r) - if packet == nil { - return fmt.Errorf("sent packet handler BUG: marking packet as not retransmittable %d (retransmission of %d) not found in history", r, p.PacketNumber) - } - h.stopRetransmissionsFor(packet, pnSpace) - } - return nil -} - -func (h *sentPacketHandler) DequeuePacketForRetransmission() *Packet { - if len(h.retransmissionQueue) == 0 { - return nil - } - packet := h.retransmissionQueue[0] - // Shift the slice and don't retain anything that isn't needed. - copy(h.retransmissionQueue, h.retransmissionQueue[1:]) - h.retransmissionQueue[len(h.retransmissionQueue)-1] = nil - h.retransmissionQueue = h.retransmissionQueue[:len(h.retransmissionQueue)-1] - return packet -} - -func (h *sentPacketHandler) DequeueProbePacket() (*Packet, error) { - pnSpace := h.getPacketNumberSpace(protocol.Encryption1RTT) - if len(h.retransmissionQueue) == 0 { - p := pnSpace.history.FirstOutstanding() - if p == nil { - return nil, errors.New("cannot dequeue a probe packet. No outstanding packets") - } - if err := h.queuePacketForRetransmission(p, pnSpace); err != nil { - return nil, err - } - } - return h.DequeuePacketForRetransmission(), nil -} - -func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { - pnSpace := h.getPacketNumberSpace(encLevel) - - var lowestUnacked protocol.PacketNumber - if p := pnSpace.history.FirstOutstanding(); p != nil { - lowestUnacked = p.PacketNumber - } else { - lowestUnacked = pnSpace.largestAcked + 1 - } - - pn := pnSpace.pns.Peek() - return pn, protocol.GetPacketNumberLengthForHeader(pn, lowestUnacked) -} - -func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber { - return h.getPacketNumberSpace(encLevel).pns.Pop() -} - -func (h *sentPacketHandler) SendMode() SendMode { - numTrackedPackets := len(h.retransmissionQueue) + h.initialPackets.history.Len() + - h.handshakePackets.history.Len() + h.oneRTTPackets.history.Len() - - // Don't send any packets if we're keeping track of the maximum number of packets. - // Note that since MaxOutstandingSentPackets is smaller than MaxTrackedSentPackets, - // we will stop sending out new data when reaching MaxOutstandingSentPackets, - // but still allow sending of retransmissions and ACKs. - if numTrackedPackets >= protocol.MaxTrackedSentPackets { - if h.logger.Debug() { - h.logger.Debugf("Limited by the number of tracked packets: tracking %d packets, maximum %d", numTrackedPackets, protocol.MaxTrackedSentPackets) - } - return SendNone - } - if h.numProbesToSend > 0 { - return SendPTO - } - // Only send ACKs if we're congestion limited. - if cwnd := h.congestion.GetCongestionWindow(); h.bytesInFlight > cwnd { - if h.logger.Debug() { - h.logger.Debugf("Congestion limited: bytes in flight %d, window %d", h.bytesInFlight, cwnd) - } - return SendAck - } - // Send retransmissions first, if there are any. - if len(h.retransmissionQueue) > 0 { - return SendRetransmission - } - if numTrackedPackets >= protocol.MaxOutstandingSentPackets { - if h.logger.Debug() { - h.logger.Debugf("Max outstanding limited: tracking %d packets, maximum: %d", numTrackedPackets, protocol.MaxOutstandingSentPackets) - } - return SendAck - } - return SendAny -} - -func (h *sentPacketHandler) TimeUntilSend() time.Time { - return h.nextSendTime -} - -func (h *sentPacketHandler) ShouldSendNumPackets() int { - if h.numProbesToSend > 0 { - // RTO probes should not be paced, but must be sent immediately. - return h.numProbesToSend - } - delay := h.congestion.TimeUntilSend(h.bytesInFlight) - if delay == 0 || delay > protocol.MinPacingDelay { - return 1 - } - return int(math.Ceil(float64(protocol.MinPacingDelay) / float64(delay))) -} - -func (h *sentPacketHandler) queueCryptoPacketsForRetransmission() error { - if err := h.queueAllPacketsForRetransmission(protocol.EncryptionInitial); err != nil { - return err - } - return h.queueAllPacketsForRetransmission(protocol.EncryptionHandshake) -} - -func (h *sentPacketHandler) queueAllPacketsForRetransmission(encLevel protocol.EncryptionLevel) error { - var packets []*Packet - pnSpace := h.getPacketNumberSpace(encLevel) - pnSpace.history.Iterate(func(p *Packet) (bool, error) { - if p.canBeRetransmitted { - packets = append(packets, p) - } - return true, nil - }) - for _, p := range packets { - h.logger.Debugf("Queueing packet %#x (%s) as a crypto retransmission", p.PacketNumber, encLevel) - if err := h.queuePacketForRetransmission(p, pnSpace); err != nil { - return err - } - } - return nil -} - -func (h *sentPacketHandler) queuePacketForRetransmission(p *Packet, pnSpace *packetNumberSpace) error { - if !p.canBeRetransmitted { - return fmt.Errorf("sent packet handler BUG: packet %d already queued for retransmission", p.PacketNumber) - } - if err := pnSpace.history.MarkCannotBeRetransmitted(p.PacketNumber); err != nil { - return err - } - h.retransmissionQueue = append(h.retransmissionQueue, p) - return nil -} - -func (h *sentPacketHandler) computeCryptoTimeout() time.Duration { - duration := utils.MaxDuration(2*h.rttStats.SmoothedOrInitialRTT(), granularity) - // exponential backoff - // There's an implicit limit to this set by the crypto timeout. - return duration << h.cryptoCount -} - -func (h *sentPacketHandler) computePTOTimeout() time.Duration { - // TODO(#1236): include the max_ack_delay - duration := utils.MaxDuration(h.rttStats.SmoothedOrInitialRTT()+4*h.rttStats.MeanDeviation(), granularity) - return duration << h.ptoCount -} - -func (h *sentPacketHandler) ResetForRetry() error { - h.cryptoCount = 0 - h.bytesInFlight = 0 - var packets []*Packet - h.initialPackets.history.Iterate(func(p *Packet) (bool, error) { - if p.canBeRetransmitted { - packets = append(packets, p) - } - return true, nil - }) - for _, p := range packets { - h.logger.Debugf("Queueing packet %#x for retransmission.", p.PacketNumber) - h.retransmissionQueue = append(h.retransmissionQueue, p) - } - h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Pop()) - h.updateLossDetectionAlarm() - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go b/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go deleted file mode 100644 index 393ebba61..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/ackhandler/sent_packet_history.go +++ /dev/null @@ -1,148 +0,0 @@ -package ackhandler - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -type sentPacketHistory struct { - packetList *PacketList - packetMap map[protocol.PacketNumber]*PacketElement - - numOutstandingPackets int - - firstOutstanding *PacketElement -} - -func newSentPacketHistory() *sentPacketHistory { - return &sentPacketHistory{ - packetList: NewPacketList(), - packetMap: make(map[protocol.PacketNumber]*PacketElement), - } -} - -func (h *sentPacketHistory) SentPacket(p *Packet) { - h.sentPacketImpl(p) -} - -func (h *sentPacketHistory) sentPacketImpl(p *Packet) *PacketElement { - el := h.packetList.PushBack(*p) - h.packetMap[p.PacketNumber] = el - if h.firstOutstanding == nil { - h.firstOutstanding = el - } - if p.canBeRetransmitted { - h.numOutstandingPackets++ - } - return el -} - -func (h *sentPacketHistory) SentPacketsAsRetransmission(packets []*Packet, retransmissionOf protocol.PacketNumber) { - retransmission, ok := h.packetMap[retransmissionOf] - // The retransmitted packet is not present anymore. - // This can happen if it was acked in between dequeueing of the retransmission and sending. - // Just treat the retransmissions as normal packets. - // TODO: This won't happen if we clear packets queued for retransmission on new ACKs. - if !ok { - for _, packet := range packets { - h.sentPacketImpl(packet) - } - return - } - retransmission.Value.retransmittedAs = make([]protocol.PacketNumber, len(packets)) - for i, packet := range packets { - retransmission.Value.retransmittedAs[i] = packet.PacketNumber - el := h.sentPacketImpl(packet) - el.Value.isRetransmission = true - el.Value.retransmissionOf = retransmissionOf - } -} - -func (h *sentPacketHistory) GetPacket(p protocol.PacketNumber) *Packet { - if el, ok := h.packetMap[p]; ok { - return &el.Value - } - return nil -} - -// Iterate iterates through all packets. -// The callback must not modify the history. -func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error { - cont := true - for el := h.packetList.Front(); cont && el != nil; el = el.Next() { - var err error - cont, err = cb(&el.Value) - if err != nil { - return err - } - } - return nil -} - -// FirstOutStanding returns the first outstanding packet. -// It must not be modified (e.g. retransmitted). -// Use DequeueFirstPacketForRetransmission() to retransmit it. -func (h *sentPacketHistory) FirstOutstanding() *Packet { - if h.firstOutstanding == nil { - return nil - } - return &h.firstOutstanding.Value -} - -// QueuePacketForRetransmission marks a packet for retransmission. -// A packet can only be queued once. -func (h *sentPacketHistory) MarkCannotBeRetransmitted(pn protocol.PacketNumber) error { - el, ok := h.packetMap[pn] - if !ok { - return fmt.Errorf("sent packet history: packet %d not found", pn) - } - if el.Value.canBeRetransmitted { - h.numOutstandingPackets-- - if h.numOutstandingPackets < 0 { - panic("numOutstandingHandshakePackets negative") - } - } - el.Value.canBeRetransmitted = false - if el == h.firstOutstanding { - h.readjustFirstOutstanding() - } - return nil -} - -// readjustFirstOutstanding readjusts the pointer to the first outstanding packet. -// This is necessary every time the first outstanding packet is deleted or retransmitted. -func (h *sentPacketHistory) readjustFirstOutstanding() { - el := h.firstOutstanding.Next() - for el != nil && !el.Value.canBeRetransmitted { - el = el.Next() - } - h.firstOutstanding = el -} - -func (h *sentPacketHistory) Len() int { - return len(h.packetMap) -} - -func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error { - el, ok := h.packetMap[p] - if !ok { - return fmt.Errorf("packet %d not found in sent packet history", p) - } - if el == h.firstOutstanding { - h.readjustFirstOutstanding() - } - if el.Value.canBeRetransmitted { - h.numOutstandingPackets-- - if h.numOutstandingPackets < 0 { - panic("numOutstandingHandshakePackets negative") - } - } - h.packetList.Remove(el) - delete(h.packetMap, p) - return nil -} - -func (h *sentPacketHistory) HasOutstandingPackets() bool { - return h.numOutstandingPackets > 0 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/bandwidth.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/bandwidth.go deleted file mode 100644 index 54269c567..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/bandwidth.go +++ /dev/null @@ -1,22 +0,0 @@ -package congestion - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// Bandwidth of a connection -type Bandwidth uint64 - -const ( - // BitsPerSecond is 1 bit per second - BitsPerSecond Bandwidth = 1 - // BytesPerSecond is 1 byte per second - BytesPerSecond = 8 * BitsPerSecond -) - -// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta -func BandwidthFromDelta(bytes protocol.ByteCount, delta time.Duration) Bandwidth { - return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/clock.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/clock.go deleted file mode 100644 index 405fae70f..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/clock.go +++ /dev/null @@ -1,18 +0,0 @@ -package congestion - -import "time" - -// A Clock returns the current time -type Clock interface { - Now() time.Time -} - -// DefaultClock implements the Clock interface using the Go stdlib clock. -type DefaultClock struct{} - -var _ Clock = DefaultClock{} - -// Now gets the current time -func (DefaultClock) Now() time.Time { - return time.Now() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic.go deleted file mode 100644 index dcf91fc6d..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic.go +++ /dev/null @@ -1,210 +0,0 @@ -package congestion - -import ( - "math" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// This cubic implementation is based on the one found in Chromiums's QUIC -// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}. - -// Constants based on TCP defaults. -// The following constants are in 2^10 fractions of a second instead of ms to -// allow a 10 shift right to divide. - -// 1024*1024^3 (first 1024 is from 0.100^3) -// where 0.100 is 100 ms which is the scaling round trip time. -const cubeScale = 40 -const cubeCongestionWindowScale = 410 -const cubeFactor protocol.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / protocol.DefaultTCPMSS - -const defaultNumConnections = 2 - -// Default Cubic backoff factor -const beta float32 = 0.7 - -// Additional backoff factor when loss occurs in the concave part of the Cubic -// curve. This additional backoff factor is expected to give up bandwidth to -// new concurrent flows and speed up convergence. -const betaLastMax float32 = 0.85 - -// Cubic implements the cubic algorithm from TCP -type Cubic struct { - clock Clock - - // Number of connections to simulate. - numConnections int - - // Time when this cycle started, after last loss event. - epoch time.Time - - // Max congestion window used just before last loss event. - // Note: to improve fairness to other streams an additional back off is - // applied to this value if the new value is below our latest value. - lastMaxCongestionWindow protocol.ByteCount - - // Number of acked bytes since the cycle started (epoch). - ackedBytesCount protocol.ByteCount - - // TCP Reno equivalent congestion window in packets. - estimatedTCPcongestionWindow protocol.ByteCount - - // Origin point of cubic function. - originPointCongestionWindow protocol.ByteCount - - // Time to origin point of cubic function in 2^10 fractions of a second. - timeToOriginPoint uint32 - - // Last congestion window in packets computed by cubic function. - lastTargetCongestionWindow protocol.ByteCount -} - -// NewCubic returns a new Cubic instance -func NewCubic(clock Clock) *Cubic { - c := &Cubic{ - clock: clock, - numConnections: defaultNumConnections, - } - c.Reset() - return c -} - -// Reset is called after a timeout to reset the cubic state -func (c *Cubic) Reset() { - c.epoch = time.Time{} - c.lastMaxCongestionWindow = 0 - c.ackedBytesCount = 0 - c.estimatedTCPcongestionWindow = 0 - c.originPointCongestionWindow = 0 - c.timeToOriginPoint = 0 - c.lastTargetCongestionWindow = 0 -} - -func (c *Cubic) alpha() float32 { - // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that - // beta here is a cwnd multiplier, and is equal to 1-beta from the paper. - // We derive the equivalent alpha for an N-connection emulation as: - b := c.beta() - return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b) -} - -func (c *Cubic) beta() float32 { - // kNConnectionBeta is the backoff factor after loss for our N-connection - // emulation, which emulates the effective backoff of an ensemble of N - // TCP-Reno connections on a single loss event. The effective multiplier is - // computed as: - return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections) -} - -func (c *Cubic) betaLastMax() float32 { - // betaLastMax is the additional backoff factor after loss for our - // N-connection emulation, which emulates the additional backoff of - // an ensemble of N TCP-Reno connections on a single loss event. The - // effective multiplier is computed as: - return (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections) -} - -// OnApplicationLimited is called on ack arrival when sender is unable to use -// the available congestion window. Resets Cubic state during quiescence. -func (c *Cubic) OnApplicationLimited() { - // When sender is not using the available congestion window, the window does - // not grow. But to be RTT-independent, Cubic assumes that the sender has been - // using the entire window during the time since the beginning of the current - // "epoch" (the end of the last loss recovery period). Since - // application-limited periods break this assumption, we reset the epoch when - // in such a period. This reset effectively freezes congestion window growth - // through application-limited periods and allows Cubic growth to continue - // when the entire window is being used. - c.epoch = time.Time{} -} - -// CongestionWindowAfterPacketLoss computes a new congestion window to use after -// a loss event. Returns the new congestion window in packets. The new -// congestion window is a multiplicative decrease of our current window. -func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol.ByteCount) protocol.ByteCount { - if currentCongestionWindow+protocol.DefaultTCPMSS < c.lastMaxCongestionWindow { - // We never reached the old max, so assume we are competing with another - // flow. Use our extra back off factor to allow the other flow to go up. - c.lastMaxCongestionWindow = protocol.ByteCount(c.betaLastMax() * float32(currentCongestionWindow)) - } else { - c.lastMaxCongestionWindow = currentCongestionWindow - } - c.epoch = time.Time{} // Reset time. - return protocol.ByteCount(float32(currentCongestionWindow) * c.beta()) -} - -// CongestionWindowAfterAck computes a new congestion window to use after a received ACK. -// Returns the new congestion window in packets. The new congestion window -// follows a cubic function that depends on the time passed since last -// packet loss. -func (c *Cubic) CongestionWindowAfterAck( - ackedBytes protocol.ByteCount, - currentCongestionWindow protocol.ByteCount, - delayMin time.Duration, - eventTime time.Time, -) protocol.ByteCount { - c.ackedBytesCount += ackedBytes - - if c.epoch.IsZero() { - // First ACK after a loss event. - c.epoch = eventTime // Start of epoch. - c.ackedBytesCount = ackedBytes // Reset count. - // Reset estimated_tcp_congestion_window_ to be in sync with cubic. - c.estimatedTCPcongestionWindow = currentCongestionWindow - if c.lastMaxCongestionWindow <= currentCongestionWindow { - c.timeToOriginPoint = 0 - c.originPointCongestionWindow = currentCongestionWindow - } else { - c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow)))) - c.originPointCongestionWindow = c.lastMaxCongestionWindow - } - } - - // Change the time unit from microseconds to 2^10 fractions per second. Take - // the round trip time in account. This is done to allow us to use shift as a - // divide operator. - elapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000) - - // Right-shifts of negative, signed numbers have implementation-dependent - // behavior, so force the offset to be positive, as is done in the kernel. - offset := int64(c.timeToOriginPoint) - elapsedTime - if offset < 0 { - offset = -offset - } - - deltaCongestionWindow := protocol.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * protocol.DefaultTCPMSS >> cubeScale - var targetCongestionWindow protocol.ByteCount - if elapsedTime > int64(c.timeToOriginPoint) { - targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow - } else { - targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow - } - // Limit the CWND increase to half the acked bytes. - targetCongestionWindow = utils.MinByteCount(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) - - // Increase the window by approximately Alpha * 1 MSS of bytes every - // time we ack an estimated tcp window of bytes. For small - // congestion windows (less than 25), the formula below will - // increase slightly slower than linearly per estimated tcp window - // of bytes. - c.estimatedTCPcongestionWindow += protocol.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(protocol.DefaultTCPMSS) / float32(c.estimatedTCPcongestionWindow)) - c.ackedBytesCount = 0 - - // We have a new cubic congestion window. - c.lastTargetCongestionWindow = targetCongestionWindow - - // Compute target congestion_window based on cubic target and estimated TCP - // congestion_window, use highest (fastest). - if targetCongestionWindow < c.estimatedTCPcongestionWindow { - targetCongestionWindow = c.estimatedTCPcongestionWindow - } - return targetCongestionWindow -} - -// SetNumConnections sets the number of emulated connections -func (c *Cubic) SetNumConnections(n int) { - c.numConnections = n -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic_sender.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic_sender.go deleted file mode 100644 index 6f3d3289a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/cubic_sender.go +++ /dev/null @@ -1,314 +0,0 @@ -package congestion - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -const ( - maxBurstBytes = 3 * protocol.DefaultTCPMSS - renoBeta float32 = 0.7 // Reno backoff factor. - defaultMinimumCongestionWindow protocol.ByteCount = 2 * protocol.DefaultTCPMSS -) - -type cubicSender struct { - hybridSlowStart HybridSlowStart - prr PrrSender - rttStats *RTTStats - stats connectionStats - cubic *Cubic - - reno bool - - // Track the largest packet that has been sent. - largestSentPacketNumber protocol.PacketNumber - - // Track the largest packet that has been acked. - largestAckedPacketNumber protocol.PacketNumber - - // Track the largest packet number outstanding when a CWND cutback occurs. - largestSentAtLastCutback protocol.PacketNumber - - // Whether the last loss event caused us to exit slowstart. - // Used for stats collection of slowstartPacketsLost - lastCutbackExitedSlowstart bool - - // When true, exit slow start with large cutback of congestion window. - slowStartLargeReduction bool - - // Congestion window in packets. - congestionWindow protocol.ByteCount - - // Minimum congestion window in packets. - minCongestionWindow protocol.ByteCount - - // Maximum congestion window. - maxCongestionWindow protocol.ByteCount - - // Slow start congestion window in bytes, aka ssthresh. - slowstartThreshold protocol.ByteCount - - // Number of connections to simulate. - numConnections int - - // ACK counter for the Reno implementation. - numAckedPackets uint64 - - initialCongestionWindow protocol.ByteCount - initialMaxCongestionWindow protocol.ByteCount - - minSlowStartExitWindow protocol.ByteCount -} - -var _ SendAlgorithm = &cubicSender{} -var _ SendAlgorithmWithDebugInfo = &cubicSender{} - -// NewCubicSender makes a new cubic sender -func NewCubicSender(clock Clock, rttStats *RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount) SendAlgorithmWithDebugInfo { - return &cubicSender{ - rttStats: rttStats, - initialCongestionWindow: initialCongestionWindow, - initialMaxCongestionWindow: initialMaxCongestionWindow, - congestionWindow: initialCongestionWindow, - minCongestionWindow: defaultMinimumCongestionWindow, - slowstartThreshold: initialMaxCongestionWindow, - maxCongestionWindow: initialMaxCongestionWindow, - numConnections: defaultNumConnections, - cubic: NewCubic(clock), - reno: reno, - } -} - -// TimeUntilSend returns when the next packet should be sent. -func (c *cubicSender) TimeUntilSend(bytesInFlight protocol.ByteCount) time.Duration { - if c.InRecovery() { - // PRR is used when in recovery. - if c.prr.CanSend(c.GetCongestionWindow(), bytesInFlight, c.GetSlowStartThreshold()) { - return 0 - } - } - return c.rttStats.SmoothedRTT() * time.Duration(protocol.DefaultTCPMSS) / time.Duration(2*c.GetCongestionWindow()) -} - -func (c *cubicSender) OnPacketSent( - sentTime time.Time, - bytesInFlight protocol.ByteCount, - packetNumber protocol.PacketNumber, - bytes protocol.ByteCount, - isRetransmittable bool, -) { - if !isRetransmittable { - return - } - if c.InRecovery() { - // PRR is used when in recovery. - c.prr.OnPacketSent(bytes) - } - c.largestSentPacketNumber = packetNumber - c.hybridSlowStart.OnPacketSent(packetNumber) -} - -func (c *cubicSender) InRecovery() bool { - return c.largestAckedPacketNumber <= c.largestSentAtLastCutback && c.largestAckedPacketNumber != 0 -} - -func (c *cubicSender) InSlowStart() bool { - return c.GetCongestionWindow() < c.GetSlowStartThreshold() -} - -func (c *cubicSender) GetCongestionWindow() protocol.ByteCount { - return c.congestionWindow -} - -func (c *cubicSender) GetSlowStartThreshold() protocol.ByteCount { - return c.slowstartThreshold -} - -func (c *cubicSender) ExitSlowstart() { - c.slowstartThreshold = c.congestionWindow -} - -func (c *cubicSender) SlowstartThreshold() protocol.ByteCount { - return c.slowstartThreshold -} - -func (c *cubicSender) MaybeExitSlowStart() { - if c.InSlowStart() && c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/protocol.DefaultTCPMSS) { - c.ExitSlowstart() - } -} - -func (c *cubicSender) OnPacketAcked( - ackedPacketNumber protocol.PacketNumber, - ackedBytes protocol.ByteCount, - priorInFlight protocol.ByteCount, - eventTime time.Time, -) { - c.largestAckedPacketNumber = utils.MaxPacketNumber(ackedPacketNumber, c.largestAckedPacketNumber) - if c.InRecovery() { - // PRR is used when in recovery. - c.prr.OnPacketAcked(ackedBytes) - return - } - c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime) - if c.InSlowStart() { - c.hybridSlowStart.OnPacketAcked(ackedPacketNumber) - } -} - -func (c *cubicSender) OnPacketLost( - packetNumber protocol.PacketNumber, - lostBytes protocol.ByteCount, - priorInFlight protocol.ByteCount, -) { - // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets - // already sent should be treated as a single loss event, since it's expected. - if packetNumber <= c.largestSentAtLastCutback { - if c.lastCutbackExitedSlowstart { - c.stats.slowstartPacketsLost++ - c.stats.slowstartBytesLost += lostBytes - if c.slowStartLargeReduction { - // Reduce congestion window by lost_bytes for every loss. - c.congestionWindow = utils.MaxByteCount(c.congestionWindow-lostBytes, c.minSlowStartExitWindow) - c.slowstartThreshold = c.congestionWindow - } - } - return - } - c.lastCutbackExitedSlowstart = c.InSlowStart() - if c.InSlowStart() { - c.stats.slowstartPacketsLost++ - } - - c.prr.OnPacketLost(priorInFlight) - - // TODO(chromium): Separate out all of slow start into a separate class. - if c.slowStartLargeReduction && c.InSlowStart() { - if c.congestionWindow >= 2*c.initialCongestionWindow { - c.minSlowStartExitWindow = c.congestionWindow / 2 - } - c.congestionWindow -= protocol.DefaultTCPMSS - } else if c.reno { - c.congestionWindow = protocol.ByteCount(float32(c.congestionWindow) * c.RenoBeta()) - } else { - c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow) - } - if c.congestionWindow < c.minCongestionWindow { - c.congestionWindow = c.minCongestionWindow - } - c.slowstartThreshold = c.congestionWindow - c.largestSentAtLastCutback = c.largestSentPacketNumber - // reset packet count from congestion avoidance mode. We start - // counting again when we're out of recovery. - c.numAckedPackets = 0 -} - -func (c *cubicSender) RenoBeta() float32 { - // kNConnectionBeta is the backoff factor after loss for our N-connection - // emulation, which emulates the effective backoff of an ensemble of N - // TCP-Reno connections on a single loss event. The effective multiplier is - // computed as: - return (float32(c.numConnections) - 1. + renoBeta) / float32(c.numConnections) -} - -// Called when we receive an ack. Normal TCP tracks how many packets one ack -// represents, but quic has a separate ack for each packet. -func (c *cubicSender) maybeIncreaseCwnd( - ackedPacketNumber protocol.PacketNumber, - ackedBytes protocol.ByteCount, - priorInFlight protocol.ByteCount, - eventTime time.Time, -) { - // Do not increase the congestion window unless the sender is close to using - // the current window. - if !c.isCwndLimited(priorInFlight) { - c.cubic.OnApplicationLimited() - return - } - if c.congestionWindow >= c.maxCongestionWindow { - return - } - if c.InSlowStart() { - // TCP slow start, exponential growth, increase by one for each ACK. - c.congestionWindow += protocol.DefaultTCPMSS - return - } - // Congestion avoidance - if c.reno { - // Classic Reno congestion avoidance. - c.numAckedPackets++ - // Divide by num_connections to smoothly increase the CWND at a faster - // rate than conventional Reno. - if c.numAckedPackets*uint64(c.numConnections) >= uint64(c.congestionWindow)/uint64(protocol.DefaultTCPMSS) { - c.congestionWindow += protocol.DefaultTCPMSS - c.numAckedPackets = 0 - } - } else { - c.congestionWindow = utils.MinByteCount(c.maxCongestionWindow, c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) - } -} - -func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool { - congestionWindow := c.GetCongestionWindow() - if bytesInFlight >= congestionWindow { - return true - } - availableBytes := congestionWindow - bytesInFlight - slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2 - return slowStartLimited || availableBytes <= maxBurstBytes -} - -// BandwidthEstimate returns the current bandwidth estimate -func (c *cubicSender) BandwidthEstimate() Bandwidth { - srtt := c.rttStats.SmoothedRTT() - if srtt == 0 { - // If we haven't measured an rtt, the bandwidth estimate is unknown. - return 0 - } - return BandwidthFromDelta(c.GetCongestionWindow(), srtt) -} - -// HybridSlowStart returns the hybrid slow start instance for testing -func (c *cubicSender) HybridSlowStart() *HybridSlowStart { - return &c.hybridSlowStart -} - -// SetNumEmulatedConnections sets the number of emulated connections -func (c *cubicSender) SetNumEmulatedConnections(n int) { - c.numConnections = utils.Max(n, 1) - c.cubic.SetNumConnections(c.numConnections) -} - -// OnRetransmissionTimeout is called on an retransmission timeout -func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) { - c.largestSentAtLastCutback = 0 - if !packetsRetransmitted { - return - } - c.hybridSlowStart.Restart() - c.cubic.Reset() - c.slowstartThreshold = c.congestionWindow / 2 - c.congestionWindow = c.minCongestionWindow -} - -// OnConnectionMigration is called when the connection is migrated (?) -func (c *cubicSender) OnConnectionMigration() { - c.hybridSlowStart.Restart() - c.prr = PrrSender{} - c.largestSentPacketNumber = 0 - c.largestAckedPacketNumber = 0 - c.largestSentAtLastCutback = 0 - c.lastCutbackExitedSlowstart = false - c.cubic.Reset() - c.numAckedPackets = 0 - c.congestionWindow = c.initialCongestionWindow - c.slowstartThreshold = c.initialMaxCongestionWindow - c.maxCongestionWindow = c.initialMaxCongestionWindow -} - -// SetSlowStartLargeReduction allows enabling the SSLR experiment -func (c *cubicSender) SetSlowStartLargeReduction(enabled bool) { - c.slowStartLargeReduction = enabled -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/hybrid_slow_start.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/hybrid_slow_start.go deleted file mode 100644 index f41c1e5c3..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/hybrid_slow_start.go +++ /dev/null @@ -1,111 +0,0 @@ -package congestion - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// Note(pwestin): the magic clamping numbers come from the original code in -// tcp_cubic.c. -const hybridStartLowWindow = protocol.ByteCount(16) - -// Number of delay samples for detecting the increase of delay. -const hybridStartMinSamples = uint32(8) - -// Exit slow start if the min rtt has increased by more than 1/8th. -const hybridStartDelayFactorExp = 3 // 2^3 = 8 -// The original paper specifies 2 and 8ms, but those have changed over time. -const hybridStartDelayMinThresholdUs = int64(4000) -const hybridStartDelayMaxThresholdUs = int64(16000) - -// HybridSlowStart implements the TCP hybrid slow start algorithm -type HybridSlowStart struct { - endPacketNumber protocol.PacketNumber - lastSentPacketNumber protocol.PacketNumber - started bool - currentMinRTT time.Duration - rttSampleCount uint32 - hystartFound bool -} - -// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase. -func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) { - s.endPacketNumber = lastSent - s.currentMinRTT = 0 - s.rttSampleCount = 0 - s.started = true -} - -// IsEndOfRound returns true if this ack is the last packet number of our current slow start round. -func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool { - return s.endPacketNumber < ack -} - -// ShouldExitSlowStart should be called on every new ack frame, since a new -// RTT measurement can be made then. -// rtt: the RTT for this ack packet. -// minRTT: is the lowest delay (RTT) we have seen during the session. -// congestionWindow: the congestion window in packets. -func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool { - if !s.started { - // Time to start the hybrid slow start. - s.StartReceiveRound(s.lastSentPacketNumber) - } - if s.hystartFound { - return true - } - // Second detection parameter - delay increase detection. - // Compare the minimum delay (s.currentMinRTT) of the current - // burst of packets relative to the minimum delay during the session. - // Note: we only look at the first few(8) packets in each burst, since we - // only want to compare the lowest RTT of the burst relative to previous - // bursts. - s.rttSampleCount++ - if s.rttSampleCount <= hybridStartMinSamples { - if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT { - s.currentMinRTT = latestRTT - } - } - // We only need to check this once per round. - if s.rttSampleCount == hybridStartMinSamples { - // Divide minRTT by 8 to get a rtt increase threshold for exiting. - minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) - // Ensure the rtt threshold is never less than 2ms or more than 16ms. - minRTTincreaseThresholdUs = utils.MinInt64(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) - minRTTincreaseThreshold := time.Duration(utils.MaxInt64(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond - - if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { - s.hystartFound = true - } - } - // Exit from slow start if the cwnd is greater than 16 and - // increasing delay is found. - return congestionWindow >= hybridStartLowWindow && s.hystartFound -} - -// OnPacketSent is called when a packet was sent -func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) { - s.lastSentPacketNumber = packetNumber -} - -// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end -// the round when the final packet of the burst is received and start it on -// the next incoming ack. -func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) { - if s.IsEndOfRound(ackedPacketNumber) { - s.started = false - } -} - -// Started returns true if started -func (s *HybridSlowStart) Started() bool { - return s.started -} - -// Restart the slow start phase -func (s *HybridSlowStart) Restart() { - s.started = false - s.hystartFound = false -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/interface.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/interface.go deleted file mode 100644 index 7c27da64a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/interface.go +++ /dev/null @@ -1,36 +0,0 @@ -package congestion - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A SendAlgorithm performs congestion control and calculates the congestion window -type SendAlgorithm interface { - TimeUntilSend(bytesInFlight protocol.ByteCount) time.Duration - OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) - GetCongestionWindow() protocol.ByteCount - MaybeExitSlowStart() - OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time) - OnPacketLost(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) - SetNumEmulatedConnections(n int) - OnRetransmissionTimeout(packetsRetransmitted bool) - OnConnectionMigration() - - // Experiments - SetSlowStartLargeReduction(enabled bool) -} - -// SendAlgorithmWithDebugInfo adds some debug functions to SendAlgorithm -type SendAlgorithmWithDebugInfo interface { - SendAlgorithm - BandwidthEstimate() Bandwidth - - // Stuff only used in testing - - HybridSlowStart() *HybridSlowStart - SlowstartThreshold() protocol.ByteCount - RenoBeta() float32 - InRecovery() bool -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/prr_sender.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/prr_sender.go deleted file mode 100644 index 5c807d190..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/prr_sender.go +++ /dev/null @@ -1,54 +0,0 @@ -package congestion - -import ( - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// PrrSender implements the Proportional Rate Reduction (PRR) per RFC 6937 -type PrrSender struct { - bytesSentSinceLoss protocol.ByteCount - bytesDeliveredSinceLoss protocol.ByteCount - ackCountSinceLoss protocol.ByteCount - bytesInFlightBeforeLoss protocol.ByteCount -} - -// OnPacketSent should be called after a packet was sent -func (p *PrrSender) OnPacketSent(sentBytes protocol.ByteCount) { - p.bytesSentSinceLoss += sentBytes -} - -// OnPacketLost should be called on the first loss that triggers a recovery -// period and all other methods in this class should only be called when in -// recovery. -func (p *PrrSender) OnPacketLost(priorInFlight protocol.ByteCount) { - p.bytesSentSinceLoss = 0 - p.bytesInFlightBeforeLoss = priorInFlight - p.bytesDeliveredSinceLoss = 0 - p.ackCountSinceLoss = 0 -} - -// OnPacketAcked should be called after a packet was acked -func (p *PrrSender) OnPacketAcked(ackedBytes protocol.ByteCount) { - p.bytesDeliveredSinceLoss += ackedBytes - p.ackCountSinceLoss++ -} - -// CanSend returns if packets can be sent -func (p *PrrSender) CanSend(congestionWindow, bytesInFlight, slowstartThreshold protocol.ByteCount) bool { - // Return QuicTime::Zero In order to ensure limited transmit always works. - if p.bytesSentSinceLoss == 0 || bytesInFlight < protocol.DefaultTCPMSS { - return true - } - if congestionWindow > bytesInFlight { - // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead - // of sending the entire available window. This prevents burst retransmits - // when more packets are lost than the CWND reduction. - // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS - return p.bytesDeliveredSinceLoss+p.ackCountSinceLoss*protocol.DefaultTCPMSS > p.bytesSentSinceLoss - } - // Implement Proportional Rate Reduction (RFC6937). - // Checks a simplified version of the PRR formula that doesn't use division: - // AvailableSendWindow = - // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent - return p.bytesDeliveredSinceLoss*slowstartThreshold > p.bytesSentSinceLoss*p.bytesInFlightBeforeLoss -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/rtt_stats.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/rtt_stats.go deleted file mode 100644 index f0ebbb236..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/rtt_stats.go +++ /dev/null @@ -1,101 +0,0 @@ -package congestion - -import ( - "time" - - "github.com/lucas-clemente/quic-go/internal/utils" -) - -const ( - rttAlpha float32 = 0.125 - oneMinusAlpha float32 = (1 - rttAlpha) - rttBeta float32 = 0.25 - oneMinusBeta float32 = (1 - rttBeta) - // The default RTT used before an RTT sample is taken. - defaultInitialRTT = 100 * time.Millisecond -) - -// RTTStats provides round-trip statistics -type RTTStats struct { - minRTT time.Duration - latestRTT time.Duration - smoothedRTT time.Duration - meanDeviation time.Duration -} - -// NewRTTStats makes a properly initialized RTTStats object -func NewRTTStats() *RTTStats { - return &RTTStats{} -} - -// MinRTT Returns the minRTT for the entire connection. -// May return Zero if no valid updates have occurred. -func (r *RTTStats) MinRTT() time.Duration { return r.minRTT } - -// LatestRTT returns the most recent rtt measurement. -// May return Zero if no valid updates have occurred. -func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT } - -// SmoothedRTT returns the EWMA smoothed RTT for the connection. -// May return Zero if no valid updates have occurred. -func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT } - -// SmoothedOrInitialRTT returns the EWMA smoothed RTT for the connection. -// If no valid updates have occurred, it returns the initial RTT. -func (r *RTTStats) SmoothedOrInitialRTT() time.Duration { - if r.smoothedRTT != 0 { - return r.smoothedRTT - } - return defaultInitialRTT -} - -// MeanDeviation gets the mean deviation -func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation } - -// UpdateRTT updates the RTT based on a new sample. -func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) { - if sendDelta == utils.InfDuration || sendDelta <= 0 { - return - } - - // Update r.minRTT first. r.minRTT does not use an rttSample corrected for - // ackDelay but the raw observed sendDelta, since poor clock granularity at - // the client may cause a high ackDelay to result in underestimation of the - // r.minRTT. - if r.minRTT == 0 || r.minRTT > sendDelta { - r.minRTT = sendDelta - } - - // Correct for ackDelay if information received from the peer results in a - // an RTT sample at least as large as minRTT. Otherwise, only use the - // sendDelta. - sample := sendDelta - if sample-r.minRTT >= ackDelay { - sample -= ackDelay - } - r.latestRTT = sample - // First time call. - if r.smoothedRTT == 0 { - r.smoothedRTT = sample - r.meanDeviation = sample / 2 - } else { - r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32(utils.AbsDuration(r.smoothedRTT-sample)/time.Microsecond)) * time.Microsecond - r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond - } -} - -// OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset. -func (r *RTTStats) OnConnectionMigration() { - r.latestRTT = 0 - r.minRTT = 0 - r.smoothedRTT = 0 - r.meanDeviation = 0 -} - -// ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt -// is larger. The mean deviation is increased to the most recent deviation if -// it's larger. -func (r *RTTStats) ExpireSmoothedMetrics() { - r.meanDeviation = utils.MaxDuration(r.meanDeviation, utils.AbsDuration(r.smoothedRTT-r.latestRTT)) - r.smoothedRTT = utils.MaxDuration(r.smoothedRTT, r.latestRTT) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/stats.go b/vendor/github.com/lucas-clemente/quic-go/internal/congestion/stats.go deleted file mode 100644 index ed669c146..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/congestion/stats.go +++ /dev/null @@ -1,8 +0,0 @@ -package congestion - -import "github.com/lucas-clemente/quic-go/internal/protocol" - -type connectionStats struct { - slowstartPacketsLost protocol.PacketNumber - slowstartBytesLost protocol.ByteCount -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go deleted file mode 100644 index 6a0aa3c58..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/base_flow_controller.go +++ /dev/null @@ -1,122 +0,0 @@ -package flowcontrol - -import ( - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type baseFlowController struct { - // for sending data - bytesSent protocol.ByteCount - sendWindow protocol.ByteCount - lastBlockedAt protocol.ByteCount - - // for receiving data - mutex sync.RWMutex - bytesRead protocol.ByteCount - highestReceived protocol.ByteCount - receiveWindow protocol.ByteCount - receiveWindowSize protocol.ByteCount - maxReceiveWindowSize protocol.ByteCount - - epochStartTime time.Time - epochStartOffset protocol.ByteCount - rttStats *congestion.RTTStats - - logger utils.Logger -} - -// IsNewlyBlocked says if it is newly blocked by flow control. -// For every offset, it only returns true once. -// If it is blocked, the offset is returned. -func (c *baseFlowController) IsNewlyBlocked() (bool, protocol.ByteCount) { - if c.sendWindowSize() != 0 || c.sendWindow == c.lastBlockedAt { - return false, 0 - } - c.lastBlockedAt = c.sendWindow - return true, c.sendWindow -} - -func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) { - c.bytesSent += n -} - -// UpdateSendWindow should be called after receiving a WindowUpdateFrame -// it returns true if the window was actually updated -func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) { - if offset > c.sendWindow { - c.sendWindow = offset - } -} - -func (c *baseFlowController) sendWindowSize() protocol.ByteCount { - // this only happens during connection establishment, when data is sent before we receive the peer's transport parameters - if c.bytesSent > c.sendWindow { - return 0 - } - return c.sendWindow - c.bytesSent -} - -func (c *baseFlowController) AddBytesRead(n protocol.ByteCount) { - c.mutex.Lock() - defer c.mutex.Unlock() - - // pretend we sent a WindowUpdate when reading the first byte - // this way auto-tuning of the window size already works for the first WindowUpdate - if c.bytesRead == 0 { - c.startNewAutoTuningEpoch() - } - c.bytesRead += n -} - -func (c *baseFlowController) hasWindowUpdate() bool { - bytesRemaining := c.receiveWindow - c.bytesRead - // update the window when more than the threshold was consumed - return bytesRemaining <= protocol.ByteCount((float64(c.receiveWindowSize) * float64((1 - protocol.WindowUpdateThreshold)))) -} - -// getWindowUpdate updates the receive window, if necessary -// it returns the new offset -func (c *baseFlowController) getWindowUpdate() protocol.ByteCount { - if !c.hasWindowUpdate() { - return 0 - } - - c.maybeAdjustWindowSize() - c.receiveWindow = c.bytesRead + c.receiveWindowSize - return c.receiveWindow -} - -// maybeAdjustWindowSize increases the receiveWindowSize if we're sending updates too often. -// For details about auto-tuning, see https://docs.google.com/document/d/1SExkMmGiz8VYzV3s9E35JQlJ73vhzCekKkDi85F1qCE/edit?usp=sharing. -func (c *baseFlowController) maybeAdjustWindowSize() { - bytesReadInEpoch := c.bytesRead - c.epochStartOffset - // don't do anything if less than half the window has been consumed - if bytesReadInEpoch <= c.receiveWindowSize/2 { - return - } - rtt := c.rttStats.SmoothedRTT() - if rtt == 0 { - return - } - - fraction := float64(bytesReadInEpoch) / float64(c.receiveWindowSize) - if time.Since(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) { - // window is consumed too fast, try to increase the window size - c.receiveWindowSize = utils.MinByteCount(2*c.receiveWindowSize, c.maxReceiveWindowSize) - } - c.startNewAutoTuningEpoch() -} - -func (c *baseFlowController) startNewAutoTuningEpoch() { - c.epochStartTime = time.Now() - c.epochStartOffset = c.bytesRead -} - -func (c *baseFlowController) checkFlowControlViolation() bool { - return c.highestReceived > c.receiveWindow -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go deleted file mode 100644 index 1393335b0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/connection_flow_controller.go +++ /dev/null @@ -1,92 +0,0 @@ -package flowcontrol - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type connectionFlowController struct { - baseFlowController - - queueWindowUpdate func() -} - -var _ ConnectionFlowController = &connectionFlowController{} - -// NewConnectionFlowController gets a new flow controller for the connection -// It is created before we receive the peer's transport paramenters, thus it starts with a sendWindow of 0. -func NewConnectionFlowController( - receiveWindow protocol.ByteCount, - maxReceiveWindow protocol.ByteCount, - queueWindowUpdate func(), - rttStats *congestion.RTTStats, - logger utils.Logger, -) ConnectionFlowController { - return &connectionFlowController{ - baseFlowController: baseFlowController{ - rttStats: rttStats, - receiveWindow: receiveWindow, - receiveWindowSize: receiveWindow, - maxReceiveWindowSize: maxReceiveWindow, - logger: logger, - }, - queueWindowUpdate: queueWindowUpdate, - } -} - -func (c *connectionFlowController) SendWindowSize() protocol.ByteCount { - return c.baseFlowController.sendWindowSize() -} - -// IncrementHighestReceived adds an increment to the highestReceived value -func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - c.highestReceived += increment - if c.checkFlowControlViolation() { - return qerr.Error(qerr.FlowControlError, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow)) - } - return nil -} - -func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) { - c.baseFlowController.AddBytesRead(n) - c.maybeQueueWindowUpdate() -} - -func (c *connectionFlowController) maybeQueueWindowUpdate() { - c.mutex.Lock() - hasWindowUpdate := c.hasWindowUpdate() - c.mutex.Unlock() - if hasWindowUpdate { - c.queueWindowUpdate() - } -} - -func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount { - c.mutex.Lock() - oldWindowSize := c.receiveWindowSize - offset := c.baseFlowController.getWindowUpdate() - if oldWindowSize < c.receiveWindowSize { - c.logger.Debugf("Increasing receive flow control window for the connection to %d kB", c.receiveWindowSize/(1<<10)) - } - c.mutex.Unlock() - return offset -} - -// EnsureMinimumWindowSize sets a minimum window size -// it should make sure that the connection-level window is increased when a stream-level window grows -func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount) { - c.mutex.Lock() - if inc > c.receiveWindowSize { - c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10)) - c.receiveWindowSize = utils.MinByteCount(inc, c.maxReceiveWindowSize) - c.startNewAutoTuningEpoch() - } - c.mutex.Unlock() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/interface.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/interface.go deleted file mode 100644 index a47e7cd76..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/interface.go +++ /dev/null @@ -1,41 +0,0 @@ -package flowcontrol - -import "github.com/lucas-clemente/quic-go/internal/protocol" - -type flowController interface { - // for sending - SendWindowSize() protocol.ByteCount - UpdateSendWindow(protocol.ByteCount) - AddBytesSent(protocol.ByteCount) - // for receiving - AddBytesRead(protocol.ByteCount) - GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary - IsNewlyBlocked() (bool, protocol.ByteCount) -} - -// A StreamFlowController is a flow controller for a QUIC stream. -type StreamFlowController interface { - flowController - // for receiving - // UpdateHighestReceived should be called when a new highest offset is received - // final has to be to true if this is the final offset of the stream, - // as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame - UpdateHighestReceived(offset protocol.ByteCount, final bool) error - // Abandon should be called when reading from the stream is aborted early, - // and there won't be any further calls to AddBytesRead. - Abandon() -} - -// The ConnectionFlowController is the flow controller for the connection. -type ConnectionFlowController interface { - flowController -} - -type connectionFlowControllerI interface { - ConnectionFlowController - // The following two methods are not supposed to be called from outside this packet, but are needed internally - // for sending - EnsureMinimumWindowSize(protocol.ByteCount) - // for receiving - IncrementHighestReceived(protocol.ByteCount) error -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/stream_flow_controller.go b/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/stream_flow_controller.go deleted file mode 100644 index bb22337df..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/flowcontrol/stream_flow_controller.go +++ /dev/null @@ -1,139 +0,0 @@ -package flowcontrol - -import ( - "fmt" - - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type streamFlowController struct { - baseFlowController - - streamID protocol.StreamID - - queueWindowUpdate func() - - connection connectionFlowControllerI - - receivedFinalOffset bool -} - -var _ StreamFlowController = &streamFlowController{} - -// NewStreamFlowController gets a new flow controller for a stream -func NewStreamFlowController( - streamID protocol.StreamID, - cfc ConnectionFlowController, - receiveWindow protocol.ByteCount, - maxReceiveWindow protocol.ByteCount, - initialSendWindow protocol.ByteCount, - queueWindowUpdate func(protocol.StreamID), - rttStats *congestion.RTTStats, - logger utils.Logger, -) StreamFlowController { - return &streamFlowController{ - streamID: streamID, - connection: cfc.(connectionFlowControllerI), - queueWindowUpdate: func() { queueWindowUpdate(streamID) }, - baseFlowController: baseFlowController{ - rttStats: rttStats, - receiveWindow: receiveWindow, - receiveWindowSize: receiveWindow, - maxReceiveWindowSize: maxReceiveWindow, - sendWindow: initialSendWindow, - logger: logger, - }, - } -} - -// UpdateHighestReceived updates the highestReceived value, if the offset is higher. -func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error { - c.mutex.Lock() - defer c.mutex.Unlock() - - // If the final offset for this stream is already known, check for consistency. - if c.receivedFinalOffset { - // If we receive another final offset, check that it's the same. - if final && offset != c.highestReceived { - return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %#x, new: %#x bytes)", c.streamID, c.highestReceived, offset)) - } - // Check that the offset is below the final offset. - if offset > c.highestReceived { - return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received offset %#x for stream %d. Final offset was already received at %#x", offset, c.streamID, c.highestReceived)) - } - } - - if final { - c.receivedFinalOffset = true - } - if offset == c.highestReceived { - return nil - } - // A higher offset was received before. - // This can happen due to reordering. - if offset <= c.highestReceived { - if final { - return qerr.Error(qerr.FinalSizeError, fmt.Sprintf("Received final offset %#x for stream %d, but already received offset %#x before", offset, c.streamID, c.highestReceived)) - } - return nil - } - - increment := offset - c.highestReceived - c.highestReceived = offset - if c.checkFlowControlViolation() { - return qerr.Error(qerr.FlowControlError, fmt.Sprintf("Received %#x bytes on stream %d, allowed %#x bytes", offset, c.streamID, c.receiveWindow)) - } - return c.connection.IncrementHighestReceived(increment) -} - -func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) { - c.baseFlowController.AddBytesRead(n) - c.maybeQueueWindowUpdate() - c.connection.AddBytesRead(n) -} - -func (c *streamFlowController) Abandon() { - if unread := c.highestReceived - c.bytesRead; unread > 0 { - c.connection.AddBytesRead(unread) - } -} - -func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) { - c.baseFlowController.AddBytesSent(n) - c.connection.AddBytesSent(n) -} - -func (c *streamFlowController) SendWindowSize() protocol.ByteCount { - return utils.MinByteCount(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize()) -} - -func (c *streamFlowController) maybeQueueWindowUpdate() { - c.mutex.Lock() - hasWindowUpdate := !c.receivedFinalOffset && c.hasWindowUpdate() - c.mutex.Unlock() - if hasWindowUpdate { - c.queueWindowUpdate() - } -} - -func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount { - // don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler - c.mutex.Lock() - // if we already received the final offset for this stream, the peer won't need any additional flow control credit - if c.receivedFinalOffset { - c.mutex.Unlock() - return 0 - } - - oldWindowSize := c.receiveWindowSize - offset := c.baseFlowController.getWindowUpdate() - if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size - c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10)) - c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier)) - } - c.mutex.Unlock() - return offset -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go deleted file mode 100644 index 07ce74f74..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/aead.go +++ /dev/null @@ -1,104 +0,0 @@ -package handshake - -import ( - "crypto/cipher" - "encoding/binary" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -type sealer struct { - aead cipher.AEAD - hpEncrypter cipher.Block - - // use a single slice to avoid allocations - nonceBuf []byte - hpMask []byte - - // short headers protect 5 bits in the first byte, long headers only 4 - is1RTT bool -} - -var _ Sealer = &sealer{} - -func newSealer(aead cipher.AEAD, hpEncrypter cipher.Block, is1RTT bool) Sealer { - return &sealer{ - aead: aead, - nonceBuf: make([]byte, aead.NonceSize()), - is1RTT: is1RTT, - hpEncrypter: hpEncrypter, - hpMask: make([]byte, hpEncrypter.BlockSize()), - } -} - -func (s *sealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { - binary.BigEndian.PutUint64(s.nonceBuf[len(s.nonceBuf)-8:], uint64(pn)) - // The AEAD we're using here will be the qtls.aeadAESGCM13. - // It uses the nonce provided here and XOR it with the IV. - return s.aead.Seal(dst, s.nonceBuf, src, ad) -} - -func (s *sealer) EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { - if len(sample) != s.hpEncrypter.BlockSize() { - panic("invalid sample size") - } - s.hpEncrypter.Encrypt(s.hpMask, sample) - if s.is1RTT { - *firstByte ^= s.hpMask[0] & 0x1f - } else { - *firstByte ^= s.hpMask[0] & 0xf - } - for i := range pnBytes { - pnBytes[i] ^= s.hpMask[i+1] - } -} - -func (s *sealer) Overhead() int { - return s.aead.Overhead() -} - -type opener struct { - aead cipher.AEAD - pnDecrypter cipher.Block - - // use a single slice to avoid allocations - nonceBuf []byte - hpMask []byte - - // short headers protect 5 bits in the first byte, long headers only 4 - is1RTT bool -} - -var _ Opener = &opener{} - -func newOpener(aead cipher.AEAD, pnDecrypter cipher.Block, is1RTT bool) Opener { - return &opener{ - aead: aead, - nonceBuf: make([]byte, aead.NonceSize()), - is1RTT: is1RTT, - pnDecrypter: pnDecrypter, - hpMask: make([]byte, pnDecrypter.BlockSize()), - } -} - -func (o *opener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) { - binary.BigEndian.PutUint64(o.nonceBuf[len(o.nonceBuf)-8:], uint64(pn)) - // The AEAD we're using here will be the qtls.aeadAESGCM13. - // It uses the nonce provided here and XOR it with the IV. - return o.aead.Open(dst, o.nonceBuf, src, ad) -} - -func (o *opener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { - if len(sample) != o.pnDecrypter.BlockSize() { - panic("invalid sample size") - } - o.pnDecrypter.Encrypt(o.hpMask, sample) - if o.is1RTT { - *firstByte ^= o.hpMask[0] & 0x1f - } else { - *firstByte ^= o.hpMask[0] & 0xf - } - for i := range pnBytes { - pnBytes[i] ^= o.hpMask[i+1] - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_generator.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_generator.go deleted file mode 100644 index 6d1288ed6..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_generator.go +++ /dev/null @@ -1,109 +0,0 @@ -package handshake - -import ( - "encoding/asn1" - "fmt" - "net" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -const ( - cookiePrefixIP byte = iota - cookiePrefixString -) - -// A Cookie is derived from the client address and can be used to verify the ownership of this address. -type Cookie struct { - RemoteAddr string - OriginalDestConnectionID protocol.ConnectionID - // The time that the Cookie was issued (resolution 1 second) - SentTime time.Time -} - -// token is the struct that is used for ASN1 serialization and deserialization -type token struct { - RemoteAddr []byte - OriginalDestConnectionID []byte - - Timestamp int64 -} - -// A CookieGenerator generates Cookies -type CookieGenerator struct { - cookieProtector cookieProtector -} - -// NewCookieGenerator initializes a new CookieGenerator -func NewCookieGenerator() (*CookieGenerator, error) { - cookieProtector, err := newCookieProtector() - if err != nil { - return nil, err - } - return &CookieGenerator{ - cookieProtector: cookieProtector, - }, nil -} - -// NewToken generates a new Cookie for a given source address -func (g *CookieGenerator) NewToken(raddr net.Addr, origConnID protocol.ConnectionID) ([]byte, error) { - data, err := asn1.Marshal(token{ - RemoteAddr: encodeRemoteAddr(raddr), - OriginalDestConnectionID: origConnID, - Timestamp: time.Now().Unix(), - }) - if err != nil { - return nil, err - } - return g.cookieProtector.NewToken(data) -} - -// DecodeToken decodes a Cookie -func (g *CookieGenerator) DecodeToken(encrypted []byte) (*Cookie, error) { - // if the client didn't send any Cookie, DecodeToken will be called with a nil-slice - if len(encrypted) == 0 { - return nil, nil - } - - data, err := g.cookieProtector.DecodeToken(encrypted) - if err != nil { - return nil, err - } - t := &token{} - rest, err := asn1.Unmarshal(data, t) - if err != nil { - return nil, err - } - if len(rest) != 0 { - return nil, fmt.Errorf("rest when unpacking token: %d", len(rest)) - } - cookie := &Cookie{ - RemoteAddr: decodeRemoteAddr(t.RemoteAddr), - SentTime: time.Unix(t.Timestamp, 0), - } - if len(t.OriginalDestConnectionID) > 0 { - cookie.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID) - } - return cookie, nil -} - -// encodeRemoteAddr encodes a remote address such that it can be saved in the Cookie -func encodeRemoteAddr(remoteAddr net.Addr) []byte { - if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok { - return append([]byte{cookiePrefixIP}, udpAddr.IP...) - } - return append([]byte{cookiePrefixString}, []byte(remoteAddr.String())...) -} - -// decodeRemoteAddr decodes the remote address saved in the Cookie -func decodeRemoteAddr(data []byte) string { - // data will never be empty for a Cookie that we generated. Check it to be on the safe side - if len(data) == 0 { - return "" - } - if data[0] == cookiePrefixIP { - return net.IP(data[1:]).String() - } - return string(data[1:]) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_protector.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_protector.go deleted file mode 100644 index 7ebdfa18c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/cookie_protector.go +++ /dev/null @@ -1,86 +0,0 @@ -package handshake - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "crypto/sha256" - "fmt" - "io" - - "golang.org/x/crypto/hkdf" -) - -// CookieProtector is used to create and verify a cookie -type cookieProtector interface { - // NewToken creates a new token - NewToken([]byte) ([]byte, error) - // DecodeToken decodes a token - DecodeToken([]byte) ([]byte, error) -} - -const ( - cookieSecretSize = 32 - cookieNonceSize = 32 -) - -// cookieProtector is used to create and verify a cookie -type cookieProtectorImpl struct { - secret []byte -} - -// newCookieProtector creates a source for source address tokens -func newCookieProtector() (cookieProtector, error) { - secret := make([]byte, cookieSecretSize) - if _, err := rand.Read(secret); err != nil { - return nil, err - } - return &cookieProtectorImpl{secret: secret}, nil -} - -// NewToken encodes data into a new token. -func (s *cookieProtectorImpl) NewToken(data []byte) ([]byte, error) { - nonce := make([]byte, cookieNonceSize) - if _, err := rand.Read(nonce); err != nil { - return nil, err - } - aead, aeadNonce, err := s.createAEAD(nonce) - if err != nil { - return nil, err - } - return append(nonce, aead.Seal(nil, aeadNonce, data, nil)...), nil -} - -// DecodeToken decodes a token. -func (s *cookieProtectorImpl) DecodeToken(p []byte) ([]byte, error) { - if len(p) < cookieNonceSize { - return nil, fmt.Errorf("Token too short: %d", len(p)) - } - nonce := p[:cookieNonceSize] - aead, aeadNonce, err := s.createAEAD(nonce) - if err != nil { - return nil, err - } - return aead.Open(nil, aeadNonce, p[cookieNonceSize:], nil) -} - -func (s *cookieProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) { - h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go cookie source")) - key := make([]byte, 32) // use a 32 byte key, in order to select AES-256 - if _, err := io.ReadFull(h, key); err != nil { - return nil, nil, err - } - aeadNonce := make([]byte, 12) - if _, err := io.ReadFull(h, aeadNonce); err != nil { - return nil, nil, err - } - c, err := aes.NewCipher(key) - if err != nil { - return nil, nil, err - } - aead, err := cipher.NewGCM(c) - if err != nil { - return nil, nil, err - } - return aead, aeadNonce, nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go deleted file mode 100644 index de6ddc684..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/crypto_setup.go +++ /dev/null @@ -1,581 +0,0 @@ -package handshake - -import ( - "crypto/aes" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "sync" - "unsafe" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/marten-seemann/qtls" -) - -type messageType uint8 - -// TLS handshake message types. -const ( - typeClientHello messageType = 1 - typeServerHello messageType = 2 - typeNewSessionTicket messageType = 4 - typeEncryptedExtensions messageType = 8 - typeCertificate messageType = 11 - typeCertificateRequest messageType = 13 - typeCertificateVerify messageType = 15 - typeFinished messageType = 20 -) - -func (m messageType) String() string { - switch m { - case typeClientHello: - return "ClientHello" - case typeServerHello: - return "ServerHello" - case typeNewSessionTicket: - return "NewSessionTicket" - case typeEncryptedExtensions: - return "EncryptedExtensions" - case typeCertificate: - return "Certificate" - case typeCertificateRequest: - return "CertificateRequest" - case typeCertificateVerify: - return "CertificateVerify" - case typeFinished: - return "Finished" - default: - return fmt.Sprintf("unknown message type: %d", m) - } -} - -// ErrOpenerNotYetAvailable is returned when an opener is requested for an encryption level, -// but the corresponding opener has not yet been initialized -// This can happen when packets arrive out of order. -var ErrOpenerNotYetAvailable = errors.New("CryptoSetup: opener at this encryption level not yet available") - -type cryptoSetup struct { - tlsConf *qtls.Config - conn *qtls.Conn - - messageChan chan []byte - - paramsChan <-chan []byte - handleParamsCallback func([]byte) - - alertChan chan uint8 - // HandleData() sends errors on the messageErrChan - messageErrChan chan error - // handshakeDone is closed as soon as the go routine running qtls.Handshake() returns - handshakeDone chan struct{} - // is closed when Close() is called - closeChan chan struct{} - - clientHelloWritten bool - clientHelloWrittenChan chan struct{} - - receivedWriteKey chan struct{} - receivedReadKey chan struct{} - // WriteRecord does a non-blocking send on this channel. - // This way, handleMessage can see if qtls tries to write a message. - // This is necessary: - // for servers: to see if a HelloRetryRequest should be sent in response to a ClientHello - // for clients: to see if a ServerHello is a HelloRetryRequest - writeRecord chan struct{} - - logger utils.Logger - - perspective protocol.Perspective - - mutex sync.Mutex // protects all members below - - readEncLevel protocol.EncryptionLevel - writeEncLevel protocol.EncryptionLevel - - initialStream io.Writer - initialOpener Opener - initialSealer Sealer - - handshakeStream io.Writer - handshakeOpener Opener - handshakeSealer Sealer - - oneRTTStream io.Writer - opener Opener - sealer Sealer -} - -var _ qtls.RecordLayer = &cryptoSetup{} -var _ CryptoSetup = &cryptoSetup{} - -// NewCryptoSetupClient creates a new crypto setup for the client -func NewCryptoSetupClient( - initialStream io.Writer, - handshakeStream io.Writer, - oneRTTStream io.Writer, - connID protocol.ConnectionID, - remoteAddr net.Addr, - tp *TransportParameters, - handleParams func([]byte), - tlsConf *tls.Config, - logger utils.Logger, -) (CryptoSetup, <-chan struct{} /* ClientHello written */, error) { - cs, clientHelloWritten, err := newCryptoSetup( - initialStream, - handshakeStream, - oneRTTStream, - connID, - tp, - handleParams, - tlsConf, - logger, - protocol.PerspectiveClient, - ) - if err != nil { - return nil, nil, err - } - cs.conn = qtls.Client(newConn(remoteAddr), cs.tlsConf) - return cs, clientHelloWritten, nil -} - -// NewCryptoSetupServer creates a new crypto setup for the server -func NewCryptoSetupServer( - initialStream io.Writer, - handshakeStream io.Writer, - oneRTTStream io.Writer, - connID protocol.ConnectionID, - remoteAddr net.Addr, - tp *TransportParameters, - handleParams func([]byte), - tlsConf *tls.Config, - logger utils.Logger, -) (CryptoSetup, error) { - cs, _, err := newCryptoSetup( - initialStream, - handshakeStream, - oneRTTStream, - connID, - tp, - handleParams, - tlsConf, - logger, - protocol.PerspectiveServer, - ) - if err != nil { - return nil, err - } - cs.conn = qtls.Server(newConn(remoteAddr), cs.tlsConf) - return cs, nil -} - -func newCryptoSetup( - initialStream io.Writer, - handshakeStream io.Writer, - oneRTTStream io.Writer, - connID protocol.ConnectionID, - tp *TransportParameters, - handleParams func([]byte), - tlsConf *tls.Config, - logger utils.Logger, - perspective protocol.Perspective, -) (*cryptoSetup, <-chan struct{} /* ClientHello written */, error) { - initialSealer, initialOpener, err := NewInitialAEAD(connID, perspective) - if err != nil { - return nil, nil, err - } - extHandler := newExtensionHandler(tp.Marshal(), perspective) - cs := &cryptoSetup{ - initialStream: initialStream, - initialSealer: initialSealer, - initialOpener: initialOpener, - handshakeStream: handshakeStream, - oneRTTStream: oneRTTStream, - readEncLevel: protocol.EncryptionInitial, - writeEncLevel: protocol.EncryptionInitial, - handleParamsCallback: handleParams, - paramsChan: extHandler.TransportParameters(), - logger: logger, - perspective: perspective, - handshakeDone: make(chan struct{}), - alertChan: make(chan uint8), - messageErrChan: make(chan error, 1), - clientHelloWrittenChan: make(chan struct{}), - messageChan: make(chan []byte, 100), - receivedReadKey: make(chan struct{}), - receivedWriteKey: make(chan struct{}), - writeRecord: make(chan struct{}), - closeChan: make(chan struct{}), - } - qtlsConf := tlsConfigToQtlsConfig(tlsConf, cs, extHandler) - cs.tlsConf = qtlsConf - return cs, cs.clientHelloWrittenChan, nil -} - -func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) error { - initialSealer, initialOpener, err := NewInitialAEAD(id, h.perspective) - if err != nil { - return err - } - h.initialSealer = initialSealer - h.initialOpener = initialOpener - return nil -} - -func (h *cryptoSetup) RunHandshake() error { - // Handle errors that might occur when HandleData() is called. - handshakeComplete := make(chan struct{}) - handshakeErrChan := make(chan error, 1) - go func() { - defer close(h.handshakeDone) - if err := h.conn.Handshake(); err != nil { - handshakeErrChan <- err - return - } - close(handshakeComplete) - }() - - select { - case <-h.closeChan: - close(h.messageChan) - // wait until the Handshake() go routine has returned - return errors.New("Handshake aborted") - case <-handshakeComplete: // return when the handshake is done - return nil - case alert := <-h.alertChan: - err := <-handshakeErrChan - return qerr.CryptoError(alert, err.Error()) - case err := <-h.messageErrChan: - // If the handshake errored because of an error that occurred during HandleData(), - // that error message will be more useful than the error message generated by Handshake(). - // Close the message chan that qtls is receiving messages from. - // This will make qtls.Handshake() return. - // Thereby the go routine running qtls.Handshake() will return. - close(h.messageChan) - return err - } -} - -func (h *cryptoSetup) Close() error { - close(h.closeChan) - // wait until qtls.Handshake() actually returned - <-h.handshakeDone - return nil -} - -// handleMessage handles a TLS handshake message. -// It is called by the crypto streams when a new message is available. -// It returns if it is done with messages on the same encryption level. -func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) bool /* stream finished */ { - msgType := messageType(data[0]) - h.logger.Debugf("Received %s message (%d bytes, encryption level: %s)", msgType, len(data), encLevel) - if err := h.checkEncryptionLevel(msgType, encLevel); err != nil { - h.messageErrChan <- err - return false - } - h.messageChan <- data - switch h.perspective { - case protocol.PerspectiveClient: - return h.handleMessageForClient(msgType) - case protocol.PerspectiveServer: - return h.handleMessageForServer(msgType) - default: - panic("") - } -} - -func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protocol.EncryptionLevel) error { - var expected protocol.EncryptionLevel - switch msgType { - case typeClientHello, - typeServerHello: - expected = protocol.EncryptionInitial - case typeEncryptedExtensions, - typeCertificate, - typeCertificateRequest, - typeCertificateVerify, - typeFinished: - expected = protocol.EncryptionHandshake - case typeNewSessionTicket: - expected = protocol.Encryption1RTT - default: - return fmt.Errorf("unexpected handshake message: %d", msgType) - } - if encLevel != expected { - return fmt.Errorf("expected handshake message %s to have encryption level %s, has %s", msgType, expected, encLevel) - } - return nil -} - -func (h *cryptoSetup) handleMessageForServer(msgType messageType) bool { - switch msgType { - case typeClientHello: - select { - case <-h.writeRecord: - // If qtls sends a HelloRetryRequest, it will only write the record. - // If it accepts the ClientHello, it will first read the transport parameters. - h.logger.Debugf("Sending HelloRetryRequest") - return false - case data := <-h.paramsChan: - h.handleParamsCallback(data) - case <-h.handshakeDone: - return false - } - // get the handshake read key - select { - case <-h.receivedReadKey: - case <-h.handshakeDone: - return false - } - // get the handshake write key - select { - case <-h.receivedWriteKey: - case <-h.handshakeDone: - return false - } - // get the 1-RTT write key - select { - case <-h.receivedWriteKey: - case <-h.handshakeDone: - return false - } - return true - case typeCertificate, typeCertificateVerify: - // nothing to do - return false - case typeFinished: - // get the 1-RTT read key - select { - case <-h.receivedReadKey: - case <-h.handshakeDone: - return false - } - return true - default: - panic("unexpected handshake message") - } -} - -func (h *cryptoSetup) handleMessageForClient(msgType messageType) bool { - switch msgType { - case typeServerHello: - // get the handshake write key - select { - case <-h.writeRecord: - // If qtls writes in response to a ServerHello, this means that this ServerHello - // is a HelloRetryRequest. - // Otherwise, we'd just wait for the Certificate message. - h.logger.Debugf("ServerHello is a HelloRetryRequest") - return false - case <-h.receivedWriteKey: - case <-h.handshakeDone: - return false - } - // get the handshake read key - select { - case <-h.receivedReadKey: - case <-h.handshakeDone: - return false - } - return true - case typeEncryptedExtensions: - select { - case data := <-h.paramsChan: - h.handleParamsCallback(data) - case <-h.handshakeDone: - return false - } - return false - case typeCertificateRequest, typeCertificate, typeCertificateVerify: - // nothing to do - return false - case typeFinished: - // get the 1-RTT read key - select { - case <-h.receivedReadKey: - case <-h.handshakeDone: - return false - } - // get the handshake write key - select { - case <-h.receivedWriteKey: - case <-h.handshakeDone: - return false - } - return true - case typeNewSessionTicket: - <-h.handshakeDone // don't process session tickets before the handshake has completed - h.conn.HandlePostHandshakeMessage() - return false - default: - panic("unexpected handshake message: ") - } -} - -// ReadHandshakeMessage is called by TLS. -// It blocks until a new handshake message is available. -func (h *cryptoSetup) ReadHandshakeMessage() ([]byte, error) { - msg, ok := <-h.messageChan - if !ok { - return nil, errors.New("error while handling the handshake message") - } - return msg, nil -} - -func (h *cryptoSetup) SetReadKey(suite *qtls.CipherSuite, trafficSecret []byte) { - key := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic key", suite.KeyLen()) - iv := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic iv", suite.IVLen()) - hpKey := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic hp", suite.KeyLen()) - hpDecrypter, err := aes.NewCipher(hpKey) - if err != nil { - panic(fmt.Sprintf("error creating new AES cipher: %s", err)) - } - - h.mutex.Lock() - switch h.readEncLevel { - case protocol.EncryptionInitial: - h.readEncLevel = protocol.EncryptionHandshake - h.handshakeOpener = newOpener(suite.AEAD(key, iv), hpDecrypter, false) - h.logger.Debugf("Installed Handshake Read keys") - case protocol.EncryptionHandshake: - h.readEncLevel = protocol.Encryption1RTT - h.opener = newOpener(suite.AEAD(key, iv), hpDecrypter, true) - h.logger.Debugf("Installed 1-RTT Read keys") - default: - panic("unexpected read encryption level") - } - h.mutex.Unlock() - h.receivedReadKey <- struct{}{} -} - -func (h *cryptoSetup) SetWriteKey(suite *qtls.CipherSuite, trafficSecret []byte) { - key := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic key", suite.KeyLen()) - iv := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic iv", suite.IVLen()) - hpKey := qtls.HkdfExpandLabel(suite.Hash(), trafficSecret, []byte{}, "quic hp", suite.KeyLen()) - hpEncrypter, err := aes.NewCipher(hpKey) - if err != nil { - panic(fmt.Sprintf("error creating new AES cipher: %s", err)) - } - - h.mutex.Lock() - switch h.writeEncLevel { - case protocol.EncryptionInitial: - h.writeEncLevel = protocol.EncryptionHandshake - h.handshakeSealer = newSealer(suite.AEAD(key, iv), hpEncrypter, false) - h.logger.Debugf("Installed Handshake Write keys") - case protocol.EncryptionHandshake: - h.writeEncLevel = protocol.Encryption1RTT - h.sealer = newSealer(suite.AEAD(key, iv), hpEncrypter, true) - h.logger.Debugf("Installed 1-RTT Write keys") - default: - panic("unexpected write encryption level") - } - h.mutex.Unlock() - h.receivedWriteKey <- struct{}{} -} - -// WriteRecord is called when TLS writes data -func (h *cryptoSetup) WriteRecord(p []byte) (int, error) { - defer func() { - select { - case h.writeRecord <- struct{}{}: - default: - } - }() - - h.mutex.Lock() - defer h.mutex.Unlock() - - switch h.writeEncLevel { - case protocol.EncryptionInitial: - // assume that the first WriteRecord call contains the ClientHello - n, err := h.initialStream.Write(p) - if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient { - h.clientHelloWritten = true - close(h.clientHelloWrittenChan) - } - return n, err - case protocol.EncryptionHandshake: - return h.handshakeStream.Write(p) - case protocol.Encryption1RTT: - return h.oneRTTStream.Write(p) - default: - panic(fmt.Sprintf("unexpected write encryption level: %s", h.writeEncLevel)) - } -} - -func (h *cryptoSetup) SendAlert(alert uint8) { - h.alertChan <- alert -} - -func (h *cryptoSetup) GetSealer() (protocol.EncryptionLevel, Sealer) { - h.mutex.Lock() - defer h.mutex.Unlock() - - if h.sealer != nil { - return protocol.Encryption1RTT, h.sealer - } - if h.handshakeSealer != nil { - return protocol.EncryptionHandshake, h.handshakeSealer - } - return protocol.EncryptionInitial, h.initialSealer -} - -func (h *cryptoSetup) GetSealerWithEncryptionLevel(level protocol.EncryptionLevel) (Sealer, error) { - errNoSealer := fmt.Errorf("CryptoSetup: no sealer with encryption level %s", level.String()) - - h.mutex.Lock() - defer h.mutex.Unlock() - - switch level { - case protocol.EncryptionInitial: - return h.initialSealer, nil - case protocol.EncryptionHandshake: - if h.handshakeSealer == nil { - return nil, errNoSealer - } - return h.handshakeSealer, nil - case protocol.Encryption1RTT: - if h.sealer == nil { - return nil, errNoSealer - } - return h.sealer, nil - default: - return nil, errNoSealer - } -} - -func (h *cryptoSetup) GetOpener(level protocol.EncryptionLevel) (Opener, error) { - h.mutex.Lock() - defer h.mutex.Unlock() - - switch level { - case protocol.EncryptionInitial: - return h.initialOpener, nil - case protocol.EncryptionHandshake: - if h.handshakeOpener == nil { - return nil, ErrOpenerNotYetAvailable - } - return h.handshakeOpener, nil - case protocol.Encryption1RTT: - if h.opener == nil { - return nil, ErrOpenerNotYetAvailable - } - return h.opener, nil - default: - return nil, fmt.Errorf("CryptoSetup: no opener with encryption level %s", level) - } -} - -func (h *cryptoSetup) ConnectionState() tls.ConnectionState { - cs := h.conn.ConnectionState() - // h.conn is a qtls.Conn, which returns a qtls.ConnectionState. - // qtls.ConnectionState is identical to the tls.ConnectionState. - // It contains an unexported field which is used ExportKeyingMaterial(). - // The only way to return a tls.ConnectionState is to use unsafe. - // In unsafe.go we check that the two objects are actually identical. - return *(*tls.ConnectionState)(unsafe.Pointer(&cs)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go deleted file mode 100644 index 5d78bbe2c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/initial_aead.go +++ /dev/null @@ -1,52 +0,0 @@ -package handshake - -import ( - "crypto" - "crypto/aes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/marten-seemann/qtls" -) - -var quicVersion1Salt = []byte{0xef, 0x4f, 0xb0, 0xab, 0xb4, 0x74, 0x70, 0xc4, 0x1b, 0xef, 0xcf, 0x80, 0x31, 0x33, 0x4f, 0xae, 0x48, 0x5e, 0x09, 0xa0} - -// NewInitialAEAD creates a new AEAD for Initial encryption / decryption. -func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective) (Sealer, Opener, error) { - clientSecret, serverSecret := computeSecrets(connID) - var mySecret, otherSecret []byte - if pers == protocol.PerspectiveClient { - mySecret = clientSecret - otherSecret = serverSecret - } else { - mySecret = serverSecret - otherSecret = clientSecret - } - myKey, myHPKey, myIV := computeInitialKeyAndIV(mySecret) - otherKey, otherHPKey, otherIV := computeInitialKeyAndIV(otherSecret) - - encrypter := qtls.AEADAESGCMTLS13(myKey, myIV) - hpEncrypter, err := aes.NewCipher(myHPKey) - if err != nil { - return nil, nil, err - } - decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV) - hpDecrypter, err := aes.NewCipher(otherHPKey) - if err != nil { - return nil, nil, err - } - return newSealer(encrypter, hpEncrypter, false), newOpener(decrypter, hpDecrypter, false), nil -} - -func computeSecrets(connID protocol.ConnectionID) (clientSecret, serverSecret []byte) { - initialSecret := qtls.HkdfExtract(crypto.SHA256, connID, quicVersion1Salt) - clientSecret = qtls.HkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) - serverSecret = qtls.HkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size()) - return -} - -func computeInitialKeyAndIV(secret []byte) (key, hpKey, iv []byte) { - key = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) - hpKey = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic hp", 16) - iv = qtls.HkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) - return -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/interface.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/interface.go deleted file mode 100644 index 225225138..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/interface.go +++ /dev/null @@ -1,52 +0,0 @@ -package handshake - -import ( - "crypto/tls" - "crypto/x509" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/marten-seemann/qtls" -) - -// Opener opens a packet -type Opener interface { - Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) - DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) -} - -// Sealer seals a packet -type Sealer interface { - Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte - EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) - Overhead() int -} - -// A tlsExtensionHandler sends and received the QUIC TLS extension. -type tlsExtensionHandler interface { - GetExtensions(msgType uint8) []qtls.Extension - ReceivedExtensions(msgType uint8, exts []qtls.Extension) - TransportParameters() <-chan []byte -} - -// CryptoSetup handles the handshake and protecting / unprotecting packets -type CryptoSetup interface { - RunHandshake() error - io.Closer - ChangeConnectionID(protocol.ConnectionID) error - - HandleMessage([]byte, protocol.EncryptionLevel) bool - ConnectionState() tls.ConnectionState - - GetSealer() (protocol.EncryptionLevel, Sealer) - GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (Sealer, error) - GetOpener(protocol.EncryptionLevel) (Opener, error) -} - -// ConnectionState records basic details about the QUIC connection. -// Warning: This API should not be considered stable and might change soon. -type ConnectionState struct { - HandshakeComplete bool // handshake is complete - ServerName string // server name requested by client, if any (server side only) - PeerCertificates []*x509.Certificate // certificate chain presented by remote peer -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/qtls.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/qtls.go deleted file mode 100644 index 8880d9cb9..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/qtls.go +++ /dev/null @@ -1,132 +0,0 @@ -package handshake - -import ( - "crypto/tls" - "net" - "time" - "unsafe" - - "github.com/marten-seemann/qtls" -) - -type conn struct { - remoteAddr net.Addr -} - -func newConn(remote net.Addr) net.Conn { - return &conn{remoteAddr: remote} -} - -var _ net.Conn = &conn{} - -func (c *conn) Read([]byte) (int, error) { return 0, nil } -func (c *conn) Write([]byte) (int, error) { return 0, nil } -func (c *conn) Close() error { return nil } -func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } -func (c *conn) LocalAddr() net.Addr { return nil } -func (c *conn) SetReadDeadline(time.Time) error { return nil } -func (c *conn) SetWriteDeadline(time.Time) error { return nil } -func (c *conn) SetDeadline(time.Time) error { return nil } - -type clientSessionCache struct { - tls.ClientSessionCache -} - -var _ qtls.ClientSessionCache = &clientSessionCache{} - -func (c *clientSessionCache) Get(sessionKey string) (*qtls.ClientSessionState, bool) { - sess, ok := c.ClientSessionCache.Get(sessionKey) - if sess == nil { - return nil, ok - } - // qtls.ClientSessionState is identical to the tls.ClientSessionState. - // In order to allow users of quic-go to use a tls.Config, - // we need this workaround to use the ClientSessionCache. - // In unsafe.go we check that the two structs are actually identical. - usess := (*[unsafe.Sizeof(*sess)]byte)(unsafe.Pointer(sess))[:] - var session qtls.ClientSessionState - usession := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:] - copy(usession, usess) - return &session, ok -} - -func (c *clientSessionCache) Put(sessionKey string, cs *qtls.ClientSessionState) { - // qtls.ClientSessionState is identical to the tls.ClientSessionState. - // In order to allow users of quic-go to use a tls.Config, - // we need this workaround to use the ClientSessionCache. - // In unsafe.go we check that the two structs are actually identical. - usess := (*[unsafe.Sizeof(*cs)]byte)(unsafe.Pointer(cs))[:] - var session tls.ClientSessionState - usession := (*[unsafe.Sizeof(session)]byte)(unsafe.Pointer(&session))[:] - copy(usession, usess) - c.ClientSessionCache.Put(sessionKey, &session) -} - -func tlsConfigToQtlsConfig( - c *tls.Config, - recordLayer qtls.RecordLayer, - extHandler tlsExtensionHandler, -) *qtls.Config { - if c == nil { - c = &tls.Config{} - } - // Clone the config first. This executes the tls.Config.serverInit(). - // This sets the SessionTicketKey, if the user didn't supply one. - c = c.Clone() - // QUIC requires TLS 1.3 or newer - minVersion := c.MinVersion - if minVersion < qtls.VersionTLS13 { - minVersion = qtls.VersionTLS13 - } - maxVersion := c.MaxVersion - if maxVersion < qtls.VersionTLS13 { - maxVersion = qtls.VersionTLS13 - } - var getConfigForClient func(ch *tls.ClientHelloInfo) (*qtls.Config, error) - if c.GetConfigForClient != nil { - getConfigForClient = func(ch *tls.ClientHelloInfo) (*qtls.Config, error) { - tlsConf, err := c.GetConfigForClient(ch) - if err != nil { - return nil, err - } - if tlsConf == nil { - return nil, nil - } - return tlsConfigToQtlsConfig(tlsConf, recordLayer, extHandler), nil - } - } - var csc qtls.ClientSessionCache - if c.ClientSessionCache != nil { - csc = &clientSessionCache{c.ClientSessionCache} - } - return &qtls.Config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - GetClientCertificate: c.GetClientCertificate, - GetConfigForClient: getConfigForClient, - VerifyPeerCertificate: c.VerifyPeerCertificate, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: csc, - MinVersion: minVersion, - MaxVersion: maxVersion, - CurvePreferences: c.CurvePreferences, - DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, - // no need to copy Renegotiation, it's not supported by TLS 1.3 - KeyLogWriter: c.KeyLogWriter, - AlternativeRecordLayer: recordLayer, - GetExtensions: extHandler.GetExtensions, - ReceivedExtensions: extHandler.ReceivedExtensions, - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/tls_extension_handler.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/tls_extension_handler.go deleted file mode 100644 index 590aafd1a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/tls_extension_handler.go +++ /dev/null @@ -1,58 +0,0 @@ -package handshake - -import ( - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/marten-seemann/qtls" -) - -const quicTLSExtensionType = 0xffa5 - -type extensionHandler struct { - ourParams []byte - paramsChan chan []byte - - perspective protocol.Perspective -} - -var _ tlsExtensionHandler = &extensionHandler{} - -// newExtensionHandler creates a new extension handler -func newExtensionHandler(params []byte, pers protocol.Perspective) tlsExtensionHandler { - return &extensionHandler{ - ourParams: params, - paramsChan: make(chan []byte), - perspective: pers, - } -} - -func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension { - if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) || - (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) { - return nil - } - return []qtls.Extension{{ - Type: quicTLSExtensionType, - Data: h.ourParams, - }} -} - -func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) { - if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) || - (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) { - return - } - - var data []byte - for _, ext := range exts { - if ext.Type == quicTLSExtensionType { - data = ext.Data - break - } - } - - h.paramsChan <- data -} - -func (h *extensionHandler) TransportParameters() <-chan []byte { - return h.paramsChan -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/transport_parameters.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/transport_parameters.go deleted file mode 100644 index 6810cbc37..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/transport_parameters.go +++ /dev/null @@ -1,260 +0,0 @@ -package handshake - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "sort" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -type transportParameterID uint16 - -const ( - originalConnectionIDParameterID transportParameterID = 0x0 - idleTimeoutParameterID transportParameterID = 0x1 - statelessResetTokenParameterID transportParameterID = 0x2 - maxPacketSizeParameterID transportParameterID = 0x3 - initialMaxDataParameterID transportParameterID = 0x4 - initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5 - initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6 - initialMaxStreamDataUniParameterID transportParameterID = 0x7 - initialMaxStreamsBidiParameterID transportParameterID = 0x8 - initialMaxStreamsUniParameterID transportParameterID = 0x9 - ackDelayExponentParameterID transportParameterID = 0xa - disableMigrationParameterID transportParameterID = 0xc -) - -// TransportParameters are parameters sent to the peer during the handshake -type TransportParameters struct { - InitialMaxStreamDataBidiLocal protocol.ByteCount - InitialMaxStreamDataBidiRemote protocol.ByteCount - InitialMaxStreamDataUni protocol.ByteCount - InitialMaxData protocol.ByteCount - - AckDelayExponent uint8 - - MaxPacketSize protocol.ByteCount - - MaxUniStreams uint64 - MaxBidiStreams uint64 - - IdleTimeout time.Duration - DisableMigration bool - - StatelessResetToken *[16]byte - OriginalConnectionID protocol.ConnectionID -} - -// Unmarshal the transport parameters -func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error { - if len(data) < 2 { - return errors.New("transport parameter data too short") - } - length := binary.BigEndian.Uint16(data[:2]) - if len(data)-2 < int(length) { - return fmt.Errorf("expected transport parameters to be %d bytes long, have %d", length, len(data)-2) - } - - // needed to check that every parameter is only sent at most once - var parameterIDs []transportParameterID - - var readAckDelayExponent bool - - r := bytes.NewReader(data[2:]) - for r.Len() >= 4 { - paramIDInt, _ := utils.BigEndian.ReadUint16(r) - paramID := transportParameterID(paramIDInt) - paramLen, _ := utils.BigEndian.ReadUint16(r) - parameterIDs = append(parameterIDs, paramID) - switch paramID { - case ackDelayExponentParameterID: - readAckDelayExponent = true - fallthrough - case initialMaxStreamDataBidiLocalParameterID, - initialMaxStreamDataBidiRemoteParameterID, - initialMaxStreamDataUniParameterID, - initialMaxDataParameterID, - initialMaxStreamsBidiParameterID, - initialMaxStreamsUniParameterID, - idleTimeoutParameterID, - maxPacketSizeParameterID: - if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil { - return err - } - default: - if r.Len() < int(paramLen) { - return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen) - } - switch paramID { - case disableMigrationParameterID: - if paramLen != 0 { - return fmt.Errorf("wrong length for disable_migration: %d (expected empty)", paramLen) - } - p.DisableMigration = true - case statelessResetTokenParameterID: - if sentBy == protocol.PerspectiveClient { - return errors.New("client sent a stateless_reset_token") - } - if paramLen != 16 { - return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen) - } - var token [16]byte - r.Read(token[:]) - p.StatelessResetToken = &token - case originalConnectionIDParameterID: - if sentBy == protocol.PerspectiveClient { - return errors.New("client sent an original_connection_id") - } - p.OriginalConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) - default: - r.Seek(int64(paramLen), io.SeekCurrent) - } - } - } - - if !readAckDelayExponent { - p.AckDelayExponent = protocol.DefaultAckDelayExponent - } - - // check that every transport parameter was sent at most once - sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] }) - for i := 0; i < len(parameterIDs)-1; i++ { - if parameterIDs[i] == parameterIDs[i+1] { - return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i]) - } - } - - if r.Len() != 0 { - return fmt.Errorf("should have read all data. Still have %d bytes", r.Len()) - } - return nil -} - -func (p *TransportParameters) readNumericTransportParameter( - r *bytes.Reader, - paramID transportParameterID, - expectedLen int, -) error { - remainingLen := r.Len() - val, err := utils.ReadVarInt(r) - if err != nil { - return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) - } - if remainingLen-r.Len() != expectedLen { - return fmt.Errorf("inconsistent transport parameter length for %d", paramID) - } - switch paramID { - case initialMaxStreamDataBidiLocalParameterID: - p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val) - case initialMaxStreamDataBidiRemoteParameterID: - p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val) - case initialMaxStreamDataUniParameterID: - p.InitialMaxStreamDataUni = protocol.ByteCount(val) - case initialMaxDataParameterID: - p.InitialMaxData = protocol.ByteCount(val) - case initialMaxStreamsBidiParameterID: - p.MaxBidiStreams = val - case initialMaxStreamsUniParameterID: - p.MaxUniStreams = val - case idleTimeoutParameterID: - p.IdleTimeout = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) - case maxPacketSizeParameterID: - if val < 1200 { - return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val) - } - p.MaxPacketSize = protocol.ByteCount(val) - case ackDelayExponentParameterID: - if val > protocol.MaxAckDelayExponent { - return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent) - } - p.AckDelayExponent = uint8(val) - default: - return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) - } - return nil -} - -// Marshal the transport parameters -func (p *TransportParameters) Marshal() []byte { - b := &bytes.Buffer{} - b.Write([]byte{0, 0}) // length. Will be replaced later - - // initial_max_stream_data_bidi_local - utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiLocalParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiLocal)))) - utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiLocal)) - // initial_max_stream_data_bidi_remote - utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataBidiRemoteParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataBidiRemote)))) - utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataBidiRemote)) - // initial_max_stream_data_uni - utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamDataUniParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxStreamDataUni)))) - utils.WriteVarInt(b, uint64(p.InitialMaxStreamDataUni)) - // initial_max_data - utils.BigEndian.WriteUint16(b, uint16(initialMaxDataParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.InitialMaxData)))) - utils.WriteVarInt(b, uint64(p.InitialMaxData)) - // initial_max_bidi_streams - utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsBidiParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxBidiStreams))) - utils.WriteVarInt(b, p.MaxBidiStreams) - // initial_max_uni_streams - utils.BigEndian.WriteUint16(b, uint16(initialMaxStreamsUniParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(p.MaxUniStreams))) - utils.WriteVarInt(b, p.MaxUniStreams) - // idle_timeout - idleTimeout := uint64(p.IdleTimeout / time.Millisecond) - utils.BigEndian.WriteUint16(b, uint16(idleTimeoutParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(idleTimeout))) - utils.WriteVarInt(b, idleTimeout) - // max_packet_size - utils.BigEndian.WriteUint16(b, uint16(maxPacketSizeParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(protocol.MaxReceivePacketSize)))) - utils.WriteVarInt(b, uint64(protocol.MaxReceivePacketSize)) - // ack_delay_exponent - // Only send it if is different from the default value. - if p.AckDelayExponent != protocol.DefaultAckDelayExponent { - utils.BigEndian.WriteUint16(b, uint16(ackDelayExponentParameterID)) - utils.BigEndian.WriteUint16(b, uint16(utils.VarIntLen(uint64(p.AckDelayExponent)))) - utils.WriteVarInt(b, uint64(p.AckDelayExponent)) - } - // disable_migration - if p.DisableMigration { - utils.BigEndian.WriteUint16(b, uint16(disableMigrationParameterID)) - utils.BigEndian.WriteUint16(b, 0) - } - if p.StatelessResetToken != nil { - utils.BigEndian.WriteUint16(b, uint16(statelessResetTokenParameterID)) - utils.BigEndian.WriteUint16(b, 16) - b.Write(p.StatelessResetToken[:]) - } - // original_connection_id - if p.OriginalConnectionID.Len() > 0 { - utils.BigEndian.WriteUint16(b, uint16(originalConnectionIDParameterID)) - utils.BigEndian.WriteUint16(b, uint16(p.OriginalConnectionID.Len())) - b.Write(p.OriginalConnectionID.Bytes()) - } - - data := b.Bytes() - binary.BigEndian.PutUint16(data[:2], uint16(b.Len()-2)) - return data -} - -// String returns a string representation, intended for logging. -func (p *TransportParameters) String() string { - logString := "&handshake.TransportParameters{OriginalConnectionID: %s, InitialMaxStreamDataBidiLocal: %#x, InitialMaxStreamDataBidiRemote: %#x, InitialMaxStreamDataUni: %#x, InitialMaxData: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, IdleTimeout: %s, AckDelayExponent: %d" - logParams := []interface{}{p.OriginalConnectionID, p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreams, p.MaxUniStreams, p.IdleTimeout, p.AckDelayExponent} - if p.StatelessResetToken != nil { // the client never sends a stateless reset token - logString += ", StatelessResetToken: %#x" - logParams = append(logParams, *p.StatelessResetToken) - } - logString += "}" - return fmt.Sprintf(logString, logParams...) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/unsafe.go b/vendor/github.com/lucas-clemente/quic-go/internal/handshake/unsafe.go deleted file mode 100644 index fb051aebf..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/handshake/unsafe.go +++ /dev/null @@ -1,38 +0,0 @@ -package handshake - -// This package uses unsafe to convert between: -// * qtls.ConnectionState and tls.ConnectionState -// * qtls.ClientSessionState and tls.ClientSessionState -// We check in init() that this conversion actually is safe. - -import ( - "crypto/tls" - "reflect" - - "github.com/marten-seemann/qtls" -) - -func init() { - if !structsEqual(&tls.ConnectionState{}, &qtls.ConnectionState{}) { - panic("qtls.ConnectionState not compatible with tls.ConnectionState") - } - if !structsEqual(&tls.ClientSessionState{}, &qtls.ClientSessionState{}) { - panic("qtls.ClientSessionState not compatible with tls.ClientSessionState") - } -} - -func structsEqual(a, b interface{}) bool { - sa := reflect.ValueOf(a).Elem() - sb := reflect.ValueOf(b).Elem() - if sa.NumField() != sb.NumField() { - return false - } - for i := 0; i < sa.NumField(); i++ { - fa := sa.Type().Field(i) - fb := sb.Type().Field(i) - if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) { - return false - } - } - return true -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go deleted file mode 100644 index f99461b2c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/connection_id.go +++ /dev/null @@ -1,69 +0,0 @@ -package protocol - -import ( - "bytes" - "crypto/rand" - "fmt" - "io" -) - -// A ConnectionID in QUIC -type ConnectionID []byte - -const maxConnectionIDLen = 18 - -// GenerateConnectionID generates a connection ID using cryptographic random -func GenerateConnectionID(len int) (ConnectionID, error) { - b := make([]byte, len) - if _, err := rand.Read(b); err != nil { - return nil, err - } - return ConnectionID(b), nil -} - -// GenerateConnectionIDForInitial generates a connection ID for the Initial packet. -// It uses a length randomly chosen between 8 and 18 bytes. -func GenerateConnectionIDForInitial() (ConnectionID, error) { - r := make([]byte, 1) - if _, err := rand.Read(r); err != nil { - return nil, err - } - len := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1) - return GenerateConnectionID(len) -} - -// ReadConnectionID reads a connection ID of length len from the given io.Reader. -// It returns io.EOF if there are not enough bytes to read. -func ReadConnectionID(r io.Reader, len int) (ConnectionID, error) { - if len == 0 { - return nil, nil - } - c := make(ConnectionID, len) - _, err := io.ReadFull(r, c) - if err == io.ErrUnexpectedEOF { - return nil, io.EOF - } - return c, err -} - -// Equal says if two connection IDs are equal -func (c ConnectionID) Equal(other ConnectionID) bool { - return bytes.Equal(c, other) -} - -// Len returns the length of the connection ID in bytes -func (c ConnectionID) Len() int { - return len(c) -} - -// Bytes returns the byte representation -func (c ConnectionID) Bytes() []byte { - return []byte(c) -} - -func (c ConnectionID) String() string { - if c.Len() == 0 { - return "(empty)" - } - return fmt.Sprintf("%#x", c.Bytes()) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/encryption_level.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/encryption_level.go deleted file mode 100644 index 4b059b3a8..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/encryption_level.go +++ /dev/null @@ -1,28 +0,0 @@ -package protocol - -// EncryptionLevel is the encryption level -// Default value is Unencrypted -type EncryptionLevel int - -const ( - // EncryptionUnspecified is a not specified encryption level - EncryptionUnspecified EncryptionLevel = iota - // EncryptionInitial is the Initial encryption level - EncryptionInitial - // EncryptionHandshake is the Handshake encryption level - EncryptionHandshake - // Encryption1RTT is the 1-RTT encryption level - Encryption1RTT -) - -func (e EncryptionLevel) String() string { - switch e { - case EncryptionInitial: - return "Initial" - case EncryptionHandshake: - return "Handshake" - case Encryption1RTT: - return "1-RTT" - } - return "unknown" -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/packet_number.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/packet_number.go deleted file mode 100644 index 405a07ac7..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/packet_number.go +++ /dev/null @@ -1,85 +0,0 @@ -package protocol - -// PacketNumberLen is the length of the packet number in bytes -type PacketNumberLen uint8 - -const ( - // PacketNumberLenInvalid is the default value and not a valid length for a packet number - PacketNumberLenInvalid PacketNumberLen = 0 - // PacketNumberLen1 is a packet number length of 1 byte - PacketNumberLen1 PacketNumberLen = 1 - // PacketNumberLen2 is a packet number length of 2 bytes - PacketNumberLen2 PacketNumberLen = 2 - // PacketNumberLen3 is a packet number length of 3 bytes - PacketNumberLen3 PacketNumberLen = 3 - // PacketNumberLen4 is a packet number length of 4 bytes - PacketNumberLen4 PacketNumberLen = 4 -) - -// DecodePacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number -func DecodePacketNumber( - packetNumberLength PacketNumberLen, - lastPacketNumber PacketNumber, - wirePacketNumber PacketNumber, -) PacketNumber { - var epochDelta PacketNumber - switch packetNumberLength { - case PacketNumberLen1: - epochDelta = PacketNumber(1) << 8 - case PacketNumberLen2: - epochDelta = PacketNumber(1) << 16 - case PacketNumberLen3: - epochDelta = PacketNumber(1) << 24 - case PacketNumberLen4: - epochDelta = PacketNumber(1) << 32 - } - epoch := lastPacketNumber & ^(epochDelta - 1) - prevEpochBegin := epoch - epochDelta - nextEpochBegin := epoch + epochDelta - return closestTo( - lastPacketNumber+1, - epoch+wirePacketNumber, - closestTo(lastPacketNumber+1, prevEpochBegin+wirePacketNumber, nextEpochBegin+wirePacketNumber), - ) -} - -func closestTo(target, a, b PacketNumber) PacketNumber { - if delta(target, a) < delta(target, b) { - return a - } - return b -} - -func delta(a, b PacketNumber) PacketNumber { - if a < b { - return b - a - } - return a - b -} - -// GetPacketNumberLengthForHeader gets the length of the packet number for the public header -// it never chooses a PacketNumberLen of 1 byte, since this is too short under certain circumstances -func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber) PacketNumberLen { - diff := uint64(packetNumber - leastUnacked) - if diff < (1 << (16 - 1)) { - return PacketNumberLen2 - } - if diff < (1 << (24 - 1)) { - return PacketNumberLen3 - } - return PacketNumberLen4 -} - -// GetPacketNumberLength gets the minimum length needed to fully represent the packet number -func GetPacketNumberLength(packetNumber PacketNumber) PacketNumberLen { - if packetNumber < (1 << (uint8(PacketNumberLen1) * 8)) { - return PacketNumberLen1 - } - if packetNumber < (1 << (uint8(PacketNumberLen2) * 8)) { - return PacketNumberLen2 - } - if packetNumber < (1 << (uint8(PacketNumberLen3) * 8)) { - return PacketNumberLen3 - } - return PacketNumberLen4 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go deleted file mode 100644 index 88e0ae458..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/params.go +++ /dev/null @@ -1,126 +0,0 @@ -package protocol - -import "time" - -// MaxPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets. -const MaxPacketSizeIPv4 = 1252 - -// MaxPacketSizeIPv6 is the maximum packet size that we use for sending IPv6 packets. -const MaxPacketSizeIPv6 = 1232 - -const defaultMaxCongestionWindowPackets = 1000 - -// DefaultMaxCongestionWindow is the default for the max congestion window -const DefaultMaxCongestionWindow ByteCount = defaultMaxCongestionWindowPackets * DefaultTCPMSS - -// InitialCongestionWindow is the initial congestion window in QUIC packets -const InitialCongestionWindow ByteCount = 32 * DefaultTCPMSS - -// MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the session. -const MaxUndecryptablePackets = 10 - -// ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window -// This is the value that Chromium is using -const ConnectionFlowControlMultiplier = 1.5 - -// InitialMaxStreamData is the stream-level flow control window for receiving data -const InitialMaxStreamData = (1 << 10) * 512 // 512 kb - -// InitialMaxData is the connection-level flow control window for receiving data -const InitialMaxData = ConnectionFlowControlMultiplier * InitialMaxStreamData - -// DefaultMaxReceiveStreamFlowControlWindow is the default maximum stream-level flow control window for receiving data, for the server -const DefaultMaxReceiveStreamFlowControlWindow = 6 * (1 << 20) // 6 MB - -// DefaultMaxReceiveConnectionFlowControlWindow is the default connection-level flow control window for receiving data, for the server -const DefaultMaxReceiveConnectionFlowControlWindow = 15 * (1 << 20) // 12 MB - -// WindowUpdateThreshold is the fraction of the receive window that has to be consumed before an higher offset is advertised to the client -const WindowUpdateThreshold = 0.25 - -// DefaultMaxIncomingStreams is the maximum number of streams that a peer may open -const DefaultMaxIncomingStreams = 100 - -// DefaultMaxIncomingUniStreams is the maximum number of unidirectional streams that a peer may open -const DefaultMaxIncomingUniStreams = 100 - -// MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed. -const MaxSessionUnprocessedPackets = defaultMaxCongestionWindowPackets - -// SkipPacketAveragePeriodLength is the average period length in which one packet number is skipped to prevent an Optimistic ACK attack -const SkipPacketAveragePeriodLength PacketNumber = 500 - -// MaxTrackedSkippedPackets is the maximum number of skipped packet numbers the SentPacketHandler keep track of for Optimistic ACK attack mitigation -const MaxTrackedSkippedPackets = 10 - -// MaxAcceptQueueSize is the maximum number of sessions that the server queues for accepting. -// If the queue is full, new connection attempts will be rejected. -const MaxAcceptQueueSize = 32 - -// CookieExpiryTime is the valid time of a cookie -const CookieExpiryTime = 24 * time.Hour - -// MaxOutstandingSentPackets is maximum number of packets saved for retransmission. -// When reached, it imposes a soft limit on sending new packets: -// Sending ACKs and retransmission is still allowed, but now new regular packets can be sent. -const MaxOutstandingSentPackets = 2 * defaultMaxCongestionWindowPackets - -// MaxTrackedSentPackets is maximum number of sent packets saved for retransmission. -// When reached, no more packets will be sent. -// This value *must* be larger than MaxOutstandingSentPackets. -const MaxTrackedSentPackets = MaxOutstandingSentPackets * 5 / 4 - -// MaxTrackedReceivedAckRanges is the maximum number of ACK ranges tracked -const MaxTrackedReceivedAckRanges = defaultMaxCongestionWindowPackets - -// MaxNonRetransmittableAcks is the maximum number of packets containing an ACK, but no retransmittable frames, that we send in a row -const MaxNonRetransmittableAcks = 19 - -// MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames -// prevents DoS attacks against the streamFrameSorter -const MaxStreamFrameSorterGaps = 1000 - -// MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams. -// This limits the size of the ClientHello and Certificates that can be received. -const MaxCryptoStreamOffset = 16 * (1 << 10) - -// MinRemoteIdleTimeout is the minimum value that we accept for the remote idle timeout -const MinRemoteIdleTimeout = 5 * time.Second - -// DefaultIdleTimeout is the default idle timeout -const DefaultIdleTimeout = 30 * time.Second - -// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds. -const DefaultHandshakeTimeout = 10 * time.Second - -// RetiredConnectionIDDeleteTimeout is the time we keep closed sessions around in order to retransmit the CONNECTION_CLOSE. -// after this time all information about the old connection will be deleted -const RetiredConnectionIDDeleteTimeout = 5 * time.Second - -// MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame. -// This avoids splitting up STREAM frames into small pieces, which has 2 advantages: -// 1. it reduces the framing overhead -// 2. it reduces the head-of-line blocking, when a packet is lost -const MinStreamFrameSize ByteCount = 128 - -// MaxPostHandshakeCryptoFrameSize is the maximum size of CRYPTO frames -// we send after the handshake completes. -const MaxPostHandshakeCryptoFrameSize ByteCount = 1000 - -// MaxAckFrameSize is the maximum size for an ACK frame that we write -// Due to the varint encoding, ACK frames can grow (almost) indefinitely large. -// The MaxAckFrameSize should be large enough to encode many ACK range, -// but must ensure that a maximum size ACK frame fits into one packet. -const MaxAckFrameSize ByteCount = 1000 - -// MinPacingDelay is the minimum duration that is used for packet pacing -// If the packet packing frequency is higher, multiple packets might be sent at once. -// Example: For a packet pacing delay of 20 microseconds, we would send 5 packets at once, wait for 100 microseconds, and so forth. -const MinPacingDelay time.Duration = 100 * time.Microsecond - -// DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections -// if no other value is configured. -const DefaultConnectionIDLength = 4 - -// AckDelayExponent is the ack delay exponent used when sending ACKs. -const AckDelayExponent = 3 diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/perspective.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/perspective.go deleted file mode 100644 index 43358fecb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/perspective.go +++ /dev/null @@ -1,26 +0,0 @@ -package protocol - -// Perspective determines if we're acting as a server or a client -type Perspective int - -// the perspectives -const ( - PerspectiveServer Perspective = 1 - PerspectiveClient Perspective = 2 -) - -// Opposite returns the perspective of the peer -func (p Perspective) Opposite() Perspective { - return 3 - p -} - -func (p Perspective) String() string { - switch p { - case PerspectiveServer: - return "Server" - case PerspectiveClient: - return "Client" - default: - return "invalid perspective" - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/protocol.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/protocol.go deleted file mode 100644 index 6e59afcb4..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/protocol.go +++ /dev/null @@ -1,75 +0,0 @@ -package protocol - -import ( - "fmt" -) - -// A PacketNumber in QUIC -type PacketNumber uint64 - -// The PacketType is the Long Header Type -type PacketType uint8 - -const ( - // PacketTypeInitial is the packet type of an Initial packet - PacketTypeInitial PacketType = 1 + iota - // PacketTypeRetry is the packet type of a Retry packet - PacketTypeRetry - // PacketTypeHandshake is the packet type of a Handshake packet - PacketTypeHandshake - // PacketType0RTT is the packet type of a 0-RTT packet - PacketType0RTT -) - -func (t PacketType) String() string { - switch t { - case PacketTypeInitial: - return "Initial" - case PacketTypeRetry: - return "Retry" - case PacketTypeHandshake: - return "Handshake" - case PacketType0RTT: - return "0-RTT Protected" - default: - return fmt.Sprintf("unknown packet type: %d", t) - } -} - -// A ByteCount in QUIC -type ByteCount uint64 - -// MaxByteCount is the maximum value of a ByteCount -const MaxByteCount = ByteCount(1<<62 - 1) - -// An ApplicationErrorCode is an application-defined error code. -type ApplicationErrorCode uint16 - -// MaxReceivePacketSize maximum packet size of any QUIC packet, based on -// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header, -// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes. -// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452. -const MaxReceivePacketSize ByteCount = 1452 - -// DefaultTCPMSS is the default maximum packet size used in the Linux TCP implementation. -// Used in QUIC for congestion window computations in bytes. -const DefaultTCPMSS ByteCount = 1460 - -// MinInitialPacketSize is the minimum size an Initial packet is required to have. -const MinInitialPacketSize = 1200 - -// MinStatelessResetSize is the minimum size of a stateless reset packet -const MinStatelessResetSize = 1 /* first byte */ + 22 /* random bytes */ + 16 /* token */ - -// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet. -const MinConnectionIDLenInitial = 8 - -// MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames -// and as the stream count in the transport parameters -const MaxStreamCount = 1 << 60 - -// DefaultAckDelayExponent is the default ack delay exponent -const DefaultAckDelayExponent = 3 - -// MaxAckDelayExponent is the maximum ack delay exponent -const MaxAckDelayExponent = 20 diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/stream_id.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/stream_id.go deleted file mode 100644 index b96e0c2bc..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/stream_id.go +++ /dev/null @@ -1,67 +0,0 @@ -package protocol - -// A StreamID in QUIC -type StreamID uint64 - -// StreamType encodes if this is a unidirectional or bidirectional stream -type StreamType uint8 - -const ( - // StreamTypeUni is a unidirectional stream - StreamTypeUni StreamType = iota - // StreamTypeBidi is a bidirectional stream - StreamTypeBidi -) - -// InitiatedBy says if the stream was initiated by the client or by the server -func (s StreamID) InitiatedBy() Perspective { - if s%2 == 0 { - return PerspectiveClient - } - return PerspectiveServer -} - -//Type says if this is a unidirectional or bidirectional stream -func (s StreamID) Type() StreamType { - if s%4 >= 2 { - return StreamTypeUni - } - return StreamTypeBidi -} - -// StreamNum returns how many streams in total are below this -// Example: for stream 9 it returns 3 (i.e. streams 1, 5 and 9) -func (s StreamID) StreamNum() uint64 { - return uint64(s/4) + 1 -} - -// MaxStreamID is the highest stream ID that a peer is allowed to open, -// when it is allowed to open numStreams. -func MaxStreamID(stype StreamType, numStreams uint64, pers Perspective) StreamID { - if numStreams == 0 { - return 0 - } - var first StreamID - switch stype { - case StreamTypeBidi: - switch pers { - case PerspectiveClient: - first = 0 - case PerspectiveServer: - first = 1 - } - case StreamTypeUni: - switch pers { - case PerspectiveClient: - first = 2 - case PerspectiveServer: - first = 3 - } - } - return first + 4*StreamID(numStreams-1) -} - -// FirstStream returns the first valid stream ID -func FirstStream(stype StreamType, pers Perspective) StreamID { - return MaxStreamID(stype, 1, pers) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go b/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go deleted file mode 100644 index 6e7d6d953..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/protocol/version.go +++ /dev/null @@ -1,119 +0,0 @@ -package protocol - -import ( - "crypto/rand" - "encoding/binary" - "fmt" - "math" -) - -// VersionNumber is a version number as int -type VersionNumber uint32 - -// gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions -const ( - gquicVersion0 = 0x51303030 - maxGquicVersion = 0x51303439 -) - -// The version numbers, making grepping easier -const ( - VersionTLS VersionNumber = VersionMilestone0_11_1 - VersionWhatever VersionNumber = 1 // for when the version doesn't matter - VersionUnknown VersionNumber = math.MaxUint32 - - VersionMilestone0_11_1 VersionNumber = 0xff000013 // QUIC WG draft-19 -) - -// SupportedVersions lists the versions that the server supports -// must be in sorted descending order -var SupportedVersions = []VersionNumber{VersionMilestone0_11_1} - -// IsValidVersion says if the version is known to quic-go -func IsValidVersion(v VersionNumber) bool { - return v == VersionTLS || IsSupportedVersion(SupportedVersions, v) -} - -func (vn VersionNumber) String() string { - switch vn { - case VersionWhatever: - return "whatever" - case VersionUnknown: - return "unknown" - case VersionMilestone0_11_1: - return "QUIC WG draft-19" - default: - if vn.isGQUIC() { - return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) - } - return fmt.Sprintf("%#x", uint32(vn)) - } -} - -// ToAltSvc returns the representation of the version for the H2 Alt-Svc parameters -func (vn VersionNumber) ToAltSvc() string { - return fmt.Sprintf("%d", vn) -} - -func (vn VersionNumber) isGQUIC() bool { - return vn > gquicVersion0 && vn <= maxGquicVersion -} - -func (vn VersionNumber) toGQUICVersion() int { - return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10) -} - -// IsSupportedVersion returns true if the server supports this version -func IsSupportedVersion(supported []VersionNumber, v VersionNumber) bool { - for _, t := range supported { - if t == v { - return true - } - } - return false -} - -// ChooseSupportedVersion finds the best version in the overlap of ours and theirs -// ours is a slice of versions that we support, sorted by our preference (descending) -// theirs is a slice of versions offered by the peer. The order does not matter. -// The bool returned indicates if a matching version was found. -func ChooseSupportedVersion(ours, theirs []VersionNumber) (VersionNumber, bool) { - for _, ourVer := range ours { - for _, theirVer := range theirs { - if ourVer == theirVer { - return ourVer, true - } - } - } - return 0, false -} - -// generateReservedVersion generates a reserved version number (v & 0x0f0f0f0f == 0x0a0a0a0a) -func generateReservedVersion() VersionNumber { - b := make([]byte, 4) - _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything - return VersionNumber((binary.BigEndian.Uint32(b) | 0x0a0a0a0a) & 0xfafafafa) -} - -// GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position -func GetGreasedVersions(supported []VersionNumber) []VersionNumber { - b := make([]byte, 1) - _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything - randPos := int(b[0]) % (len(supported) + 1) - greased := make([]VersionNumber, len(supported)+1) - copy(greased, supported[:randPos]) - greased[randPos] = generateReservedVersion() - copy(greased[randPos+1:], supported[randPos:]) - return greased -} - -// StripGreasedVersions strips all greased versions from a slice of versions -func StripGreasedVersions(versions []VersionNumber) []VersionNumber { - realVersions := make([]VersionNumber, 0, len(versions)) - for _, v := range versions { - if v&0x0f0f0f0f != 0x0a0a0a0a { - realVersions = append(realVersions, v) - } - } - return realVersions -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qerr/error_codes.go b/vendor/github.com/lucas-clemente/quic-go/internal/qerr/error_codes.go deleted file mode 100644 index 091de1ca3..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/qerr/error_codes.go +++ /dev/null @@ -1,71 +0,0 @@ -package qerr - -import ( - "fmt" - - "github.com/marten-seemann/qtls" -) - -// ErrorCode can be used as a normal error without reason. -type ErrorCode uint16 - -// The error codes defined by QUIC -const ( - NoError ErrorCode = 0x0 - InternalError ErrorCode = 0x1 - ServerBusy ErrorCode = 0x2 - FlowControlError ErrorCode = 0x3 - StreamLimitError ErrorCode = 0x4 - StreamStateError ErrorCode = 0x5 - FinalSizeError ErrorCode = 0x6 - FrameEncodingError ErrorCode = 0x7 - TransportParameterError ErrorCode = 0x8 - VersionNegotiationError ErrorCode = 0x9 - ProtocolViolation ErrorCode = 0xa - InvalidMigration ErrorCode = 0xc -) - -func (e ErrorCode) isCryptoError() bool { - return e >= 0x100 && e < 0x200 -} - -func (e ErrorCode) Error() string { - if e.isCryptoError() { - return fmt.Sprintf("%s: %s", e.String(), qtls.Alert(e-0x100).Error()) - } - return e.String() -} - -func (e ErrorCode) String() string { - switch e { - case NoError: - return "NO_ERROR" - case InternalError: - return "INTERNAL_ERROR" - case ServerBusy: - return "SERVER_BUSY" - case FlowControlError: - return "FLOW_CONTROL_ERROR" - case StreamLimitError: - return "STREAM_LIMIT_ERROR" - case StreamStateError: - return "STREAM_STATE_ERROR" - case FinalSizeError: - return "FINAL_SIZE_ERROR" - case FrameEncodingError: - return "FRAME_ENCODING_ERROR" - case TransportParameterError: - return "TRANSPORT_PARAMETER_ERROR" - case VersionNegotiationError: - return "VERSION_NEGOTIATION_ERROR" - case ProtocolViolation: - return "PROTOCOL_VIOLATION" - case InvalidMigration: - return "INVALID_MIGRATION" - default: - if e.isCryptoError() { - return "CRYPTO_ERROR" - } - return fmt.Sprintf("unknown error code: %#x", uint16(e)) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/qerr/quic_error.go b/vendor/github.com/lucas-clemente/quic-go/internal/qerr/quic_error.go deleted file mode 100644 index 3a1718b02..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/qerr/quic_error.go +++ /dev/null @@ -1,73 +0,0 @@ -package qerr - -import ( - "fmt" - "net" -) - -// A QuicError consists of an error code plus a error reason -type QuicError struct { - ErrorCode ErrorCode - ErrorMessage string - isTimeout bool -} - -var _ net.Error = &QuicError{} - -// Error creates a new QuicError instance -func Error(errorCode ErrorCode, errorMessage string) *QuicError { - return &QuicError{ - ErrorCode: errorCode, - ErrorMessage: errorMessage, - } -} - -// TimeoutError creates a new QuicError instance for a timeout error -func TimeoutError(errorMessage string) *QuicError { - return &QuicError{ - ErrorMessage: errorMessage, - isTimeout: true, - } -} - -// CryptoError create a new QuicError instance for a crypto error -func CryptoError(tlsAlert uint8, errorMessage string) *QuicError { - return &QuicError{ - ErrorCode: 0x100 + ErrorCode(tlsAlert), - ErrorMessage: errorMessage, - } -} - -func (e *QuicError) Error() string { - if len(e.ErrorMessage) == 0 { - return e.ErrorCode.Error() - } - return fmt.Sprintf("%s: %s", e.ErrorCode.String(), e.ErrorMessage) -} - -// IsCryptoError says if this error is a crypto error -func (e *QuicError) IsCryptoError() bool { - return e.ErrorCode.isCryptoError() -} - -// Temporary says if the error is temporary. -func (e *QuicError) Temporary() bool { - return false -} - -// Timeout says if this error is a timeout. -func (e *QuicError) Timeout() bool { - return e.isTimeout -} - -// ToQuicError converts an arbitrary error to a QuicError. It leaves QuicErrors -// unchanged, and properly handles `ErrorCode`s. -func ToQuicError(err error) *QuicError { - switch e := err.(type) { - case *QuicError: - return e - case ErrorCode: - return Error(e, "") - } - return Error(InternalError, err.Error()) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/atomic_bool.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/atomic_bool.go deleted file mode 100644 index cf4642504..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/atomic_bool.go +++ /dev/null @@ -1,22 +0,0 @@ -package utils - -import "sync/atomic" - -// An AtomicBool is an atomic bool -type AtomicBool struct { - v int32 -} - -// Set sets the value -func (a *AtomicBool) Set(value bool) { - var n int32 - if value { - n = 1 - } - atomic.StoreInt32(&a.v, n) -} - -// Get gets the value -func (a *AtomicBool) Get() bool { - return atomic.LoadInt32(&a.v) != 0 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteinterval_linkedlist.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteinterval_linkedlist.go deleted file mode 100644 index 096023ef2..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteinterval_linkedlist.go +++ /dev/null @@ -1,217 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package utils - -// Linked list implementation from the Go standard library. - -// ByteIntervalElement is an element of a linked list. -type ByteIntervalElement struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *ByteIntervalElement - - // The list to which this element belongs. - list *ByteIntervalList - - // The value stored with this element. - Value ByteInterval -} - -// Next returns the next list element or nil. -func (e *ByteIntervalElement) Next() *ByteIntervalElement { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// Prev returns the previous list element or nil. -func (e *ByteIntervalElement) Prev() *ByteIntervalElement { - if p := e.prev; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// ByteIntervalList is a linked list of ByteIntervals. -type ByteIntervalList struct { - root ByteIntervalElement // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element -} - -// Init initializes or clears list l. -func (l *ByteIntervalList) Init() *ByteIntervalList { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// NewByteIntervalList returns an initialized list. -func NewByteIntervalList() *ByteIntervalList { return new(ByteIntervalList).Init() } - -// Len returns the number of elements of list l. -// The complexity is O(1). -func (l *ByteIntervalList) Len() int { return l.len } - -// Front returns the first element of list l or nil if the list is empty. -func (l *ByteIntervalList) Front() *ByteIntervalElement { - if l.len == 0 { - return nil - } - return l.root.next -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *ByteIntervalList) Back() *ByteIntervalElement { - if l.len == 0 { - return nil - } - return l.root.prev -} - -// lazyInit lazily initializes a zero List value. -func (l *ByteIntervalList) lazyInit() { - if l.root.next == nil { - l.Init() - } -} - -// insert inserts e after at, increments l.len, and returns e. -func (l *ByteIntervalList) insert(e, at *ByteIntervalElement) *ByteIntervalElement { - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - e.list = l - l.len++ - return e -} - -// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). -func (l *ByteIntervalList) insertValue(v ByteInterval, at *ByteIntervalElement) *ByteIntervalElement { - return l.insert(&ByteIntervalElement{Value: v}, at) -} - -// remove removes e from its list, decrements l.len, and returns e. -func (l *ByteIntervalList) remove(e *ByteIntervalElement) *ByteIntervalElement { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - e.list = nil - l.len-- - return e -} - -// Remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *ByteIntervalList) Remove(e *ByteIntervalElement) ByteInterval { - if e.list == l { - // if e.list == l, l must have been initialized when e was inserted - // in l or l == nil (e is a zero Element) and l.remove will crash - l.remove(e) - } - return e.Value -} - -// PushFront inserts a new element e with value v at the front of list l and returns e. -func (l *ByteIntervalList) PushFront(v ByteInterval) *ByteIntervalElement { - l.lazyInit() - return l.insertValue(v, &l.root) -} - -// PushBack inserts a new element e with value v at the back of list l and returns e. -func (l *ByteIntervalList) PushBack(v ByteInterval) *ByteIntervalElement { - l.lazyInit() - return l.insertValue(v, l.root.prev) -} - -// InsertBefore inserts a new element e with value v immediately before mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *ByteIntervalList) InsertBefore(v ByteInterval, mark *ByteIntervalElement) *ByteIntervalElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark.prev) -} - -// InsertAfter inserts a new element e with value v immediately after mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *ByteIntervalList) InsertAfter(v ByteInterval, mark *ByteIntervalElement) *ByteIntervalElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark) -} - -// MoveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *ByteIntervalList) MoveToFront(e *ByteIntervalElement) { - if e.list != l || l.root.next == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), &l.root) -} - -// MoveToBack moves element e to the back of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *ByteIntervalList) MoveToBack(e *ByteIntervalElement) { - if e.list != l || l.root.prev == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), l.root.prev) -} - -// MoveBefore moves element e to its new position before mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *ByteIntervalList) MoveBefore(e, mark *ByteIntervalElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark.prev) -} - -// MoveAfter moves element e to its new position after mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *ByteIntervalList) MoveAfter(e, mark *ByteIntervalElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark) -} - -// PushBackList inserts a copy of an other list at the back of list l. -// The lists l and other may be the same. They must not be nil. -func (l *ByteIntervalList) PushBackList(other *ByteIntervalList) { - l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { - l.insertValue(e.Value, l.root.prev) - } -} - -// PushFrontList inserts a copy of an other list at the front of list l. -// The lists l and other may be the same. They must not be nil. -func (l *ByteIntervalList) PushFrontList(other *ByteIntervalList) { - l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { - l.insertValue(e.Value, &l.root) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder.go deleted file mode 100644 index 6b92cfa26..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder.go +++ /dev/null @@ -1,17 +0,0 @@ -package utils - -import ( - "bytes" - "io" -) - -// A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers. -type ByteOrder interface { - ReadUintN(b io.ByteReader, length uint8) (uint64, error) - ReadUint32(io.ByteReader) (uint32, error) - ReadUint16(io.ByteReader) (uint16, error) - - WriteUintN(b *bytes.Buffer, length uint8, value uint64) - WriteUint32(*bytes.Buffer, uint32) - WriteUint16(*bytes.Buffer, uint16) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder_big_endian.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder_big_endian.go deleted file mode 100644 index eede9cd72..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/byteorder_big_endian.go +++ /dev/null @@ -1,74 +0,0 @@ -package utils - -import ( - "bytes" - "io" -) - -// BigEndian is the big-endian implementation of ByteOrder. -var BigEndian ByteOrder = bigEndian{} - -type bigEndian struct{} - -var _ ByteOrder = &bigEndian{} - -// ReadUintN reads N bytes -func (bigEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) { - var res uint64 - for i := uint8(0); i < length; i++ { - bt, err := b.ReadByte() - if err != nil { - return 0, err - } - res ^= uint64(bt) << ((length - 1 - i) * 8) - } - return res, nil -} - -// ReadUint32 reads a uint32 -func (bigEndian) ReadUint32(b io.ByteReader) (uint32, error) { - var b1, b2, b3, b4 uint8 - var err error - if b4, err = b.ReadByte(); err != nil { - return 0, err - } - if b3, err = b.ReadByte(); err != nil { - return 0, err - } - if b2, err = b.ReadByte(); err != nil { - return 0, err - } - if b1, err = b.ReadByte(); err != nil { - return 0, err - } - return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16 + uint32(b4)<<24, nil -} - -// ReadUint16 reads a uint16 -func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) { - var b1, b2 uint8 - var err error - if b2, err = b.ReadByte(); err != nil { - return 0, err - } - if b1, err = b.ReadByte(); err != nil { - return 0, err - } - return uint16(b1) + uint16(b2)<<8, nil -} - -func (bigEndian) WriteUintN(b *bytes.Buffer, length uint8, i uint64) { - for j := length; j > 0; j-- { - b.WriteByte(uint8(i >> (8 * (j - 1)))) - } -} - -// WriteUint32 writes a uint32 -func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) { - b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)}) -} - -// WriteUint16 writes a uint16 -func (bigEndian) WriteUint16(b *bytes.Buffer, i uint16) { - b.Write([]byte{uint8(i >> 8), uint8(i)}) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/gen.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/gen.go deleted file mode 100644 index bb839be66..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/gen.go +++ /dev/null @@ -1,4 +0,0 @@ -package utils - -//go:generate genny -pkg utils -in linkedlist/linkedlist.go -out byteinterval_linkedlist.go gen Item=ByteInterval -//go:generate genny -pkg utils -in linkedlist/linkedlist.go -out packetinterval_linkedlist.go gen Item=PacketInterval diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/host.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/host.go deleted file mode 100644 index a1d6453b0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/host.go +++ /dev/null @@ -1,27 +0,0 @@ -package utils - -import ( - "net/url" - "strings" -) - -// HostnameFromAddr determines the hostname in an address string -func HostnameFromAddr(addr string) (string, error) { - p, err := url.Parse(addr) - if err != nil { - return "", err - } - h := p.Host - - // copied from https://golang.org/src/net/http/transport.go - if hasPort(h) { - h = h[:strings.LastIndex(h, ":")] - } - - return h, nil -} - -// copied from https://golang.org/src/net/http/http.go -func hasPort(s string) bool { - return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/log.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/log.go deleted file mode 100644 index e27f01b4a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/log.go +++ /dev/null @@ -1,131 +0,0 @@ -package utils - -import ( - "fmt" - "log" - "os" - "strings" - "time" -) - -// LogLevel of quic-go -type LogLevel uint8 - -const ( - // LogLevelNothing disables - LogLevelNothing LogLevel = iota - // LogLevelError enables err logs - LogLevelError - // LogLevelInfo enables info logs (e.g. packets) - LogLevelInfo - // LogLevelDebug enables debug logs (e.g. packet contents) - LogLevelDebug -) - -const logEnv = "QUIC_GO_LOG_LEVEL" - -// A Logger logs. -type Logger interface { - SetLogLevel(LogLevel) - SetLogTimeFormat(format string) - WithPrefix(prefix string) Logger - Debug() bool - - Errorf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Debugf(format string, args ...interface{}) -} - -// DefaultLogger is used by quic-go for logging. -var DefaultLogger Logger - -type defaultLogger struct { - prefix string - - logLevel LogLevel - timeFormat string -} - -var _ Logger = &defaultLogger{} - -// SetLogLevel sets the log level -func (l *defaultLogger) SetLogLevel(level LogLevel) { - l.logLevel = level -} - -// SetLogTimeFormat sets the format of the timestamp -// an empty string disables the logging of timestamps -func (l *defaultLogger) SetLogTimeFormat(format string) { - log.SetFlags(0) // disable timestamp logging done by the log package - l.timeFormat = format -} - -// Debugf logs something -func (l *defaultLogger) Debugf(format string, args ...interface{}) { - if l.logLevel == LogLevelDebug { - l.logMessage(format, args...) - } -} - -// Infof logs something -func (l *defaultLogger) Infof(format string, args ...interface{}) { - if l.logLevel >= LogLevelInfo { - l.logMessage(format, args...) - } -} - -// Errorf logs something -func (l *defaultLogger) Errorf(format string, args ...interface{}) { - if l.logLevel >= LogLevelError { - l.logMessage(format, args...) - } -} - -func (l *defaultLogger) logMessage(format string, args ...interface{}) { - var pre string - - if len(l.timeFormat) > 0 { - pre = time.Now().Format(l.timeFormat) + " " - } - if len(l.prefix) > 0 { - pre += l.prefix + " " - } - log.Printf(pre+format, args...) -} - -func (l *defaultLogger) WithPrefix(prefix string) Logger { - if len(l.prefix) > 0 { - prefix = l.prefix + " " + prefix - } - return &defaultLogger{ - logLevel: l.logLevel, - timeFormat: l.timeFormat, - prefix: prefix, - } -} - -// Debug returns true if the log level is LogLevelDebug -func (l *defaultLogger) Debug() bool { - return l.logLevel == LogLevelDebug -} - -func init() { - DefaultLogger = &defaultLogger{} - DefaultLogger.SetLogLevel(readLoggingEnv()) -} - -func readLoggingEnv() LogLevel { - switch strings.ToLower(os.Getenv(logEnv)) { - case "": - return LogLevelNothing - case "debug": - return LogLevelDebug - case "info": - return LogLevelInfo - case "error": - return LogLevelError - default: - fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/lucas-clemente/quic-go/wiki/Logging") - return LogLevelNothing - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/minmax.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/minmax.go deleted file mode 100644 index 84cbec7b9..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/minmax.go +++ /dev/null @@ -1,159 +0,0 @@ -package utils - -import ( - "math" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// InfDuration is a duration of infinite length -const InfDuration = time.Duration(math.MaxInt64) - -// Max returns the maximum of two Ints -func Max(a, b int) int { - if a < b { - return b - } - return a -} - -// MaxUint32 returns the maximum of two uint32 -func MaxUint32(a, b uint32) uint32 { - if a < b { - return b - } - return a -} - -// MaxUint64 returns the maximum of two uint64 -func MaxUint64(a, b uint64) uint64 { - if a < b { - return b - } - return a -} - -// MinUint64 returns the maximum of two uint64 -func MinUint64(a, b uint64) uint64 { - if a < b { - return a - } - return b -} - -// Min returns the minimum of two Ints -func Min(a, b int) int { - if a < b { - return a - } - return b -} - -// MinUint32 returns the maximum of two uint32 -func MinUint32(a, b uint32) uint32 { - if a < b { - return a - } - return b -} - -// MinInt64 returns the minimum of two int64 -func MinInt64(a, b int64) int64 { - if a < b { - return a - } - return b -} - -// MaxInt64 returns the minimum of two int64 -func MaxInt64(a, b int64) int64 { - if a > b { - return a - } - return b -} - -// MinByteCount returns the minimum of two ByteCounts -func MinByteCount(a, b protocol.ByteCount) protocol.ByteCount { - if a < b { - return a - } - return b -} - -// MaxByteCount returns the maximum of two ByteCounts -func MaxByteCount(a, b protocol.ByteCount) protocol.ByteCount { - if a < b { - return b - } - return a -} - -// MaxDuration returns the max duration -func MaxDuration(a, b time.Duration) time.Duration { - if a > b { - return a - } - return b -} - -// MinDuration returns the minimum duration -func MinDuration(a, b time.Duration) time.Duration { - if a > b { - return b - } - return a -} - -// AbsDuration returns the absolute value of a time duration -func AbsDuration(d time.Duration) time.Duration { - if d >= 0 { - return d - } - return -d -} - -// MinTime returns the earlier time -func MinTime(a, b time.Time) time.Time { - if a.After(b) { - return b - } - return a -} - -// MinNonZeroTime returns the earlist time that is not time.Time{} -// If both a and b are time.Time{}, it returns time.Time{} -func MinNonZeroTime(a, b time.Time) time.Time { - if a.IsZero() { - return b - } - if b.IsZero() { - return a - } - return MinTime(a, b) -} - -// MaxTime returns the later time -func MaxTime(a, b time.Time) time.Time { - if a.After(b) { - return a - } - return b -} - -// MaxPacketNumber returns the max packet number -func MaxPacketNumber(a, b protocol.PacketNumber) protocol.PacketNumber { - if a > b { - return a - } - return b -} - -// MinPacketNumber returns the min packet number -func MinPacketNumber(a, b protocol.PacketNumber) protocol.PacketNumber { - if a < b { - return a - } - return b -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/packet_interval.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/packet_interval.go deleted file mode 100644 index 62cc8b9cb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/packet_interval.go +++ /dev/null @@ -1,9 +0,0 @@ -package utils - -import "github.com/lucas-clemente/quic-go/internal/protocol" - -// PacketInterval is an interval from one PacketNumber to the other -type PacketInterval struct { - Start protocol.PacketNumber - End protocol.PacketNumber -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/packetinterval_linkedlist.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/packetinterval_linkedlist.go deleted file mode 100644 index b461e85a9..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/packetinterval_linkedlist.go +++ /dev/null @@ -1,217 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package utils - -// Linked list implementation from the Go standard library. - -// PacketIntervalElement is an element of a linked list. -type PacketIntervalElement struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *PacketIntervalElement - - // The list to which this element belongs. - list *PacketIntervalList - - // The value stored with this element. - Value PacketInterval -} - -// Next returns the next list element or nil. -func (e *PacketIntervalElement) Next() *PacketIntervalElement { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// Prev returns the previous list element or nil. -func (e *PacketIntervalElement) Prev() *PacketIntervalElement { - if p := e.prev; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// PacketIntervalList is a linked list of PacketIntervals. -type PacketIntervalList struct { - root PacketIntervalElement // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element -} - -// Init initializes or clears list l. -func (l *PacketIntervalList) Init() *PacketIntervalList { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// NewPacketIntervalList returns an initialized list. -func NewPacketIntervalList() *PacketIntervalList { return new(PacketIntervalList).Init() } - -// Len returns the number of elements of list l. -// The complexity is O(1). -func (l *PacketIntervalList) Len() int { return l.len } - -// Front returns the first element of list l or nil if the list is empty. -func (l *PacketIntervalList) Front() *PacketIntervalElement { - if l.len == 0 { - return nil - } - return l.root.next -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *PacketIntervalList) Back() *PacketIntervalElement { - if l.len == 0 { - return nil - } - return l.root.prev -} - -// lazyInit lazily initializes a zero List value. -func (l *PacketIntervalList) lazyInit() { - if l.root.next == nil { - l.Init() - } -} - -// insert inserts e after at, increments l.len, and returns e. -func (l *PacketIntervalList) insert(e, at *PacketIntervalElement) *PacketIntervalElement { - n := at.next - at.next = e - e.prev = at - e.next = n - n.prev = e - e.list = l - l.len++ - return e -} - -// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). -func (l *PacketIntervalList) insertValue(v PacketInterval, at *PacketIntervalElement) *PacketIntervalElement { - return l.insert(&PacketIntervalElement{Value: v}, at) -} - -// remove removes e from its list, decrements l.len, and returns e. -func (l *PacketIntervalList) remove(e *PacketIntervalElement) *PacketIntervalElement { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - e.list = nil - l.len-- - return e -} - -// Remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *PacketIntervalList) Remove(e *PacketIntervalElement) PacketInterval { - if e.list == l { - // if e.list == l, l must have been initialized when e was inserted - // in l or l == nil (e is a zero Element) and l.remove will crash - l.remove(e) - } - return e.Value -} - -// PushFront inserts a new element e with value v at the front of list l and returns e. -func (l *PacketIntervalList) PushFront(v PacketInterval) *PacketIntervalElement { - l.lazyInit() - return l.insertValue(v, &l.root) -} - -// PushBack inserts a new element e with value v at the back of list l and returns e. -func (l *PacketIntervalList) PushBack(v PacketInterval) *PacketIntervalElement { - l.lazyInit() - return l.insertValue(v, l.root.prev) -} - -// InsertBefore inserts a new element e with value v immediately before mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *PacketIntervalList) InsertBefore(v PacketInterval, mark *PacketIntervalElement) *PacketIntervalElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark.prev) -} - -// InsertAfter inserts a new element e with value v immediately after mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *PacketIntervalList) InsertAfter(v PacketInterval, mark *PacketIntervalElement) *PacketIntervalElement { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark) -} - -// MoveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *PacketIntervalList) MoveToFront(e *PacketIntervalElement) { - if e.list != l || l.root.next == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), &l.root) -} - -// MoveToBack moves element e to the back of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *PacketIntervalList) MoveToBack(e *PacketIntervalElement) { - if e.list != l || l.root.prev == e { - return - } - // see comment in List.Remove about initialization of l - l.insert(l.remove(e), l.root.prev) -} - -// MoveBefore moves element e to its new position before mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *PacketIntervalList) MoveBefore(e, mark *PacketIntervalElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark.prev) -} - -// MoveAfter moves element e to its new position after mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *PacketIntervalList) MoveAfter(e, mark *PacketIntervalElement) { - if e.list != l || e == mark || mark.list != l { - return - } - l.insert(l.remove(e), mark) -} - -// PushBackList inserts a copy of an other list at the back of list l. -// The lists l and other may be the same. They must not be nil. -func (l *PacketIntervalList) PushBackList(other *PacketIntervalList) { - l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { - l.insertValue(e.Value, l.root.prev) - } -} - -// PushFrontList inserts a copy of an other list at the front of list l. -// The lists l and other may be the same. They must not be nil. -func (l *PacketIntervalList) PushFrontList(other *PacketIntervalList) { - l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { - l.insertValue(e.Value, &l.root) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/streamframe_interval.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/streamframe_interval.go deleted file mode 100644 index ec16d251b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/streamframe_interval.go +++ /dev/null @@ -1,9 +0,0 @@ -package utils - -import "github.com/lucas-clemente/quic-go/internal/protocol" - -// ByteInterval is an interval from one ByteCount to the other -type ByteInterval struct { - Start protocol.ByteCount - End protocol.ByteCount -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/timer.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/timer.go deleted file mode 100644 index 1fefc6ec8..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/timer.go +++ /dev/null @@ -1,48 +0,0 @@ -package utils - -import ( - "math" - "time" -) - -// A Timer wrapper that behaves correctly when resetting -type Timer struct { - t *time.Timer - read bool - deadline time.Time -} - -// NewTimer creates a new timer that is not set -func NewTimer() *Timer { - return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))} -} - -// Chan returns the channel of the wrapped timer -func (t *Timer) Chan() <-chan time.Time { - return t.t.C -} - -// Reset the timer, no matter whether the value was read or not -func (t *Timer) Reset(deadline time.Time) { - if deadline.Equal(t.deadline) && !t.read { - // No need to reset the timer - return - } - - // We need to drain the timer if the value from its channel was not read yet. - // See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU - if !t.t.Stop() && !t.read { - <-t.t.C - } - if !deadline.IsZero() { - t.t.Reset(time.Until(deadline)) - } - - t.read = false - t.deadline = deadline -} - -// SetRead should be called after the value from the chan was read -func (t *Timer) SetRead() { - t.read = true -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/utils/varint.go b/vendor/github.com/lucas-clemente/quic-go/internal/utils/varint.go deleted file mode 100644 index 35e8674e2..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/utils/varint.go +++ /dev/null @@ -1,101 +0,0 @@ -package utils - -import ( - "bytes" - "fmt" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// taken from the QUIC draft -const ( - maxVarInt1 = 63 - maxVarInt2 = 16383 - maxVarInt4 = 1073741823 - maxVarInt8 = 4611686018427387903 -) - -// ReadVarInt reads a number in the QUIC varint format -func ReadVarInt(b io.ByteReader) (uint64, error) { - firstByte, err := b.ReadByte() - if err != nil { - return 0, err - } - // the first two bits of the first byte encode the length - len := 1 << ((firstByte & 0xc0) >> 6) - b1 := firstByte & (0xff - 0xc0) - if len == 1 { - return uint64(b1), nil - } - b2, err := b.ReadByte() - if err != nil { - return 0, err - } - if len == 2 { - return uint64(b2) + uint64(b1)<<8, nil - } - b3, err := b.ReadByte() - if err != nil { - return 0, err - } - b4, err := b.ReadByte() - if err != nil { - return 0, err - } - if len == 4 { - return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil - } - b5, err := b.ReadByte() - if err != nil { - return 0, err - } - b6, err := b.ReadByte() - if err != nil { - return 0, err - } - b7, err := b.ReadByte() - if err != nil { - return 0, err - } - b8, err := b.ReadByte() - if err != nil { - return 0, err - } - return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil -} - -// WriteVarInt writes a number in the QUIC varint format -func WriteVarInt(b *bytes.Buffer, i uint64) { - if i <= maxVarInt1 { - b.WriteByte(uint8(i)) - } else if i <= maxVarInt2 { - b.Write([]byte{uint8(i>>8) | 0x40, uint8(i)}) - } else if i <= maxVarInt4 { - b.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}) - } else if i <= maxVarInt8 { - b.Write([]byte{ - uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32), - uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i), - }) - } else { - panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) - } -} - -// VarIntLen determines the number of bytes that will be needed to write a number -func VarIntLen(i uint64) protocol.ByteCount { - if i <= maxVarInt1 { - return 1 - } - if i <= maxVarInt2 { - return 2 - } - if i <= maxVarInt4 { - return 4 - } - if i <= maxVarInt8 { - return 8 - } - panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_frame.go deleted file mode 100644 index 7909bffa8..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_frame.go +++ /dev/null @@ -1,226 +0,0 @@ -package wire - -import ( - "bytes" - "errors" - "sort" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges") - -// An AckFrame is an ACK frame -type AckFrame struct { - AckRanges []AckRange // has to be ordered. The highest ACK range goes first, the lowest ACK range goes last - DelayTime time.Duration -} - -// parseAckFrame reads an ACK frame -func parseAckFrame(r *bytes.Reader, ackDelayExponent uint8, version protocol.VersionNumber) (*AckFrame, error) { - typeByte, err := r.ReadByte() - if err != nil { - return nil, err - } - ecn := typeByte&0x1 > 0 - - frame := &AckFrame{} - - la, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - largestAcked := protocol.PacketNumber(la) - delay, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - frame.DelayTime = time.Duration(delay*1< largestAcked { - return nil, errors.New("invalid first ACK range") - } - smallest := largestAcked - ackBlock - - // read all the other ACK ranges - frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largestAcked}) - for i := uint64(0); i < numBlocks; i++ { - g, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - gap := protocol.PacketNumber(g) - if smallest < gap+2 { - return nil, errInvalidAckRanges - } - largest := smallest - gap - 2 - - ab, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - ackBlock := protocol.PacketNumber(ab) - - if ackBlock > largest { - return nil, errInvalidAckRanges - } - smallest = largest - ackBlock - frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largest}) - } - - if !frame.validateAckRanges() { - return nil, errInvalidAckRanges - } - - // parse (and skip) the ECN section - if ecn { - for i := 0; i < 3; i++ { - if _, err := utils.ReadVarInt(r); err != nil { - return nil, err - } - } - } - - return frame, nil -} - -// Write writes an ACK frame. -func (f *AckFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x2) - utils.WriteVarInt(b, uint64(f.LargestAcked())) - utils.WriteVarInt(b, encodeAckDelay(f.DelayTime)) - - numRanges := f.numEncodableAckRanges() - utils.WriteVarInt(b, uint64(numRanges-1)) - - // write the first range - _, firstRange := f.encodeAckRange(0) - utils.WriteVarInt(b, firstRange) - - // write all the other range - for i := 1; i < numRanges; i++ { - gap, len := f.encodeAckRange(i) - utils.WriteVarInt(b, gap) - utils.WriteVarInt(b, len) - } - return nil -} - -// Length of a written frame -func (f *AckFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - largestAcked := f.AckRanges[0].Largest - numRanges := f.numEncodableAckRanges() - - length := 1 + utils.VarIntLen(uint64(largestAcked)) + utils.VarIntLen(encodeAckDelay(f.DelayTime)) - - length += utils.VarIntLen(uint64(numRanges - 1)) - lowestInFirstRange := f.AckRanges[0].Smallest - length += utils.VarIntLen(uint64(largestAcked - lowestInFirstRange)) - - for i := 1; i < numRanges; i++ { - gap, len := f.encodeAckRange(i) - length += utils.VarIntLen(gap) - length += utils.VarIntLen(len) - } - return length -} - -// gets the number of ACK ranges that can be encoded -// such that the resulting frame is smaller than the maximum ACK frame size -func (f *AckFrame) numEncodableAckRanges() int { - length := 1 + utils.VarIntLen(uint64(f.LargestAcked())) + utils.VarIntLen(encodeAckDelay(f.DelayTime)) - length += 2 // assume that the number of ranges will consume 2 bytes - for i := 1; i < len(f.AckRanges); i++ { - gap, len := f.encodeAckRange(i) - rangeLen := utils.VarIntLen(gap) + utils.VarIntLen(len) - if length+rangeLen > protocol.MaxAckFrameSize { - // Writing range i would exceed the MaxAckFrameSize. - // So encode one range less than that. - return i - 1 - } - length += rangeLen - } - return len(f.AckRanges) -} - -func (f *AckFrame) encodeAckRange(i int) (uint64 /* gap */, uint64 /* length */) { - if i == 0 { - return 0, uint64(f.AckRanges[0].Largest - f.AckRanges[0].Smallest) - } - return uint64(f.AckRanges[i-1].Smallest - f.AckRanges[i].Largest - 2), - uint64(f.AckRanges[i].Largest - f.AckRanges[i].Smallest) -} - -// HasMissingRanges returns if this frame reports any missing packets -func (f *AckFrame) HasMissingRanges() bool { - return len(f.AckRanges) > 1 -} - -func (f *AckFrame) validateAckRanges() bool { - if len(f.AckRanges) == 0 { - return false - } - - // check the validity of every single ACK range - for _, ackRange := range f.AckRanges { - if ackRange.Smallest > ackRange.Largest { - return false - } - } - - // check the consistency for ACK with multiple NACK ranges - for i, ackRange := range f.AckRanges { - if i == 0 { - continue - } - lastAckRange := f.AckRanges[i-1] - if lastAckRange.Smallest <= ackRange.Smallest { - return false - } - if lastAckRange.Smallest <= ackRange.Largest+1 { - return false - } - } - - return true -} - -// LargestAcked is the largest acked packet number -func (f *AckFrame) LargestAcked() protocol.PacketNumber { - return f.AckRanges[0].Largest -} - -// LowestAcked is the lowest acked packet number -func (f *AckFrame) LowestAcked() protocol.PacketNumber { - return f.AckRanges[len(f.AckRanges)-1].Smallest -} - -// AcksPacket determines if this ACK frame acks a certain packet number -func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool { - if p < f.LowestAcked() || p > f.LargestAcked() { - return false - } - - i := sort.Search(len(f.AckRanges), func(i int) bool { - return p >= f.AckRanges[i].Smallest - }) - // i will always be < len(f.AckRanges), since we checked above that p is not bigger than the largest acked - return p <= f.AckRanges[i].Largest -} - -func encodeAckDelay(delay time.Duration) uint64 { - return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent))) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_range.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_range.go deleted file mode 100644 index 0f4185801..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ack_range.go +++ /dev/null @@ -1,14 +0,0 @@ -package wire - -import "github.com/lucas-clemente/quic-go/internal/protocol" - -// AckRange is an ACK range -type AckRange struct { - Smallest protocol.PacketNumber - Largest protocol.PacketNumber -} - -// Len returns the number of packets contained in this ACK range -func (r AckRange) Len() protocol.PacketNumber { - return r.Largest - r.Smallest + 1 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/connection_close_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/connection_close_frame.go deleted file mode 100644 index 60378a5d8..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/connection_close_frame.go +++ /dev/null @@ -1,81 +0,0 @@ -package wire - -import ( - "bytes" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A ConnectionCloseFrame is a CONNECTION_CLOSE frame -type ConnectionCloseFrame struct { - IsApplicationError bool - ErrorCode qerr.ErrorCode - ReasonPhrase string -} - -func parseConnectionCloseFrame(r *bytes.Reader, version protocol.VersionNumber) (*ConnectionCloseFrame, error) { - typeByte, err := r.ReadByte() - if err != nil { - return nil, err - } - - f := &ConnectionCloseFrame{IsApplicationError: typeByte == 0x1d} - ec, err := utils.BigEndian.ReadUint16(r) - if err != nil { - return nil, err - } - f.ErrorCode = qerr.ErrorCode(ec) - // read the Frame Type, if this is not an application error - if !f.IsApplicationError { - if _, err := utils.ReadVarInt(r); err != nil { - return nil, err - } - } - var reasonPhraseLen uint64 - reasonPhraseLen, err = utils.ReadVarInt(r) - if err != nil { - return nil, err - } - // shortcut to prevent the unnecessary allocation of dataLen bytes - // if the dataLen is larger than the remaining length of the packet - // reading the whole reason phrase would result in EOF when attempting to READ - if int(reasonPhraseLen) > r.Len() { - return nil, io.EOF - } - - reasonPhrase := make([]byte, reasonPhraseLen) - if _, err := io.ReadFull(r, reasonPhrase); err != nil { - // this should never happen, since we already checked the reasonPhraseLen earlier - return nil, err - } - f.ReasonPhrase = string(reasonPhrase) - return f, nil -} - -// Length of a written frame -func (f *ConnectionCloseFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - length := 1 + 2 + utils.VarIntLen(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase)) - if !f.IsApplicationError { - length++ // for the frame type - } - return length -} - -func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if f.IsApplicationError { - b.WriteByte(0x1d) - } else { - b.WriteByte(0x1c) - } - - utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode)) - if !f.IsApplicationError { - utils.WriteVarInt(b, 0) - } - utils.WriteVarInt(b, uint64(len(f.ReasonPhrase))) - b.WriteString(f.ReasonPhrase) - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/crypto_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/crypto_frame.go deleted file mode 100644 index eeafea974..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/crypto_frame.go +++ /dev/null @@ -1,71 +0,0 @@ -package wire - -import ( - "bytes" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A CryptoFrame is a CRYPTO frame -type CryptoFrame struct { - Offset protocol.ByteCount - Data []byte -} - -func parseCryptoFrame(r *bytes.Reader, _ protocol.VersionNumber) (*CryptoFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - frame := &CryptoFrame{} - offset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - frame.Offset = protocol.ByteCount(offset) - dataLen, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - if dataLen > uint64(r.Len()) { - return nil, io.EOF - } - if dataLen != 0 { - frame.Data = make([]byte, dataLen) - if _, err := io.ReadFull(r, frame.Data); err != nil { - // this should never happen, since we already checked the dataLen earlier - return nil, err - } - } - return frame, nil -} - -func (f *CryptoFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x6) - utils.WriteVarInt(b, uint64(f.Offset)) - utils.WriteVarInt(b, uint64(len(f.Data))) - b.Write(f.Data) - return nil -} - -// Length of a written frame -func (f *CryptoFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.Offset)) + utils.VarIntLen(uint64(len(f.Data))) + protocol.ByteCount(len(f.Data)) -} - -// MaxDataLen returns the maximum data length -func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount { - // pretend that the data size will be 1 bytes - // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards - headerLen := 1 + utils.VarIntLen(uint64(f.Offset)) + 1 - if headerLen > maxSize { - return 0 - } - maxDataLen := maxSize - headerLen - if utils.VarIntLen(uint64(maxDataLen)) != 1 { - maxDataLen-- - } - return maxDataLen -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/data_blocked_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/data_blocked_frame.go deleted file mode 100644 index 91c05ccfb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/data_blocked_frame.go +++ /dev/null @@ -1,38 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A DataBlockedFrame is a DATA_BLOCKED frame -type DataBlockedFrame struct { - DataLimit protocol.ByteCount -} - -func parseDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DataBlockedFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - offset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - return &DataBlockedFrame{ - DataLimit: protocol.ByteCount(offset), - }, nil -} - -func (f *DataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - typeByte := uint8(0x14) - b.WriteByte(typeByte) - utils.WriteVarInt(b, uint64(f.DataLimit)) - return nil -} - -// Length of a written frame -func (f *DataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.DataLimit)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go deleted file mode 100644 index 19a0b064e..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/extended_header.go +++ /dev/null @@ -1,204 +0,0 @@ -package wire - -import ( - "bytes" - "errors" - "fmt" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// ExtendedHeader is the header of a QUIC packet. -type ExtendedHeader struct { - Header - - typeByte byte - - PacketNumberLen protocol.PacketNumberLen - PacketNumber protocol.PacketNumber - - KeyPhase int -} - -func (h *ExtendedHeader) parse(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) { - // read the (now unencrypted) first byte - var err error - h.typeByte, err = b.ReadByte() - if err != nil { - return nil, err - } - if _, err := b.Seek(int64(h.ParsedLen())-1, io.SeekCurrent); err != nil { - return nil, err - } - if h.IsLongHeader { - return h.parseLongHeader(b, v) - } - return h.parseShortHeader(b, v) -} - -func (h *ExtendedHeader) parseLongHeader(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) { - if h.typeByte&0xc != 0 { - return nil, errors.New("5th and 6th bit must be 0") - } - if err := h.readPacketNumber(b); err != nil { - return nil, err - } - return h, nil -} - -func (h *ExtendedHeader) parseShortHeader(b *bytes.Reader, v protocol.VersionNumber) (*ExtendedHeader, error) { - if h.typeByte&0x18 != 0 { - return nil, errors.New("4th and 5th bit must be 0") - } - - h.KeyPhase = int(h.typeByte&0x4) >> 2 - - if err := h.readPacketNumber(b); err != nil { - return nil, err - } - return h, nil -} - -func (h *ExtendedHeader) readPacketNumber(b *bytes.Reader) error { - h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1 - pn, err := utils.BigEndian.ReadUintN(b, uint8(h.PacketNumberLen)) - if err != nil { - return err - } - h.PacketNumber = protocol.PacketNumber(pn) - return nil -} - -// Write writes the Header. -func (h *ExtendedHeader) Write(b *bytes.Buffer, ver protocol.VersionNumber) error { - if h.IsLongHeader { - return h.writeLongHeader(b, ver) - } - return h.writeShortHeader(b, ver) -} - -func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, v protocol.VersionNumber) error { - var packetType uint8 - switch h.Type { - case protocol.PacketTypeInitial: - packetType = 0x0 - case protocol.PacketType0RTT: - packetType = 0x1 - case protocol.PacketTypeHandshake: - packetType = 0x2 - case protocol.PacketTypeRetry: - packetType = 0x3 - } - firstByte := 0xc0 | packetType<<4 - if h.Type == protocol.PacketTypeRetry { - odcil, err := encodeSingleConnIDLen(h.OrigDestConnectionID) - if err != nil { - return err - } - firstByte |= odcil - } else { // Retry packets don't have a packet number - firstByte |= uint8(h.PacketNumberLen - 1) - } - - b.WriteByte(firstByte) - utils.BigEndian.WriteUint32(b, uint32(h.Version)) - connIDLen, err := encodeConnIDLen(h.DestConnectionID, h.SrcConnectionID) - if err != nil { - return err - } - b.WriteByte(connIDLen) - b.Write(h.DestConnectionID.Bytes()) - b.Write(h.SrcConnectionID.Bytes()) - - switch h.Type { - case protocol.PacketTypeRetry: - b.Write(h.OrigDestConnectionID.Bytes()) - b.Write(h.Token) - return nil - case protocol.PacketTypeInitial: - utils.WriteVarInt(b, uint64(len(h.Token))) - b.Write(h.Token) - } - - utils.WriteVarInt(b, uint64(h.Length)) - return h.writePacketNumber(b) -} - -// TODO: add support for the key phase -func (h *ExtendedHeader) writeShortHeader(b *bytes.Buffer, v protocol.VersionNumber) error { - typeByte := 0x40 | uint8(h.PacketNumberLen-1) - typeByte |= byte(h.KeyPhase << 2) - - b.WriteByte(typeByte) - b.Write(h.DestConnectionID.Bytes()) - return h.writePacketNumber(b) -} - -func (h *ExtendedHeader) writePacketNumber(b *bytes.Buffer) error { - if h.PacketNumberLen == protocol.PacketNumberLenInvalid || h.PacketNumberLen > protocol.PacketNumberLen4 { - return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen) - } - utils.BigEndian.WriteUintN(b, uint8(h.PacketNumberLen), uint64(h.PacketNumber)) - return nil -} - -// GetLength determines the length of the Header. -func (h *ExtendedHeader) GetLength(v protocol.VersionNumber) protocol.ByteCount { - if h.IsLongHeader { - length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + utils.VarIntLen(uint64(h.Length)) - if h.Type == protocol.PacketTypeInitial { - length += utils.VarIntLen(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token)) - } - return length - } - - length := protocol.ByteCount(1 /* type byte */ + h.DestConnectionID.Len()) - length += protocol.ByteCount(h.PacketNumberLen) - return length -} - -// Log logs the Header -func (h *ExtendedHeader) Log(logger utils.Logger) { - if h.IsLongHeader { - var token string - if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry { - if len(h.Token) == 0 { - token = "Token: (empty), " - } else { - token = fmt.Sprintf("Token: %#x, ", h.Token) - } - if h.Type == protocol.PacketTypeRetry { - logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sOrigDestConnectionID: %s, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.OrigDestConnectionID, h.Version) - return - } - } - logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %#x, PacketNumberLen: %d, Length: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.Length, h.Version) - } else { - logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, KeyPhase: %d}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, h.KeyPhase) - } -} - -func encodeConnIDLen(dest, src protocol.ConnectionID) (byte, error) { - dcil, err := encodeSingleConnIDLen(dest) - if err != nil { - return 0, err - } - scil, err := encodeSingleConnIDLen(src) - if err != nil { - return 0, err - } - return scil | dcil<<4, nil -} - -func encodeSingleConnIDLen(id protocol.ConnectionID) (byte, error) { - len := id.Len() - if len == 0 { - return 0, nil - } - if len < 4 || len > 18 { - return 0, fmt.Errorf("invalid connection ID length: %d bytes", len) - } - return byte(len - 3), nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/frame_parser.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/frame_parser.go deleted file mode 100644 index e9d3d6a59..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/frame_parser.go +++ /dev/null @@ -1,97 +0,0 @@ -package wire - -import ( - "bytes" - "fmt" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" -) - -type frameParser struct { - ackDelayExponent uint8 - - version protocol.VersionNumber -} - -// NewFrameParser creates a new frame parser. -func NewFrameParser(v protocol.VersionNumber) FrameParser { - return &frameParser{version: v} -} - -// ParseNextFrame parses the next frame -// It skips PADDING frames. -func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) { - for r.Len() != 0 { - typeByte, _ := r.ReadByte() - if typeByte == 0x0 { // PADDING frame - continue - } - r.UnreadByte() - - return p.parseFrame(r, typeByte, encLevel) - } - return nil, nil -} - -func (p *frameParser) parseFrame(r *bytes.Reader, typeByte byte, encLevel protocol.EncryptionLevel) (Frame, error) { - var frame Frame - var err error - if typeByte&0xf8 == 0x8 { - frame, err = parseStreamFrame(r, p.version) - if err != nil { - return nil, qerr.Error(qerr.FrameEncodingError, err.Error()) - } - return frame, nil - } - switch typeByte { - case 0x1: - frame, err = parsePingFrame(r, p.version) - case 0x2, 0x3: - ackDelayExponent := p.ackDelayExponent - if encLevel != protocol.Encryption1RTT { - ackDelayExponent = protocol.DefaultAckDelayExponent - } - frame, err = parseAckFrame(r, ackDelayExponent, p.version) - case 0x4: - frame, err = parseResetStreamFrame(r, p.version) - case 0x5: - frame, err = parseStopSendingFrame(r, p.version) - case 0x6: - frame, err = parseCryptoFrame(r, p.version) - case 0x7: - frame, err = parseNewTokenFrame(r, p.version) - case 0x10: - frame, err = parseMaxDataFrame(r, p.version) - case 0x11: - frame, err = parseMaxStreamDataFrame(r, p.version) - case 0x12, 0x13: - frame, err = parseMaxStreamsFrame(r, p.version) - case 0x14: - frame, err = parseDataBlockedFrame(r, p.version) - case 0x15: - frame, err = parseStreamDataBlockedFrame(r, p.version) - case 0x16, 0x17: - frame, err = parseStreamsBlockedFrame(r, p.version) - case 0x18: - frame, err = parseNewConnectionIDFrame(r, p.version) - case 0x19: - frame, err = parseRetireConnectionIDFrame(r, p.version) - case 0x1a: - frame, err = parsePathChallengeFrame(r, p.version) - case 0x1b: - frame, err = parsePathResponseFrame(r, p.version) - case 0x1c, 0x1d: - frame, err = parseConnectionCloseFrame(r, p.version) - default: - err = fmt.Errorf("unknown type byte 0x%x", typeByte) - } - if err != nil { - return nil, qerr.Error(qerr.FrameEncodingError, err.Error()) - } - return frame, nil -} - -func (p *frameParser) SetAckDelayExponent(exp uint8) { - p.ackDelayExponent = exp -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go deleted file mode 100644 index 21eb784c3..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/header.go +++ /dev/null @@ -1,251 +0,0 @@ -package wire - -import ( - "bytes" - "errors" - "fmt" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// ParseConnectionID parses the destination connection ID of a packet. -// It uses the data slice for the connection ID. -// That means that the connection ID must not be used after the packet buffer is released. -func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) { - if len(data) == 0 { - return nil, io.EOF - } - isLongHeader := data[0]&0x80 > 0 - if !isLongHeader { - if len(data) < shortHeaderConnIDLen+1 { - return nil, io.EOF - } - return protocol.ConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil - } - if len(data) < 6 { - return nil, io.EOF - } - destConnIDLen, _ := decodeConnIDLen(data[5]) - if len(data) < 6+destConnIDLen { - return nil, io.EOF - } - return protocol.ConnectionID(data[6 : 6+destConnIDLen]), nil -} - -// IsVersionNegotiationPacket says if this is a version negotiation packet -func IsVersionNegotiationPacket(b []byte) bool { - if len(b) < 5 { - return false - } - return b[0]&0x80 > 0 && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 -} - -var errUnsupportedVersion = errors.New("unsupported version") - -// The Header is the version independent part of the header -type Header struct { - Version protocol.VersionNumber - SrcConnectionID protocol.ConnectionID - DestConnectionID protocol.ConnectionID - - IsLongHeader bool - Type protocol.PacketType - Length protocol.ByteCount - - Token []byte - SupportedVersions []protocol.VersionNumber // sent in a Version Negotiation Packet - OrigDestConnectionID protocol.ConnectionID // sent in the Retry packet - - typeByte byte - parsedLen protocol.ByteCount // how many bytes were read while parsing this header -} - -// ParsePacket parses a packet. -// If the packet has a long header, the packet is cut according to the length field. -// If we understand the version, the packet is header up unto the packet number. -// Otherwise, only the invariant part of the header is parsed. -func ParsePacket(data []byte, shortHeaderConnIDLen int) (*Header, []byte /* packet data */, []byte /* rest */, error) { - hdr, err := parseHeader(bytes.NewReader(data), shortHeaderConnIDLen) - if err != nil { - if err == errUnsupportedVersion { - return hdr, nil, nil, nil - } - return nil, nil, nil, err - } - var rest []byte - if hdr.IsLongHeader { - if protocol.ByteCount(len(data)) < hdr.ParsedLen()+hdr.Length { - return nil, nil, nil, fmt.Errorf("packet length (%d bytes) is smaller than the expected length (%d bytes)", len(data)-int(hdr.ParsedLen()), hdr.Length) - } - packetLen := int(hdr.ParsedLen() + hdr.Length) - rest = data[packetLen:] - data = data[:packetLen] - } - return hdr, data, rest, nil -} - -// ParseHeader parses the header. -// For short header packets: up to the packet number. -// For long header packets: -// * if we understand the version: up to the packet number -// * if not, only the invariant part of the header -func parseHeader(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) { - startLen := b.Len() - h, err := parseHeaderImpl(b, shortHeaderConnIDLen) - if err != nil { - return h, err - } - h.parsedLen = protocol.ByteCount(startLen - b.Len()) - return h, err -} - -func parseHeaderImpl(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) { - typeByte, err := b.ReadByte() - if err != nil { - return nil, err - } - - h := &Header{ - typeByte: typeByte, - IsLongHeader: typeByte&0x80 > 0, - } - - if !h.IsLongHeader { - if h.typeByte&0x40 == 0 { - return nil, errors.New("not a QUIC packet") - } - if err := h.parseShortHeader(b, shortHeaderConnIDLen); err != nil { - return nil, err - } - return h, nil - } - return h, h.parseLongHeader(b) -} - -func (h *Header) parseShortHeader(b *bytes.Reader, shortHeaderConnIDLen int) error { - var err error - h.DestConnectionID, err = protocol.ReadConnectionID(b, shortHeaderConnIDLen) - return err -} - -func (h *Header) parseLongHeader(b *bytes.Reader) error { - v, err := utils.BigEndian.ReadUint32(b) - if err != nil { - return err - } - h.Version = protocol.VersionNumber(v) - if h.Version != 0 && h.typeByte&0x40 == 0 { - return errors.New("not a QUIC packet") - } - connIDLenByte, err := b.ReadByte() - if err != nil { - return err - } - dcil, scil := decodeConnIDLen(connIDLenByte) - h.DestConnectionID, err = protocol.ReadConnectionID(b, dcil) - if err != nil { - return err - } - h.SrcConnectionID, err = protocol.ReadConnectionID(b, scil) - if err != nil { - return err - } - if h.Version == 0 { - return h.parseVersionNegotiationPacket(b) - } - // If we don't understand the version, we have no idea how to interpret the rest of the bytes - if !protocol.IsSupportedVersion(protocol.SupportedVersions, h.Version) { - return errUnsupportedVersion - } - - switch (h.typeByte & 0x30) >> 4 { - case 0x0: - h.Type = protocol.PacketTypeInitial - case 0x1: - h.Type = protocol.PacketType0RTT - case 0x2: - h.Type = protocol.PacketTypeHandshake - case 0x3: - h.Type = protocol.PacketTypeRetry - } - - if h.Type == protocol.PacketTypeRetry { - odcil := decodeSingleConnIDLen(h.typeByte & 0xf) - h.OrigDestConnectionID, err = protocol.ReadConnectionID(b, odcil) - if err != nil { - return err - } - h.Token = make([]byte, b.Len()) - if _, err := io.ReadFull(b, h.Token); err != nil { - return err - } - return nil - } - - if h.Type == protocol.PacketTypeInitial { - tokenLen, err := utils.ReadVarInt(b) - if err != nil { - return err - } - if tokenLen > uint64(b.Len()) { - return io.EOF - } - h.Token = make([]byte, tokenLen) - if _, err := io.ReadFull(b, h.Token); err != nil { - return err - } - } - - pl, err := utils.ReadVarInt(b) - if err != nil { - return err - } - h.Length = protocol.ByteCount(pl) - return nil -} - -func (h *Header) parseVersionNegotiationPacket(b *bytes.Reader) error { - if b.Len() == 0 { - return errors.New("Version Negoation packet has empty version list") - } - if b.Len()%4 != 0 { - return errors.New("Version Negotation packet has a version list with an invalid length") - } - h.SupportedVersions = make([]protocol.VersionNumber, b.Len()/4) - for i := 0; b.Len() > 0; i++ { - v, err := utils.BigEndian.ReadUint32(b) - if err != nil { - return err - } - h.SupportedVersions[i] = protocol.VersionNumber(v) - } - return nil -} - -// ParsedLen returns the number of bytes that were consumed when parsing the header -func (h *Header) ParsedLen() protocol.ByteCount { - return h.parsedLen -} - -// ParseExtended parses the version dependent part of the header. -// The Reader has to be set such that it points to the first byte of the header. -func (h *Header) ParseExtended(b *bytes.Reader, ver protocol.VersionNumber) (*ExtendedHeader, error) { - return h.toExtendedHeader().parse(b, ver) -} - -func (h *Header) toExtendedHeader() *ExtendedHeader { - return &ExtendedHeader{Header: *h} -} - -func decodeConnIDLen(enc byte) (int /*dest conn id len*/, int /*src conn id len*/) { - return decodeSingleConnIDLen(enc >> 4), decodeSingleConnIDLen(enc & 0xf) -} - -func decodeSingleConnIDLen(enc uint8) int { - if enc == 0 { - return 0 - } - return int(enc) + 3 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/interface.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/interface.go deleted file mode 100644 index 99fdc80fb..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/interface.go +++ /dev/null @@ -1,19 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A Frame in QUIC -type Frame interface { - Write(b *bytes.Buffer, version protocol.VersionNumber) error - Length(version protocol.VersionNumber) protocol.ByteCount -} - -// A FrameParser parses QUIC frames, one by one. -type FrameParser interface { - ParseNext(*bytes.Reader, protocol.EncryptionLevel) (Frame, error) - SetAckDelayExponent(uint8) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/log.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/log.go deleted file mode 100644 index 3c56ac458..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/log.go +++ /dev/null @@ -1,43 +0,0 @@ -package wire - -import ( - "fmt" - "strings" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// LogFrame logs a frame, either sent or received -func LogFrame(logger utils.Logger, frame Frame, sent bool) { - if !logger.Debug() { - return - } - dir := "<-" - if sent { - dir = "->" - } - switch f := frame.(type) { - case *CryptoFrame: - dataLen := protocol.ByteCount(len(f.Data)) - logger.Debugf("\t%s &wire.CryptoFrame{Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, f.Offset, dataLen, f.Offset+dataLen) - case *StreamFrame: - logger.Debugf("\t%s &wire.StreamFrame{StreamID: %d, FinBit: %t, Offset: 0x%x, Data length: 0x%x, Offset + Data length: 0x%x}", dir, f.StreamID, f.FinBit, f.Offset, f.DataLen(), f.Offset+f.DataLen()) - case *AckFrame: - if len(f.AckRanges) > 1 { - ackRanges := make([]string, len(f.AckRanges)) - for i, r := range f.AckRanges { - ackRanges[i] = fmt.Sprintf("{Largest: %#x, Smallest: %#x}", r.Largest, r.Smallest) - } - logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %#x, LowestAcked: %#x, AckRanges: {%s}, DelayTime: %s}", dir, f.LargestAcked(), f.LowestAcked(), strings.Join(ackRanges, ", "), f.DelayTime.String()) - } else { - logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %#x, LowestAcked: %#x, DelayTime: %s}", dir, f.LargestAcked(), f.LowestAcked(), f.DelayTime.String()) - } - case *NewConnectionIDFrame: - logger.Debugf("\t%s &wire.NewConnectionIDFrame{SequenceNumber: %d, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.ConnectionID, f.StatelessResetToken) - case *NewTokenFrame: - logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token) - default: - logger.Debugf("\t%s %#v", dir, frame) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_data_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_data_frame.go deleted file mode 100644 index c4a9be0df..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_data_frame.go +++ /dev/null @@ -1,40 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A MaxDataFrame carries flow control information for the connection -type MaxDataFrame struct { - ByteOffset protocol.ByteCount -} - -// parseMaxDataFrame parses a MAX_DATA frame -func parseMaxDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxDataFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - frame := &MaxDataFrame{} - byteOffset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - frame.ByteOffset = protocol.ByteCount(byteOffset) - return frame, nil -} - -//Write writes a MAX_STREAM_DATA frame -func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x10) - utils.WriteVarInt(b, uint64(f.ByteOffset)) - return nil -} - -// Length of a written frame -func (f *MaxDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.ByteOffset)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_stream_data_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_stream_data_frame.go deleted file mode 100644 index 2566f1c9b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_stream_data_frame.go +++ /dev/null @@ -1,46 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A MaxStreamDataFrame is a MAX_STREAM_DATA frame -type MaxStreamDataFrame struct { - StreamID protocol.StreamID - ByteOffset protocol.ByteCount -} - -func parseMaxStreamDataFrame(r *bytes.Reader, version protocol.VersionNumber) (*MaxStreamDataFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - sid, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - offset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - - return &MaxStreamDataFrame{ - StreamID: protocol.StreamID(sid), - ByteOffset: protocol.ByteCount(offset), - }, nil -} - -func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x11) - utils.WriteVarInt(b, uint64(f.StreamID)) - utils.WriteVarInt(b, uint64(f.ByteOffset)) - return nil -} - -// Length of a written frame -func (f *MaxStreamDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.StreamID)) + utils.VarIntLen(uint64(f.ByteOffset)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_streams_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_streams_frame.go deleted file mode 100644 index c7a3cd41c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/max_streams_frame.go +++ /dev/null @@ -1,51 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A MaxStreamsFrame is a MAX_STREAMS frame -type MaxStreamsFrame struct { - Type protocol.StreamType - MaxStreams uint64 -} - -func parseMaxStreamsFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamsFrame, error) { - typeByte, err := r.ReadByte() - if err != nil { - return nil, err - } - - f := &MaxStreamsFrame{} - switch typeByte { - case 0x12: - f.Type = protocol.StreamTypeBidi - case 0x13: - f.Type = protocol.StreamTypeUni - } - streamID, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - f.MaxStreams = streamID - return f, nil -} - -func (f *MaxStreamsFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - switch f.Type { - case protocol.StreamTypeBidi: - b.WriteByte(0x12) - case protocol.StreamTypeUni: - b.WriteByte(0x13) - } - utils.WriteVarInt(b, f.MaxStreams) - return nil -} - -// Length of a written frame -func (f *MaxStreamsFrame) Length(protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(f.MaxStreams) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_connection_id_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_connection_id_frame.go deleted file mode 100644 index 9a612aa6c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_connection_id_frame.go +++ /dev/null @@ -1,70 +0,0 @@ -package wire - -import ( - "bytes" - "fmt" - "io" - - "github.com/lucas-clemente/quic-go/internal/utils" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A NewConnectionIDFrame is a NEW_CONNECTION_ID frame -type NewConnectionIDFrame struct { - SequenceNumber uint64 - ConnectionID protocol.ConnectionID - StatelessResetToken [16]byte -} - -func parseNewConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewConnectionIDFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - seq, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - connIDLen, err := r.ReadByte() - if err != nil { - return nil, err - } - if connIDLen < 4 || connIDLen > 18 { - return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen) - } - connID, err := protocol.ReadConnectionID(r, int(connIDLen)) - if err != nil { - return nil, err - } - frame := &NewConnectionIDFrame{ - SequenceNumber: seq, - ConnectionID: connID, - } - if _, err := io.ReadFull(r, frame.StatelessResetToken[:]); err != nil { - if err == io.ErrUnexpectedEOF { - return nil, io.EOF - } - return nil, err - } - - return frame, nil -} - -func (f *NewConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x18) - utils.WriteVarInt(b, f.SequenceNumber) - connIDLen := f.ConnectionID.Len() - if connIDLen < 4 || connIDLen > 18 { - return fmt.Errorf("invalid connection ID length: %d", connIDLen) - } - b.WriteByte(uint8(connIDLen)) - b.Write(f.ConnectionID.Bytes()) - b.Write(f.StatelessResetToken[:]) - return nil -} - -// Length of a written frame -func (f *NewConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(f.SequenceNumber) + 1 /* connection ID length */ + protocol.ByteCount(f.ConnectionID.Len()) + 16 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_token_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_token_frame.go deleted file mode 100644 index 2cf6fce5e..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/new_token_frame.go +++ /dev/null @@ -1,44 +0,0 @@ -package wire - -import ( - "bytes" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A NewTokenFrame is a NEW_TOKEN frame -type NewTokenFrame struct { - Token []byte -} - -func parseNewTokenFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewTokenFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - tokenLen, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - if uint64(r.Len()) < tokenLen { - return nil, io.EOF - } - token := make([]byte, int(tokenLen)) - if _, err := io.ReadFull(r, token); err != nil { - return nil, err - } - return &NewTokenFrame{Token: token}, nil -} - -func (f *NewTokenFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x7) - utils.WriteVarInt(b, uint64(len(f.Token))) - b.Write(f.Token) - return nil -} - -// Length of a written frame -func (f *NewTokenFrame) Length(protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(len(f.Token))) + protocol.ByteCount(len(f.Token)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_challenge_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_challenge_frame.go deleted file mode 100644 index d35ee3b54..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_challenge_frame.go +++ /dev/null @@ -1,38 +0,0 @@ -package wire - -import ( - "bytes" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A PathChallengeFrame is a PATH_CHALLENGE frame -type PathChallengeFrame struct { - Data [8]byte -} - -func parsePathChallengeFrame(r *bytes.Reader, version protocol.VersionNumber) (*PathChallengeFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - frame := &PathChallengeFrame{} - if _, err := io.ReadFull(r, frame.Data[:]); err != nil { - if err == io.ErrUnexpectedEOF { - return nil, io.EOF - } - return nil, err - } - return frame, nil -} - -func (f *PathChallengeFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x1a) - b.Write(f.Data[:]) - return nil -} - -// Length of a written frame -func (f *PathChallengeFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { - return 1 + 8 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_response_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_response_frame.go deleted file mode 100644 index 20d8fd721..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/path_response_frame.go +++ /dev/null @@ -1,38 +0,0 @@ -package wire - -import ( - "bytes" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A PathResponseFrame is a PATH_RESPONSE frame -type PathResponseFrame struct { - Data [8]byte -} - -func parsePathResponseFrame(r *bytes.Reader, version protocol.VersionNumber) (*PathResponseFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - frame := &PathResponseFrame{} - if _, err := io.ReadFull(r, frame.Data[:]); err != nil { - if err == io.ErrUnexpectedEOF { - return nil, io.EOF - } - return nil, err - } - return frame, nil -} - -func (f *PathResponseFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x1b) - b.Write(f.Data[:]) - return nil -} - -// Length of a written frame -func (f *PathResponseFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { - return 1 + 8 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ping_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/ping_frame.go deleted file mode 100644 index aed6857b5..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/ping_frame.go +++ /dev/null @@ -1,27 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// A PingFrame is a PING frame -type PingFrame struct{} - -func parsePingFrame(r *bytes.Reader, version protocol.VersionNumber) (*PingFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - return &PingFrame{}, nil -} - -func (f *PingFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x1) - return nil -} - -// Length of a written frame -func (f *PingFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/reset_stream_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/reset_stream_frame.go deleted file mode 100644 index d3a40dcf9..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/reset_stream_frame.go +++ /dev/null @@ -1,58 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A ResetStreamFrame is a RESET_STREAM frame in QUIC -type ResetStreamFrame struct { - StreamID protocol.StreamID - ErrorCode protocol.ApplicationErrorCode - ByteOffset protocol.ByteCount -} - -func parseResetStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*ResetStreamFrame, error) { - if _, err := r.ReadByte(); err != nil { // read the TypeByte - return nil, err - } - - var streamID protocol.StreamID - var errorCode uint16 - var byteOffset protocol.ByteCount - sid, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - streamID = protocol.StreamID(sid) - errorCode, err = utils.BigEndian.ReadUint16(r) - if err != nil { - return nil, err - } - bo, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - byteOffset = protocol.ByteCount(bo) - - return &ResetStreamFrame{ - StreamID: streamID, - ErrorCode: protocol.ApplicationErrorCode(errorCode), - ByteOffset: byteOffset, - }, nil -} - -func (f *ResetStreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x4) - utils.WriteVarInt(b, uint64(f.StreamID)) - utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode)) - utils.WriteVarInt(b, uint64(f.ByteOffset)) - return nil -} - -// Length of a written frame -func (f *ResetStreamFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.StreamID)) + 2 + utils.VarIntLen(uint64(f.ByteOffset)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/retire_connection_id_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/retire_connection_id_frame.go deleted file mode 100644 index 9a715a4c4..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/retire_connection_id_frame.go +++ /dev/null @@ -1,36 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame -type RetireConnectionIDFrame struct { - SequenceNumber uint64 -} - -func parseRetireConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*RetireConnectionIDFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - seq, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - return &RetireConnectionIDFrame{SequenceNumber: seq}, nil -} - -func (f *RetireConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x19) - utils.WriteVarInt(b, f.SequenceNumber) - return nil -} - -// Length of a written frame -func (f *RetireConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(f.SequenceNumber) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stop_sending_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/stop_sending_frame.go deleted file mode 100644 index f9a5d60b0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stop_sending_frame.go +++ /dev/null @@ -1,47 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A StopSendingFrame is a STOP_SENDING frame -type StopSendingFrame struct { - StreamID protocol.StreamID - ErrorCode protocol.ApplicationErrorCode -} - -// parseStopSendingFrame parses a STOP_SENDING frame -func parseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSendingFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - streamID, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - errorCode, err := utils.BigEndian.ReadUint16(r) - if err != nil { - return nil, err - } - - return &StopSendingFrame{ - StreamID: protocol.StreamID(streamID), - ErrorCode: protocol.ApplicationErrorCode(errorCode), - }, nil -} - -// Length of a written frame -func (f *StopSendingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.StreamID)) + 2 -} - -func (f *StopSendingFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - b.WriteByte(0x5) - utils.WriteVarInt(b, uint64(f.StreamID)) - utils.BigEndian.WriteUint16(b, uint16(f.ErrorCode)) - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_data_blocked_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_data_blocked_frame.go deleted file mode 100644 index 9f2e90be0..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_data_blocked_frame.go +++ /dev/null @@ -1,46 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame -type StreamDataBlockedFrame struct { - StreamID protocol.StreamID - DataLimit protocol.ByteCount -} - -func parseStreamDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamDataBlockedFrame, error) { - if _, err := r.ReadByte(); err != nil { - return nil, err - } - - sid, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - offset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - - return &StreamDataBlockedFrame{ - StreamID: protocol.StreamID(sid), - DataLimit: protocol.ByteCount(offset), - }, nil -} - -func (f *StreamDataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - b.WriteByte(0x15) - utils.WriteVarInt(b, uint64(f.StreamID)) - utils.WriteVarInt(b, uint64(f.DataLimit)) - return nil -} - -// Length of a written frame -func (f *StreamDataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(uint64(f.StreamID)) + utils.VarIntLen(uint64(f.DataLimit)) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_frame.go deleted file mode 100644 index dfc1d1adf..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/stream_frame.go +++ /dev/null @@ -1,168 +0,0 @@ -package wire - -import ( - "bytes" - "errors" - "io" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A StreamFrame of QUIC -type StreamFrame struct { - StreamID protocol.StreamID - FinBit bool - DataLenPresent bool - Offset protocol.ByteCount - Data []byte -} - -func parseStreamFrame(r *bytes.Reader, version protocol.VersionNumber) (*StreamFrame, error) { - typeByte, err := r.ReadByte() - if err != nil { - return nil, err - } - - hasOffset := typeByte&0x4 > 0 - frame := &StreamFrame{ - FinBit: typeByte&0x1 > 0, - DataLenPresent: typeByte&0x2 > 0, - } - - streamID, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - frame.StreamID = protocol.StreamID(streamID) - if hasOffset { - offset, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - frame.Offset = protocol.ByteCount(offset) - } - - var dataLen uint64 - if frame.DataLenPresent { - var err error - dataLen, err = utils.ReadVarInt(r) - if err != nil { - return nil, err - } - // shortcut to prevent the unnecessary allocation of dataLen bytes - // if the dataLen is larger than the remaining length of the packet - // reading the packet contents would result in EOF when attempting to READ - if dataLen > uint64(r.Len()) { - return nil, io.EOF - } - } else { - // The rest of the packet is data - dataLen = uint64(r.Len()) - } - if dataLen != 0 { - frame.Data = make([]byte, dataLen) - if _, err := io.ReadFull(r, frame.Data); err != nil { - // this should never happen, since we already checked the dataLen earlier - return nil, err - } - } - if frame.Offset+frame.DataLen() > protocol.MaxByteCount { - return nil, qerr.Error(qerr.FrameEncodingError, "stream data overflows maximum offset") - } - return frame, nil -} - -// Write writes a STREAM frame -func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { - if len(f.Data) == 0 && !f.FinBit { - return errors.New("StreamFrame: attempting to write empty frame without FIN") - } - - typeByte := byte(0x8) - if f.FinBit { - typeByte ^= 0x1 - } - hasOffset := f.Offset != 0 - if f.DataLenPresent { - typeByte ^= 0x2 - } - if hasOffset { - typeByte ^= 0x4 - } - b.WriteByte(typeByte) - utils.WriteVarInt(b, uint64(f.StreamID)) - if hasOffset { - utils.WriteVarInt(b, uint64(f.Offset)) - } - if f.DataLenPresent { - utils.WriteVarInt(b, uint64(f.DataLen())) - } - b.Write(f.Data) - return nil -} - -// Length returns the total length of the STREAM frame -func (f *StreamFrame) Length(version protocol.VersionNumber) protocol.ByteCount { - length := 1 + utils.VarIntLen(uint64(f.StreamID)) - if f.Offset != 0 { - length += utils.VarIntLen(uint64(f.Offset)) - } - if f.DataLenPresent { - length += utils.VarIntLen(uint64(f.DataLen())) - } - return length + f.DataLen() -} - -// DataLen gives the length of data in bytes -func (f *StreamFrame) DataLen() protocol.ByteCount { - return protocol.ByteCount(len(f.Data)) -} - -// MaxDataLen returns the maximum data length -// If 0 is returned, writing will fail (a STREAM frame must contain at least 1 byte of data). -func (f *StreamFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.VersionNumber) protocol.ByteCount { - headerLen := 1 + utils.VarIntLen(uint64(f.StreamID)) - if f.Offset != 0 { - headerLen += utils.VarIntLen(uint64(f.Offset)) - } - if f.DataLenPresent { - // pretend that the data size will be 1 bytes - // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards - headerLen++ - } - if headerLen > maxSize { - return 0 - } - maxDataLen := maxSize - headerLen - if f.DataLenPresent && utils.VarIntLen(uint64(maxDataLen)) != 1 { - maxDataLen-- - } - return maxDataLen -} - -// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. -// If n >= len(frame), nil is returned and nothing is modified. -func (f *StreamFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.VersionNumber) (*StreamFrame, error) { - if maxSize >= f.Length(version) { - return nil, nil - } - - n := f.MaxDataLen(maxSize, version) - if n == 0 { - return nil, errors.New("too small") - } - newFrame := &StreamFrame{ - FinBit: false, - StreamID: f.StreamID, - Offset: f.Offset, - Data: f.Data[:n], - DataLenPresent: f.DataLenPresent, - } - - f.Data = f.Data[n:] - f.Offset += n - - return newFrame, nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/streams_blocked_frame.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/streams_blocked_frame.go deleted file mode 100644 index b41d68f1b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/streams_blocked_frame.go +++ /dev/null @@ -1,52 +0,0 @@ -package wire - -import ( - "bytes" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// A StreamsBlockedFrame is a STREAMS_BLOCKED frame -type StreamsBlockedFrame struct { - Type protocol.StreamType - StreamLimit uint64 -} - -func parseStreamsBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamsBlockedFrame, error) { - typeByte, err := r.ReadByte() - if err != nil { - return nil, err - } - - f := &StreamsBlockedFrame{} - switch typeByte { - case 0x16: - f.Type = protocol.StreamTypeBidi - case 0x17: - f.Type = protocol.StreamTypeUni - } - streamLimit, err := utils.ReadVarInt(r) - if err != nil { - return nil, err - } - f.StreamLimit = streamLimit - - return f, nil -} - -func (f *StreamsBlockedFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { - switch f.Type { - case protocol.StreamTypeBidi: - b.WriteByte(0x16) - case protocol.StreamTypeUni: - b.WriteByte(0x17) - } - utils.WriteVarInt(b, f.StreamLimit) - return nil -} - -// Length of a written frame -func (f *StreamsBlockedFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { - return 1 + utils.VarIntLen(f.StreamLimit) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go b/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go deleted file mode 100644 index bfc27af77..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/internal/wire/version_negotiation.go +++ /dev/null @@ -1,31 +0,0 @@ -package wire - -import ( - "bytes" - "crypto/rand" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" -) - -// ComposeVersionNegotiation composes a Version Negotiation -func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) { - greasedVersions := protocol.GetGreasedVersions(versions) - expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* connection ID length field */ + destConnID.Len() + srcConnID.Len() + len(greasedVersions)*4 - buf := bytes.NewBuffer(make([]byte, 0, expectedLen)) - r := make([]byte, 1) - _, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here. - buf.WriteByte(r[0] | 0xc0) - utils.BigEndian.WriteUint32(buf, 0) // version 0 - connIDLen, err := encodeConnIDLen(destConnID, srcConnID) - if err != nil { - return nil, err - } - buf.WriteByte(connIDLen) - buf.Write(destConnID) - buf.Write(srcConnID) - for _, v := range greasedVersions { - utils.BigEndian.WriteUint32(buf, uint32(v)) - } - return buf.Bytes(), nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/mockgen.go b/vendor/github.com/lucas-clemente/quic-go/mockgen.go deleted file mode 100644 index 1a882c358..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/mockgen.go +++ /dev/null @@ -1,21 +0,0 @@ -package quic - -//go:generate sh -c "./mockgen_private.sh quic mock_stream_internal_test.go github.com/lucas-clemente/quic-go streamI" -//go:generate sh -c "./mockgen_private.sh quic mock_crypto_stream_test.go github.com/lucas-clemente/quic-go cryptoStream" -//go:generate sh -c "./mockgen_private.sh quic mock_receive_stream_internal_test.go github.com/lucas-clemente/quic-go receiveStreamI" -//go:generate sh -c "./mockgen_private.sh quic mock_send_stream_internal_test.go github.com/lucas-clemente/quic-go sendStreamI" -//go:generate sh -c "./mockgen_private.sh quic mock_stream_sender_test.go github.com/lucas-clemente/quic-go streamSender" -//go:generate sh -c "./mockgen_private.sh quic mock_stream_getter_test.go github.com/lucas-clemente/quic-go streamGetter" -//go:generate sh -c "./mockgen_private.sh quic mock_crypto_data_handler_test.go github.com/lucas-clemente/quic-go cryptoDataHandler" -//go:generate sh -c "./mockgen_private.sh quic mock_frame_source_test.go github.com/lucas-clemente/quic-go frameSource" -//go:generate sh -c "./mockgen_private.sh quic mock_ack_frame_source_test.go github.com/lucas-clemente/quic-go ackFrameSource" -//go:generate sh -c "./mockgen_private.sh quic mock_stream_manager_test.go github.com/lucas-clemente/quic-go streamManager" -//go:generate sh -c "./mockgen_private.sh quic mock_sealing_manager_test.go github.com/lucas-clemente/quic-go sealingManager" -//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker" -//go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer" -//go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner" -//go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession" -//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/lucas-clemente/quic-go packetHandler" -//go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler" -//go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager" -//go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/lucas-clemente/quic-go multiplexer" diff --git a/vendor/github.com/lucas-clemente/quic-go/mockgen_private.sh b/vendor/github.com/lucas-clemente/quic-go/mockgen_private.sh deleted file mode 100644 index 0ba5f64ef..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/mockgen_private.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Mockgen refuses to generate mocks private types. -# This script copies the quic package to a temporary directory, and adds an public alias for the private type. -# It then creates a mock for this public (alias) type. - -TEMP_DIR=$(mktemp -d) -mkdir -p $TEMP_DIR/src/github.com/lucas-clemente/quic-go/ - -# uppercase the name of the interface -INTERFACE_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${4:0:1})${4:1}" - -# copy all .go files to a temporary directory -rsync -r --exclude 'vendor' --include='*.go' --include '*/' --exclude '*' $GOPATH/src/github.com/lucas-clemente/quic-go/ $TEMP_DIR/src/github.com/lucas-clemente/quic-go/ - -# create a public alias for the interface, so that mockgen can process it -echo -e "package $1\n" > $TEMP_DIR/src/github.com/lucas-clemente/quic-go/mockgen_interface.go -echo "type $INTERFACE_NAME = $4" >> $TEMP_DIR/src/github.com/lucas-clemente/quic-go/mockgen_interface.go - -export GOPATH="$TEMP_DIR:$GOPATH" - -mockgen -package $1 -self_package $1 -destination $2 $3 $INTERFACE_NAME - -# mockgen imports quic-go as 'import quic_go github.com/lucas_clemente/quic-go' -sed -i '' 's/quic_go.//g' $2 -goimports -w $2 - -rm -r "$TEMP_DIR" diff --git a/vendor/github.com/lucas-clemente/quic-go/multiplexer.go b/vendor/github.com/lucas-clemente/quic-go/multiplexer.go deleted file mode 100644 index eeffca53b..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/multiplexer.go +++ /dev/null @@ -1,89 +0,0 @@ -package quic - -import ( - "bytes" - "fmt" - "net" - "sync" - - "github.com/lucas-clemente/quic-go/internal/utils" -) - -var ( - connMuxerOnce sync.Once - connMuxer multiplexer -) - -type multiplexer interface { - AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte) (packetHandlerManager, error) - RemoveConn(net.PacketConn) error -} - -type connManager struct { - connIDLen int - statelessResetKey []byte - manager packetHandlerManager -} - -// The connMultiplexer listens on multiple net.PacketConns and dispatches -// incoming packets to the session handler. -type connMultiplexer struct { - mutex sync.Mutex - - conns map[net.PacketConn]connManager - newPacketHandlerManager func(net.PacketConn, int, []byte, utils.Logger) packetHandlerManager // so it can be replaced in the tests - - logger utils.Logger -} - -var _ multiplexer = &connMultiplexer{} - -func getMultiplexer() multiplexer { - connMuxerOnce.Do(func() { - connMuxer = &connMultiplexer{ - conns: make(map[net.PacketConn]connManager), - logger: utils.DefaultLogger.WithPrefix("muxer"), - newPacketHandlerManager: newPacketHandlerMap, - } - }) - return connMuxer -} - -func (m *connMultiplexer) AddConn( - c net.PacketConn, - connIDLen int, - statelessResetKey []byte, -) (packetHandlerManager, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - p, ok := m.conns[c] - if !ok { - manager := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, m.logger) - p = connManager{ - connIDLen: connIDLen, - statelessResetKey: statelessResetKey, - manager: manager, - } - m.conns[c] = p - } - if p.connIDLen != connIDLen { - return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen) - } - if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) { - return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn") - } - return p.manager, nil -} - -func (m *connMultiplexer) RemoveConn(c net.PacketConn) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - if _, ok := m.conns[c]; !ok { - return fmt.Errorf("cannote remove connection, connection is unknown") - } - - delete(m.conns, c) - return nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go b/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go deleted file mode 100644 index 0af6a4074..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/packet_handler_map.go +++ /dev/null @@ -1,274 +0,0 @@ -package quic - -import ( - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "errors" - "hash" - "net" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// The packetHandlerMap stores packetHandlers, identified by connection ID. -// It is used: -// * by the server to store sessions -// * when multiplexing outgoing connections to store clients -type packetHandlerMap struct { - mutex sync.RWMutex - - conn net.PacketConn - connIDLen int - - handlers map[string] /* string(ConnectionID)*/ packetHandler - resetTokens map[[16]byte] /* stateless reset token */ packetHandler - server unknownPacketHandler - - listening chan struct{} // is closed when listen returns - closed bool - - deleteRetiredSessionsAfter time.Duration - - statelessResetEnabled bool - statelessResetHasher hash.Hash - - logger utils.Logger -} - -var _ packetHandlerManager = &packetHandlerMap{} - -func newPacketHandlerMap( - conn net.PacketConn, - connIDLen int, - statelessResetKey []byte, - logger utils.Logger, -) packetHandlerManager { - m := &packetHandlerMap{ - conn: conn, - connIDLen: connIDLen, - listening: make(chan struct{}), - handlers: make(map[string]packetHandler), - resetTokens: make(map[[16]byte]packetHandler), - deleteRetiredSessionsAfter: protocol.RetiredConnectionIDDeleteTimeout, - statelessResetEnabled: len(statelessResetKey) > 0, - statelessResetHasher: hmac.New(sha256.New, statelessResetKey), - logger: logger, - } - go m.listen() - return m -} - -func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) { - h.mutex.Lock() - h.handlers[string(id)] = handler - h.mutex.Unlock() -} - -func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { - h.removeByConnectionIDAsString(string(id)) -} - -func (h *packetHandlerMap) removeByConnectionIDAsString(id string) { - h.mutex.Lock() - delete(h.handlers, id) - h.mutex.Unlock() -} - -func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { - h.retireByConnectionIDAsString(string(id)) -} - -func (h *packetHandlerMap) retireByConnectionIDAsString(id string) { - time.AfterFunc(h.deleteRetiredSessionsAfter, func() { - h.removeByConnectionIDAsString(id) - }) -} - -func (h *packetHandlerMap) AddResetToken(token [16]byte, handler packetHandler) { - h.mutex.Lock() - h.resetTokens[token] = handler - h.mutex.Unlock() -} - -func (h *packetHandlerMap) RemoveResetToken(token [16]byte) { - h.mutex.Lock() - delete(h.resetTokens, token) - h.mutex.Unlock() -} - -func (h *packetHandlerMap) SetServer(s unknownPacketHandler) { - h.mutex.Lock() - h.server = s - h.mutex.Unlock() -} - -func (h *packetHandlerMap) CloseServer() { - h.mutex.Lock() - h.server = nil - var wg sync.WaitGroup - for id, handler := range h.handlers { - if handler.getPerspective() == protocol.PerspectiveServer { - wg.Add(1) - go func(id string, handler packetHandler) { - // session.Close() blocks until the CONNECTION_CLOSE has been sent and the run-loop has stopped - _ = handler.Close() - h.retireByConnectionIDAsString(id) - wg.Done() - }(id, handler) - } - } - h.mutex.Unlock() - wg.Wait() -} - -// Close the underlying connection and wait until listen() has returned. -func (h *packetHandlerMap) Close() error { - if err := h.conn.Close(); err != nil { - return err - } - <-h.listening // wait until listening returns - return nil -} - -func (h *packetHandlerMap) close(e error) error { - h.mutex.Lock() - if h.closed { - h.mutex.Unlock() - return nil - } - h.closed = true - - var wg sync.WaitGroup - for _, handler := range h.handlers { - wg.Add(1) - go func(handler packetHandler) { - handler.destroy(e) - wg.Done() - }(handler) - } - - if h.server != nil { - h.server.closeWithError(e) - } - h.mutex.Unlock() - wg.Wait() - return getMultiplexer().RemoveConn(h.conn) -} - -func (h *packetHandlerMap) listen() { - defer close(h.listening) - for { - buffer := getPacketBuffer() - data := buffer.Slice - // The packet size should not exceed protocol.MaxReceivePacketSize bytes - // If it does, we only read a truncated packet, which will then end up undecryptable - n, addr, err := h.conn.ReadFrom(data) - if err != nil { - h.close(err) - return - } - h.handlePacket(addr, buffer, data[:n]) - } -} - -func (h *packetHandlerMap) handlePacket( - addr net.Addr, - buffer *packetBuffer, - data []byte, -) { - connID, err := wire.ParseConnectionID(data, h.connIDLen) - if err != nil { - h.logger.Debugf("error parsing connection ID on packet from %s: %s", addr, err) - return - } - rcvTime := time.Now() - - h.mutex.RLock() - defer h.mutex.RUnlock() - - if isStatelessReset := h.maybeHandleStatelessReset(data); isStatelessReset { - return - } - - handler, handlerFound := h.handlers[string(connID)] - - p := &receivedPacket{ - remoteAddr: addr, - rcvTime: rcvTime, - buffer: buffer, - data: data, - } - if handlerFound { // existing session - handler.handlePacket(p) - return - } - if data[0]&0x80 == 0 { - go h.maybeSendStatelessReset(p, connID) - return - } - if h.server == nil { // no server set - h.logger.Debugf("received a packet with an unexpected connection ID %s", connID) - return - } - h.server.handlePacket(p) -} - -func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool { - // stateless resets are always short header packets - if data[0]&0x80 != 0 { - return false - } - if len(data) < protocol.MinStatelessResetSize { - return false - } - - var token [16]byte - copy(token[:], data[len(data)-16:]) - if sess, ok := h.resetTokens[token]; ok { - h.logger.Debugf("Received a stateless retry with token %#x. Closing session.", token) - go sess.destroy(errors.New("received a stateless reset")) - return true - } - return false -} - -func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) [16]byte { - var token [16]byte - if !h.statelessResetEnabled { - // Return a random stateless reset token. - // This token will be sent in the server's transport parameters. - // By using a random token, an off-path attacker won't be able to disrupt the connection. - rand.Read(token[:]) - return token - } - h.statelessResetHasher.Write(connID.Bytes()) - copy(token[:], h.statelessResetHasher.Sum(nil)) - h.statelessResetHasher.Reset() - return token -} - -func (h *packetHandlerMap) maybeSendStatelessReset(p *receivedPacket, connID protocol.ConnectionID) { - defer p.buffer.Release() - if !h.statelessResetEnabled { - return - } - // Don't send a stateless reset in response to very small packets. - // This includes packets that could be stateless resets. - if len(p.data) <= protocol.MinStatelessResetSize { - return - } - token := h.GetStatelessResetToken(connID) - h.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) - data := make([]byte, 23) - rand.Read(data) - data[0] = (data[0] & 0x7f) | 0x40 - data = append(data, token[:]...) - if _, err := h.conn.WriteTo(data, p.remoteAddr); err != nil { - h.logger.Debugf("Error sending Stateless Reset: %s", err) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/packet_packer.go b/vendor/github.com/lucas-clemente/quic-go/packet_packer.go deleted file mode 100644 index 7fae340e6..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/packet_packer.go +++ /dev/null @@ -1,491 +0,0 @@ -package quic - -import ( - "bytes" - "errors" - "fmt" - "net" - "time" - - "github.com/lucas-clemente/quic-go/internal/ackhandler" - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type packer interface { - PackPacket() (*packedPacket, error) - MaybePackAckPacket() (*packedPacket, error) - PackRetransmission(packet *ackhandler.Packet) ([]*packedPacket, error) - PackConnectionClose(*wire.ConnectionCloseFrame) (*packedPacket, error) - - HandleTransportParameters(*handshake.TransportParameters) - SetToken([]byte) - ChangeDestConnectionID(protocol.ConnectionID) -} - -type packedPacket struct { - header *wire.ExtendedHeader - raw []byte - frames []wire.Frame - - buffer *packetBuffer -} - -func (p *packedPacket) EncryptionLevel() protocol.EncryptionLevel { - if !p.header.IsLongHeader { - return protocol.Encryption1RTT - } - switch p.header.Type { - case protocol.PacketTypeInitial: - return protocol.EncryptionInitial - case protocol.PacketTypeHandshake: - return protocol.EncryptionHandshake - default: - return protocol.EncryptionUnspecified - } -} - -func (p *packedPacket) IsRetransmittable() bool { - return ackhandler.HasRetransmittableFrames(p.frames) -} - -func (p *packedPacket) ToAckHandlerPacket() *ackhandler.Packet { - return &ackhandler.Packet{ - PacketNumber: p.header.PacketNumber, - PacketType: p.header.Type, - Frames: p.frames, - Length: protocol.ByteCount(len(p.raw)), - EncryptionLevel: p.EncryptionLevel(), - SendTime: time.Now(), - } -} - -func getMaxPacketSize(addr net.Addr) protocol.ByteCount { - maxSize := protocol.ByteCount(protocol.MinInitialPacketSize) - // If this is not a UDP address, we don't know anything about the MTU. - // Use the minimum size of an Initial packet as the max packet size. - if udpAddr, ok := addr.(*net.UDPAddr); ok { - // If ip is not an IPv4 address, To4 returns nil. - // Note that there might be some corner cases, where this is not correct. - // See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6. - if udpAddr.IP.To4() == nil { - maxSize = protocol.MaxPacketSizeIPv6 - } else { - maxSize = protocol.MaxPacketSizeIPv4 - } - } - return maxSize -} - -type packetNumberManager interface { - PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) - PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber -} - -type sealingManager interface { - GetSealer() (protocol.EncryptionLevel, handshake.Sealer) - GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (handshake.Sealer, error) -} - -type frameSource interface { - AppendStreamFrames([]wire.Frame, protocol.ByteCount) []wire.Frame - AppendControlFrames([]wire.Frame, protocol.ByteCount) ([]wire.Frame, protocol.ByteCount) -} - -type ackFrameSource interface { - GetAckFrame(protocol.EncryptionLevel) *wire.AckFrame -} - -type packetPacker struct { - destConnID protocol.ConnectionID - srcConnID protocol.ConnectionID - - perspective protocol.Perspective - version protocol.VersionNumber - cryptoSetup sealingManager - - initialStream cryptoStream - handshakeStream cryptoStream - - token []byte - - pnManager packetNumberManager - framer frameSource - acks ackFrameSource - - maxPacketSize protocol.ByteCount - numNonRetransmittableAcks int -} - -var _ packer = &packetPacker{} - -func newPacketPacker( - destConnID protocol.ConnectionID, - srcConnID protocol.ConnectionID, - initialStream cryptoStream, - handshakeStream cryptoStream, - packetNumberManager packetNumberManager, - remoteAddr net.Addr, // only used for determining the max packet size - cryptoSetup sealingManager, - framer frameSource, - acks ackFrameSource, - perspective protocol.Perspective, - version protocol.VersionNumber, -) *packetPacker { - return &packetPacker{ - cryptoSetup: cryptoSetup, - destConnID: destConnID, - srcConnID: srcConnID, - initialStream: initialStream, - handshakeStream: handshakeStream, - perspective: perspective, - version: version, - framer: framer, - acks: acks, - pnManager: packetNumberManager, - maxPacketSize: getMaxPacketSize(remoteAddr), - } -} - -// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame -func (p *packetPacker) PackConnectionClose(ccf *wire.ConnectionCloseFrame) (*packedPacket, error) { - frames := []wire.Frame{ccf} - encLevel, sealer := p.cryptoSetup.GetSealer() - header := p.getHeader(encLevel) - return p.writeAndSealPacket(header, frames, encLevel, sealer) -} - -func (p *packetPacker) MaybePackAckPacket() (*packedPacket, error) { - ack := p.acks.GetAckFrame(protocol.Encryption1RTT) - if ack == nil { - return nil, nil - } - // TODO(#1534): only pack ACKs with the right encryption level - encLevel, sealer := p.cryptoSetup.GetSealer() - header := p.getHeader(encLevel) - frames := []wire.Frame{ack} - return p.writeAndSealPacket(header, frames, encLevel, sealer) -} - -// PackRetransmission packs a retransmission -// For packets sent after completion of the handshake, it might happen that 2 packets have to be sent. -// This can happen e.g. when a longer packet number is used in the header. -func (p *packetPacker) PackRetransmission(packet *ackhandler.Packet) ([]*packedPacket, error) { - var controlFrames []wire.Frame - var streamFrames []*wire.StreamFrame - for _, f := range packet.Frames { - // CRYPTO frames are treated as control frames here. - // Since we're making sure that the header can never be larger for a retransmission, - // we never have to split CRYPTO frames. - if sf, ok := f.(*wire.StreamFrame); ok { - sf.DataLenPresent = true - streamFrames = append(streamFrames, sf) - } else { - controlFrames = append(controlFrames, f) - } - } - - var packets []*packedPacket - encLevel := packet.EncryptionLevel - sealer, err := p.cryptoSetup.GetSealerWithEncryptionLevel(encLevel) - if err != nil { - return nil, err - } - for len(controlFrames) > 0 || len(streamFrames) > 0 { - var frames []wire.Frame - var length protocol.ByteCount - - header := p.getHeader(encLevel) - headerLen := header.GetLength(p.version) - maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLen - - for len(controlFrames) > 0 { - frame := controlFrames[0] - frameLen := frame.Length(p.version) - if length+frameLen > maxSize { - break - } - length += frameLen - frames = append(frames, frame) - controlFrames = controlFrames[1:] - } - - for len(streamFrames) > 0 && length+protocol.MinStreamFrameSize < maxSize { - frame := streamFrames[0] - frame.DataLenPresent = false - frameToAdd := frame - - sf, err := frame.MaybeSplitOffFrame(maxSize-length, p.version) - if err != nil { - return nil, err - } - if sf != nil { - frameToAdd = sf - } else { - streamFrames = streamFrames[1:] - } - frame.DataLenPresent = true - length += frameToAdd.Length(p.version) - frames = append(frames, frameToAdd) - } - if sf, ok := frames[len(frames)-1].(*wire.StreamFrame); ok { - sf.DataLenPresent = false - } - p, err := p.writeAndSealPacket(header, frames, encLevel, sealer) - if err != nil { - return nil, err - } - packets = append(packets, p) - } - return packets, nil -} - -// PackPacket packs a new packet -// the other controlFrames are sent in the next packet, but might be queued and sent in the next packet if the packet would overflow MaxPacketSize otherwise -func (p *packetPacker) PackPacket() (*packedPacket, error) { - packet, err := p.maybePackCryptoPacket() - if err != nil { - return nil, err - } - if packet != nil { - return packet, nil - } - - encLevel, sealer := p.cryptoSetup.GetSealer() - header := p.getHeader(encLevel) - headerLen := header.GetLength(p.version) - if err != nil { - return nil, err - } - - maxSize := p.maxPacketSize - protocol.ByteCount(sealer.Overhead()) - headerLen - frames, err := p.composeNextPacket(maxSize) - if err != nil { - return nil, err - } - - // Check if we have enough frames to send - if len(frames) == 0 { - return nil, nil - } - // check if this packet only contains an ACK - if !ackhandler.HasRetransmittableFrames(frames) { - if p.numNonRetransmittableAcks >= protocol.MaxNonRetransmittableAcks { - frames = append(frames, &wire.PingFrame{}) - p.numNonRetransmittableAcks = 0 - } else { - p.numNonRetransmittableAcks++ - } - } else { - p.numNonRetransmittableAcks = 0 - } - - return p.writeAndSealPacket(header, frames, encLevel, sealer) -} - -func (p *packetPacker) maybePackCryptoPacket() (*packedPacket, error) { - var s cryptoStream - var encLevel protocol.EncryptionLevel - - hasData := p.initialStream.HasData() - ack := p.acks.GetAckFrame(protocol.EncryptionInitial) - if hasData || ack != nil { - s = p.initialStream - encLevel = protocol.EncryptionInitial - } else { - hasData = p.handshakeStream.HasData() - ack = p.acks.GetAckFrame(protocol.EncryptionHandshake) - if hasData || ack != nil { - s = p.handshakeStream - encLevel = protocol.EncryptionHandshake - } - } - if s == nil { - return nil, nil - } - sealer, err := p.cryptoSetup.GetSealerWithEncryptionLevel(encLevel) - if err != nil { - // The sealer - return nil, err - } - - hdr := p.getHeader(encLevel) - hdrLen := hdr.GetLength(p.version) - var length protocol.ByteCount - frames := make([]wire.Frame, 0, 2) - if ack != nil { - frames = append(frames, ack) - length += ack.Length(p.version) - } - if hasData { - cf := s.PopCryptoFrame(p.maxPacketSize - hdrLen - protocol.ByteCount(sealer.Overhead()) - length) - frames = append(frames, cf) - } - return p.writeAndSealPacket(hdr, frames, encLevel, sealer) -} - -func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount) ([]wire.Frame, error) { - var length protocol.ByteCount - var frames []wire.Frame - - // ACKs need to go first, so that the sentPacketHandler will recognize them - if ack := p.acks.GetAckFrame(protocol.Encryption1RTT); ack != nil { - frames = append(frames, ack) - length += ack.Length(p.version) - } - - var lengthAdded protocol.ByteCount - frames, lengthAdded = p.framer.AppendControlFrames(frames, maxFrameSize-length) - length += lengthAdded - - // temporarily increase the maxFrameSize by the (minimum) length of the DataLen field - // this leads to a properly sized packet in all cases, since we do all the packet length calculations with STREAM frames that have the DataLen set - // however, for the last STREAM frame in the packet, we can omit the DataLen, thus yielding a packet of exactly the correct size - // the length is encoded to either 1 or 2 bytes - maxFrameSize++ - - frames = p.framer.AppendStreamFrames(frames, maxFrameSize-length) - if len(frames) > 0 { - lastFrame := frames[len(frames)-1] - if sf, ok := lastFrame.(*wire.StreamFrame); ok { - sf.DataLenPresent = false - } - } - return frames, nil -} - -func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.ExtendedHeader { - pn, pnLen := p.pnManager.PeekPacketNumber(encLevel) - header := &wire.ExtendedHeader{} - header.PacketNumber = pn - header.PacketNumberLen = pnLen - header.Version = p.version - header.DestConnectionID = p.destConnID - - if encLevel != protocol.Encryption1RTT { - header.IsLongHeader = true - // Always send Initial and Handshake packets with the maximum packet number length. - // This simplifies retransmissions: Since the header can't get any larger, - // we don't need to split CRYPTO frames. - header.PacketNumberLen = protocol.PacketNumberLen4 - header.SrcConnectionID = p.srcConnID - // Set the length to the maximum packet size. - // Since it is encoded as a varint, this guarantees us that the header will end up at most as big as GetLength() returns. - header.Length = p.maxPacketSize - switch encLevel { - case protocol.EncryptionInitial: - header.Type = protocol.PacketTypeInitial - case protocol.EncryptionHandshake: - header.Type = protocol.PacketTypeHandshake - } - } - - return header -} - -func (p *packetPacker) writeAndSealPacket( - header *wire.ExtendedHeader, - frames []wire.Frame, - encLevel protocol.EncryptionLevel, - sealer handshake.Sealer, -) (*packedPacket, error) { - packetBuffer := getPacketBuffer() - buffer := bytes.NewBuffer(packetBuffer.Slice[:0]) - - addPaddingForInitial := p.perspective == protocol.PerspectiveClient && header.Type == protocol.PacketTypeInitial - - if header.IsLongHeader { - if p.perspective == protocol.PerspectiveClient && header.Type == protocol.PacketTypeInitial { - header.Token = p.token - } - if addPaddingForInitial { - headerLen := header.GetLength(p.version) - header.Length = protocol.ByteCount(header.PacketNumberLen) + protocol.MinInitialPacketSize - headerLen - } else { - // long header packets always use 4 byte packet number, so we never need to pad short payloads - length := protocol.ByteCount(sealer.Overhead()) + protocol.ByteCount(header.PacketNumberLen) - for _, frame := range frames { - length += frame.Length(p.version) - } - header.Length = length - } - } - - if err := header.Write(buffer, p.version); err != nil { - return nil, err - } - payloadOffset := buffer.Len() - - // write all frames but the last one - for _, frame := range frames[:len(frames)-1] { - if err := frame.Write(buffer, p.version); err != nil { - return nil, err - } - } - lastFrame := frames[len(frames)-1] - if addPaddingForInitial { - // when appending padding, we need to make sure that the last STREAM frames has the data length set - if sf, ok := lastFrame.(*wire.StreamFrame); ok { - sf.DataLenPresent = true - } - } else { - payloadLen := buffer.Len() - payloadOffset + int(lastFrame.Length(p.version)) - if paddingLen := 4 - int(header.PacketNumberLen) - payloadLen; paddingLen > 0 { - // Pad the packet such that packet number length + payload length is 4 bytes. - // This is needed to enable the peer to get a 16 byte sample for header protection. - buffer.Write(bytes.Repeat([]byte{0}, paddingLen)) - } - } - if err := lastFrame.Write(buffer, p.version); err != nil { - return nil, err - } - - if addPaddingForInitial { - paddingLen := protocol.MinInitialPacketSize - sealer.Overhead() - buffer.Len() - if paddingLen > 0 { - buffer.Write(bytes.Repeat([]byte{0}, paddingLen)) - } - } - - if size := protocol.ByteCount(buffer.Len() + sealer.Overhead()); size > p.maxPacketSize { - return nil, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, p.maxPacketSize) - } - - raw := buffer.Bytes() - _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], header.PacketNumber, raw[:payloadOffset]) - raw = raw[0 : buffer.Len()+sealer.Overhead()] - - pnOffset := payloadOffset - int(header.PacketNumberLen) - sealer.EncryptHeader( - raw[pnOffset+4:pnOffset+4+16], - &raw[0], - raw[pnOffset:payloadOffset], - ) - - num := p.pnManager.PopPacketNumber(encLevel) - if num != header.PacketNumber { - return nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match") - } - return &packedPacket{ - header: header, - raw: raw, - frames: frames, - buffer: packetBuffer, - }, nil -} - -func (p *packetPacker) ChangeDestConnectionID(connID protocol.ConnectionID) { - p.destConnID = connID -} - -func (p *packetPacker) SetToken(token []byte) { - p.token = token -} - -func (p *packetPacker) HandleTransportParameters(params *handshake.TransportParameters) { - if params.MaxPacketSize != 0 { - p.maxPacketSize = utils.MinByteCount(p.maxPacketSize, params.MaxPacketSize) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/packet_unpacker.go b/vendor/github.com/lucas-clemente/quic-go/packet_unpacker.go deleted file mode 100644 index f3b5a2f77..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/packet_unpacker.go +++ /dev/null @@ -1,102 +0,0 @@ -package quic - -import ( - "bytes" - "fmt" - - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type unpackedPacket struct { - packetNumber protocol.PacketNumber // the decoded packet number - hdr *wire.ExtendedHeader - encryptionLevel protocol.EncryptionLevel - data []byte -} - -// The packetUnpacker unpacks QUIC packets. -type packetUnpacker struct { - cs handshake.CryptoSetup - - largestRcvdPacketNumber protocol.PacketNumber - - version protocol.VersionNumber -} - -var _ unpacker = &packetUnpacker{} - -func newPacketUnpacker(cs handshake.CryptoSetup, version protocol.VersionNumber) unpacker { - return &packetUnpacker{ - cs: cs, - version: version, - } -} - -func (u *packetUnpacker) Unpack(hdr *wire.Header, data []byte) (*unpackedPacket, error) { - r := bytes.NewReader(data) - - var encLevel protocol.EncryptionLevel - switch hdr.Type { - case protocol.PacketTypeInitial: - encLevel = protocol.EncryptionInitial - case protocol.PacketTypeHandshake: - encLevel = protocol.EncryptionHandshake - default: - if hdr.IsLongHeader { - return nil, fmt.Errorf("unknown packet type: %s", hdr.Type) - } - encLevel = protocol.Encryption1RTT - } - opener, err := u.cs.GetOpener(encLevel) - if err != nil { - return nil, err - } - hdrLen := int(hdr.ParsedLen()) - if len(data) < hdrLen+4+16 { - return nil, fmt.Errorf("Packet too small. Expected at least 20 bytes after the header, got %d", len(data)-hdrLen) - } - // The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it. - // 1. save a copy of the 4 bytes - origPNBytes := make([]byte, 4) - copy(origPNBytes, data[hdrLen:hdrLen+4]) - // 2. decrypt the header, assuming a 4 byte packet number - opener.DecryptHeader( - data[hdrLen+4:hdrLen+4+16], - &data[0], - data[hdrLen:hdrLen+4], - ) - // 3. parse the header (and learn the actual length of the packet number) - extHdr, err := hdr.ParseExtended(r, u.version) - if err != nil { - return nil, fmt.Errorf("error parsing extended header: %s", err) - } - extHdrLen := hdrLen + int(extHdr.PacketNumberLen) - // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier - if extHdr.PacketNumberLen != protocol.PacketNumberLen4 { - copy(data[extHdrLen:hdrLen+4], origPNBytes[int(extHdr.PacketNumberLen):]) - } - - pn := protocol.DecodePacketNumber( - extHdr.PacketNumberLen, - u.largestRcvdPacketNumber, - extHdr.PacketNumber, - ) - - decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], pn, data[:extHdrLen]) - if err != nil { - return nil, err - } - - // Only do this after decrypting, so we are sure the packet is not attacker-controlled - u.largestRcvdPacketNumber = utils.MaxPacketNumber(u.largestRcvdPacketNumber, pn) - - return &unpackedPacket{ - hdr: extHdr, - packetNumber: pn, - encryptionLevel: encLevel, - data: decrypted, - }, nil -} diff --git a/vendor/github.com/lucas-clemente/quic-go/receive_stream.go b/vendor/github.com/lucas-clemente/quic-go/receive_stream.go deleted file mode 100644 index de76335e7..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/receive_stream.go +++ /dev/null @@ -1,324 +0,0 @@ -package quic - -import ( - "fmt" - "io" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type receiveStreamI interface { - ReceiveStream - - handleStreamFrame(*wire.StreamFrame) error - handleResetStreamFrame(*wire.ResetStreamFrame) error - closeForShutdown(error) - getWindowUpdate() protocol.ByteCount -} - -type receiveStream struct { - mutex sync.Mutex - - streamID protocol.StreamID - - sender streamSender - - frameQueue *frameSorter - readOffset protocol.ByteCount - finalOffset protocol.ByteCount - - currentFrame []byte - currentFrameIsLast bool // is the currentFrame the last frame on this stream - readPosInFrame int - - closeForShutdownErr error - cancelReadErr error - resetRemotelyErr StreamError - - closedForShutdown bool // set when CloseForShutdown() is called - finRead bool // set once we read a frame with a FinBit - canceledRead bool // set when CancelRead() is called - resetRemotely bool // set when HandleResetStreamFrame() is called - - readChan chan struct{} - deadline time.Time - - flowController flowcontrol.StreamFlowController - version protocol.VersionNumber -} - -var _ ReceiveStream = &receiveStream{} -var _ receiveStreamI = &receiveStream{} - -func newReceiveStream( - streamID protocol.StreamID, - sender streamSender, - flowController flowcontrol.StreamFlowController, - version protocol.VersionNumber, -) *receiveStream { - return &receiveStream{ - streamID: streamID, - sender: sender, - flowController: flowController, - frameQueue: newFrameSorter(), - readChan: make(chan struct{}, 1), - finalOffset: protocol.MaxByteCount, - version: version, - } -} - -func (s *receiveStream) StreamID() protocol.StreamID { - return s.streamID -} - -// Read implements io.Reader. It is not thread safe! -func (s *receiveStream) Read(p []byte) (int, error) { - s.mutex.Lock() - completed, n, err := s.readImpl(p) - s.mutex.Unlock() - - if completed { - s.streamCompleted() - } - return n, err -} - -func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, error) { - if s.finRead { - return false, 0, io.EOF - } - if s.canceledRead { - return false, 0, s.cancelReadErr - } - if s.resetRemotely { - return false, 0, s.resetRemotelyErr - } - if s.closedForShutdown { - return false, 0, s.closeForShutdownErr - } - - bytesRead := 0 - for bytesRead < len(p) { - if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) { - s.dequeueNextFrame() - } - if s.currentFrame == nil && bytesRead > 0 { - return false, bytesRead, s.closeForShutdownErr - } - - var deadlineTimer *utils.Timer - for { - // Stop waiting on errors - if s.closedForShutdown { - return false, bytesRead, s.closeForShutdownErr - } - if s.canceledRead { - return false, bytesRead, s.cancelReadErr - } - if s.resetRemotely { - return false, bytesRead, s.resetRemotelyErr - } - - deadline := s.deadline - if !deadline.IsZero() { - if !time.Now().Before(deadline) { - return false, bytesRead, errDeadline - } - if deadlineTimer == nil { - deadlineTimer = utils.NewTimer() - } - deadlineTimer.Reset(deadline) - } - - if s.currentFrame != nil || s.currentFrameIsLast { - break - } - - s.mutex.Unlock() - if deadline.IsZero() { - <-s.readChan - } else { - select { - case <-s.readChan: - case <-deadlineTimer.Chan(): - deadlineTimer.SetRead() - } - } - s.mutex.Lock() - if s.currentFrame == nil { - s.dequeueNextFrame() - } - } - - if bytesRead > len(p) { - return false, bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p)) - } - if s.readPosInFrame > len(s.currentFrame) { - return false, bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, len(s.currentFrame)) - } - - s.mutex.Unlock() - - m := copy(p[bytesRead:], s.currentFrame[s.readPosInFrame:]) - s.readPosInFrame += m - bytesRead += m - s.readOffset += protocol.ByteCount(m) - - s.mutex.Lock() - // when a RESET_STREAM was received, the was already informed about the final byteOffset for this stream - if !s.resetRemotely { - s.flowController.AddBytesRead(protocol.ByteCount(m)) - } - - if s.readPosInFrame >= len(s.currentFrame) && s.currentFrameIsLast { - s.finRead = true - return true, bytesRead, io.EOF - } - } - return false, bytesRead, nil -} - -func (s *receiveStream) dequeueNextFrame() { - var offset protocol.ByteCount - offset, s.currentFrame = s.frameQueue.Pop() - s.currentFrameIsLast = offset+protocol.ByteCount(len(s.currentFrame)) >= s.finalOffset - s.readPosInFrame = 0 -} - -func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) { - s.mutex.Lock() - completed := s.cancelReadImpl(errorCode) - s.mutex.Unlock() - - if completed { - s.streamCompleted() - } -} - -func (s *receiveStream) cancelReadImpl(errorCode protocol.ApplicationErrorCode) bool /* completed */ { - if s.finRead || s.canceledRead || s.resetRemotely { - return false - } - s.canceledRead = true - s.cancelReadErr = fmt.Errorf("Read on stream %d canceled with error code %d", s.streamID, errorCode) - s.signalRead() - s.sender.queueControlFrame(&wire.StopSendingFrame{ - StreamID: s.streamID, - ErrorCode: errorCode, - }) - // We're done with this stream if the final offset was already received. - return s.finalOffset != protocol.MaxByteCount -} - -func (s *receiveStream) handleStreamFrame(frame *wire.StreamFrame) error { - s.mutex.Lock() - completed, err := s.handleStreamFrameImpl(frame) - s.mutex.Unlock() - - if completed { - s.streamCompleted() - } - return err -} - -func (s *receiveStream) handleStreamFrameImpl(frame *wire.StreamFrame) (bool /* completed */, error) { - maxOffset := frame.Offset + frame.DataLen() - if err := s.flowController.UpdateHighestReceived(maxOffset, frame.FinBit); err != nil { - return false, err - } - if frame.FinBit { - s.finalOffset = maxOffset - } - if s.canceledRead { - return frame.FinBit, nil - } - if err := s.frameQueue.Push(frame.Data, frame.Offset); err != nil { - return false, err - } - s.signalRead() - return false, nil -} - -func (s *receiveStream) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { - s.mutex.Lock() - completed, err := s.handleResetStreamFrameImpl(frame) - s.mutex.Unlock() - - if completed { - s.streamCompleted() - } - return err -} - -func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame) (bool /*completed */, error) { - if s.closedForShutdown { - return false, nil - } - if err := s.flowController.UpdateHighestReceived(frame.ByteOffset, true); err != nil { - return false, err - } - s.finalOffset = frame.ByteOffset - - // ignore duplicate RESET_STREAM frames for this stream (after checking their final offset) - if s.resetRemotely { - return false, nil - } - s.resetRemotely = true - s.resetRemotelyErr = streamCanceledError{ - errorCode: frame.ErrorCode, - error: fmt.Errorf("Stream %d was reset with error code %d", s.streamID, frame.ErrorCode), - } - s.signalRead() - return true, nil -} - -func (s *receiveStream) CloseRemote(offset protocol.ByteCount) { - s.handleStreamFrame(&wire.StreamFrame{FinBit: true, Offset: offset}) -} - -func (s *receiveStream) SetReadDeadline(t time.Time) error { - s.mutex.Lock() - s.deadline = t - s.mutex.Unlock() - s.signalRead() - return nil -} - -// CloseForShutdown closes a stream abruptly. -// It makes Read unblock (and return the error) immediately. -// The peer will NOT be informed about this: the stream is closed without sending a FIN or RESET. -func (s *receiveStream) closeForShutdown(err error) { - s.mutex.Lock() - s.closedForShutdown = true - s.closeForShutdownErr = err - s.mutex.Unlock() - s.signalRead() -} - -func (s *receiveStream) getWindowUpdate() protocol.ByteCount { - return s.flowController.GetWindowUpdate() -} - -func (s *receiveStream) streamCompleted() { - s.mutex.Lock() - finRead := s.finRead - s.mutex.Unlock() - - if !finRead { - s.flowController.Abandon() - } - s.sender.onStreamCompleted(s.streamID) -} - -// signalRead performs a non-blocking send on the readChan -func (s *receiveStream) signalRead() { - select { - case s.readChan <- struct{}{}: - default: - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/send_stream.go b/vendor/github.com/lucas-clemente/quic-go/send_stream.go deleted file mode 100644 index 46f24c4be..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/send_stream.go +++ /dev/null @@ -1,332 +0,0 @@ -package quic - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type sendStreamI interface { - SendStream - handleStopSendingFrame(*wire.StopSendingFrame) - hasData() bool - popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool) - closeForShutdown(error) - handleMaxStreamDataFrame(*wire.MaxStreamDataFrame) -} - -type sendStream struct { - mutex sync.Mutex - - ctx context.Context - ctxCancel context.CancelFunc - - streamID protocol.StreamID - sender streamSender - - writeOffset protocol.ByteCount - - cancelWriteErr error - closeForShutdownErr error - - closedForShutdown bool // set when CloseForShutdown() is called - finishedWriting bool // set once Close() is called - canceledWrite bool // set when CancelWrite() is called, or a STOP_SENDING frame is received - finSent bool // set when a STREAM_FRAME with FIN bit has b - - dataForWriting []byte - - writeChan chan struct{} - deadline time.Time - - flowController flowcontrol.StreamFlowController - - version protocol.VersionNumber -} - -var _ SendStream = &sendStream{} -var _ sendStreamI = &sendStream{} - -func newSendStream( - streamID protocol.StreamID, - sender streamSender, - flowController flowcontrol.StreamFlowController, - version protocol.VersionNumber, -) *sendStream { - s := &sendStream{ - streamID: streamID, - sender: sender, - flowController: flowController, - writeChan: make(chan struct{}, 1), - version: version, - } - s.ctx, s.ctxCancel = context.WithCancel(context.Background()) - return s -} - -func (s *sendStream) StreamID() protocol.StreamID { - return s.streamID // same for receiveStream and sendStream -} - -func (s *sendStream) Write(p []byte) (int, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.finishedWriting { - return 0, fmt.Errorf("write on closed stream %d", s.streamID) - } - if s.canceledWrite { - return 0, s.cancelWriteErr - } - if s.closeForShutdownErr != nil { - return 0, s.closeForShutdownErr - } - if !s.deadline.IsZero() && !time.Now().Before(s.deadline) { - return 0, errDeadline - } - if len(p) == 0 { - return 0, nil - } - - s.dataForWriting = p - - var ( - deadlineTimer *utils.Timer - bytesWritten int - notifiedSender bool - ) - for { - bytesWritten = len(p) - len(s.dataForWriting) - deadline := s.deadline - if !deadline.IsZero() { - if !time.Now().Before(deadline) { - s.dataForWriting = nil - return bytesWritten, errDeadline - } - if deadlineTimer == nil { - deadlineTimer = utils.NewTimer() - } - deadlineTimer.Reset(deadline) - } - if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown { - break - } - - s.mutex.Unlock() - if !notifiedSender { - s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex - notifiedSender = true - } - if deadline.IsZero() { - <-s.writeChan - } else { - select { - case <-s.writeChan: - case <-deadlineTimer.Chan(): - deadlineTimer.SetRead() - } - } - s.mutex.Lock() - } - - if s.closeForShutdownErr != nil { - return bytesWritten, s.closeForShutdownErr - } else if s.cancelWriteErr != nil { - return bytesWritten, s.cancelWriteErr - } - return bytesWritten, nil -} - -// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream -// maxBytes is the maximum length this frame (including frame header) will have. -func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more data to send */) { - s.mutex.Lock() - completed, frame, hasMoreData := s.popStreamFrameImpl(maxBytes) - s.mutex.Unlock() - - if completed { - s.sender.onStreamCompleted(s.streamID) - } - return frame, hasMoreData -} - -func (s *sendStream) popStreamFrameImpl(maxBytes protocol.ByteCount) (bool /* completed */, *wire.StreamFrame, bool /* has more data to send */) { - if s.canceledWrite || s.closeForShutdownErr != nil { - return false, nil, false - } - - frame := &wire.StreamFrame{ - StreamID: s.streamID, - Offset: s.writeOffset, - DataLenPresent: true, - } - maxDataLen := frame.MaxDataLen(maxBytes, s.version) - if maxDataLen == 0 { // a STREAM frame must have at least one byte of data - return false, nil, s.dataForWriting != nil - } - frame.Data, frame.FinBit = s.getDataForWriting(maxDataLen) - if len(frame.Data) == 0 && !frame.FinBit { - // this can happen if: - // - popStreamFrame is called but there's no data for writing - // - there's data for writing, but the stream is stream-level flow control blocked - // - there's data for writing, but the stream is connection-level flow control blocked - if s.dataForWriting == nil { - return false, nil, false - } - if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked { - s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{ - StreamID: s.streamID, - DataLimit: offset, - }) - return false, nil, false - } - return false, nil, true - } - if frame.FinBit { - s.finSent = true - } - return frame.FinBit, frame, s.dataForWriting != nil -} - -func (s *sendStream) hasData() bool { - s.mutex.Lock() - hasData := len(s.dataForWriting) > 0 - s.mutex.Unlock() - return hasData -} - -func (s *sendStream) getDataForWriting(maxBytes protocol.ByteCount) ([]byte, bool /* should send FIN */) { - if s.dataForWriting == nil { - return nil, s.finishedWriting && !s.finSent - } - - maxBytes = utils.MinByteCount(maxBytes, s.flowController.SendWindowSize()) - if maxBytes == 0 { - return nil, false - } - - var ret []byte - if protocol.ByteCount(len(s.dataForWriting)) > maxBytes { - ret = make([]byte, int(maxBytes)) - copy(ret, s.dataForWriting[:maxBytes]) - s.dataForWriting = s.dataForWriting[maxBytes:] - } else { - ret = make([]byte, len(s.dataForWriting)) - copy(ret, s.dataForWriting) - s.dataForWriting = nil - s.signalWrite() - } - s.writeOffset += protocol.ByteCount(len(ret)) - s.flowController.AddBytesSent(protocol.ByteCount(len(ret))) - return ret, s.finishedWriting && s.dataForWriting == nil && !s.finSent -} - -func (s *sendStream) Close() error { - s.mutex.Lock() - if s.canceledWrite { - s.mutex.Unlock() - return fmt.Errorf("Close called for canceled stream %d", s.streamID) - } - s.finishedWriting = true - s.mutex.Unlock() - - s.sender.onHasStreamData(s.streamID) // need to send the FIN, must be called without holding the mutex - s.ctxCancel() - return nil -} - -func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) { - s.mutex.Lock() - completed := s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode)) - s.mutex.Unlock() - - if completed { - s.sender.onStreamCompleted(s.streamID) // must be called without holding the mutex - } -} - -// must be called after locking the mutex -func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) bool /*completed */ { - if s.canceledWrite || s.finishedWriting { - return false - } - s.canceledWrite = true - s.cancelWriteErr = writeErr - s.signalWrite() - s.sender.queueControlFrame(&wire.ResetStreamFrame{ - StreamID: s.streamID, - ByteOffset: s.writeOffset, - ErrorCode: errorCode, - }) - // TODO(#991): cancel retransmissions for this stream - s.ctxCancel() - return true -} - -func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) { - s.mutex.Lock() - hasStreamData := s.dataForWriting != nil - s.mutex.Unlock() - - s.flowController.UpdateSendWindow(frame.ByteOffset) - if hasStreamData { - s.sender.onHasStreamData(s.streamID) - } -} - -func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) { - s.mutex.Lock() - completed := s.handleStopSendingFrameImpl(frame) - s.mutex.Unlock() - - if completed { - s.sender.onStreamCompleted(s.streamID) - } -} - -// must be called after locking the mutex -func (s *sendStream) handleStopSendingFrameImpl(frame *wire.StopSendingFrame) bool /*completed*/ { - writeErr := streamCanceledError{ - errorCode: frame.ErrorCode, - error: fmt.Errorf("Stream %d was reset with error code %d", s.streamID, frame.ErrorCode), - } - return s.cancelWriteImpl(errorCodeStopping, writeErr) -} - -func (s *sendStream) Context() context.Context { - return s.ctx -} - -func (s *sendStream) SetWriteDeadline(t time.Time) error { - s.mutex.Lock() - s.deadline = t - s.mutex.Unlock() - s.signalWrite() - return nil -} - -// CloseForShutdown closes a stream abruptly. -// It makes Write unblock (and return the error) immediately. -// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. -func (s *sendStream) closeForShutdown(err error) { - s.mutex.Lock() - s.closedForShutdown = true - s.closeForShutdownErr = err - s.mutex.Unlock() - s.signalWrite() - s.ctxCancel() -} - -// signalWrite performs a non-blocking send on the writeChan -func (s *sendStream) signalWrite() { - select { - case s.writeChan <- struct{}{}: - default: - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/server.go b/vendor/github.com/lucas-clemente/quic-go/server.go deleted file mode 100644 index fe2f9388a..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/server.go +++ /dev/null @@ -1,558 +0,0 @@ -package quic - -import ( - "bytes" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "sync" - "sync/atomic" - "time" - - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -// packetHandler handles packets -type packetHandler interface { - handlePacket(*receivedPacket) - io.Closer - destroy(error) - getPerspective() protocol.Perspective -} - -type unknownPacketHandler interface { - handlePacket(*receivedPacket) - closeWithError(error) error -} - -type packetHandlerManager interface { - io.Closer - Add(protocol.ConnectionID, packetHandler) - Retire(protocol.ConnectionID) - Remove(protocol.ConnectionID) - AddResetToken([16]byte, packetHandler) - RemoveResetToken([16]byte) - GetStatelessResetToken(protocol.ConnectionID) [16]byte - SetServer(unknownPacketHandler) - CloseServer() -} - -type quicSession interface { - Session - handlePacket(*receivedPacket) - GetVersion() protocol.VersionNumber - getPerspective() protocol.Perspective - run() error - destroy(error) - closeForRecreating() protocol.PacketNumber - closeRemote(error) -} - -type sessionRunner interface { - OnHandshakeComplete(Session) - Retire(protocol.ConnectionID) - Remove(protocol.ConnectionID) - AddResetToken([16]byte, packetHandler) - RemoveResetToken([16]byte) -} - -type runner struct { - packetHandlerManager - - onHandshakeCompleteImpl func(Session) -} - -func (r *runner) OnHandshakeComplete(s Session) { r.onHandshakeCompleteImpl(s) } - -var _ sessionRunner = &runner{} - -// A Listener of QUIC -type server struct { - mutex sync.Mutex - - tlsConf *tls.Config - config *Config - - conn net.PacketConn - // If the server is started with ListenAddr, we create a packet conn. - // If it is started with Listen, we take a packet conn as a parameter. - createdPacketConn bool - - cookieGenerator *handshake.CookieGenerator - - sessionHandler packetHandlerManager - - // set as a member, so they can be set in the tests - newSession func(connection, sessionRunner, protocol.ConnectionID /* original connection ID */, protocol.ConnectionID /* destination connection ID */, protocol.ConnectionID /* source connection ID */, *Config, *tls.Config, *handshake.TransportParameters, utils.Logger, protocol.VersionNumber) (quicSession, error) - - serverError error - errorChan chan struct{} - closed bool - - sessionQueue chan Session - sessionQueueLen int32 // to be used as an atomic - - sessionRunner sessionRunner - - logger utils.Logger -} - -var _ Listener = &server{} -var _ unknownPacketHandler = &server{} - -// ListenAddr creates a QUIC server listening on a given address. -// The tls.Config must not be nil and must contain a certificate configuration. -// The quic.Config may be nil, in that case the default values will be used. -func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, error) { - udpAddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } - conn, err := net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, err - } - serv, err := listen(conn, tlsConf, config) - if err != nil { - return nil, err - } - serv.createdPacketConn = true - return serv, nil -} - -// Listen listens for QUIC connections on a given net.PacketConn. -// A single PacketConn only be used for a single call to Listen. -// The PacketConn can be used for simultaneous calls to Dial. -// QUIC connection IDs are used for demultiplexing the different connections. -// The tls.Config must not be nil and must contain a certificate configuration. -// The quic.Config may be nil, in that case the default values will be used. -func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, error) { - return listen(conn, tlsConf, config) -} - -func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (*server, error) { - // TODO(#1655): only require that tls.Config.Certificates or tls.Config.GetCertificate is set - if tlsConf == nil || len(tlsConf.Certificates) == 0 { - return nil, errors.New("quic: Certificates not set in tls.Config") - } - config = populateServerConfig(config) - for _, v := range config.Versions { - if !protocol.IsValidVersion(v) { - return nil, fmt.Errorf("%s is not a valid QUIC version", v) - } - } - - sessionHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey) - if err != nil { - return nil, err - } - s := &server{ - conn: conn, - tlsConf: tlsConf, - config: config, - sessionHandler: sessionHandler, - sessionQueue: make(chan Session), - errorChan: make(chan struct{}), - newSession: newSession, - logger: utils.DefaultLogger.WithPrefix("server"), - } - if err := s.setup(); err != nil { - return nil, err - } - sessionHandler.SetServer(s) - s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) - return s, nil -} - -func (s *server) setup() error { - s.sessionRunner = &runner{ - packetHandlerManager: s.sessionHandler, - onHandshakeCompleteImpl: func(sess Session) { - go func() { - atomic.AddInt32(&s.sessionQueueLen, 1) - defer atomic.AddInt32(&s.sessionQueueLen, -1) - select { - case s.sessionQueue <- sess: - // blocks until the session is accepted - case <-sess.Context().Done(): - // don't pass sessions that were already closed to Accept() - } - }() - }, - } - cookieGenerator, err := handshake.NewCookieGenerator() - if err != nil { - return err - } - s.cookieGenerator = cookieGenerator - return nil -} - -var defaultAcceptCookie = func(clientAddr net.Addr, cookie *Cookie) bool { - if cookie == nil { - return false - } - if time.Now().After(cookie.SentTime.Add(protocol.CookieExpiryTime)) { - return false - } - var sourceAddr string - if udpAddr, ok := clientAddr.(*net.UDPAddr); ok { - sourceAddr = udpAddr.IP.String() - } else { - sourceAddr = clientAddr.String() - } - return sourceAddr == cookie.RemoteAddr -} - -// populateServerConfig populates fields in the quic.Config with their default values, if none are set -// it may be called with nil -func populateServerConfig(config *Config) *Config { - if config == nil { - config = &Config{} - } - versions := config.Versions - if len(versions) == 0 { - versions = protocol.SupportedVersions - } - - vsa := defaultAcceptCookie - if config.AcceptCookie != nil { - vsa = config.AcceptCookie - } - - handshakeTimeout := protocol.DefaultHandshakeTimeout - if config.HandshakeTimeout != 0 { - handshakeTimeout = config.HandshakeTimeout - } - idleTimeout := protocol.DefaultIdleTimeout - if config.IdleTimeout != 0 { - idleTimeout = config.IdleTimeout - } - - maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow - if maxReceiveStreamFlowControlWindow == 0 { - maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow - } - maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow - if maxReceiveConnectionFlowControlWindow == 0 { - maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow - } - maxIncomingStreams := config.MaxIncomingStreams - if maxIncomingStreams == 0 { - maxIncomingStreams = protocol.DefaultMaxIncomingStreams - } else if maxIncomingStreams < 0 { - maxIncomingStreams = 0 - } - maxIncomingUniStreams := config.MaxIncomingUniStreams - if maxIncomingUniStreams == 0 { - maxIncomingUniStreams = protocol.DefaultMaxIncomingUniStreams - } else if maxIncomingUniStreams < 0 { - maxIncomingUniStreams = 0 - } - connIDLen := config.ConnectionIDLength - if connIDLen == 0 { - connIDLen = protocol.DefaultConnectionIDLength - } - - return &Config{ - Versions: versions, - HandshakeTimeout: handshakeTimeout, - IdleTimeout: idleTimeout, - AcceptCookie: vsa, - KeepAlive: config.KeepAlive, - MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow, - MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow, - MaxIncomingStreams: maxIncomingStreams, - MaxIncomingUniStreams: maxIncomingUniStreams, - ConnectionIDLength: connIDLen, - StatelessResetKey: config.StatelessResetKey, - } -} - -// Accept returns newly openend sessions -func (s *server) Accept() (Session, error) { - var sess Session - select { - case sess = <-s.sessionQueue: - return sess, nil - case <-s.errorChan: - return nil, s.serverError - } -} - -// Close the server -func (s *server) Close() error { - s.mutex.Lock() - defer s.mutex.Unlock() - if s.closed { - return nil - } - return s.closeWithMutex() -} - -func (s *server) closeWithMutex() error { - s.sessionHandler.CloseServer() - if s.serverError == nil { - s.serverError = errors.New("server closed") - } - var err error - // If the server was started with ListenAddr, we created the packet conn. - // We need to close it in order to make the go routine reading from that conn return. - if s.createdPacketConn { - err = s.sessionHandler.Close() - } - s.closed = true - close(s.errorChan) - return err -} - -func (s *server) closeWithError(e error) error { - s.mutex.Lock() - defer s.mutex.Unlock() - if s.closed { - return nil - } - s.serverError = e - return s.closeWithMutex() -} - -// Addr returns the server's network address -func (s *server) Addr() net.Addr { - return s.conn.LocalAddr() -} - -func (s *server) handlePacket(p *receivedPacket) { - go func() { - if shouldReleaseBuffer := s.handlePacketImpl(p); !shouldReleaseBuffer { - p.buffer.Release() - } - }() -} - -func (s *server) handlePacketImpl(p *receivedPacket) bool /* was the packet passed on to a session */ { - if len(p.data) < protocol.MinInitialPacketSize { - s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", len(p.data)) - return false - } - // If we're creating a new session, the packet will be passed to the session. - // The header will then be parsed again. - hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength) - if err != nil { - s.logger.Debugf("Error parsing packet: %s", err) - return false - } - // Short header packets should never end up here in the first place - if !hdr.IsLongHeader { - return false - } - // send a Version Negotiation Packet if the client is speaking a different protocol version - if !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) { - s.sendVersionNegotiationPacket(p, hdr) - return false - } - if hdr.IsLongHeader && hdr.Type != protocol.PacketTypeInitial { - // Drop long header packets. - // There's litte point in sending a Stateless Reset, since the client - // might not have received the token yet. - return false - } - - s.logger.Debugf("<- Received Initial packet.") - - sess, connID, err := s.handleInitialImpl(p, hdr) - if err != nil { - s.logger.Errorf("Error occurred handling initial packet: %s", err) - return false - } - if sess == nil { // a retry was done, or the connection attempt was rejected - return false - } - // Don't put the packet buffer back if a new session was created. - // The session will handle the packet and take of that. - s.sessionHandler.Add(connID, sess) - return true -} - -func (s *server) handleInitialImpl(p *receivedPacket, hdr *wire.Header) (quicSession, protocol.ConnectionID, error) { - if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { - return nil, nil, errors.New("too short connection ID") - } - - var cookie *Cookie - var origDestConnectionID protocol.ConnectionID - if len(hdr.Token) > 0 { - c, err := s.cookieGenerator.DecodeToken(hdr.Token) - if err == nil { - cookie = &Cookie{ - RemoteAddr: c.RemoteAddr, - SentTime: c.SentTime, - } - origDestConnectionID = c.OriginalDestConnectionID - } - } - if !s.config.AcceptCookie(p.remoteAddr, cookie) { - // Log the Initial packet now. - // If no Retry is sent, the packet will be logged by the session. - (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) - return nil, nil, s.sendRetry(p.remoteAddr, hdr) - } - - if queueLen := atomic.LoadInt32(&s.sessionQueueLen); queueLen >= protocol.MaxAcceptQueueSize { - s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize) - return nil, nil, s.sendServerBusy(p.remoteAddr, hdr) - } - - connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) - if err != nil { - return nil, nil, err - } - s.logger.Debugf("Changing connection ID to %s.", connID) - sess, err := s.createNewSession( - p.remoteAddr, - origDestConnectionID, - hdr.DestConnectionID, - hdr.SrcConnectionID, - connID, - hdr.Version, - ) - if err != nil { - return nil, nil, err - } - sess.handlePacket(p) - return sess, connID, nil -} - -func (s *server) createNewSession( - remoteAddr net.Addr, - origDestConnID protocol.ConnectionID, - clientDestConnID protocol.ConnectionID, - destConnID protocol.ConnectionID, - srcConnID protocol.ConnectionID, - version protocol.VersionNumber, -) (quicSession, error) { - token := s.sessionHandler.GetStatelessResetToken(srcConnID) - params := &handshake.TransportParameters{ - InitialMaxStreamDataBidiLocal: protocol.InitialMaxStreamData, - InitialMaxStreamDataBidiRemote: protocol.InitialMaxStreamData, - InitialMaxStreamDataUni: protocol.InitialMaxStreamData, - InitialMaxData: protocol.InitialMaxData, - IdleTimeout: s.config.IdleTimeout, - MaxBidiStreams: uint64(s.config.MaxIncomingStreams), - MaxUniStreams: uint64(s.config.MaxIncomingUniStreams), - AckDelayExponent: protocol.AckDelayExponent, - DisableMigration: true, - StatelessResetToken: &token, - OriginalConnectionID: origDestConnID, - } - sess, err := s.newSession( - &conn{pconn: s.conn, currentAddr: remoteAddr}, - s.sessionRunner, - clientDestConnID, - destConnID, - srcConnID, - s.config, - s.tlsConf, - params, - s.logger, - version, - ) - if err != nil { - return nil, err - } - go sess.run() - return sess, nil -} - -func (s *server) sendRetry(remoteAddr net.Addr, hdr *wire.Header) error { - token, err := s.cookieGenerator.NewToken(remoteAddr, hdr.DestConnectionID) - if err != nil { - return err - } - connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) - if err != nil { - return err - } - replyHdr := &wire.ExtendedHeader{} - replyHdr.IsLongHeader = true - replyHdr.Type = protocol.PacketTypeRetry - replyHdr.Version = hdr.Version - replyHdr.SrcConnectionID = connID - replyHdr.DestConnectionID = hdr.SrcConnectionID - replyHdr.OrigDestConnectionID = hdr.DestConnectionID - replyHdr.Token = token - s.logger.Debugf("Changing connection ID to %s.\n-> Sending Retry", connID) - replyHdr.Log(s.logger) - buf := &bytes.Buffer{} - if err := replyHdr.Write(buf, hdr.Version); err != nil { - return err - } - if _, err := s.conn.WriteTo(buf.Bytes(), remoteAddr); err != nil { - s.logger.Debugf("Error sending Retry: %s", err) - } - return nil -} - -func (s *server) sendServerBusy(remoteAddr net.Addr, hdr *wire.Header) error { - sealer, _, err := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer) - if err != nil { - return err - } - packetBuffer := getPacketBuffer() - defer packetBuffer.Release() - buf := bytes.NewBuffer(packetBuffer.Slice[:0]) - - ccf := &wire.ConnectionCloseFrame{ErrorCode: qerr.ServerBusy} - - replyHdr := &wire.ExtendedHeader{} - replyHdr.IsLongHeader = true - replyHdr.Type = protocol.PacketTypeInitial - replyHdr.Version = hdr.Version - replyHdr.SrcConnectionID = hdr.DestConnectionID - replyHdr.DestConnectionID = hdr.SrcConnectionID - replyHdr.PacketNumberLen = protocol.PacketNumberLen4 - replyHdr.Length = 4 /* packet number len */ + ccf.Length(hdr.Version) + protocol.ByteCount(sealer.Overhead()) - if err := replyHdr.Write(buf, hdr.Version); err != nil { - return err - } - payloadOffset := buf.Len() - - if err := ccf.Write(buf, hdr.Version); err != nil { - return err - } - - raw := buf.Bytes() - _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], replyHdr.PacketNumber, raw[:payloadOffset]) - raw = raw[0 : buf.Len()+sealer.Overhead()] - - pnOffset := payloadOffset - int(replyHdr.PacketNumberLen) - sealer.EncryptHeader( - raw[pnOffset+4:pnOffset+4+16], - &raw[0], - raw[pnOffset:payloadOffset], - ) - - replyHdr.Log(s.logger) - wire.LogFrame(s.logger, ccf, true) - if _, err := s.conn.WriteTo(raw, remoteAddr); err != nil { - s.logger.Debugf("Error rejecting connection: %s", err) - } - return nil -} - -func (s *server) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) { - s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version) - data, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions) - if err != nil { - s.logger.Debugf("Error composing Version Negotiation: %s", err) - return - } - if _, err := s.conn.WriteTo(data, p.remoteAddr); err != nil { - s.logger.Debugf("Error sending Version Negotiation: %s", err) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/session.go b/vendor/github.com/lucas-clemente/quic-go/session.go deleted file mode 100644 index ba1f55661..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/session.go +++ /dev/null @@ -1,1287 +0,0 @@ -package quic - -import ( - "bytes" - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "reflect" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/ackhandler" - "github.com/lucas-clemente/quic-go/internal/congestion" - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type unpacker interface { - Unpack(hdr *wire.Header, data []byte) (*unpackedPacket, error) -} - -type streamGetter interface { - GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) - GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) -} - -type streamManager interface { - GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) - GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) - OpenStream() (Stream, error) - OpenUniStream() (SendStream, error) - OpenStreamSync() (Stream, error) - OpenUniStreamSync() (SendStream, error) - AcceptStream() (Stream, error) - AcceptUniStream() (ReceiveStream, error) - DeleteStream(protocol.StreamID) error - UpdateLimits(*handshake.TransportParameters) error - HandleMaxStreamsFrame(*wire.MaxStreamsFrame) error - CloseWithError(error) -} - -type cryptoStreamHandler interface { - RunHandshake() error - ChangeConnectionID(protocol.ConnectionID) error - io.Closer - ConnectionState() tls.ConnectionState -} - -type receivedPacket struct { - remoteAddr net.Addr - rcvTime time.Time - data []byte - - buffer *packetBuffer -} - -func (p *receivedPacket) Clone() *receivedPacket { - return &receivedPacket{ - remoteAddr: p.remoteAddr, - rcvTime: p.rcvTime, - data: p.data, - buffer: p.buffer, - } -} - -type closeError struct { - err error - remote bool - sendClose bool -} - -var errCloseForRecreating = errors.New("closing session in order to recreate it") - -// A Session is a QUIC session -type session struct { - sessionRunner sessionRunner - - destConnID protocol.ConnectionID - origDestConnID protocol.ConnectionID // if the server sends a Retry, this is the connection ID we used initially - srcConnID protocol.ConnectionID - - perspective protocol.Perspective - initialVersion protocol.VersionNumber // if version negotiation is performed, this is the version we initially tried - version protocol.VersionNumber - config *Config - - conn connection - - streamsMap streamManager - - rttStats *congestion.RTTStats - - cryptoStreamManager *cryptoStreamManager - sentPacketHandler ackhandler.SentPacketHandler - receivedPacketHandler ackhandler.ReceivedPacketHandler - framer framer - windowUpdateQueue *windowUpdateQueue - connFlowController flowcontrol.ConnectionFlowController - - unpacker unpacker - frameParser wire.FrameParser - packer packer - - cryptoStreamHandler cryptoStreamHandler - - receivedPackets chan *receivedPacket - sendingScheduled chan struct{} - - closeOnce sync.Once - closed utils.AtomicBool - // closeChan is used to notify the run loop that it should terminate - closeChan chan closeError - connectionClosePacket *packedPacket - packetsReceivedAfterClose int - - ctx context.Context - ctxCancel context.CancelFunc - - undecryptablePackets []*receivedPacket - - clientHelloWritten <-chan struct{} - handshakeCompleteChan chan struct{} // is closed when the handshake completes - handshakeComplete bool - - receivedRetry bool - receivedFirstPacket bool - receivedFirstForwardSecurePacket bool - - sessionCreationTime time.Time - // The idle timeout is set based on the max of the time we received the last packet... - lastPacketReceivedTime time.Time - // ... and the time we sent a new retransmittable packet after receiving a packet. - firstRetransmittablePacketAfterIdleSentTime time.Time - // pacingDeadline is the time when the next packet should be sent - pacingDeadline time.Time - - peerParams *handshake.TransportParameters - - timer *utils.Timer - // keepAlivePingSent stores whether a Ping frame was sent to the peer or not - // it is reset as soon as we receive a packet from the peer - keepAlivePingSent bool - - logger utils.Logger -} - -var _ Session = &session{} -var _ streamSender = &session{} - -var newSession = func( - conn connection, - runner sessionRunner, - clientDestConnID protocol.ConnectionID, - destConnID protocol.ConnectionID, - srcConnID protocol.ConnectionID, - conf *Config, - tlsConf *tls.Config, - params *handshake.TransportParameters, - logger utils.Logger, - v protocol.VersionNumber, -) (quicSession, error) { - s := &session{ - conn: conn, - sessionRunner: runner, - config: conf, - srcConnID: srcConnID, - destConnID: destConnID, - perspective: protocol.PerspectiveServer, - handshakeCompleteChan: make(chan struct{}), - logger: logger, - version: v, - } - s.preSetup() - s.sentPacketHandler = ackhandler.NewSentPacketHandler(0, s.rttStats, s.logger) - s.streamsMap = newStreamsMap( - s, - s.newFlowController, - uint64(s.config.MaxIncomingStreams), - uint64(s.config.MaxIncomingUniStreams), - s.perspective, - s.version, - ) - s.framer = newFramer(s.streamsMap, s.version) - initialStream := newCryptoStream() - handshakeStream := newCryptoStream() - oneRTTStream := newPostHandshakeCryptoStream(s.framer) - cs, err := handshake.NewCryptoSetupServer( - initialStream, - handshakeStream, - oneRTTStream, - clientDestConnID, - conn.RemoteAddr(), - params, - s.processTransportParameters, - tlsConf, - logger, - ) - if err != nil { - return nil, err - } - s.cryptoStreamHandler = cs - s.packer = newPacketPacker( - s.destConnID, - s.srcConnID, - initialStream, - handshakeStream, - s.sentPacketHandler, - s.RemoteAddr(), - cs, - s.framer, - s.receivedPacketHandler, - s.perspective, - s.version, - ) - s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, oneRTTStream) - - if err := s.postSetup(); err != nil { - return nil, err - } - s.unpacker = newPacketUnpacker(cs, s.version) - return s, nil -} - -// declare this as a variable, such that we can it mock it in the tests -var newClientSession = func( - conn connection, - runner sessionRunner, - destConnID protocol.ConnectionID, - srcConnID protocol.ConnectionID, - conf *Config, - tlsConf *tls.Config, - initialPacketNumber protocol.PacketNumber, - params *handshake.TransportParameters, - initialVersion protocol.VersionNumber, - logger utils.Logger, - v protocol.VersionNumber, -) (quicSession, error) { - s := &session{ - conn: conn, - sessionRunner: runner, - config: conf, - srcConnID: srcConnID, - destConnID: destConnID, - perspective: protocol.PerspectiveClient, - handshakeCompleteChan: make(chan struct{}), - logger: logger, - initialVersion: initialVersion, - version: v, - } - s.preSetup() - s.sentPacketHandler = ackhandler.NewSentPacketHandler(initialPacketNumber, s.rttStats, s.logger) - initialStream := newCryptoStream() - handshakeStream := newCryptoStream() - oneRTTStream := newPostHandshakeCryptoStream(s.framer) - cs, clientHelloWritten, err := handshake.NewCryptoSetupClient( - initialStream, - handshakeStream, - oneRTTStream, - s.destConnID, - conn.RemoteAddr(), - params, - s.processTransportParameters, - tlsConf, - logger, - ) - if err != nil { - return nil, err - } - s.clientHelloWritten = clientHelloWritten - s.cryptoStreamHandler = cs - s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, oneRTTStream) - s.unpacker = newPacketUnpacker(cs, s.version) - s.streamsMap = newStreamsMap( - s, - s.newFlowController, - uint64(s.config.MaxIncomingStreams), - uint64(s.config.MaxIncomingUniStreams), - s.perspective, - s.version, - ) - s.framer = newFramer(s.streamsMap, s.version) - s.packer = newPacketPacker( - s.destConnID, - s.srcConnID, - initialStream, - handshakeStream, - s.sentPacketHandler, - s.RemoteAddr(), - cs, - s.framer, - s.receivedPacketHandler, - s.perspective, - s.version, - ) - return s, s.postSetup() -} - -func (s *session) preSetup() { - s.frameParser = wire.NewFrameParser(s.version) - s.rttStats = &congestion.RTTStats{} - s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.rttStats, s.logger, s.version) - s.connFlowController = flowcontrol.NewConnectionFlowController( - protocol.InitialMaxData, - protocol.ByteCount(s.config.MaxReceiveConnectionFlowControlWindow), - s.onHasConnectionWindowUpdate, - s.rttStats, - s.logger, - ) -} - -func (s *session) postSetup() error { - s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets) - s.closeChan = make(chan closeError, 1) - s.sendingScheduled = make(chan struct{}, 1) - s.undecryptablePackets = make([]*receivedPacket, 0, protocol.MaxUndecryptablePackets) - s.ctx, s.ctxCancel = context.WithCancel(context.Background()) - - s.timer = utils.NewTimer() - now := time.Now() - s.lastPacketReceivedTime = now - s.sessionCreationTime = now - - s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame) - return nil -} - -// run the session main loop -func (s *session) run() error { - defer s.ctxCancel() - - go func() { - if err := s.cryptoStreamHandler.RunHandshake(); err != nil { - s.closeLocal(err) - return - } - close(s.handshakeCompleteChan) - }() - if s.perspective == protocol.PerspectiveClient { - select { - case <-s.clientHelloWritten: - s.scheduleSending() - case closeErr := <-s.closeChan: - // put the close error back into the channel, so that the run loop can receive it - s.closeChan <- closeErr - } - } - - var closeErr closeError - -runLoop: - for { - // Close immediately if requested - select { - case closeErr = <-s.closeChan: - break runLoop - case <-s.handshakeCompleteChan: - s.handleHandshakeComplete() - default: - } - - s.maybeResetTimer() - - select { - case closeErr = <-s.closeChan: - break runLoop - case <-s.timer.Chan(): - s.timer.SetRead() - // We do all the interesting stuff after the switch statement, so - // nothing to see here. - case <-s.sendingScheduled: - // We do all the interesting stuff after the switch statement, so - // nothing to see here. - case p := <-s.receivedPackets: - // Only reset the timers if this packet was actually processed. - // This avoids modifying any state when handling undecryptable packets, - // which could be injected by an attacker. - if wasProcessed := s.handlePacketImpl(p); !wasProcessed { - continue - } - case <-s.handshakeCompleteChan: - s.handleHandshakeComplete() - } - - now := time.Now() - if timeout := s.sentPacketHandler.GetAlarmTimeout(); !timeout.IsZero() && timeout.Before(now) { - // This could cause packets to be retransmitted. - // Check it before trying to send packets. - if err := s.sentPacketHandler.OnAlarm(); err != nil { - s.closeLocal(err) - } - } - - var pacingDeadline time.Time - if s.pacingDeadline.IsZero() { // the timer didn't have a pacing deadline set - pacingDeadline = s.sentPacketHandler.TimeUntilSend() - } - if s.config.KeepAlive && !s.keepAlivePingSent && s.handshakeComplete && s.firstRetransmittablePacketAfterIdleSentTime.IsZero() && time.Since(s.lastPacketReceivedTime) >= s.peerParams.IdleTimeout/2 { - // send a PING frame since there is no activity in the session - s.logger.Debugf("Sending a keep-alive ping to keep the connection alive.") - s.framer.QueueControlFrame(&wire.PingFrame{}) - s.keepAlivePingSent = true - } else if !pacingDeadline.IsZero() && now.Before(pacingDeadline) { - // If we get to this point before the pacing deadline, we should wait until that deadline. - // This can happen when scheduleSending is called, or a packet is received. - // Set the timer and restart the run loop. - s.pacingDeadline = pacingDeadline - continue - } - - if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.HandshakeTimeout { - s.destroy(qerr.TimeoutError("Handshake did not complete in time")) - continue - } - if s.handshakeComplete && now.Sub(s.idleTimeoutStartTime()) >= s.config.IdleTimeout { - s.destroy(qerr.TimeoutError("No recent network activity")) - continue - } - - if err := s.sendPackets(); err != nil { - s.closeLocal(err) - } - } - - s.handleCloseError(closeErr) - s.closed.Set(true) - s.logger.Infof("Connection %s closed.", s.srcConnID) - s.cryptoStreamHandler.Close() - return closeErr.err -} - -func (s *session) Context() context.Context { - return s.ctx -} - -func (s *session) ConnectionState() tls.ConnectionState { - return s.cryptoStreamHandler.ConnectionState() -} - -func (s *session) maybeResetTimer() { - var deadline time.Time - if s.config.KeepAlive && s.handshakeComplete && !s.keepAlivePingSent { - deadline = s.idleTimeoutStartTime().Add(s.peerParams.IdleTimeout / 2) - } else { - deadline = s.idleTimeoutStartTime().Add(s.config.IdleTimeout) - } - - if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() { - deadline = utils.MinTime(deadline, ackAlarm) - } - if lossTime := s.sentPacketHandler.GetAlarmTimeout(); !lossTime.IsZero() { - deadline = utils.MinTime(deadline, lossTime) - } - if !s.handshakeComplete { - handshakeDeadline := s.sessionCreationTime.Add(s.config.HandshakeTimeout) - deadline = utils.MinTime(deadline, handshakeDeadline) - } - if !s.pacingDeadline.IsZero() { - deadline = utils.MinTime(deadline, s.pacingDeadline) - } - - s.timer.Reset(deadline) -} - -func (s *session) idleTimeoutStartTime() time.Time { - return utils.MaxTime(s.lastPacketReceivedTime, s.firstRetransmittablePacketAfterIdleSentTime) -} - -func (s *session) handleHandshakeComplete() { - s.handshakeComplete = true - s.handshakeCompleteChan = nil // prevent this case from ever being selected again - s.sessionRunner.OnHandshakeComplete(s) - - // The client completes the handshake first (after sending the CFIN). - // We need to make sure they learn about the peer completing the handshake, - // in order to stop retransmitting handshake packets. - // They will stop retransmitting handshake packets when receiving the first forward-secure packet. - // We need to make sure that a retransmittable forward-secure packet is sent, - // independent from the application protocol. - if s.perspective == protocol.PerspectiveServer { - s.queueControlFrame(&wire.PingFrame{}) - s.sentPacketHandler.SetHandshakeComplete() - } -} - -func (s *session) handlePacketImpl(rp *receivedPacket) bool { - var counter uint8 - var lastConnID protocol.ConnectionID - var processed bool - data := rp.data - p := rp - for len(data) > 0 { - if counter > 0 { - p = p.Clone() - p.data = data - } - - hdr, packetData, rest, err := wire.ParsePacket(p.data, s.srcConnID.Len()) - if err != nil { - s.logger.Debugf("error parsing packet: %s", err) - break - } - - if counter > 0 && !hdr.DestConnectionID.Equal(lastConnID) { - s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", hdr.DestConnectionID, lastConnID) - break - } - lastConnID = hdr.DestConnectionID - - if counter > 0 { - p.buffer.Split() - } - counter++ - - // only log if this actually a coalesced packet - if s.logger.Debug() && (counter > 1 || len(rest) > 0) { - s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest)) - } - p.data = packetData - if wasProcessed := s.handleSinglePacket(p, hdr); wasProcessed { - processed = true - } - data = rest - } - p.buffer.MaybeRelease() - return processed -} - -func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ { - var wasQueued bool - - defer func() { - // Put back the packet buffer if the packet wasn't queued for later decryption. - if !wasQueued { - p.buffer.Decrement() - } - }() - - if hdr.Type == protocol.PacketTypeRetry { - return s.handleRetryPacket(p, hdr) - } - - // The server can change the source connection ID with the first Handshake packet. - // After this, all packets with a different source connection have to be ignored. - if s.receivedFirstPacket && hdr.IsLongHeader && !hdr.SrcConnectionID.Equal(s.destConnID) { - s.logger.Debugf("Dropping packet with unexpected source connection ID: %s (expected %s)", hdr.SrcConnectionID, s.destConnID) - return false - } - // drop 0-RTT packets - if hdr.Type == protocol.PacketType0RTT { - return false - } - - packet, err := s.unpacker.Unpack(hdr, p.data) - if err != nil { - if err == handshake.ErrOpenerNotYetAvailable { - // Sealer for this encryption level not yet available. - // Try again later. - wasQueued = true - s.tryQueueingUndecryptablePacket(p) - return false - } - // This might be a packet injected by an attacker. - // Drop it. - s.logger.Debugf("Dropping packet that could not be unpacked. Unpack error: %s", err) - return false - } - - if s.logger.Debug() { - s.logger.Debugf("<- Reading packet %#x (%d bytes) for connection %s, %s", packet.packetNumber, len(p.data), hdr.DestConnectionID, packet.encryptionLevel) - packet.hdr.Log(s.logger) - } - - if err := s.handleUnpackedPacket(packet, p.rcvTime); err != nil { - s.closeLocal(err) - return false - } - return true -} - -func (s *session) handleRetryPacket(p *receivedPacket, hdr *wire.Header) bool /* was this a valid Retry */ { - if s.perspective == protocol.PerspectiveServer { - s.logger.Debugf("Ignoring Retry.") - return false - } - if s.receivedFirstPacket { - s.logger.Debugf("Ignoring Retry, since we already received a packet.") - return false - } - (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) - if !hdr.OrigDestConnectionID.Equal(s.destConnID) { - s.logger.Debugf("Ignoring spoofed Retry. Original Destination Connection ID: %s, expected: %s", hdr.OrigDestConnectionID, s.destConnID) - return false - } - if hdr.SrcConnectionID.Equal(s.destConnID) { - s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") - return false - } - // If a token is already set, this means that we already received a Retry from the server. - // Ignore this Retry packet. - if s.receivedRetry { - s.logger.Debugf("Ignoring Retry, since a Retry was already received.") - return false - } - s.logger.Debugf("<- Received Retry") - s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) - s.origDestConnID = s.destConnID - s.destConnID = hdr.SrcConnectionID - s.receivedRetry = true - if err := s.sentPacketHandler.ResetForRetry(); err != nil { - s.closeLocal(err) - return false - } - s.cryptoStreamHandler.ChangeConnectionID(s.destConnID) - s.packer.SetToken(hdr.Token) - s.packer.ChangeDestConnectionID(s.destConnID) - s.scheduleSending() - return true -} - -func (s *session) handleUnpackedPacket(packet *unpackedPacket, rcvTime time.Time) error { - if len(packet.data) == 0 { - return qerr.Error(qerr.ProtocolViolation, "empty packet") - } - - // The server can change the source connection ID with the first Handshake packet. - if s.perspective == protocol.PerspectiveClient && !s.receivedFirstPacket && packet.hdr.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(s.destConnID) { - s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", packet.hdr.SrcConnectionID) - s.destConnID = packet.hdr.SrcConnectionID - s.packer.ChangeDestConnectionID(s.destConnID) - } - - s.receivedFirstPacket = true - s.lastPacketReceivedTime = rcvTime - s.firstRetransmittablePacketAfterIdleSentTime = time.Time{} - s.keepAlivePingSent = false - - // The client completes the handshake first (after sending the CFIN). - // We know that the server completed the handshake as soon as we receive a forward-secure packet. - if s.perspective == protocol.PerspectiveClient { - if !s.receivedFirstForwardSecurePacket && packet.encryptionLevel == protocol.Encryption1RTT { - s.receivedFirstForwardSecurePacket = true - s.sentPacketHandler.SetHandshakeComplete() - } - } - - r := bytes.NewReader(packet.data) - var isRetransmittable bool - for { - frame, err := s.frameParser.ParseNext(r, packet.encryptionLevel) - if err != nil { - return err - } - if frame == nil { - break - } - if ackhandler.IsFrameRetransmittable(frame) { - isRetransmittable = true - } - if err := s.handleFrame(frame, packet.packetNumber, packet.encryptionLevel); err != nil { - return err - } - } - - if err := s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, packet.encryptionLevel, rcvTime, isRetransmittable); err != nil { - return err - } - return nil -} - -func (s *session) handleFrame(f wire.Frame, pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) error { - var err error - wire.LogFrame(s.logger, f, false) - switch frame := f.(type) { - case *wire.CryptoFrame: - err = s.handleCryptoFrame(frame, encLevel) - case *wire.StreamFrame: - err = s.handleStreamFrame(frame, encLevel) - case *wire.AckFrame: - err = s.handleAckFrame(frame, pn, encLevel) - case *wire.ConnectionCloseFrame: - s.closeRemote(qerr.Error(frame.ErrorCode, frame.ReasonPhrase)) - case *wire.ResetStreamFrame: - err = s.handleResetStreamFrame(frame) - case *wire.MaxDataFrame: - s.handleMaxDataFrame(frame) - case *wire.MaxStreamDataFrame: - err = s.handleMaxStreamDataFrame(frame) - case *wire.MaxStreamsFrame: - err = s.handleMaxStreamsFrame(frame) - case *wire.DataBlockedFrame: - case *wire.StreamDataBlockedFrame: - case *wire.StreamsBlockedFrame: - case *wire.StopSendingFrame: - err = s.handleStopSendingFrame(frame) - case *wire.PingFrame: - case *wire.PathChallengeFrame: - s.handlePathChallengeFrame(frame) - case *wire.PathResponseFrame: - // since we don't send PATH_CHALLENGEs, we don't expect PATH_RESPONSEs - err = errors.New("unexpected PATH_RESPONSE frame") - case *wire.NewTokenFrame: - case *wire.NewConnectionIDFrame: - case *wire.RetireConnectionIDFrame: - // since we don't send new connection IDs, we don't expect retirements - err = errors.New("unexpected RETIRE_CONNECTION_ID frame") - default: - err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name()) - } - return err -} - -// handlePacket is called by the server with a new packet -func (s *session) handlePacket(p *receivedPacket) { - if s.closed.Get() { - s.handlePacketAfterClosed(p) - } - // Discard packets once the amount of queued packets is larger than - // the channel size, protocol.MaxSessionUnprocessedPackets - select { - case s.receivedPackets <- p: - default: - } -} - -func (s *session) handlePacketAfterClosed(p *receivedPacket) { - s.packetsReceivedAfterClose++ - if s.connectionClosePacket == nil { - return - } - // exponential backoff - // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving - for n := s.packetsReceivedAfterClose; n > 1; n = n / 2 { - if n%2 != 0 { - return - } - } - s.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", s.packetsReceivedAfterClose) - if err := s.conn.Write(s.connectionClosePacket.raw); err != nil { - s.logger.Debugf("Error retransmitting CONNECTION_CLOSE: %s", err) - } -} - -func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { - encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel) - if err != nil { - return err - } - s.logger.Debugf("Handled crypto frame at level %s. encLevelChanged: %t", encLevel, encLevelChanged) - if encLevelChanged { - s.tryDecryptingQueuedPackets() - } - return nil -} - -func (s *session) handleStreamFrame(frame *wire.StreamFrame, encLevel protocol.EncryptionLevel) error { - // TODO(#1261): implement strict rules for frames types in unencrypted packets - if encLevel < protocol.Encryption1RTT { - return qerr.Error(qerr.ProtocolViolation, fmt.Sprintf("received unencrypted stream data on stream %d", frame.StreamID)) - } - str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // Stream is closed and already garbage collected - // ignore this StreamFrame - return nil - } - return str.handleStreamFrame(frame) -} - -func (s *session) handleMaxDataFrame(frame *wire.MaxDataFrame) { - s.connFlowController.UpdateSendWindow(frame.ByteOffset) -} - -func (s *session) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { - str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil - } - str.handleMaxStreamDataFrame(frame) - return nil -} - -func (s *session) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) error { - return s.streamsMap.HandleMaxStreamsFrame(frame) -} - -func (s *session) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { - str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil - } - return str.handleResetStreamFrame(frame) -} - -func (s *session) handleStopSendingFrame(frame *wire.StopSendingFrame) error { - str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) - if err != nil { - return err - } - if str == nil { - // stream is closed and already garbage collected - return nil - } - str.handleStopSendingFrame(frame) - return nil -} - -func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { - s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data}) -} - -func (s *session) handleAckFrame(frame *wire.AckFrame, pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) error { - if err := s.sentPacketHandler.ReceivedAck(frame, pn, encLevel, s.lastPacketReceivedTime); err != nil { - return err - } - if encLevel == protocol.Encryption1RTT { - s.receivedPacketHandler.IgnoreBelow(s.sentPacketHandler.GetLowestPacketNotConfirmedAcked()) - } - return nil -} - -// closeLocal closes the session and send a CONNECTION_CLOSE containing the error -func (s *session) closeLocal(e error) { - s.closeOnce.Do(func() { - if e == nil { - s.logger.Infof("Closing session.") - } else { - s.logger.Errorf("Closing session with error: %s", e) - } - s.sessionRunner.Retire(s.srcConnID) - s.closeChan <- closeError{err: e, sendClose: true, remote: false} - }) -} - -// destroy closes the session without sending the error on the wire -func (s *session) destroy(e error) { - s.closeOnce.Do(func() { - if nerr, ok := e.(net.Error); ok && nerr.Timeout() { - s.logger.Errorf("Destroying session %s: %s", s.destConnID, e) - } else { - s.logger.Errorf("Destroying session %s with error: %s", s.destConnID, e) - } - s.sessionRunner.Remove(s.srcConnID) - s.closeChan <- closeError{err: e, sendClose: false, remote: false} - }) -} - -// closeForRecreating closes the session in order to recreate it immediately afterwards -// It returns the first packet number that should be used in the new session. -func (s *session) closeForRecreating() protocol.PacketNumber { - s.destroy(errCloseForRecreating) - nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial) - return nextPN -} - -func (s *session) closeRemote(e error) { - s.closeOnce.Do(func() { - s.logger.Errorf("Peer closed session with error: %s", e) - s.sessionRunner.Remove(s.srcConnID) - s.closeChan <- closeError{err: e, remote: true} - }) -} - -// Close the connection. It sends a qerr.NoError. -// It waits until the run loop has stopped before returning -func (s *session) Close() error { - s.closeLocal(nil) - <-s.ctx.Done() - return nil -} - -func (s *session) CloseWithError(code protocol.ApplicationErrorCode, e error) error { - s.closeLocal(qerr.Error(qerr.ErrorCode(code), e.Error())) - <-s.ctx.Done() - return nil -} - -func (s *session) handleCloseError(closeErr closeError) { - if closeErr.err == nil { - closeErr.err = qerr.NoError - } - - var quicErr *qerr.QuicError - var ok bool - if quicErr, ok = closeErr.err.(*qerr.QuicError); !ok { - quicErr = qerr.ToQuicError(closeErr.err) - } - - s.streamsMap.CloseWithError(quicErr) - - if !closeErr.sendClose { - return - } - // If this is a remote close we're done here - if closeErr.remote { - return - } - if err := s.sendConnectionClose(quicErr); err != nil { - s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) - } -} - -func (s *session) processTransportParameters(data []byte) { - var params *handshake.TransportParameters - var err error - switch s.perspective { - case protocol.PerspectiveClient: - params, err = s.processTransportParametersForClient(data) - case protocol.PerspectiveServer: - params, err = s.processTransportParametersForServer(data) - } - if err != nil { - s.closeLocal(err) - return - } - s.logger.Debugf("Received Transport Parameters: %s", params) - s.peerParams = params - if err := s.streamsMap.UpdateLimits(params); err != nil { - s.closeLocal(err) - return - } - s.packer.HandleTransportParameters(params) - s.frameParser.SetAckDelayExponent(params.AckDelayExponent) - s.connFlowController.UpdateSendWindow(params.InitialMaxData) - if params.StatelessResetToken != nil { - s.sessionRunner.AddResetToken(*params.StatelessResetToken, s) - } -} - -func (s *session) processTransportParametersForClient(data []byte) (*handshake.TransportParameters, error) { - params := &handshake.TransportParameters{} - if err := params.Unmarshal(data, s.perspective.Opposite()); err != nil { - return nil, err - } - - // check the Retry token - if !params.OriginalConnectionID.Equal(s.origDestConnID) { - return nil, fmt.Errorf("expected original_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalConnectionID) - } - - return params, nil -} - -func (s *session) processTransportParametersForServer(data []byte) (*handshake.TransportParameters, error) { - params := &handshake.TransportParameters{} - if err := params.Unmarshal(data, s.perspective.Opposite()); err != nil { - return nil, err - } - return params, nil -} - -func (s *session) sendPackets() error { - s.pacingDeadline = time.Time{} - - sendMode := s.sentPacketHandler.SendMode() - if sendMode == ackhandler.SendNone { // shortcut: return immediately if there's nothing to send - return nil - } - - numPackets := s.sentPacketHandler.ShouldSendNumPackets() - var numPacketsSent int -sendLoop: - for { - switch sendMode { - case ackhandler.SendNone: - break sendLoop - case ackhandler.SendAck: - // If we already sent packets, and the send mode switches to SendAck, - // we've just become congestion limited. - // There's no need to try to send an ACK at this moment. - if numPacketsSent > 0 { - return nil - } - // We can at most send a single ACK only packet. - // There will only be a new ACK after receiving new packets. - // SendAck is only returned when we're congestion limited, so we don't need to set the pacingt timer. - return s.maybeSendAckOnlyPacket() - case ackhandler.SendPTO: - if err := s.sendProbePacket(); err != nil { - return err - } - numPacketsSent++ - case ackhandler.SendRetransmission: - sentPacket, err := s.maybeSendRetransmission() - if err != nil { - return err - } - if sentPacket { - numPacketsSent++ - // This can happen if a retransmission queued, but it wasn't necessary to send it. - // e.g. when an Initial is queued, but we already received a packet from the server. - } - case ackhandler.SendAny: - sentPacket, err := s.sendPacket() - if err != nil { - return err - } - if !sentPacket { - break sendLoop - } - numPacketsSent++ - default: - return fmt.Errorf("BUG: invalid send mode %d", sendMode) - } - if numPacketsSent >= numPackets { - break - } - sendMode = s.sentPacketHandler.SendMode() - } - // Only start the pacing timer if we sent as many packets as we were allowed. - // There will probably be more to send when calling sendPacket again. - if numPacketsSent == numPackets { - s.pacingDeadline = s.sentPacketHandler.TimeUntilSend() - } - return nil -} - -func (s *session) maybeSendAckOnlyPacket() error { - packet, err := s.packer.MaybePackAckPacket() - if err != nil { - return err - } - if packet == nil { - return nil - } - s.sentPacketHandler.SentPacket(packet.ToAckHandlerPacket()) - return s.sendPackedPacket(packet) -} - -// maybeSendRetransmission sends retransmissions for at most one packet. -// It takes care that Initials aren't retransmitted, if a packet from the server was already received. -func (s *session) maybeSendRetransmission() (bool, error) { - retransmitPacket := s.sentPacketHandler.DequeuePacketForRetransmission() - if retransmitPacket == nil { - return false, nil - } - - s.logger.Debugf("Dequeueing retransmission for packet 0x%x (%s)", retransmitPacket.PacketNumber, retransmitPacket.EncryptionLevel) - packets, err := s.packer.PackRetransmission(retransmitPacket) - if err != nil { - return false, err - } - ackhandlerPackets := make([]*ackhandler.Packet, len(packets)) - for i, packet := range packets { - ackhandlerPackets[i] = packet.ToAckHandlerPacket() - } - s.sentPacketHandler.SentPacketsAsRetransmission(ackhandlerPackets, retransmitPacket.PacketNumber) - for _, packet := range packets { - if err := s.sendPackedPacket(packet); err != nil { - return false, err - } - } - return true, nil -} - -func (s *session) sendProbePacket() error { - p, err := s.sentPacketHandler.DequeueProbePacket() - if err != nil { - return err - } - s.logger.Debugf("Sending a retransmission for %#x as a probe packet.", p.PacketNumber) - - packets, err := s.packer.PackRetransmission(p) - if err != nil { - return err - } - ackhandlerPackets := make([]*ackhandler.Packet, len(packets)) - for i, packet := range packets { - ackhandlerPackets[i] = packet.ToAckHandlerPacket() - } - s.sentPacketHandler.SentPacketsAsRetransmission(ackhandlerPackets, p.PacketNumber) - for _, packet := range packets { - if err := s.sendPackedPacket(packet); err != nil { - return err - } - } - return nil -} - -func (s *session) sendPacket() (bool, error) { - if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked { - s.framer.QueueControlFrame(&wire.DataBlockedFrame{DataLimit: offset}) - } - s.windowUpdateQueue.QueueAll() - - packet, err := s.packer.PackPacket() - if err != nil || packet == nil { - return false, err - } - s.sentPacketHandler.SentPacket(packet.ToAckHandlerPacket()) - if err := s.sendPackedPacket(packet); err != nil { - return false, err - } - return true, nil -} - -func (s *session) sendPackedPacket(packet *packedPacket) error { - defer packet.buffer.Release() - if s.firstRetransmittablePacketAfterIdleSentTime.IsZero() && packet.IsRetransmittable() { - s.firstRetransmittablePacketAfterIdleSentTime = time.Now() - } - s.logPacket(packet) - return s.conn.Write(packet.raw) -} - -func (s *session) sendConnectionClose(quicErr *qerr.QuicError) error { - var reason string - // don't send details of crypto errors - if !quicErr.IsCryptoError() { - reason = quicErr.ErrorMessage - } - packet, err := s.packer.PackConnectionClose(&wire.ConnectionCloseFrame{ - ErrorCode: quicErr.ErrorCode, - ReasonPhrase: reason, - }) - if err != nil { - return err - } - s.connectionClosePacket = packet - s.logPacket(packet) - return s.conn.Write(packet.raw) -} - -func (s *session) logPacket(packet *packedPacket) { - if !s.logger.Debug() { - // We don't need to allocate the slices for calling the format functions - return - } - s.logger.Debugf("-> Sending packet 0x%x (%d bytes) for connection %s, %s", packet.header.PacketNumber, len(packet.raw), s.srcConnID, packet.EncryptionLevel()) - packet.header.Log(s.logger) - for _, frame := range packet.frames { - wire.LogFrame(s.logger, frame, true) - } -} - -// GetOrOpenStream either returns an existing stream, a newly opened stream, or nil if a stream with the provided ID is already closed. -// It is *only* needed for gQUIC's H2. -// It will be removed as soon as gQUIC moves towards the IETF H2/QUIC stream mapping. -func (s *session) GetOrOpenStream(id protocol.StreamID) (Stream, error) { - str, err := s.streamsMap.GetOrOpenSendStream(id) - if str != nil { - if bstr, ok := str.(Stream); ok { - return bstr, err - } - return nil, fmt.Errorf("Stream %d is not a bidirectional stream", id) - } - // make sure to return an actual nil value here, not an Stream with value nil - return nil, err -} - -// AcceptStream returns the next stream openend by the peer -func (s *session) AcceptStream() (Stream, error) { - return s.streamsMap.AcceptStream() -} - -func (s *session) AcceptUniStream() (ReceiveStream, error) { - return s.streamsMap.AcceptUniStream() -} - -// OpenStream opens a stream -func (s *session) OpenStream() (Stream, error) { - return s.streamsMap.OpenStream() -} - -func (s *session) OpenStreamSync() (Stream, error) { - return s.streamsMap.OpenStreamSync() -} - -func (s *session) OpenUniStream() (SendStream, error) { - return s.streamsMap.OpenUniStream() -} - -func (s *session) OpenUniStreamSync() (SendStream, error) { - return s.streamsMap.OpenUniStreamSync() -} - -func (s *session) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { - var initialSendWindow protocol.ByteCount - if s.peerParams != nil { - if id.Type() == protocol.StreamTypeUni { - initialSendWindow = s.peerParams.InitialMaxStreamDataUni - } else { - if id.InitiatedBy() == s.perspective { - initialSendWindow = s.peerParams.InitialMaxStreamDataBidiRemote - } else { - initialSendWindow = s.peerParams.InitialMaxStreamDataBidiLocal - } - } - } - return flowcontrol.NewStreamFlowController( - id, - s.connFlowController, - protocol.InitialMaxStreamData, - protocol.ByteCount(s.config.MaxReceiveStreamFlowControlWindow), - initialSendWindow, - s.onHasStreamWindowUpdate, - s.rttStats, - s.logger, - ) -} - -// scheduleSending signals that we have data for sending -func (s *session) scheduleSending() { - select { - case s.sendingScheduled <- struct{}{}: - default: - } -} - -func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket) { - if s.handshakeComplete { - s.logger.Debugf("Received undecryptable packet from %s after the handshake (%d bytes)", p.remoteAddr.String(), len(p.data)) - return - } - if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { - s.logger.Infof("Dropping undecrytable packet (%d bytes). Undecryptable packet queue full.", len(p.data)) - return - } - s.logger.Infof("Queueing packet (%d bytes) for later decryption", len(p.data)) - s.undecryptablePackets = append(s.undecryptablePackets, p) -} - -func (s *session) tryDecryptingQueuedPackets() { - for _, p := range s.undecryptablePackets { - s.handlePacket(p) - } - s.undecryptablePackets = s.undecryptablePackets[:0] -} - -func (s *session) queueControlFrame(f wire.Frame) { - s.framer.QueueControlFrame(f) - s.scheduleSending() -} - -func (s *session) onHasStreamWindowUpdate(id protocol.StreamID) { - s.windowUpdateQueue.AddStream(id) - s.scheduleSending() -} - -func (s *session) onHasConnectionWindowUpdate() { - s.windowUpdateQueue.AddConnection() - s.scheduleSending() -} - -func (s *session) onHasStreamData(id protocol.StreamID) { - s.framer.AddActiveStream(id) - s.scheduleSending() -} - -func (s *session) onStreamCompleted(id protocol.StreamID) { - if err := s.streamsMap.DeleteStream(id); err != nil { - s.closeLocal(err) - } -} - -func (s *session) LocalAddr() net.Addr { - return s.conn.LocalAddr() -} - -func (s *session) RemoteAddr() net.Addr { - return s.conn.RemoteAddr() -} - -func (s *session) getPerspective() protocol.Perspective { - return s.perspective -} - -func (s *session) GetVersion() protocol.VersionNumber { - return s.version -} diff --git a/vendor/github.com/lucas-clemente/quic-go/stream.go b/vendor/github.com/lucas-clemente/quic-go/stream.go deleted file mode 100644 index dfd0cc6a1..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/stream.go +++ /dev/null @@ -1,163 +0,0 @@ -package quic - -import ( - "net" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -const errorCodeStopping protocol.ApplicationErrorCode = 0 - -// The streamSender is notified by the stream about various events. -type streamSender interface { - queueControlFrame(wire.Frame) - onHasStreamData(protocol.StreamID) - // must be called without holding the mutex that is acquired by closeForShutdown - onStreamCompleted(protocol.StreamID) -} - -// Each of the both stream halves gets its own uniStreamSender. -// This is necessary in order to keep track when both halves have been completed. -type uniStreamSender struct { - streamSender - onStreamCompletedImpl func() -} - -func (s *uniStreamSender) queueControlFrame(f wire.Frame) { - s.streamSender.queueControlFrame(f) -} - -func (s *uniStreamSender) onHasStreamData(id protocol.StreamID) { - s.streamSender.onHasStreamData(id) -} - -func (s *uniStreamSender) onStreamCompleted(protocol.StreamID) { - s.onStreamCompletedImpl() -} - -var _ streamSender = &uniStreamSender{} - -type streamI interface { - Stream - closeForShutdown(error) - // for receiving - handleStreamFrame(*wire.StreamFrame) error - handleResetStreamFrame(*wire.ResetStreamFrame) error - getWindowUpdate() protocol.ByteCount - // for sending - hasData() bool - handleStopSendingFrame(*wire.StopSendingFrame) - popStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool) - handleMaxStreamDataFrame(*wire.MaxStreamDataFrame) -} - -var _ receiveStreamI = (streamI)(nil) -var _ sendStreamI = (streamI)(nil) - -// A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface -// -// Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually. -type stream struct { - receiveStream - sendStream - - completedMutex sync.Mutex - sender streamSender - receiveStreamCompleted bool - sendStreamCompleted bool - - version protocol.VersionNumber -} - -var _ Stream = &stream{} - -type deadlineError struct{} - -func (deadlineError) Error() string { return "deadline exceeded" } -func (deadlineError) Temporary() bool { return true } -func (deadlineError) Timeout() bool { return true } - -var errDeadline net.Error = &deadlineError{} - -type streamCanceledError struct { - error - errorCode protocol.ApplicationErrorCode -} - -func (streamCanceledError) Canceled() bool { return true } -func (e streamCanceledError) ErrorCode() protocol.ApplicationErrorCode { return e.errorCode } - -var _ StreamError = &streamCanceledError{} - -// newStream creates a new Stream -func newStream(streamID protocol.StreamID, - sender streamSender, - flowController flowcontrol.StreamFlowController, - version protocol.VersionNumber, -) *stream { - s := &stream{sender: sender, version: version} - senderForSendStream := &uniStreamSender{ - streamSender: sender, - onStreamCompletedImpl: func() { - s.completedMutex.Lock() - s.sendStreamCompleted = true - s.checkIfCompleted() - s.completedMutex.Unlock() - }, - } - s.sendStream = *newSendStream(streamID, senderForSendStream, flowController, version) - senderForReceiveStream := &uniStreamSender{ - streamSender: sender, - onStreamCompletedImpl: func() { - s.completedMutex.Lock() - s.receiveStreamCompleted = true - s.checkIfCompleted() - s.completedMutex.Unlock() - }, - } - s.receiveStream = *newReceiveStream(streamID, senderForReceiveStream, flowController, version) - return s -} - -// need to define StreamID() here, since both receiveStream and readStream have a StreamID() -func (s *stream) StreamID() protocol.StreamID { - // the result is same for receiveStream and sendStream - return s.sendStream.StreamID() -} - -func (s *stream) Close() error { - if err := s.sendStream.Close(); err != nil { - return err - } - return nil -} - -func (s *stream) SetDeadline(t time.Time) error { - _ = s.SetReadDeadline(t) // SetReadDeadline never errors - _ = s.SetWriteDeadline(t) // SetWriteDeadline never errors - return nil -} - -// CloseForShutdown closes a stream abruptly. -// It makes Read and Write unblock (and return the error) immediately. -// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. -func (s *stream) closeForShutdown(err error) { - s.sendStream.closeForShutdown(err) - s.receiveStream.closeForShutdown(err) -} - -func (s *stream) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { - return s.receiveStream.handleResetStreamFrame(frame) -} - -// checkIfCompleted is called from the uniStreamSender, when one of the stream halves is completed. -// It makes sure that the onStreamCompleted callback is only called if both receive and send side have completed. -func (s *stream) checkIfCompleted() { - if s.sendStreamCompleted && s.receiveStreamCompleted { - s.sender.onStreamCompleted(s.StreamID()) - } -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map.go b/vendor/github.com/lucas-clemente/quic-go/streams_map.go deleted file mode 100644 index 846ece004..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map.go +++ /dev/null @@ -1,192 +0,0 @@ -package quic - -import ( - "errors" - "fmt" - "net" - - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/handshake" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type streamOpenErr struct{ error } - -var _ net.Error = &streamOpenErr{} - -func (e streamOpenErr) Temporary() bool { return e.error == errTooManyOpenStreams } -func (streamOpenErr) Timeout() bool { return false } - -// errTooManyOpenStreams is used internally by the outgoing streams maps. -var errTooManyOpenStreams = errors.New("too many open streams") - -type streamsMap struct { - perspective protocol.Perspective - - sender streamSender - newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController - - outgoingBidiStreams *outgoingBidiStreamsMap - outgoingUniStreams *outgoingUniStreamsMap - incomingBidiStreams *incomingBidiStreamsMap - incomingUniStreams *incomingUniStreamsMap -} - -var _ streamManager = &streamsMap{} - -func newStreamsMap( - sender streamSender, - newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController, - maxIncomingStreams uint64, - maxIncomingUniStreams uint64, - perspective protocol.Perspective, - version protocol.VersionNumber, -) streamManager { - m := &streamsMap{ - perspective: perspective, - newFlowController: newFlowController, - sender: sender, - } - newBidiStream := func(id protocol.StreamID) streamI { - return newStream(id, m.sender, m.newFlowController(id), version) - } - newUniSendStream := func(id protocol.StreamID) sendStreamI { - return newSendStream(id, m.sender, m.newFlowController(id), version) - } - newUniReceiveStream := func(id protocol.StreamID) receiveStreamI { - return newReceiveStream(id, m.sender, m.newFlowController(id), version) - } - m.outgoingBidiStreams = newOutgoingBidiStreamsMap( - protocol.FirstStream(protocol.StreamTypeBidi, perspective), - newBidiStream, - sender.queueControlFrame, - ) - m.incomingBidiStreams = newIncomingBidiStreamsMap( - protocol.FirstStream(protocol.StreamTypeBidi, perspective.Opposite()), - protocol.MaxStreamID(protocol.StreamTypeBidi, maxIncomingStreams, perspective.Opposite()), - maxIncomingStreams, - sender.queueControlFrame, - newBidiStream, - ) - m.outgoingUniStreams = newOutgoingUniStreamsMap( - protocol.FirstStream(protocol.StreamTypeUni, perspective), - newUniSendStream, - sender.queueControlFrame, - ) - m.incomingUniStreams = newIncomingUniStreamsMap( - protocol.FirstStream(protocol.StreamTypeUni, perspective.Opposite()), - protocol.MaxStreamID(protocol.StreamTypeUni, maxIncomingUniStreams, perspective.Opposite()), - maxIncomingUniStreams, - sender.queueControlFrame, - newUniReceiveStream, - ) - return m -} - -func (m *streamsMap) OpenStream() (Stream, error) { - return m.outgoingBidiStreams.OpenStream() -} - -func (m *streamsMap) OpenStreamSync() (Stream, error) { - return m.outgoingBidiStreams.OpenStreamSync() -} - -func (m *streamsMap) OpenUniStream() (SendStream, error) { - return m.outgoingUniStreams.OpenStream() -} - -func (m *streamsMap) OpenUniStreamSync() (SendStream, error) { - return m.outgoingUniStreams.OpenStreamSync() -} - -func (m *streamsMap) AcceptStream() (Stream, error) { - return m.incomingBidiStreams.AcceptStream() -} - -func (m *streamsMap) AcceptUniStream() (ReceiveStream, error) { - return m.incomingUniStreams.AcceptStream() -} - -func (m *streamsMap) DeleteStream(id protocol.StreamID) error { - switch id.Type() { - case protocol.StreamTypeUni: - if id.InitiatedBy() == m.perspective { - return m.outgoingUniStreams.DeleteStream(id) - } - return m.incomingUniStreams.DeleteStream(id) - case protocol.StreamTypeBidi: - if id.InitiatedBy() == m.perspective { - return m.outgoingBidiStreams.DeleteStream(id) - } - return m.incomingBidiStreams.DeleteStream(id) - } - panic("") -} - -func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) { - switch id.Type() { - case protocol.StreamTypeUni: - if id.InitiatedBy() == m.perspective { - // an outgoing unidirectional stream is a send stream, not a receive stream - return nil, fmt.Errorf("peer attempted to open receive stream %d", id) - } - return m.incomingUniStreams.GetOrOpenStream(id) - case protocol.StreamTypeBidi: - if id.InitiatedBy() == m.perspective { - return m.outgoingBidiStreams.GetStream(id) - } - return m.incomingBidiStreams.GetOrOpenStream(id) - } - panic("") -} - -func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) { - switch id.Type() { - case protocol.StreamTypeUni: - if id.InitiatedBy() == m.perspective { - return m.outgoingUniStreams.GetStream(id) - } - // an incoming unidirectional stream is a receive stream, not a send stream - return nil, fmt.Errorf("peer attempted to open send stream %d", id) - case protocol.StreamTypeBidi: - if id.InitiatedBy() == m.perspective { - return m.outgoingBidiStreams.GetStream(id) - } - return m.incomingBidiStreams.GetOrOpenStream(id) - } - panic("") -} - -func (m *streamsMap) HandleMaxStreamsFrame(f *wire.MaxStreamsFrame) error { - if f.MaxStreams > protocol.MaxStreamCount { - return qerr.StreamLimitError - } - id := protocol.MaxStreamID(f.Type, f.MaxStreams, m.perspective) - switch id.Type() { - case protocol.StreamTypeUni: - m.outgoingUniStreams.SetMaxStream(id) - case protocol.StreamTypeBidi: - fmt.Printf("") - m.outgoingBidiStreams.SetMaxStream(id) - } - return nil -} - -func (m *streamsMap) UpdateLimits(p *handshake.TransportParameters) error { - if p.MaxBidiStreams > protocol.MaxStreamCount || p.MaxUniStreams > protocol.MaxStreamCount { - return qerr.StreamLimitError - } - // Max{Uni,Bidi}StreamID returns the highest stream ID that the peer is allowed to open. - m.outgoingBidiStreams.SetMaxStream(protocol.MaxStreamID(protocol.StreamTypeBidi, p.MaxBidiStreams, m.perspective)) - m.outgoingUniStreams.SetMaxStream(protocol.MaxStreamID(protocol.StreamTypeUni, p.MaxUniStreams, m.perspective)) - return nil -} - -func (m *streamsMap) CloseWithError(err error) { - m.outgoingBidiStreams.CloseWithError(err) - m.outgoingUniStreams.CloseWithError(err) - m.incomingBidiStreams.CloseWithError(err) - m.incomingUniStreams.CloseWithError(err) -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_generic_helper.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_generic_helper.go deleted file mode 100644 index 692f093e4..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_generic_helper.go +++ /dev/null @@ -1,17 +0,0 @@ -package quic - -import ( - "github.com/cheekybits/genny/generic" - - "github.com/lucas-clemente/quic-go/internal/protocol" -) - -// In the auto-generated streams maps, we need to be able to close the streams. -// Therefore, extend the generic.Type with the stream close method. -// This definition must be in a file that Genny doesn't process. -type item interface { - generic.Type - closeForShutdown(error) -} - -const streamTypeGeneric protocol.StreamType = protocol.StreamTypeUni diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_bidi.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_bidi.go deleted file mode 100644 index 787c3d903..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_bidi.go +++ /dev/null @@ -1,162 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type incomingBidiStreamsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]streamI - // When a stream is deleted before it was accepted, we can't delete it immediately. - // We need to wait until the application accepts it, and delete it immediately then. - streamsToDelete map[protocol.StreamID]struct{} // used as a set - - nextStreamToAccept protocol.StreamID // the next stream that will be returned by AcceptStream() - nextStreamToOpen protocol.StreamID // the highest stream that the peer openend - maxStream protocol.StreamID // the highest stream that the peer is allowed to open - maxNumStreams uint64 // maximum number of streams - - newStream func(protocol.StreamID) streamI - queueMaxStreamID func(*wire.MaxStreamsFrame) - - closeErr error -} - -func newIncomingBidiStreamsMap( - nextStreamToAccept protocol.StreamID, - initialMaxStreamID protocol.StreamID, - maxNumStreams uint64, - queueControlFrame func(wire.Frame), - newStream func(protocol.StreamID) streamI, -) *incomingBidiStreamsMap { - m := &incomingBidiStreamsMap{ - streams: make(map[protocol.StreamID]streamI), - streamsToDelete: make(map[protocol.StreamID]struct{}), - nextStreamToAccept: nextStreamToAccept, - nextStreamToOpen: nextStreamToAccept, - maxStream: initialMaxStreamID, - maxNumStreams: maxNumStreams, - newStream: newStream, - queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *incomingBidiStreamsMap) AcceptStream() (streamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - var id protocol.StreamID - var str streamI - for { - id = m.nextStreamToAccept - var ok bool - if m.closeErr != nil { - return nil, m.closeErr - } - str, ok = m.streams[id] - if ok { - break - } - m.cond.Wait() - } - m.nextStreamToAccept += 4 - // If this stream was completed before being accepted, we can delete it now. - if _, ok := m.streamsToDelete[id]; ok { - delete(m.streamsToDelete, id) - if err := m.deleteStream(id); err != nil { - return nil, err - } - } - return str, nil -} - -func (m *incomingBidiStreamsMap) GetOrOpenStream(id protocol.StreamID) (streamI, error) { - m.mutex.RLock() - if id > m.maxStream { - m.mutex.RUnlock() - return nil, fmt.Errorf("peer tried to open stream %d (current limit: %d)", id, m.maxStream) - } - // if the id is smaller than the highest we accepted - // * this stream exists in the map, and we can return it, or - // * this stream was already closed, then we can return the nil - if id < m.nextStreamToOpen { - var s streamI - // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. - if _, ok := m.streamsToDelete[id]; !ok { - s = m.streams[id] - } - m.mutex.RUnlock() - return s, nil - } - m.mutex.RUnlock() - - m.mutex.Lock() - // no need to check the two error conditions from above again - // * maxStream can only increase, so if the id was valid before, it definitely is valid now - // * highestStream is only modified by this function - for newID := m.nextStreamToOpen; newID <= id; newID += 4 { - m.streams[newID] = m.newStream(newID) - m.cond.Signal() - } - m.nextStreamToOpen = id + 4 - s := m.streams[id] - m.mutex.Unlock() - return s, nil -} - -func (m *incomingBidiStreamsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - return m.deleteStream(id) -} - -func (m *incomingBidiStreamsMap) deleteStream(id protocol.StreamID) error { - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - - // Don't delete this stream yet, if it was not yet accepted. - // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. - if id >= m.nextStreamToAccept { - if _, ok := m.streamsToDelete[id]; ok { - return fmt.Errorf("Tried to delete stream %d multiple times", id) - } - m.streamsToDelete[id] = struct{}{} - return nil - } - - delete(m.streams, id) - // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream - if m.maxNumStreams > uint64(len(m.streams)) { - numNewStreams := m.maxNumStreams - uint64(len(m.streams)) - m.maxStream = m.nextStreamToOpen + protocol.StreamID((numNewStreams-1)*4) - m.queueMaxStreamID(&wire.MaxStreamsFrame{ - Type: protocol.StreamTypeBidi, - MaxStreams: m.maxStream.StreamNum(), - }) - } - return nil -} - -func (m *incomingBidiStreamsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.mutex.Unlock() - m.cond.Broadcast() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_generic.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_generic.go deleted file mode 100644 index 503f02908..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_generic.go +++ /dev/null @@ -1,160 +0,0 @@ -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -//go:generate genny -in $GOFILE -out streams_map_incoming_bidi.go gen "item=streamI Item=BidiStream streamTypeGeneric=protocol.StreamTypeBidi" -//go:generate genny -in $GOFILE -out streams_map_incoming_uni.go gen "item=receiveStreamI Item=UniStream streamTypeGeneric=protocol.StreamTypeUni" -type incomingItemsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]item - // When a stream is deleted before it was accepted, we can't delete it immediately. - // We need to wait until the application accepts it, and delete it immediately then. - streamsToDelete map[protocol.StreamID]struct{} // used as a set - - nextStreamToAccept protocol.StreamID // the next stream that will be returned by AcceptStream() - nextStreamToOpen protocol.StreamID // the highest stream that the peer openend - maxStream protocol.StreamID // the highest stream that the peer is allowed to open - maxNumStreams uint64 // maximum number of streams - - newStream func(protocol.StreamID) item - queueMaxStreamID func(*wire.MaxStreamsFrame) - - closeErr error -} - -func newIncomingItemsMap( - nextStreamToAccept protocol.StreamID, - initialMaxStreamID protocol.StreamID, - maxNumStreams uint64, - queueControlFrame func(wire.Frame), - newStream func(protocol.StreamID) item, -) *incomingItemsMap { - m := &incomingItemsMap{ - streams: make(map[protocol.StreamID]item), - streamsToDelete: make(map[protocol.StreamID]struct{}), - nextStreamToAccept: nextStreamToAccept, - nextStreamToOpen: nextStreamToAccept, - maxStream: initialMaxStreamID, - maxNumStreams: maxNumStreams, - newStream: newStream, - queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *incomingItemsMap) AcceptStream() (item, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - var id protocol.StreamID - var str item - for { - id = m.nextStreamToAccept - var ok bool - if m.closeErr != nil { - return nil, m.closeErr - } - str, ok = m.streams[id] - if ok { - break - } - m.cond.Wait() - } - m.nextStreamToAccept += 4 - // If this stream was completed before being accepted, we can delete it now. - if _, ok := m.streamsToDelete[id]; ok { - delete(m.streamsToDelete, id) - if err := m.deleteStream(id); err != nil { - return nil, err - } - } - return str, nil -} - -func (m *incomingItemsMap) GetOrOpenStream(id protocol.StreamID) (item, error) { - m.mutex.RLock() - if id > m.maxStream { - m.mutex.RUnlock() - return nil, fmt.Errorf("peer tried to open stream %d (current limit: %d)", id, m.maxStream) - } - // if the id is smaller than the highest we accepted - // * this stream exists in the map, and we can return it, or - // * this stream was already closed, then we can return the nil - if id < m.nextStreamToOpen { - var s item - // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. - if _, ok := m.streamsToDelete[id]; !ok { - s = m.streams[id] - } - m.mutex.RUnlock() - return s, nil - } - m.mutex.RUnlock() - - m.mutex.Lock() - // no need to check the two error conditions from above again - // * maxStream can only increase, so if the id was valid before, it definitely is valid now - // * highestStream is only modified by this function - for newID := m.nextStreamToOpen; newID <= id; newID += 4 { - m.streams[newID] = m.newStream(newID) - m.cond.Signal() - } - m.nextStreamToOpen = id + 4 - s := m.streams[id] - m.mutex.Unlock() - return s, nil -} - -func (m *incomingItemsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - return m.deleteStream(id) -} - -func (m *incomingItemsMap) deleteStream(id protocol.StreamID) error { - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - - // Don't delete this stream yet, if it was not yet accepted. - // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. - if id >= m.nextStreamToAccept { - if _, ok := m.streamsToDelete[id]; ok { - return fmt.Errorf("Tried to delete stream %d multiple times", id) - } - m.streamsToDelete[id] = struct{}{} - return nil - } - - delete(m.streams, id) - // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream - if m.maxNumStreams > uint64(len(m.streams)) { - numNewStreams := m.maxNumStreams - uint64(len(m.streams)) - m.maxStream = m.nextStreamToOpen + protocol.StreamID((numNewStreams-1)*4) - m.queueMaxStreamID(&wire.MaxStreamsFrame{ - Type: streamTypeGeneric, - MaxStreams: m.maxStream.StreamNum(), - }) - } - return nil -} - -func (m *incomingItemsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.mutex.Unlock() - m.cond.Broadcast() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_uni.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_uni.go deleted file mode 100644 index f36fcee54..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_incoming_uni.go +++ /dev/null @@ -1,162 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type incomingUniStreamsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]receiveStreamI - // When a stream is deleted before it was accepted, we can't delete it immediately. - // We need to wait until the application accepts it, and delete it immediately then. - streamsToDelete map[protocol.StreamID]struct{} // used as a set - - nextStreamToAccept protocol.StreamID // the next stream that will be returned by AcceptStream() - nextStreamToOpen protocol.StreamID // the highest stream that the peer openend - maxStream protocol.StreamID // the highest stream that the peer is allowed to open - maxNumStreams uint64 // maximum number of streams - - newStream func(protocol.StreamID) receiveStreamI - queueMaxStreamID func(*wire.MaxStreamsFrame) - - closeErr error -} - -func newIncomingUniStreamsMap( - nextStreamToAccept protocol.StreamID, - initialMaxStreamID protocol.StreamID, - maxNumStreams uint64, - queueControlFrame func(wire.Frame), - newStream func(protocol.StreamID) receiveStreamI, -) *incomingUniStreamsMap { - m := &incomingUniStreamsMap{ - streams: make(map[protocol.StreamID]receiveStreamI), - streamsToDelete: make(map[protocol.StreamID]struct{}), - nextStreamToAccept: nextStreamToAccept, - nextStreamToOpen: nextStreamToAccept, - maxStream: initialMaxStreamID, - maxNumStreams: maxNumStreams, - newStream: newStream, - queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *incomingUniStreamsMap) AcceptStream() (receiveStreamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - var id protocol.StreamID - var str receiveStreamI - for { - id = m.nextStreamToAccept - var ok bool - if m.closeErr != nil { - return nil, m.closeErr - } - str, ok = m.streams[id] - if ok { - break - } - m.cond.Wait() - } - m.nextStreamToAccept += 4 - // If this stream was completed before being accepted, we can delete it now. - if _, ok := m.streamsToDelete[id]; ok { - delete(m.streamsToDelete, id) - if err := m.deleteStream(id); err != nil { - return nil, err - } - } - return str, nil -} - -func (m *incomingUniStreamsMap) GetOrOpenStream(id protocol.StreamID) (receiveStreamI, error) { - m.mutex.RLock() - if id > m.maxStream { - m.mutex.RUnlock() - return nil, fmt.Errorf("peer tried to open stream %d (current limit: %d)", id, m.maxStream) - } - // if the id is smaller than the highest we accepted - // * this stream exists in the map, and we can return it, or - // * this stream was already closed, then we can return the nil - if id < m.nextStreamToOpen { - var s receiveStreamI - // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. - if _, ok := m.streamsToDelete[id]; !ok { - s = m.streams[id] - } - m.mutex.RUnlock() - return s, nil - } - m.mutex.RUnlock() - - m.mutex.Lock() - // no need to check the two error conditions from above again - // * maxStream can only increase, so if the id was valid before, it definitely is valid now - // * highestStream is only modified by this function - for newID := m.nextStreamToOpen; newID <= id; newID += 4 { - m.streams[newID] = m.newStream(newID) - m.cond.Signal() - } - m.nextStreamToOpen = id + 4 - s := m.streams[id] - m.mutex.Unlock() - return s, nil -} - -func (m *incomingUniStreamsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - return m.deleteStream(id) -} - -func (m *incomingUniStreamsMap) deleteStream(id protocol.StreamID) error { - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - - // Don't delete this stream yet, if it was not yet accepted. - // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. - if id >= m.nextStreamToAccept { - if _, ok := m.streamsToDelete[id]; ok { - return fmt.Errorf("Tried to delete stream %d multiple times", id) - } - m.streamsToDelete[id] = struct{}{} - return nil - } - - delete(m.streams, id) - // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream - if m.maxNumStreams > uint64(len(m.streams)) { - numNewStreams := m.maxNumStreams - uint64(len(m.streams)) - m.maxStream = m.nextStreamToOpen + protocol.StreamID((numNewStreams-1)*4) - m.queueMaxStreamID(&wire.MaxStreamsFrame{ - Type: protocol.StreamTypeUni, - MaxStreams: m.maxStream.StreamNum(), - }) - } - return nil -} - -func (m *incomingUniStreamsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.mutex.Unlock() - m.cond.Broadcast() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_bidi.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_bidi.go deleted file mode 100644 index a4457775c..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_bidi.go +++ /dev/null @@ -1,147 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type outgoingBidiStreamsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]streamI - - nextStream protocol.StreamID // stream ID of the stream returned by OpenStream(Sync) - maxStream protocol.StreamID // the maximum stream ID we're allowed to open - maxStreamSet bool // was maxStream set. If not, it's not possible to any stream (also works for stream 0) - blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream - - newStream func(protocol.StreamID) streamI - queueStreamIDBlocked func(*wire.StreamsBlockedFrame) - - closeErr error -} - -func newOutgoingBidiStreamsMap( - nextStream protocol.StreamID, - newStream func(protocol.StreamID) streamI, - queueControlFrame func(wire.Frame), -) *outgoingBidiStreamsMap { - m := &outgoingBidiStreamsMap{ - streams: make(map[protocol.StreamID]streamI), - nextStream: nextStream, - newStream: newStream, - queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *outgoingBidiStreamsMap) OpenStream() (streamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.closeErr != nil { - return nil, m.closeErr - } - - str, err := m.openStreamImpl() - if err != nil { - return nil, streamOpenErr{err} - } - return str, nil -} - -func (m *outgoingBidiStreamsMap) OpenStreamSync() (streamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - for { - if m.closeErr != nil { - return nil, m.closeErr - } - str, err := m.openStreamImpl() - if err == nil { - return str, nil - } - if err != nil && err != errTooManyOpenStreams { - return nil, streamOpenErr{err} - } - m.cond.Wait() - } -} - -func (m *outgoingBidiStreamsMap) openStreamImpl() (streamI, error) { - if !m.maxStreamSet || m.nextStream > m.maxStream { - if !m.blockedSent { - if m.maxStreamSet { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: protocol.StreamTypeBidi, - StreamLimit: m.maxStream.StreamNum(), - }) - } else { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: protocol.StreamTypeBidi, - StreamLimit: 0, - }) - } - m.blockedSent = true - } - return nil, errTooManyOpenStreams - } - s := m.newStream(m.nextStream) - m.streams[m.nextStream] = s - m.nextStream += 4 - return s, nil -} - -func (m *outgoingBidiStreamsMap) GetStream(id protocol.StreamID) (streamI, error) { - m.mutex.RLock() - if id >= m.nextStream { - m.mutex.RUnlock() - return nil, qerr.Error(qerr.StreamStateError, fmt.Sprintf("peer attempted to open stream %d", id)) - } - s := m.streams[id] - m.mutex.RUnlock() - return s, nil -} - -func (m *outgoingBidiStreamsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - delete(m.streams, id) - return nil -} - -func (m *outgoingBidiStreamsMap) SetMaxStream(id protocol.StreamID) { - m.mutex.Lock() - if !m.maxStreamSet || id > m.maxStream { - m.maxStream = id - m.maxStreamSet = true - m.blockedSent = false - m.cond.Broadcast() - } - m.mutex.Unlock() -} - -func (m *outgoingBidiStreamsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.cond.Broadcast() - m.mutex.Unlock() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_generic.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_generic.go deleted file mode 100644 index c0657b902..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_generic.go +++ /dev/null @@ -1,145 +0,0 @@ -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -//go:generate genny -in $GOFILE -out streams_map_outgoing_bidi.go gen "item=streamI Item=BidiStream streamTypeGeneric=protocol.StreamTypeBidi" -//go:generate genny -in $GOFILE -out streams_map_outgoing_uni.go gen "item=sendStreamI Item=UniStream streamTypeGeneric=protocol.StreamTypeUni" -type outgoingItemsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]item - - nextStream protocol.StreamID // stream ID of the stream returned by OpenStream(Sync) - maxStream protocol.StreamID // the maximum stream ID we're allowed to open - maxStreamSet bool // was maxStream set. If not, it's not possible to any stream (also works for stream 0) - blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream - - newStream func(protocol.StreamID) item - queueStreamIDBlocked func(*wire.StreamsBlockedFrame) - - closeErr error -} - -func newOutgoingItemsMap( - nextStream protocol.StreamID, - newStream func(protocol.StreamID) item, - queueControlFrame func(wire.Frame), -) *outgoingItemsMap { - m := &outgoingItemsMap{ - streams: make(map[protocol.StreamID]item), - nextStream: nextStream, - newStream: newStream, - queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *outgoingItemsMap) OpenStream() (item, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.closeErr != nil { - return nil, m.closeErr - } - - str, err := m.openStreamImpl() - if err != nil { - return nil, streamOpenErr{err} - } - return str, nil -} - -func (m *outgoingItemsMap) OpenStreamSync() (item, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - for { - if m.closeErr != nil { - return nil, m.closeErr - } - str, err := m.openStreamImpl() - if err == nil { - return str, nil - } - if err != nil && err != errTooManyOpenStreams { - return nil, streamOpenErr{err} - } - m.cond.Wait() - } -} - -func (m *outgoingItemsMap) openStreamImpl() (item, error) { - if !m.maxStreamSet || m.nextStream > m.maxStream { - if !m.blockedSent { - if m.maxStreamSet { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: streamTypeGeneric, - StreamLimit: m.maxStream.StreamNum(), - }) - } else { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: streamTypeGeneric, - StreamLimit: 0, - }) - } - m.blockedSent = true - } - return nil, errTooManyOpenStreams - } - s := m.newStream(m.nextStream) - m.streams[m.nextStream] = s - m.nextStream += 4 - return s, nil -} - -func (m *outgoingItemsMap) GetStream(id protocol.StreamID) (item, error) { - m.mutex.RLock() - if id >= m.nextStream { - m.mutex.RUnlock() - return nil, qerr.Error(qerr.StreamStateError, fmt.Sprintf("peer attempted to open stream %d", id)) - } - s := m.streams[id] - m.mutex.RUnlock() - return s, nil -} - -func (m *outgoingItemsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - delete(m.streams, id) - return nil -} - -func (m *outgoingItemsMap) SetMaxStream(id protocol.StreamID) { - m.mutex.Lock() - if !m.maxStreamSet || id > m.maxStream { - m.maxStream = id - m.maxStreamSet = true - m.blockedSent = false - m.cond.Broadcast() - } - m.mutex.Unlock() -} - -func (m *outgoingItemsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.cond.Broadcast() - m.mutex.Unlock() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_uni.go b/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_uni.go deleted file mode 100644 index a38240a63..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/streams_map_outgoing_uni.go +++ /dev/null @@ -1,147 +0,0 @@ -// This file was automatically generated by genny. -// Any changes will be lost if this file is regenerated. -// see https://github.com/cheekybits/genny - -package quic - -import ( - "fmt" - "sync" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/qerr" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type outgoingUniStreamsMap struct { - mutex sync.RWMutex - cond sync.Cond - - streams map[protocol.StreamID]sendStreamI - - nextStream protocol.StreamID // stream ID of the stream returned by OpenStream(Sync) - maxStream protocol.StreamID // the maximum stream ID we're allowed to open - maxStreamSet bool // was maxStream set. If not, it's not possible to any stream (also works for stream 0) - blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream - - newStream func(protocol.StreamID) sendStreamI - queueStreamIDBlocked func(*wire.StreamsBlockedFrame) - - closeErr error -} - -func newOutgoingUniStreamsMap( - nextStream protocol.StreamID, - newStream func(protocol.StreamID) sendStreamI, - queueControlFrame func(wire.Frame), -) *outgoingUniStreamsMap { - m := &outgoingUniStreamsMap{ - streams: make(map[protocol.StreamID]sendStreamI), - nextStream: nextStream, - newStream: newStream, - queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, - } - m.cond.L = &m.mutex - return m -} - -func (m *outgoingUniStreamsMap) OpenStream() (sendStreamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - if m.closeErr != nil { - return nil, m.closeErr - } - - str, err := m.openStreamImpl() - if err != nil { - return nil, streamOpenErr{err} - } - return str, nil -} - -func (m *outgoingUniStreamsMap) OpenStreamSync() (sendStreamI, error) { - m.mutex.Lock() - defer m.mutex.Unlock() - - for { - if m.closeErr != nil { - return nil, m.closeErr - } - str, err := m.openStreamImpl() - if err == nil { - return str, nil - } - if err != nil && err != errTooManyOpenStreams { - return nil, streamOpenErr{err} - } - m.cond.Wait() - } -} - -func (m *outgoingUniStreamsMap) openStreamImpl() (sendStreamI, error) { - if !m.maxStreamSet || m.nextStream > m.maxStream { - if !m.blockedSent { - if m.maxStreamSet { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: protocol.StreamTypeUni, - StreamLimit: m.maxStream.StreamNum(), - }) - } else { - m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ - Type: protocol.StreamTypeUni, - StreamLimit: 0, - }) - } - m.blockedSent = true - } - return nil, errTooManyOpenStreams - } - s := m.newStream(m.nextStream) - m.streams[m.nextStream] = s - m.nextStream += 4 - return s, nil -} - -func (m *outgoingUniStreamsMap) GetStream(id protocol.StreamID) (sendStreamI, error) { - m.mutex.RLock() - if id >= m.nextStream { - m.mutex.RUnlock() - return nil, qerr.Error(qerr.StreamStateError, fmt.Sprintf("peer attempted to open stream %d", id)) - } - s := m.streams[id] - m.mutex.RUnlock() - return s, nil -} - -func (m *outgoingUniStreamsMap) DeleteStream(id protocol.StreamID) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - if _, ok := m.streams[id]; !ok { - return fmt.Errorf("Tried to delete unknown stream %d", id) - } - delete(m.streams, id) - return nil -} - -func (m *outgoingUniStreamsMap) SetMaxStream(id protocol.StreamID) { - m.mutex.Lock() - if !m.maxStreamSet || id > m.maxStream { - m.maxStream = id - m.maxStreamSet = true - m.blockedSent = false - m.cond.Broadcast() - } - m.mutex.Unlock() -} - -func (m *outgoingUniStreamsMap) CloseWithError(err error) { - m.mutex.Lock() - m.closeErr = err - for _, str := range m.streams { - str.closeForShutdown(err) - } - m.cond.Broadcast() - m.mutex.Unlock() -} diff --git a/vendor/github.com/lucas-clemente/quic-go/window_update_queue.go b/vendor/github.com/lucas-clemente/quic-go/window_update_queue.go deleted file mode 100644 index 64b912a3f..000000000 --- a/vendor/github.com/lucas-clemente/quic-go/window_update_queue.go +++ /dev/null @@ -1,71 +0,0 @@ -package quic - -import ( - "sync" - - "github.com/lucas-clemente/quic-go/internal/flowcontrol" - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/wire" -) - -type windowUpdateQueue struct { - mutex sync.Mutex - - queue map[protocol.StreamID]bool // used as a set - queuedConn bool // connection-level window update - - streamGetter streamGetter - connFlowController flowcontrol.ConnectionFlowController - callback func(wire.Frame) -} - -func newWindowUpdateQueue( - streamGetter streamGetter, - connFC flowcontrol.ConnectionFlowController, - cb func(wire.Frame), -) *windowUpdateQueue { - return &windowUpdateQueue{ - queue: make(map[protocol.StreamID]bool), - streamGetter: streamGetter, - connFlowController: connFC, - callback: cb, - } -} - -func (q *windowUpdateQueue) AddStream(id protocol.StreamID) { - q.mutex.Lock() - q.queue[id] = true - q.mutex.Unlock() -} - -func (q *windowUpdateQueue) AddConnection() { - q.mutex.Lock() - q.queuedConn = true - q.mutex.Unlock() -} - -func (q *windowUpdateQueue) QueueAll() { - q.mutex.Lock() - // queue a connection-level window update - if q.queuedConn { - q.callback(&wire.MaxDataFrame{ByteOffset: q.connFlowController.GetWindowUpdate()}) - q.queuedConn = false - } - // queue all stream-level window updates - for id := range q.queue { - str, err := q.streamGetter.GetOrOpenReceiveStream(id) - if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update - continue - } - offset := str.getWindowUpdate() - if offset == 0 { // can happen if we received a final offset, right after queueing the window update - continue - } - q.callback(&wire.MaxStreamDataFrame{ - StreamID: id, - ByteOffset: offset, - }) - delete(q.queue, id) - } - q.mutex.Unlock() -} diff --git a/vendor/github.com/marten-seemann/qtls/alert.go b/vendor/github.com/marten-seemann/qtls/alert.go deleted file mode 100644 index ba1c9ce39..000000000 --- a/vendor/github.com/marten-seemann/qtls/alert.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2009 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 qtls - -import "strconv" - -type alert uint8 - -// Alert is a TLS alert -type Alert = alert - -const ( - // alert level - alertLevelWarning = 1 - alertLevelError = 2 -) - -const ( - alertCloseNotify alert = 0 - alertUnexpectedMessage alert = 10 - alertBadRecordMAC alert = 20 - alertDecryptionFailed alert = 21 - alertRecordOverflow alert = 22 - alertDecompressionFailure alert = 30 - alertHandshakeFailure alert = 40 - alertBadCertificate alert = 42 - alertUnsupportedCertificate alert = 43 - alertCertificateRevoked alert = 44 - alertCertificateExpired alert = 45 - alertCertificateUnknown alert = 46 - alertIllegalParameter alert = 47 - alertUnknownCA alert = 48 - alertAccessDenied alert = 49 - alertDecodeError alert = 50 - alertDecryptError alert = 51 - alertProtocolVersion alert = 70 - alertInsufficientSecurity alert = 71 - alertInternalError alert = 80 - alertInappropriateFallback alert = 86 - alertUserCanceled alert = 90 - alertNoRenegotiation alert = 100 - alertMissingExtension alert = 109 - alertUnsupportedExtension alert = 110 - alertNoApplicationProtocol alert = 120 -) - -var alertText = map[alert]string{ - alertCloseNotify: "close notify", - alertUnexpectedMessage: "unexpected message", - alertBadRecordMAC: "bad record MAC", - alertDecryptionFailed: "decryption failed", - alertRecordOverflow: "record overflow", - alertDecompressionFailure: "decompression failure", - alertHandshakeFailure: "handshake failure", - alertBadCertificate: "bad certificate", - alertUnsupportedCertificate: "unsupported certificate", - alertCertificateRevoked: "revoked certificate", - alertCertificateExpired: "expired certificate", - alertCertificateUnknown: "unknown certificate", - alertIllegalParameter: "illegal parameter", - alertUnknownCA: "unknown certificate authority", - alertAccessDenied: "access denied", - alertDecodeError: "error decoding message", - alertDecryptError: "error decrypting message", - alertProtocolVersion: "protocol version not supported", - alertInsufficientSecurity: "insufficient security level", - alertInternalError: "internal error", - alertInappropriateFallback: "inappropriate fallback", - alertUserCanceled: "user canceled", - alertNoRenegotiation: "no renegotiation", - alertMissingExtension: "missing extension", - alertUnsupportedExtension: "unsupported extension", - alertNoApplicationProtocol: "no application protocol", -} - -func (e alert) String() string { - s, ok := alertText[e] - if ok { - return "tls: " + s - } - return "tls: alert(" + strconv.Itoa(int(e)) + ")" -} - -func (e alert) Error() string { - return e.String() -} diff --git a/vendor/github.com/marten-seemann/qtls/auth.go b/vendor/github.com/marten-seemann/qtls/auth.go deleted file mode 100644 index b8fd7f9ba..000000000 --- a/vendor/github.com/marten-seemann/qtls/auth.go +++ /dev/null @@ -1,221 +0,0 @@ -// 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 qtls - -import ( - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rsa" - "encoding/asn1" - "errors" - "fmt" - "hash" - "io" -) - -// pickSignatureAlgorithm selects a signature algorithm that is compatible with -// the given public key and the list of algorithms from the peer and this side. -// The lists of signature algorithms (peerSigAlgs and ourSigAlgs) are ignored -// for tlsVersion < VersionTLS12. -// -// The returned SignatureScheme codepoint is only meaningful for TLS 1.2, -// previous TLS versions have a fixed hash function. -func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (sigAlg SignatureScheme, sigType uint8, hashFunc crypto.Hash, err error) { - if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 { - // For TLS 1.1 and before, the signature algorithm could not be - // negotiated and the hash is fixed based on the signature type. For TLS - // 1.2, if the client didn't send signature_algorithms extension then we - // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1. - switch pubkey.(type) { - case *rsa.PublicKey: - if tlsVersion < VersionTLS12 { - return 0, signaturePKCS1v15, crypto.MD5SHA1, nil - } else { - return PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1, nil - } - case *ecdsa.PublicKey: - return ECDSAWithSHA1, signatureECDSA, crypto.SHA1, nil - default: - return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey) - } - } - for _, sigAlg := range peerSigAlgs { - if !isSupportedSignatureAlgorithm(sigAlg, ourSigAlgs) { - continue - } - hashAlg, err := hashFromSignatureScheme(sigAlg) - if err != nil { - panic("tls: supported signature algorithm has an unknown hash function") - } - sigType := signatureFromSignatureScheme(sigAlg) - switch pubkey.(type) { - case *rsa.PublicKey: - if sigType == signaturePKCS1v15 || sigType == signatureRSAPSS { - return sigAlg, sigType, hashAlg, nil - } - case *ecdsa.PublicKey: - if sigType == signatureECDSA { - return sigAlg, sigType, hashAlg, nil - } - default: - return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey) - } - } - return 0, 0, 0, errors.New("tls: peer doesn't support any common signature algorithms") -} - -// verifyHandshakeSignature verifies a signature against pre-hashed handshake -// contents. -func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, digest, sig []byte) error { - switch sigType { - case signatureECDSA: - pubKey, ok := pubkey.(*ecdsa.PublicKey) - if !ok { - return errors.New("tls: ECDSA signing requires a ECDSA public key") - } - ecdsaSig := new(ecdsaSignature) - if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { - return err - } - if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("tls: ECDSA signature contained zero or negative values") - } - if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("tls: ECDSA verification failure") - } - case signaturePKCS1v15: - pubKey, ok := pubkey.(*rsa.PublicKey) - if !ok { - return errors.New("tls: RSA signing requires a RSA public key") - } - if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { - return err - } - case signatureRSAPSS: - pubKey, ok := pubkey.(*rsa.PublicKey) - if !ok { - return errors.New("tls: RSA signing requires a RSA public key") - } - signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash} - if err := rsa.VerifyPSS(pubKey, hashFunc, digest, sig, signOpts); err != nil { - return err - } - default: - return errors.New("tls: unknown signature algorithm") - } - return nil -} - -const ( - serverSignatureContext = "TLS 1.3, server CertificateVerify\x00" - clientSignatureContext = "TLS 1.3, client CertificateVerify\x00" -) - -var signaturePadding = []byte{ - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, -} - -// writeSignedMessage writes the content to be signed by certificate keys in TLS -// 1.3 to sigHash. See RFC 8446, Section 4.4.3. -func writeSignedMessage(sigHash io.Writer, context string, transcript hash.Hash) { - sigHash.Write(signaturePadding) - io.WriteString(sigHash, context) - sigHash.Write(transcript.Sum(nil)) -} - -// signatureSchemesForCertificate returns the list of supported SignatureSchemes -// for a given certificate, based on the public key and the protocol version. It -// does not support the crypto.Decrypter interface, so shouldn't be used on the -// server side in TLS 1.2 and earlier. -func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme { - priv, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil - } - - switch pub := priv.Public().(type) { - case *ecdsa.PublicKey: - if version != VersionTLS13 { - // In TLS 1.2 and earlier, ECDSA algorithms are not - // constrained to a single curve. - return []SignatureScheme{ - ECDSAWithP256AndSHA256, - ECDSAWithP384AndSHA384, - ECDSAWithP521AndSHA512, - ECDSAWithSHA1, - } - } - switch pub.Curve { - case elliptic.P256(): - return []SignatureScheme{ECDSAWithP256AndSHA256} - case elliptic.P384(): - return []SignatureScheme{ECDSAWithP384AndSHA384} - case elliptic.P521(): - return []SignatureScheme{ECDSAWithP521AndSHA512} - default: - return nil - } - case *rsa.PublicKey: - if version != VersionTLS13 { - return []SignatureScheme{ - PSSWithSHA256, - PSSWithSHA384, - PSSWithSHA512, - PKCS1WithSHA256, - PKCS1WithSHA384, - PKCS1WithSHA512, - PKCS1WithSHA1, - } - } - // RSA keys with RSA-PSS OID are not supported by crypto/x509. - return []SignatureScheme{ - PSSWithSHA256, - PSSWithSHA384, - PSSWithSHA512, - } - default: - return nil - } -} - -// unsupportedCertificateError returns a helpful error for certificates with -// an unsupported private key. -func unsupportedCertificateError(cert *Certificate) error { - switch cert.PrivateKey.(type) { - case rsa.PrivateKey, ecdsa.PrivateKey: - return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T", - cert.PrivateKey, cert.PrivateKey) - } - - signer, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer", - cert.PrivateKey) - } - - switch pub := signer.Public().(type) { - case *ecdsa.PublicKey: - switch pub.Curve { - case elliptic.P256(): - case elliptic.P384(): - case elliptic.P521(): - default: - return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name) - } - case *rsa.PublicKey: - default: - return fmt.Errorf("tls: unsupported certificate key (%T)", pub) - } - - return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey) -} diff --git a/vendor/github.com/marten-seemann/qtls/cipher_suites.go b/vendor/github.com/marten-seemann/qtls/cipher_suites.go deleted file mode 100644 index e0b7e2f19..000000000 --- a/vendor/github.com/marten-seemann/qtls/cipher_suites.go +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2010 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 qtls - -import ( - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/hmac" - "crypto/rc4" - "crypto/sha1" - "crypto/sha256" - "crypto/x509" - "hash" - - "golang.org/x/crypto/chacha20poly1305" -) - -// a keyAgreement implements the client and server side of a TLS key agreement -// protocol by generating and processing key exchange messages. -type keyAgreement interface { - // On the server side, the first two methods are called in order. - - // In the case that the key agreement protocol doesn't use a - // ServerKeyExchange message, generateServerKeyExchange can return nil, - // nil. - generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) - processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error) - - // On the client side, the next two methods are called in order. - - // This method may not be called if the server doesn't send a - // ServerKeyExchange message. - processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error - generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) -} - -const ( - // suiteECDH indicates that the cipher suite involves elliptic curve - // Diffie-Hellman. This means that it should only be selected when the - // client indicates that it supports ECC with a curve and point format - // that we're happy with. - suiteECDHE = 1 << iota - // suiteECDSA indicates that the cipher suite involves an ECDSA - // signature and therefore may only be selected when the server's - // certificate is ECDSA. If this is not set then the cipher suite is - // RSA based. - suiteECDSA - // suiteTLS12 indicates that the cipher suite should only be advertised - // and accepted when using TLS 1.2. - suiteTLS12 - // suiteSHA384 indicates that the cipher suite uses SHA384 as the - // handshake hash. - suiteSHA384 - // suiteDefaultOff indicates that this cipher suite is not included by - // default. - suiteDefaultOff -) - -type CipherSuite struct { - *cipherSuiteTLS13 -} - -func (c *CipherSuite) Hash() crypto.Hash { return c.hash } -func (c *CipherSuite) KeyLen() int { return c.keyLen } -func (c *CipherSuite) IVLen() int { return aeadNonceLength } -func (c *CipherSuite) AEAD(key, fixedNonce []byte) cipher.AEAD { return c.aead(key, fixedNonce) } - -// A cipherSuite is a specific combination of key agreement, cipher and MAC function. -type cipherSuite struct { - id uint16 - // the lengths, in bytes, of the key material needed for each component. - keyLen int - macLen int - ivLen int - ka func(version uint16) keyAgreement - // flags is a bitmask of the suite* values, above. - flags int - cipher func(key, iv []byte, isRead bool) interface{} - mac func(version uint16, macKey []byte) macFunction - aead func(key, fixedNonce []byte) aead -} - -var cipherSuites = []*cipherSuite{ - // Ciphersuite order is chosen so that ECDHE comes before plain RSA and - // AEADs are the top preference. - {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, - {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, - {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, - {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil}, - {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, - {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, - {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, - - // RC4-based cipher suites are disabled by default. - {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteDefaultOff, cipherRC4, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteDefaultOff, cipherRC4, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil}, -} - -// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash -// algorithm to be used with HKDF. See RFC 8446, Appendix B.4. -type cipherSuiteTLS13 struct { - id uint16 - keyLen int - aead func(key, fixedNonce []byte) aead - hash crypto.Hash -} - -var cipherSuitesTLS13 = []*cipherSuiteTLS13{ - {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256}, - {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256}, - {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384}, -} - -func cipherRC4(key, iv []byte, isRead bool) interface{} { - cipher, _ := rc4.NewCipher(key) - return cipher -} - -func cipher3DES(key, iv []byte, isRead bool) interface{} { - block, _ := des.NewTripleDESCipher(key) - if isRead { - return cipher.NewCBCDecrypter(block, iv) - } - return cipher.NewCBCEncrypter(block, iv) -} - -func cipherAES(key, iv []byte, isRead bool) interface{} { - block, _ := aes.NewCipher(key) - if isRead { - return cipher.NewCBCDecrypter(block, iv) - } - return cipher.NewCBCEncrypter(block, iv) -} - -// macSHA1 returns a macFunction for the given protocol version. -func macSHA1(version uint16, key []byte) macFunction { - if version == VersionSSL30 { - mac := ssl30MAC{ - h: sha1.New(), - key: make([]byte, len(key)), - } - copy(mac.key, key) - return mac - } - return tls10MAC{h: hmac.New(newConstantTimeHash(sha1.New), key)} -} - -// macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2 -// so the given version is ignored. -func macSHA256(version uint16, key []byte) macFunction { - return tls10MAC{h: hmac.New(sha256.New, key)} -} - -type macFunction interface { - // Size returns the length of the MAC. - Size() int - // MAC appends the MAC of (seq, header, data) to out. The extra data is fed - // into the MAC after obtaining the result to normalize timing. The result - // is only valid until the next invocation of MAC as the buffer is reused. - MAC(seq, header, data, extra []byte) []byte -} - -type aead interface { - cipher.AEAD - - // explicitNonceLen returns the number of bytes of explicit nonce - // included in each record. This is eight for older AEADs and - // zero for modern ones. - explicitNonceLen() int -} - -const ( - aeadNonceLength = 12 - noncePrefixLength = 4 -) - -// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to -// each call. -type prefixNonceAEAD struct { - // nonce contains the fixed part of the nonce in the first four bytes. - nonce [aeadNonceLength]byte - aead cipher.AEAD -} - -func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength } -func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() } -func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() } - -func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { - copy(f.nonce[4:], nonce) - return f.aead.Seal(out, f.nonce[:], plaintext, additionalData) -} - -func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { - copy(f.nonce[4:], nonce) - return f.aead.Open(out, f.nonce[:], ciphertext, additionalData) -} - -// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce -// before each call. -type xorNonceAEAD struct { - nonceMask [aeadNonceLength]byte - aead cipher.AEAD -} - -func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number -func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } -func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } - -func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - - return result -} - -func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - - return result, err -} - -func aeadAESGCM(key, noncePrefix []byte) aead { - if len(noncePrefix) != noncePrefixLength { - panic("tls: internal error: wrong nonce length") - } - aes, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - aead, err := cipher.NewGCM(aes) - if err != nil { - panic(err) - } - - ret := &prefixNonceAEAD{aead: aead} - copy(ret.nonce[:], noncePrefix) - return ret -} - -// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 -func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { - return aeadAESGCMTLS13(key, fixedNonce) -} - -func aeadAESGCMTLS13(key, nonceMask []byte) aead { - if len(nonceMask) != aeadNonceLength { - panic("tls: internal error: wrong nonce length") - } - aes, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - aead, err := cipher.NewGCM(aes) - if err != nil { - panic(err) - } - - ret := &xorNonceAEAD{aead: aead} - copy(ret.nonceMask[:], nonceMask) - return ret -} - -func aeadChaCha20Poly1305(key, nonceMask []byte) aead { - if len(nonceMask) != aeadNonceLength { - panic("tls: internal error: wrong nonce length") - } - aead, err := chacha20poly1305.New(key) - if err != nil { - panic(err) - } - - ret := &xorNonceAEAD{aead: aead} - copy(ret.nonceMask[:], nonceMask) - return ret -} - -// ssl30MAC implements the SSLv3 MAC function, as defined in -// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 -type ssl30MAC struct { - h hash.Hash - key []byte - buf []byte -} - -func (s ssl30MAC) Size() int { - return s.h.Size() -} - -var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} - -var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} - -// MAC does not offer constant timing guarantees for SSL v3.0, since it's deemed -// useless considering the similar, protocol-level POODLE vulnerability. -func (s ssl30MAC) MAC(seq, header, data, extra []byte) []byte { - padLength := 48 - if s.h.Size() == 20 { - padLength = 40 - } - - s.h.Reset() - s.h.Write(s.key) - s.h.Write(ssl30Pad1[:padLength]) - s.h.Write(seq) - s.h.Write(header[:1]) - s.h.Write(header[3:5]) - s.h.Write(data) - s.buf = s.h.Sum(s.buf[:0]) - - s.h.Reset() - s.h.Write(s.key) - s.h.Write(ssl30Pad2[:padLength]) - s.h.Write(s.buf) - return s.h.Sum(s.buf[:0]) -} - -type constantTimeHash interface { - hash.Hash - ConstantTimeSum(b []byte) []byte -} - -// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces -// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC. -type cthWrapper struct { - h constantTimeHash -} - -func (c *cthWrapper) Size() int { return c.h.Size() } -func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() } -func (c *cthWrapper) Reset() { c.h.Reset() } -func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) } -func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) } - -func newConstantTimeHash(h func() hash.Hash) func() hash.Hash { - return func() hash.Hash { - return &cthWrapper{h().(constantTimeHash)} - } -} - -// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3. -type tls10MAC struct { - h hash.Hash - buf []byte -} - -func (s tls10MAC) Size() int { - return s.h.Size() -} - -// MAC is guaranteed to take constant time, as long as -// len(seq)+len(header)+len(data)+len(extra) is constant. extra is not fed into -// the MAC, but is only provided to make the timing profile constant. -func (s tls10MAC) MAC(seq, header, data, extra []byte) []byte { - s.h.Reset() - s.h.Write(seq) - s.h.Write(header) - s.h.Write(data) - res := s.h.Sum(s.buf[:0]) - if extra != nil { - s.h.Write(extra) - } - return res -} - -func rsaKA(version uint16) keyAgreement { - return rsaKeyAgreement{} -} - -func ecdheECDSAKA(version uint16) keyAgreement { - return &ecdheKeyAgreement{ - isRSA: false, - version: version, - } -} - -func ecdheRSAKA(version uint16) keyAgreement { - return &ecdheKeyAgreement{ - isRSA: true, - version: version, - } -} - -// mutualCipherSuite returns a cipherSuite given a list of supported -// ciphersuites and the id requested by the peer. -func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { - for _, id := range have { - if id == want { - return cipherSuiteByID(id) - } - } - return nil -} - -func cipherSuiteByID(id uint16) *cipherSuite { - for _, cipherSuite := range cipherSuites { - if cipherSuite.id == id { - return cipherSuite - } - } - return nil -} - -func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 { - for _, id := range have { - if id == want { - return cipherSuiteTLS13ByID(id) - } - } - return nil -} - -func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 { - for _, cipherSuite := range cipherSuitesTLS13 { - if cipherSuite.id == id { - return cipherSuite - } - } - return nil -} - -// A list of cipher suite IDs that are, or have been, implemented by this -// package. -// -// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml -const ( - // TLS 1.0 - 1.2 cipher suites. - TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 - TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a - TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f - TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 - TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c - TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c - TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009 - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a - TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca8 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca9 - - // TLS 1.3 cipher suites. - TLS_AES_128_GCM_SHA256 uint16 = 0x1301 - TLS_AES_256_GCM_SHA384 uint16 = 0x1302 - TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 - - // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator - // that the client is doing version fallback. See RFC 7507. - TLS_FALLBACK_SCSV uint16 = 0x5600 -) diff --git a/vendor/github.com/marten-seemann/qtls/common.go b/vendor/github.com/marten-seemann/qtls/common.go deleted file mode 100644 index df4c98e86..000000000 --- a/vendor/github.com/marten-seemann/qtls/common.go +++ /dev/null @@ -1,1148 +0,0 @@ -// Copyright 2009 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 qtls - -import ( - "container/list" - "crypto/rand" - "crypto/sha512" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io" - "math/big" - "os" - "strings" - "sync" - "time" - - "golang.org/x/sys/cpu" -) - -const ( - VersionSSL30 = 0x0300 - VersionTLS10 = 0x0301 - VersionTLS11 = 0x0302 - VersionTLS12 = 0x0303 - VersionTLS13 = 0x0304 -) - -const ( - maxPlaintext = 16384 // maximum plaintext payload length - maxCiphertext = 16384 + 2048 // maximum ciphertext payload length - maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3 - recordHeaderLen = 5 // record header length - maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - maxUselessRecords = 16 // maximum number of consecutive non-advancing records -) - -// TLS record types. -type recordType uint8 - -const ( - recordTypeChangeCipherSpec recordType = 20 - recordTypeAlert recordType = 21 - recordTypeHandshake recordType = 22 - recordTypeApplicationData recordType = 23 -) - -// TLS handshake message types. -const ( - typeHelloRequest uint8 = 0 - typeClientHello uint8 = 1 - typeServerHello uint8 = 2 - typeNewSessionTicket uint8 = 4 - typeEndOfEarlyData uint8 = 5 - typeEncryptedExtensions uint8 = 8 - typeCertificate uint8 = 11 - typeServerKeyExchange uint8 = 12 - typeCertificateRequest uint8 = 13 - typeServerHelloDone uint8 = 14 - typeCertificateVerify uint8 = 15 - typeClientKeyExchange uint8 = 16 - typeFinished uint8 = 20 - typeCertificateStatus uint8 = 22 - typeKeyUpdate uint8 = 24 - typeNextProtocol uint8 = 67 // Not IANA assigned - typeMessageHash uint8 = 254 // synthetic message -) - -// TLS compression types. -const ( - compressionNone uint8 = 0 -) - -type Extension struct { - Type uint16 - Data []byte -} - -// TLS extension numbers -const ( - extensionServerName uint16 = 0 - extensionStatusRequest uint16 = 5 - extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7 - extensionSupportedPoints uint16 = 11 - extensionSignatureAlgorithms uint16 = 13 - extensionALPN uint16 = 16 - extensionSCT uint16 = 18 - extensionSessionTicket uint16 = 35 - extensionPreSharedKey uint16 = 41 - extensionEarlyData uint16 = 42 - extensionSupportedVersions uint16 = 43 - extensionCookie uint16 = 44 - extensionPSKModes uint16 = 45 - extensionCertificateAuthorities uint16 = 47 - extensionSignatureAlgorithmsCert uint16 = 50 - extensionKeyShare uint16 = 51 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned - extensionRenegotiationInfo uint16 = 0xff01 -) - -// TLS signaling cipher suite values -const ( - scsvRenegotiation uint16 = 0x00ff -) - -// CurveID is a tls.CurveID -type CurveID = tls.CurveID - -const ( - CurveP256 CurveID = 23 - CurveP384 CurveID = 24 - CurveP521 CurveID = 25 - X25519 CurveID = 29 -) - -// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8. -type keyShare struct { - group CurveID - data []byte -} - -// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9. -const ( - pskModePlain uint8 = 0 - pskModeDHE uint8 = 1 -) - -// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved -// session. See RFC 8446, Section 4.2.11. -type pskIdentity struct { - label []byte - obfuscatedTicketAge uint32 -} - -// TLS Elliptic Curve Point Formats -// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 -const ( - pointFormatUncompressed uint8 = 0 -) - -// TLS CertificateStatusType (RFC 3546) -const ( - statusTypeOCSP uint8 = 1 -) - -// Certificate types (for certificateRequestMsg) -const ( - certTypeRSASign = 1 - certTypeECDSASign = 64 // RFC 4492, Section 5.5 -) - -// Signature algorithms (for internal signaling use). Starting at 16 to avoid overlap with -// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do. -const ( - signaturePKCS1v15 uint8 = iota + 16 - signatureECDSA - signatureRSAPSS -) - -// supportedSignatureAlgorithms contains the signature and hash algorithms that -// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+ -// CertificateRequest. The two fields are merged to match with TLS 1.3. -// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc. -var supportedSignatureAlgorithms = []SignatureScheme{ - PSSWithSHA256, - PSSWithSHA384, - PSSWithSHA512, - PKCS1WithSHA256, - ECDSAWithP256AndSHA256, - PKCS1WithSHA384, - ECDSAWithP384AndSHA384, - PKCS1WithSHA512, - ECDSAWithP521AndSHA512, - PKCS1WithSHA1, - ECDSAWithSHA1, -} - -// RSA-PSS is disabled in TLS 1.2 for Go 1.12. See Issue 30055. -var supportedSignatureAlgorithmsTLS12 = supportedSignatureAlgorithms[3:] - -// helloRetryRequestRandom is set as the Random value of a ServerHello -// to signal that the message is actually a HelloRetryRequest. -var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3. - 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, - 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, - 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, - 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, -} - -const ( - // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server - // random as a downgrade protection if the server would be capable of - // negotiating a higher version. See RFC 8446, Section 4.1.3. - downgradeCanaryTLS12 = "DOWNGRD\x01" - downgradeCanaryTLS11 = "DOWNGRD\x00" -) - -// ConnectionState records basic TLS details about the connection. -type ConnectionState struct { - Version uint16 // TLS version used by the connection (e.g. VersionTLS12) - HandshakeComplete bool // TLS handshake is complete - DidResume bool // connection resumes a previous TLS connection - CipherSuite uint16 // cipher suite in use (TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, ...) - NegotiatedProtocol string // negotiated next protocol (not guaranteed to be from Config.NextProtos) - NegotiatedProtocolIsMutual bool // negotiated protocol was advertised by server (client side only) - ServerName string // server name requested by client, if any (server side only) - PeerCertificates []*x509.Certificate // certificate chain presented by remote peer - VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates - SignedCertificateTimestamps [][]byte // SCTs from the peer, if any - OCSPResponse []byte // stapled OCSP response from peer, if any - - // ekm is a closure exposed via ExportKeyingMaterial. - ekm func(label string, context []byte, length int) ([]byte, error) - - // TLSUnique contains the "tls-unique" channel binding value (see RFC - // 5929, section 3). For resumed sessions this value will be nil - // because resumption does not include enough context (see - // https://mitls.org/pages/attacks/3SHAKE#channelbindings). This will - // change in future versions of Go once the TLS master-secret fix has - // been standardized and implemented. It is not defined in TLS 1.3. - TLSUnique []byte -} - -// ExportKeyingMaterial returns length bytes of exported key material in a new -// slice as defined in RFC 5705. If context is nil, it is not used as part of -// the seed. If the connection was set to allow renegotiation via -// Config.Renegotiation, this function will return an error. -func (cs *ConnectionState) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { - return cs.ekm(label, context, length) -} - -// ClientAuthType is tls.ClientAuthType -type ClientAuthType = tls.ClientAuthType - -const ( - NoClientCert ClientAuthType = iota - RequestClientCert - RequireAnyClientCert - VerifyClientCertIfGiven - RequireAndVerifyClientCert -) - -// requiresClientCert reports whether the ClientAuthType requires a client -// certificate to be provided. -func requiresClientCert(c ClientAuthType) bool { - switch c { - case RequireAnyClientCert, RequireAndVerifyClientCert: - return true - default: - return false - } -} - -// ClientSessionState contains the state needed by clients to resume TLS -// sessions. -type ClientSessionState struct { - sessionTicket []uint8 // Encrypted ticket used for session resumption with server - vers uint16 // SSL/TLS version negotiated for the session - cipherSuite uint16 // Ciphersuite negotiated for the session - masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret - serverCertificates []*x509.Certificate // Certificate chain presented by the server - verifiedChains [][]*x509.Certificate // Certificate chains we built for verification - receivedAt time.Time // When the session ticket was received from the server - - // TLS 1.3 fields. - nonce []byte // Ticket nonce sent by the server, to derive PSK - useBy time.Time // Expiration of the ticket lifetime as set by the server - ageAdd uint32 // Random obfuscation factor for sending the ticket age -} - -// ClientSessionCache is a cache of ClientSessionState objects that can be used -// by a client to resume a TLS session with a given server. ClientSessionCache -// implementations should expect to be called concurrently from different -// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not -// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which -// are supported via this interface. -type ClientSessionCache interface { - // Get searches for a ClientSessionState associated with the given key. - // On return, ok is true if one was found. - Get(sessionKey string) (session *ClientSessionState, ok bool) - - // Put adds the ClientSessionState to the cache with the given key. It might - // get called multiple times in a connection if a TLS 1.3 server provides - // more than one session ticket. If called with a nil *ClientSessionState, - // it should remove the cache entry. - Put(sessionKey string, cs *ClientSessionState) -} - -// SignatureScheme is a tls.SignatureScheme -type SignatureScheme = tls.SignatureScheme - -const ( - // RSASSA-PKCS1-v1_5 algorithms. - PKCS1WithSHA256 SignatureScheme = 0x0401 - PKCS1WithSHA384 SignatureScheme = 0x0501 - PKCS1WithSHA512 SignatureScheme = 0x0601 - - // RSASSA-PSS algorithms with public key OID rsaEncryption. - PSSWithSHA256 SignatureScheme = 0x0804 - PSSWithSHA384 SignatureScheme = 0x0805 - PSSWithSHA512 SignatureScheme = 0x0806 - - // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3. - ECDSAWithP256AndSHA256 SignatureScheme = 0x0403 - ECDSAWithP384AndSHA384 SignatureScheme = 0x0503 - ECDSAWithP521AndSHA512 SignatureScheme = 0x0603 - - // Legacy signature and hash algorithms for TLS 1.2. - PKCS1WithSHA1 SignatureScheme = 0x0201 - ECDSAWithSHA1 SignatureScheme = 0x0203 -) - -// A ClientHelloInfo is a tls.ClientHelloInfo -type ClientHelloInfo = tls.ClientHelloInfo - -// The CertificateRequestInfo is a tls.CertificateRequestInfo -type CertificateRequestInfo = tls.CertificateRequestInfo - -// RenegotiationSupport enumerates the different levels of support for TLS -// renegotiation. TLS renegotiation is the act of performing subsequent -// handshakes on a connection after the first. This significantly complicates -// the state machine and has been the source of numerous, subtle security -// issues. Initiating a renegotiation is not supported, but support for -// accepting renegotiation requests may be enabled. -// -// Even when enabled, the server may not change its identity between handshakes -// (i.e. the leaf certificate must be the same). Additionally, concurrent -// handshake and application data flow is not permitted so renegotiation can -// only be used with protocols that synchronise with the renegotiation, such as -// HTTPS. -// -// Renegotiation is not defined in TLS 1.3. -type RenegotiationSupport int - -const ( - // RenegotiateNever disables renegotiation. - RenegotiateNever RenegotiationSupport = iota - - // RenegotiateOnceAsClient allows a remote server to request - // renegotiation once per connection. - RenegotiateOnceAsClient - - // RenegotiateFreelyAsClient allows a remote server to repeatedly - // request renegotiation. - RenegotiateFreelyAsClient -) - -// A Config structure is used to configure a TLS client or server. -// After one has been passed to a TLS function it must not be -// modified. A Config may be reused; the tls package will also not -// modify it. -type Config struct { - // Rand provides the source of entropy for nonces and RSA blinding. - // If Rand is nil, TLS uses the cryptographic random reader in package - // crypto/rand. - // The Reader must be safe for use by multiple goroutines. - Rand io.Reader - - // Time returns the current time as the number of seconds since the epoch. - // If Time is nil, TLS uses time.Now. - Time func() time.Time - - // Certificates contains one or more certificate chains to present to - // the other side of the connection. Server configurations must include - // at least one certificate or else set GetCertificate. Clients doing - // client-authentication may set either Certificates or - // GetClientCertificate. - Certificates []Certificate - - // NameToCertificate maps from a certificate name to an element of - // Certificates. Note that a certificate name can be of the form - // '*.example.com' and so doesn't have to be a domain name as such. - // See Config.BuildNameToCertificate - // The nil value causes the first element of Certificates to be used - // for all connections. - NameToCertificate map[string]*Certificate - - // GetCertificate returns a Certificate based on the given - // ClientHelloInfo. It will only be called if the client supplies SNI - // information or if Certificates is empty. - // - // If GetCertificate is nil or returns nil, then the certificate is - // retrieved from NameToCertificate. If NameToCertificate is nil, the - // first element of Certificates will be used. - GetCertificate func(*ClientHelloInfo) (*Certificate, error) - - // GetClientCertificate, if not nil, is called when a server requests a - // certificate from a client. If set, the contents of Certificates will - // be ignored. - // - // If GetClientCertificate returns an error, the handshake will be - // aborted and that error will be returned. Otherwise - // GetClientCertificate must return a non-nil Certificate. If - // Certificate.Certificate is empty then no certificate will be sent to - // the server. If this is unacceptable to the server then it may abort - // the handshake. - // - // GetClientCertificate may be called multiple times for the same - // connection if renegotiation occurs or if TLS 1.3 is in use. - GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) - - // GetConfigForClient, if not nil, is called after a ClientHello is - // received from a client. It may return a non-nil Config in order to - // change the Config that will be used to handle this connection. If - // the returned Config is nil, the original Config will be used. The - // Config returned by this callback may not be subsequently modified. - // - // If GetConfigForClient is nil, the Config passed to Server() will be - // used for all connections. - // - // Uniquely for the fields in the returned Config, session ticket keys - // will be duplicated from the original Config if not set. - // Specifically, if SetSessionTicketKeys was called on the original - // config but not on the returned config then the ticket keys from the - // original config will be copied into the new config before use. - // Otherwise, if SessionTicketKey was set in the original config but - // not in the returned config then it will be copied into the returned - // config before use. If neither of those cases applies then the key - // material from the returned config will be used for session tickets. - GetConfigForClient func(*ClientHelloInfo) (*Config, error) - - // VerifyPeerCertificate, if not nil, is called after normal - // certificate verification by either a TLS client or server. It - // receives the raw ASN.1 certificates provided by the peer and also - // any verified chains that normal processing found. If it returns a - // non-nil error, the handshake is aborted and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. If normal verification is disabled by - // setting InsecureSkipVerify, or (for a server) when ClientAuth is - // RequestClientCert or RequireAnyClientCert, then this callback will - // be considered but the verifiedChains argument will always be nil. - VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error - - // RootCAs defines the set of root certificate authorities - // that clients use when verifying server certificates. - // If RootCAs is nil, TLS uses the host's root CA set. - RootCAs *x509.CertPool - - // NextProtos is a list of supported application level protocols, in - // order of preference. - NextProtos []string - - // ServerName is used to verify the hostname on the returned - // certificates unless InsecureSkipVerify is given. It is also included - // in the client's handshake to support virtual hosting unless it is - // an IP address. - ServerName string - - // ClientAuth determines the server's policy for - // TLS Client Authentication. The default is NoClientCert. - ClientAuth ClientAuthType - - // ClientCAs defines the set of root certificate authorities - // that servers use if required to verify a client certificate - // by the policy in ClientAuth. - ClientCAs *x509.CertPool - - // InsecureSkipVerify controls whether a client verifies the - // server's certificate chain and host name. - // If InsecureSkipVerify is true, TLS accepts any certificate - // presented by the server and any host name in that certificate. - // In this mode, TLS is susceptible to man-in-the-middle attacks. - // This should be used only for testing. - InsecureSkipVerify bool - - // CipherSuites is a list of supported cipher suites for TLS versions up to - // TLS 1.2. If CipherSuites is nil, a default list of secure cipher suites - // is used, with a preference order based on hardware performance. The - // default cipher suites might change over Go versions. Note that TLS 1.3 - // ciphersuites are not configurable. - CipherSuites []uint16 - - // PreferServerCipherSuites controls whether the server selects the - // client's most preferred ciphersuite, or the server's most preferred - // ciphersuite. If true then the server's preference, as expressed in - // the order of elements in CipherSuites, is used. - PreferServerCipherSuites bool - - // SessionTicketsDisabled may be set to true to disable session ticket and - // PSK (resumption) support. Note that on clients, session ticket support is - // also disabled if ClientSessionCache is nil. - SessionTicketsDisabled bool - - // SessionTicketKey is used by TLS servers to provide session resumption. - // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled - // with random data before the first server handshake. - // - // If multiple servers are terminating connections for the same host - // they should all have the same SessionTicketKey. If the - // SessionTicketKey leaks, previously recorded and future TLS - // connections using that key might be compromised. - SessionTicketKey [32]byte - - // ClientSessionCache is a cache of ClientSessionState entries for TLS - // session resumption. It is only used by clients. - ClientSessionCache ClientSessionCache - - // MinVersion contains the minimum SSL/TLS version that is acceptable. - // If zero, then TLS 1.0 is taken as the minimum. - MinVersion uint16 - - // MaxVersion contains the maximum SSL/TLS version that is acceptable. - // If zero, then the maximum version supported by this package is used, - // which is currently TLS 1.3. - MaxVersion uint16 - - // CurvePreferences contains the elliptic curves that will be used in - // an ECDHE handshake, in preference order. If empty, the default will - // be used. The client will use the first preference as the type for - // its key share in TLS 1.3. This may change in the future. - CurvePreferences []CurveID - - // DynamicRecordSizingDisabled disables adaptive sizing of TLS records. - // When true, the largest possible TLS record size is always used. When - // false, the size of TLS records may be adjusted in an attempt to - // improve latency. - DynamicRecordSizingDisabled bool - - // Renegotiation controls what types of renegotiation are supported. - // The default, none, is correct for the vast majority of applications. - Renegotiation RenegotiationSupport - - // KeyLogWriter optionally specifies a destination for TLS master secrets - // in NSS key log format that can be used to allow external programs - // such as Wireshark to decrypt TLS connections. - // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. - // Use of KeyLogWriter compromises security and should only be - // used for debugging. - KeyLogWriter io.Writer - - // GetExtensions, if not nil, is called before a message that allows - // sending of extensions is sent. - // Currently only implemented for the ClientHello message (for the client) - // and for the EncryptedExtensions message (for the server). - // Only valid for TLS 1.3. - GetExtensions func(handshakeMessageType uint8) []Extension - - // ReceivedExtensions, if not nil, is called when a message that allows the - // inclusion of extensions is received. - // It is called with an empty slice of extensions, if the message didn't - // contain any extensions. - // Currently only implemented for the ClientHello message (sent by the - // client) and for the EncryptedExtensions message (sent by the server). - // Only valid for TLS 1.3. - ReceivedExtensions func(handshakeMessageType uint8, exts []Extension) - - serverInitOnce sync.Once // guards calling (*Config).serverInit - - // mutex protects sessionTicketKeys. - mutex sync.RWMutex - // sessionTicketKeys contains zero or more ticket keys. If the length - // is zero, SessionTicketsDisabled must be true. The first key is used - // for new tickets and any subsequent keys can be used to decrypt old - // tickets. - sessionTicketKeys []ticketKey - - // AlternativeRecordLayer is used by QUIC - AlternativeRecordLayer RecordLayer -} - -type RecordLayer interface { - SetReadKey(suite *CipherSuite, trafficSecret []byte) - SetWriteKey(suite *CipherSuite, trafficSecret []byte) - ReadHandshakeMessage() ([]byte, error) - WriteRecord([]byte) (int, error) - SendAlert(uint8) -} - -// ticketKeyNameLen is the number of bytes of identifier that is prepended to -// an encrypted session ticket in order to identify the key used to encrypt it. -const ticketKeyNameLen = 16 - -// ticketKey is the internal representation of a session ticket key. -type ticketKey struct { - // keyName is an opaque byte string that serves to identify the session - // ticket key. It's exposed as plaintext in every session ticket. - keyName [ticketKeyNameLen]byte - aesKey [16]byte - hmacKey [16]byte -} - -// ticketKeyFromBytes converts from the external representation of a session -// ticket key to a ticketKey. Externally, session ticket keys are 32 random -// bytes and this function expands that into sufficient name and key material. -func ticketKeyFromBytes(b [32]byte) (key ticketKey) { - hashed := sha512.Sum512(b[:]) - copy(key.keyName[:], hashed[:ticketKeyNameLen]) - copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16]) - copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32]) - return key -} - -// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session -// ticket, and the lifetime we set for tickets we send. -const maxSessionTicketLifetime = 7 * 24 * time.Hour - -// Clone returns a shallow clone of c. It is safe to clone a Config that is -// being used concurrently by a TLS client or server. -func (c *Config) Clone() *Config { - // Running serverInit ensures that it's safe to read - // SessionTicketsDisabled. - c.serverInitOnce.Do(func() { c.serverInit(nil) }) - - var sessionTicketKeys []ticketKey - c.mutex.RLock() - sessionTicketKeys = c.sessionTicketKeys - c.mutex.RUnlock() - - return &Config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - GetClientCertificate: c.GetClientCertificate, - GetConfigForClient: c.GetConfigForClient, - VerifyPeerCertificate: c.VerifyPeerCertificate, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: c.ClientSessionCache, - MinVersion: c.MinVersion, - MaxVersion: c.MaxVersion, - CurvePreferences: c.CurvePreferences, - DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, - Renegotiation: c.Renegotiation, - KeyLogWriter: c.KeyLogWriter, - GetExtensions: c.GetExtensions, - ReceivedExtensions: c.ReceivedExtensions, - sessionTicketKeys: sessionTicketKeys, - } -} - -// serverInit is run under c.serverInitOnce to do initialization of c. If c was -// returned by a GetConfigForClient callback then the argument should be the -// Config that was passed to Server, otherwise it should be nil. -func (c *Config) serverInit(originalConfig *Config) { - if c.SessionTicketsDisabled || len(c.ticketKeys()) != 0 { - return - } - - alreadySet := false - for _, b := range c.SessionTicketKey { - if b != 0 { - alreadySet = true - break - } - } - - if !alreadySet { - if originalConfig != nil { - copy(c.SessionTicketKey[:], originalConfig.SessionTicketKey[:]) - } else if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { - c.SessionTicketsDisabled = true - return - } - } - - if originalConfig != nil { - originalConfig.mutex.RLock() - c.sessionTicketKeys = originalConfig.sessionTicketKeys - originalConfig.mutex.RUnlock() - } else { - c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)} - } -} - -func (c *Config) ticketKeys() []ticketKey { - c.mutex.RLock() - // c.sessionTicketKeys is constant once created. SetSessionTicketKeys - // will only update it by replacing it with a new value. - ret := c.sessionTicketKeys - c.mutex.RUnlock() - return ret -} - -// SetSessionTicketKeys updates the session ticket keys for a server. The first -// key will be used when creating new tickets, while all keys can be used for -// decrypting tickets. It is safe to call this function while the server is -// running in order to rotate the session ticket keys. The function will panic -// if keys is empty. -func (c *Config) SetSessionTicketKeys(keys [][32]byte) { - if len(keys) == 0 { - panic("tls: keys must have at least one key") - } - - newKeys := make([]ticketKey, len(keys)) - for i, bytes := range keys { - newKeys[i] = ticketKeyFromBytes(bytes) - } - - c.mutex.Lock() - c.sessionTicketKeys = newKeys - c.mutex.Unlock() -} - -func (c *Config) rand() io.Reader { - r := c.Rand - if r == nil { - return rand.Reader - } - return r -} - -func (c *Config) time() time.Time { - t := c.Time - if t == nil { - t = time.Now - } - return t() -} - -func (c *Config) cipherSuites() []uint16 { - s := c.CipherSuites - if s == nil { - s = defaultCipherSuites() - } - return s -} - -var supportedVersions = []uint16{ - VersionTLS13, - VersionTLS12, - VersionTLS11, - VersionTLS10, - VersionSSL30, -} - -func (c *Config) supportedVersions(isClient bool) []uint16 { - versions := make([]uint16, 0, len(supportedVersions)) - for _, v := range supportedVersions { - if c != nil && c.MinVersion != 0 && v < c.MinVersion { - continue - } - if c != nil && c.MaxVersion != 0 && v > c.MaxVersion { - continue - } - // TLS 1.0 is the minimum version supported as a client. - if isClient && v < VersionTLS10 { - continue - } - // TLS 1.3 is opt-in in Go 1.12. - if v == VersionTLS13 && !isTLS13Supported() { - continue - } - versions = append(versions, v) - } - return versions -} - -// tls13Support caches the result for isTLS13Supported. -var tls13Support struct { - sync.Once - cached bool -} - -// isTLS13Supported returns whether the program opted into TLS 1.3 via -// GODEBUG=tls13=1. It's cached after the first execution. -func isTLS13Supported() bool { - return true - tls13Support.Do(func() { - tls13Support.cached = goDebugString("tls13") == "1" - }) - return tls13Support.cached -} - -// goDebugString returns the value of the named GODEBUG key. -// GODEBUG is of the form "key=val,key2=val2". -func goDebugString(key string) string { - s := os.Getenv("GODEBUG") - for i := 0; i < len(s)-len(key)-1; i++ { - if i > 0 && s[i-1] != ',' { - continue - } - afterKey := s[i+len(key):] - if afterKey[0] != '=' || s[i:i+len(key)] != key { - continue - } - val := afterKey[1:] - for i, b := range val { - if b == ',' { - return val[:i] - } - } - return val - } - return "" -} - -func (c *Config) maxSupportedVersion(isClient bool) uint16 { - supportedVersions := c.supportedVersions(isClient) - if len(supportedVersions) == 0 { - return 0 - } - return supportedVersions[0] -} - -// supportedVersionsFromMax returns a list of supported versions derived from a -// legacy maximum version value. Note that only versions supported by this -// library are returned. Any newer peer will use supportedVersions anyway. -func supportedVersionsFromMax(maxVersion uint16) []uint16 { - versions := make([]uint16, 0, len(supportedVersions)) - for _, v := range supportedVersions { - if v > maxVersion { - continue - } - versions = append(versions, v) - } - return versions -} - -var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} - -func (c *Config) curvePreferences() []CurveID { - if c == nil || len(c.CurvePreferences) == 0 { - return defaultCurvePreferences - } - return c.CurvePreferences -} - -// mutualVersion returns the protocol version to use given the advertised -// versions of the peer. Priority is given to the peer preference order. -func (c *Config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) { - supportedVersions := c.supportedVersions(isClient) - for _, peerVersion := range peerVersions { - for _, v := range supportedVersions { - if v == peerVersion { - return v, true - } - } - } - return 0, false -} - -// getCertificate returns the best certificate for the given ClientHelloInfo, -// defaulting to the first element of c.Certificates. -func (c *Config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) { - if c.GetCertificate != nil && - (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) { - cert, err := c.GetCertificate(clientHello) - if cert != nil || err != nil { - return cert, err - } - } - - if len(c.Certificates) == 0 { - return nil, errors.New("tls: no certificates configured") - } - - if len(c.Certificates) == 1 || c.NameToCertificate == nil { - // There's only one choice, so no point doing any work. - return &c.Certificates[0], nil - } - - name := strings.ToLower(clientHello.ServerName) - for len(name) > 0 && name[len(name)-1] == '.' { - name = name[:len(name)-1] - } - - if cert, ok := c.NameToCertificate[name]; ok { - return cert, nil - } - - // try replacing labels in the name with wildcards until we get a - // match. - labels := strings.Split(name, ".") - for i := range labels { - labels[i] = "*" - candidate := strings.Join(labels, ".") - if cert, ok := c.NameToCertificate[candidate]; ok { - return cert, nil - } - } - - // If nothing matches, return the first certificate. - return &c.Certificates[0], nil -} - -// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate -// from the CommonName and SubjectAlternateName fields of each of the leaf -// certificates. -func (c *Config) BuildNameToCertificate() { - c.NameToCertificate = make(map[string]*Certificate) - for i := range c.Certificates { - cert := &c.Certificates[i] - x509Cert := cert.Leaf - if x509Cert == nil { - var err error - x509Cert, err = x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - continue - } - } - if len(x509Cert.Subject.CommonName) > 0 { - c.NameToCertificate[x509Cert.Subject.CommonName] = cert - } - for _, san := range x509Cert.DNSNames { - c.NameToCertificate[san] = cert - } - } -} - -const ( - keyLogLabelTLS12 = "CLIENT_RANDOM" - keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET" - keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET" - keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0" - keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0" -) - -func (c *Config) writeKeyLog(label string, clientRandom, secret []byte) error { - if c.KeyLogWriter == nil { - return nil - } - - logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret)) - - writerMutex.Lock() - _, err := c.KeyLogWriter.Write(logLine) - writerMutex.Unlock() - - return err -} - -// writerMutex protects all KeyLogWriters globally. It is rarely enabled, -// and is only for debugging, so a global mutex saves space. -var writerMutex sync.Mutex - -// A Certificate is a tls.Certificate -type Certificate = tls.Certificate - -type handshakeMessage interface { - marshal() []byte - unmarshal([]byte) bool -} - -// lruSessionCache is a ClientSessionCache implementation that uses an LRU -// caching strategy. -type lruSessionCache struct { - sync.Mutex - - m map[string]*list.Element - q *list.List - capacity int -} - -type lruSessionCacheEntry struct { - sessionKey string - state *ClientSessionState -} - -// NewLRUClientSessionCache returns a ClientSessionCache with the given -// capacity that uses an LRU strategy. If capacity is < 1, a default capacity -// is used instead. -func NewLRUClientSessionCache(capacity int) ClientSessionCache { - const defaultSessionCacheCapacity = 64 - - if capacity < 1 { - capacity = defaultSessionCacheCapacity - } - return &lruSessionCache{ - m: make(map[string]*list.Element), - q: list.New(), - capacity: capacity, - } -} - -// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry -// corresponding to sessionKey is removed from the cache instead. -func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) { - c.Lock() - defer c.Unlock() - - if elem, ok := c.m[sessionKey]; ok { - if cs == nil { - c.q.Remove(elem) - delete(c.m, sessionKey) - } else { - entry := elem.Value.(*lruSessionCacheEntry) - entry.state = cs - c.q.MoveToFront(elem) - } - return - } - - if c.q.Len() < c.capacity { - entry := &lruSessionCacheEntry{sessionKey, cs} - c.m[sessionKey] = c.q.PushFront(entry) - return - } - - elem := c.q.Back() - entry := elem.Value.(*lruSessionCacheEntry) - delete(c.m, entry.sessionKey) - entry.sessionKey = sessionKey - entry.state = cs - c.q.MoveToFront(elem) - c.m[sessionKey] = elem -} - -// Get returns the ClientSessionState value associated with a given key. It -// returns (nil, false) if no value is found. -func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) { - c.Lock() - defer c.Unlock() - - if elem, ok := c.m[sessionKey]; ok { - c.q.MoveToFront(elem) - return elem.Value.(*lruSessionCacheEntry).state, true - } - return nil, false -} - -// TODO(jsing): Make these available to both crypto/x509 and crypto/tls. -type dsaSignature struct { - R, S *big.Int -} - -type ecdsaSignature dsaSignature - -var emptyConfig Config - -func defaultConfig() *Config { - return &emptyConfig -} - -var ( - once sync.Once - varDefaultCipherSuites []uint16 - varDefaultCipherSuitesTLS13 []uint16 -) - -func defaultCipherSuites() []uint16 { - once.Do(initDefaultCipherSuites) - return varDefaultCipherSuites -} - -func defaultCipherSuitesTLS13() []uint16 { - once.Do(initDefaultCipherSuites) - return varDefaultCipherSuitesTLS13 -} - -func initDefaultCipherSuites() { - var topCipherSuites []uint16 - - // Check the cpu flags for each platform that has optimized GCM implementations. - // Worst case, these variables will just all be false. - var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - // TODO: check for s390 - // hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - hasGCMAsmS390X = false - - hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X - ) - - if hasGCMAsm { - // If AES-GCM hardware is provided then prioritise AES-GCM - // cipher suites. - topCipherSuites = []uint16{ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - } - varDefaultCipherSuitesTLS13 = []uint16{ - TLS_AES_128_GCM_SHA256, - TLS_CHACHA20_POLY1305_SHA256, - TLS_AES_256_GCM_SHA384, - } - } else { - // Without AES-GCM hardware, we put the ChaCha20-Poly1305 - // cipher suites first. - topCipherSuites = []uint16{ - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - } - varDefaultCipherSuitesTLS13 = []uint16{ - TLS_CHACHA20_POLY1305_SHA256, - TLS_AES_128_GCM_SHA256, - TLS_AES_256_GCM_SHA384, - } - } - - varDefaultCipherSuites = make([]uint16, 0, len(cipherSuites)) - varDefaultCipherSuites = append(varDefaultCipherSuites, topCipherSuites...) - -NextCipherSuite: - for _, suite := range cipherSuites { - if suite.flags&suiteDefaultOff != 0 { - continue - } - for _, existing := range varDefaultCipherSuites { - if existing == suite.id { - continue NextCipherSuite - } - } - varDefaultCipherSuites = append(varDefaultCipherSuites, suite.id) - } -} - -func unexpectedMessageError(wanted, got interface{}) error { - return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted) -} - -func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool { - for _, s := range supportedSignatureAlgorithms { - if s == sigAlg { - return true - } - } - return false -} - -// signatureFromSignatureScheme maps a signature algorithm to the underlying -// signature method (without hash function). -func signatureFromSignatureScheme(signatureAlgorithm SignatureScheme) uint8 { - switch signatureAlgorithm { - case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512: - return signaturePKCS1v15 - case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512: - return signatureRSAPSS - case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512: - return signatureECDSA - default: - return 0 - } -} diff --git a/vendor/github.com/marten-seemann/qtls/conn.go b/vendor/github.com/marten-seemann/qtls/conn.go deleted file mode 100644 index bbb67e563..000000000 --- a/vendor/github.com/marten-seemann/qtls/conn.go +++ /dev/null @@ -1,1467 +0,0 @@ -// Copyright 2010 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. - -// TLS low level connection and record layer - -package qtls - -import ( - "bytes" - "crypto/cipher" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "io" - "net" - "sync" - "sync/atomic" - "time" -) - -// A Conn represents a secured connection. -// It implements the net.Conn interface. -type Conn struct { - // constant - conn net.Conn - isClient bool - - // handshakeStatus is 1 if the connection is currently transferring - // application data (i.e. is not currently processing a handshake). - // This field is only to be accessed with sync/atomic. - handshakeStatus uint32 - // constant after handshake; protected by handshakeMutex - handshakeMutex sync.Mutex - handshakeErr error // error resulting from handshake - vers uint16 // TLS version - haveVers bool // version has been negotiated - config *Config // configuration passed to constructor - // handshakes counts the number of handshakes performed on the - // connection so far. If renegotiation is disabled then this is either - // zero or one. - handshakes int - didResume bool // whether this connection was a session resumption - cipherSuite uint16 - ocspResponse []byte // stapled OCSP response - scts [][]byte // signed certificate timestamps from server - peerCertificates []*x509.Certificate - // verifiedChains contains the certificate chains that we built, as - // opposed to the ones presented by the server. - verifiedChains [][]*x509.Certificate - // serverName contains the server name indicated by the client, if any. - serverName string - // secureRenegotiation is true if the server echoed the secure - // renegotiation extension. (This is meaningless as a server because - // renegotiation is not supported in that case.) - secureRenegotiation bool - // ekm is a closure for exporting keying material. - ekm func(label string, context []byte, length int) ([]byte, error) - // resumptionSecret is the resumption_master_secret for handling - // NewSessionTicket messages. nil if config.SessionTicketsDisabled. - resumptionSecret []byte - - // clientFinishedIsFirst is true if the client sent the first Finished - // message during the most recent handshake. This is recorded because - // the first transmitted Finished message is the tls-unique - // channel-binding value. - clientFinishedIsFirst bool - - // closeNotifyErr is any error from sending the alertCloseNotify record. - closeNotifyErr error - // closeNotifySent is true if the Conn attempted to send an - // alertCloseNotify record. - closeNotifySent bool - - // clientFinished and serverFinished contain the Finished message sent - // by the client or server in the most recent handshake. This is - // retained to support the renegotiation extension and tls-unique - // channel-binding. - clientFinished [12]byte - serverFinished [12]byte - - clientProtocol string - clientProtocolFallback bool - - // input/output - in, out halfConn - rawInput bytes.Buffer // raw input, starting with a record header - input bytes.Reader // application data waiting to be read, from rawInput.Next - hand bytes.Buffer // handshake data waiting to be read - outBuf []byte // scratch buffer used by out.encrypt - buffering bool // whether records are buffered in sendBuf - sendBuf []byte // a buffer of records waiting to be sent - - // bytesSent counts the bytes of application data sent. - // packetsSent counts packets. - bytesSent int64 - packetsSent int64 - - // retryCount counts the number of consecutive non-advancing records - // received by Conn.readRecord. That is, records that neither advance the - // handshake, nor deliver application data. Protected by in.Mutex. - retryCount int - - // activeCall is an atomic int32; the low bit is whether Close has - // been called. the rest of the bits are the number of goroutines - // in Conn.Write. - activeCall int32 - - tmp [16]byte -} - -// Access to net.Conn methods. -// Cannot just embed net.Conn because that would -// export the struct field too. - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// SetDeadline sets the read and write deadlines associated with the connection. -// A zero value for t means Read and Write will not time out. -// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. -func (c *Conn) SetDeadline(t time.Time) error { - return c.conn.SetDeadline(t) -} - -// SetReadDeadline sets the read deadline on the underlying connection. -// A zero value for t means Read will not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetWriteDeadline sets the write deadline on the underlying connection. -// A zero value for t means Write will not time out. -// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. -func (c *Conn) SetWriteDeadline(t time.Time) error { - return c.conn.SetWriteDeadline(t) -} - -// A halfConn represents one direction of the record layer -// connection, either sending or receiving. -type halfConn struct { - sync.Mutex - - err error // first permanent error - version uint16 // protocol version - cipher interface{} // cipher algorithm - mac macFunction - seq [8]byte // 64-bit sequence number - additionalData [13]byte // to avoid allocs; interface method args escape - - nextCipher interface{} // next encryption state - nextMac macFunction // next MAC algorithm - - trafficSecret []byte // current TLS 1.3 traffic secret - - setKeyCallback func(suite *CipherSuite, trafficSecret []byte) -} - -func (hc *halfConn) setErrorLocked(err error) error { - hc.err = err - return err -} - -// prepareCipherSpec sets the encryption and MAC states -// that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { - hc.version = version - hc.nextCipher = cipher - hc.nextMac = mac -} - -// changeCipherSpec changes the encryption and MAC states -// to the ones previously passed to prepareCipherSpec. -func (hc *halfConn) changeCipherSpec() error { - if hc.nextCipher == nil || hc.version == VersionTLS13 { - return alertInternalError - } - hc.cipher = hc.nextCipher - hc.mac = hc.nextMac - hc.nextCipher = nil - hc.nextMac = nil - for i := range hc.seq { - hc.seq[i] = 0 - } - return nil -} - -func (hc *halfConn) exportKey(suite *cipherSuiteTLS13, trafficSecret []byte) { - if hc.setKeyCallback != nil { - hc.setKeyCallback(&CipherSuite{suite}, trafficSecret) - } -} - -func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) { - hc.trafficSecret = secret - key, iv := suite.trafficKey(secret) - hc.cipher = suite.aead(key, iv) - for i := range hc.seq { - hc.seq[i] = 0 - } -} - -// incSeq increments the sequence number. -func (hc *halfConn) incSeq() { - for i := 7; i >= 0; i-- { - hc.seq[i]++ - if hc.seq[i] != 0 { - return - } - } - - // Not allowed to let sequence number wrap. - // Instead, must renegotiate before it does. - // Not likely enough to bother. - panic("TLS: sequence number wraparound") -} - -// explicitNonceLen returns the number of bytes of explicit nonce or IV included -// in each record. Explicit nonces are present only in CBC modes after TLS 1.0 -// and in certain AEAD modes in TLS 1.2. -func (hc *halfConn) explicitNonceLen() int { - if hc.cipher == nil { - return 0 - } - - switch c := hc.cipher.(type) { - case cipher.Stream: - return 0 - case aead: - return c.explicitNonceLen() - case cbcMode: - // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack. - if hc.version >= VersionTLS11 { - return c.BlockSize() - } - return 0 - default: - panic("unknown cipher type") - } -} - -// extractPadding returns, in constant time, the length of the padding to remove -// from the end of payload. It also returns a byte which is equal to 255 if the -// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2. -func extractPadding(payload []byte) (toRemove int, good byte) { - if len(payload) < 1 { - return 0, 0 - } - - paddingLen := payload[len(payload)-1] - t := uint(len(payload)-1) - uint(paddingLen) - // if len(payload) >= (paddingLen - 1) then the MSB of t is zero - good = byte(int32(^t) >> 31) - - // The maximum possible padding length plus the actual length field - toCheck := 256 - // The length of the padded data is public, so we can use an if here - if toCheck > len(payload) { - toCheck = len(payload) - } - - for i := 0; i < toCheck; i++ { - t := uint(paddingLen) - uint(i) - // if i <= paddingLen then the MSB of t is zero - mask := byte(int32(^t) >> 31) - b := payload[len(payload)-1-i] - good &^= mask&paddingLen ^ mask&b - } - - // We AND together the bits of good and replicate the result across - // all the bits. - good &= good << 4 - good &= good << 2 - good &= good << 1 - good = uint8(int8(good) >> 7) - - toRemove = int(paddingLen) + 1 - return -} - -// extractPaddingSSL30 is a replacement for extractPadding in the case that the -// protocol version is SSLv3. In this version, the contents of the padding -// are random and cannot be checked. -func extractPaddingSSL30(payload []byte) (toRemove int, good byte) { - if len(payload) < 1 { - return 0, 0 - } - - paddingLen := int(payload[len(payload)-1]) + 1 - if paddingLen > len(payload) { - return 0, 0 - } - - return paddingLen, 255 -} - -func roundUp(a, b int) int { - return a + (b-a%b)%b -} - -// cbcMode is an interface for block ciphers using cipher block chaining. -type cbcMode interface { - cipher.BlockMode - SetIV([]byte) -} - -// decrypt authenticates and decrypts the record if protection is active at -// this stage. The returned plaintext might overlap with the input. -func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) { - var plaintext []byte - typ := recordType(record[0]) - payload := record[recordHeaderLen:] - - // In TLS 1.3, change_cipher_spec messages are to be ignored without being - // decrypted. See RFC 8446, Appendix D.4. - if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec { - return payload, typ, nil - } - - paddingGood := byte(255) - paddingLen := 0 - - explicitNonceLen := hc.explicitNonceLen() - - if hc.cipher != nil { - switch c := hc.cipher.(type) { - case cipher.Stream: - c.XORKeyStream(payload, payload) - case aead: - if len(payload) < explicitNonceLen { - return nil, 0, alertBadRecordMAC - } - nonce := payload[:explicitNonceLen] - if len(nonce) == 0 { - nonce = hc.seq[:] - } - payload = payload[explicitNonceLen:] - - additionalData := hc.additionalData[:] - if hc.version == VersionTLS13 { - additionalData = record[:recordHeaderLen] - } else { - copy(additionalData, hc.seq[:]) - copy(additionalData[8:], record[:3]) - n := len(payload) - c.Overhead() - additionalData[11] = byte(n >> 8) - additionalData[12] = byte(n) - } - - var err error - plaintext, err = c.Open(payload[:0], nonce, payload, additionalData) - if err != nil { - return nil, 0, alertBadRecordMAC - } - case cbcMode: - blockSize := c.BlockSize() - minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize) - if len(payload)%blockSize != 0 || len(payload) < minPayload { - return nil, 0, alertBadRecordMAC - } - - if explicitNonceLen > 0 { - c.SetIV(payload[:explicitNonceLen]) - payload = payload[explicitNonceLen:] - } - c.CryptBlocks(payload, payload) - - // In a limited attempt to protect against CBC padding oracles like - // Lucky13, the data past paddingLen (which is secret) is passed to - // the MAC function as extra data, to be fed into the HMAC after - // computing the digest. This makes the MAC roughly constant time as - // long as the digest computation is constant time and does not - // affect the subsequent write, modulo cache effects. - if hc.version == VersionSSL30 { - paddingLen, paddingGood = extractPaddingSSL30(payload) - } else { - paddingLen, paddingGood = extractPadding(payload) - } - default: - panic("unknown cipher type") - } - - if hc.version == VersionTLS13 { - if typ != recordTypeApplicationData { - return nil, 0, alertUnexpectedMessage - } - if len(plaintext) > maxPlaintext+1 { - return nil, 0, alertRecordOverflow - } - // Remove padding and find the ContentType scanning from the end. - for i := len(plaintext) - 1; i >= 0; i-- { - if plaintext[i] != 0 { - typ = recordType(plaintext[i]) - plaintext = plaintext[:i] - break - } - if i == 0 { - return nil, 0, alertUnexpectedMessage - } - } - } - } else { - plaintext = payload - } - - if hc.mac != nil { - macSize := hc.mac.Size() - if len(payload) < macSize { - return nil, 0, alertBadRecordMAC - } - - n := len(payload) - macSize - paddingLen - n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 } - record[3] = byte(n >> 8) - record[4] = byte(n) - remoteMAC := payload[n : n+macSize] - localMAC := hc.mac.MAC(hc.seq[0:], record[:recordHeaderLen], payload[:n], payload[n+macSize:]) - - if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { - return nil, 0, alertBadRecordMAC - } - - plaintext = payload[:n] - } - - hc.incSeq() - return plaintext, typ, nil -} - -func (c *Conn) setAlternativeRecordLayer() { - if c.config.AlternativeRecordLayer != nil { - c.in.setKeyCallback = c.config.AlternativeRecordLayer.SetReadKey - c.out.setKeyCallback = c.config.AlternativeRecordLayer.SetWriteKey - } -} - -// sliceForAppend extends the input slice by n bytes. head is the full extended -// slice, while tail is the appended part. If the original slice has sufficient -// capacity no allocation is performed. -func sliceForAppend(in []byte, n int) (head, tail []byte) { - if total := len(in) + n; cap(in) >= total { - head = in[:total] - } else { - head = make([]byte, total) - copy(head, in) - } - tail = head[len(in):] - return -} - -// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and -// appends it to record, which contains the record header. -func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) { - if hc.cipher == nil { - return append(record, payload...), nil - } - - var explicitNonce []byte - if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 { - record, explicitNonce = sliceForAppend(record, explicitNonceLen) - if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 { - // The AES-GCM construction in TLS has an explicit nonce so that the - // nonce can be random. However, the nonce is only 8 bytes which is - // too small for a secure, random nonce. Therefore we use the - // sequence number as the nonce. The 3DES-CBC construction also has - // an 8 bytes nonce but its nonces must be unpredictable (see RFC - // 5246, Appendix F.3), forcing us to use randomness. That's not - // 3DES' biggest problem anyway because the birthday bound on block - // collision is reached first due to its simlarly small block size - // (see the Sweet32 attack). - copy(explicitNonce, hc.seq[:]) - } else { - if _, err := io.ReadFull(rand, explicitNonce); err != nil { - return nil, err - } - } - } - - var mac []byte - if hc.mac != nil { - mac = hc.mac.MAC(hc.seq[:], record[:recordHeaderLen], payload, nil) - } - - var dst []byte - switch c := hc.cipher.(type) { - case cipher.Stream: - record, dst = sliceForAppend(record, len(payload)+len(mac)) - c.XORKeyStream(dst[:len(payload)], payload) - c.XORKeyStream(dst[len(payload):], mac) - case aead: - nonce := explicitNonce - if len(nonce) == 0 { - nonce = hc.seq[:] - } - - if hc.version == VersionTLS13 { - record = append(record, payload...) - - // Encrypt the actual ContentType and replace the plaintext one. - record = append(record, record[0]) - record[0] = byte(recordTypeApplicationData) - - n := len(payload) + 1 + c.Overhead() - record[3] = byte(n >> 8) - record[4] = byte(n) - - record = c.Seal(record[:recordHeaderLen], - nonce, record[recordHeaderLen:], record[:recordHeaderLen]) - } else { - copy(hc.additionalData[:], hc.seq[:]) - copy(hc.additionalData[8:], record) - record = c.Seal(record, nonce, payload, hc.additionalData[:]) - } - case cbcMode: - blockSize := c.BlockSize() - plaintextLen := len(payload) + len(mac) - paddingLen := blockSize - plaintextLen%blockSize - record, dst = sliceForAppend(record, plaintextLen+paddingLen) - copy(dst, payload) - copy(dst[len(payload):], mac) - for i := plaintextLen; i < len(dst); i++ { - dst[i] = byte(paddingLen - 1) - } - if len(explicitNonce) > 0 { - c.SetIV(explicitNonce) - } - c.CryptBlocks(dst, dst) - default: - panic("unknown cipher type") - } - - // Update length to include nonce, MAC and any block padding needed. - n := len(record) - recordHeaderLen - record[3] = byte(n >> 8) - record[4] = byte(n) - hc.incSeq() - - return record, nil -} - -// RecordHeaderError is returned when a TLS record header is invalid. -type RecordHeaderError struct { - // Msg contains a human readable string that describes the error. - Msg string - // RecordHeader contains the five bytes of TLS record header that - // triggered the error. - RecordHeader [5]byte - // Conn provides the underlying net.Conn in the case that a client - // sent an initial handshake that didn't look like TLS. - // It is nil if there's already been a handshake or a TLS alert has - // been written to the connection. - Conn net.Conn -} - -func (e RecordHeaderError) Error() string { return "tls: " + e.Msg } - -func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) { - err.Msg = msg - err.Conn = conn - copy(err.RecordHeader[:], c.rawInput.Bytes()) - return err -} - -func (c *Conn) readRecord() error { - return c.readRecordOrCCS(false) -} - -func (c *Conn) readChangeCipherSpec() error { - return c.readRecordOrCCS(true) -} - -// readRecordOrCCS reads one or more TLS records from the connection and -// updates the record layer state. Some invariants: -// * c.in must be locked -// * c.input must be empty -// During the handshake one and only one of the following will happen: -// - c.hand grows -// - c.in.changeCipherSpec is called -// - an error is returned -// After the handshake one and only one of the following will happen: -// - c.hand grows -// - c.input is set -// - an error is returned -func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { - if c.in.err != nil { - return c.in.err - } - handshakeComplete := c.handshakeComplete() - - // This function modifies c.rawInput, which owns the c.input memory. - if c.input.Len() != 0 { - return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data")) - } - c.input.Reset(nil) - - // Read header, payload. - if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { - // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify - // is an error, but popular web sites seem to do this, so we accept it - // if and only if at the record boundary. - if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 { - err = io.EOF - } - if e, ok := err.(net.Error); !ok || !e.Temporary() { - c.in.setErrorLocked(err) - } - return err - } - hdr := c.rawInput.Bytes()[:recordHeaderLen] - typ := recordType(hdr[0]) - - // No valid TLS record has a type of 0x80, however SSLv2 handshakes - // start with a uint16 length where the MSB is set and the first record - // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests - // an SSLv2 client. - if !handshakeComplete && typ == 0x80 { - c.sendAlert(alertProtocolVersion) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received")) - } - - vers := uint16(hdr[1])<<8 | uint16(hdr[2]) - n := int(hdr[3])<<8 | int(hdr[4]) - if c.haveVers && c.vers != VersionTLS13 && vers != c.vers { - c.sendAlert(alertProtocolVersion) - msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) - } - if !c.haveVers { - // First message, be extra suspicious: this might not be a TLS - // client. Bail out before reading a full 'body', if possible. - // The current max version is 3.3 so if the version is >= 16.0, - // it's probably not real. - if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 { - return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake")) - } - } - if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext { - c.sendAlert(alertRecordOverflow) - msg := fmt.Sprintf("oversized record received with length %d", n) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) - } - if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil { - if e, ok := err.(net.Error); !ok || !e.Temporary() { - c.in.setErrorLocked(err) - } - return err - } - - // Process message. - record := c.rawInput.Next(recordHeaderLen + n) - data, typ, err := c.in.decrypt(record) - if err != nil { - return c.in.setErrorLocked(c.sendAlert(err.(alert))) - } - if len(data) > maxPlaintext { - return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow)) - } - - // Application Data messages are always protected. - if c.in.cipher == nil && typ == recordTypeApplicationData { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 { - // This is a state-advancing message: reset the retry count. - c.retryCount = 0 - } - - // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3. - if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - switch typ { - default: - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - - case recordTypeAlert: - if len(data) != 2 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - if alert(data[1]) == alertCloseNotify { - return c.in.setErrorLocked(io.EOF) - } - if c.vers == VersionTLS13 { - return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) - } - switch data[0] { - case alertLevelWarning: - // Drop the record on the floor and retry. - return c.retryReadRecord(expectChangeCipherSpec) - case alertLevelError: - return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) - default: - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - case recordTypeChangeCipherSpec: - if len(data) != 1 || data[0] != 1 { - return c.in.setErrorLocked(c.sendAlert(alertDecodeError)) - } - // Handshake messages are not allowed to fragment across the CCS. - if c.hand.Len() > 0 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - // In TLS 1.3, change_cipher_spec records are ignored until the - // Finished. See RFC 8446, Appendix D.4. Note that according to Section - // 5, a server can send a ChangeCipherSpec before its ServerHello, when - // c.vers is still unset. That's not useful though and suspicious if the - // server then selects a lower protocol version, so don't allow that. - if c.vers == VersionTLS13 { - return c.retryReadRecord(expectChangeCipherSpec) - } - if !expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - if err := c.in.changeCipherSpec(); err != nil { - return c.in.setErrorLocked(c.sendAlert(err.(alert))) - } - - case recordTypeApplicationData: - if !handshakeComplete || expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - // Some OpenSSL servers send empty records in order to randomize the - // CBC IV. Ignore a limited number of empty records. - if len(data) == 0 { - return c.retryReadRecord(expectChangeCipherSpec) - } - // Note that data is owned by c.rawInput, following the Next call above, - // to avoid copying the plaintext. This is safe because c.rawInput is - // not read from or written to until c.input is drained. - c.input.Reset(data) - - case recordTypeHandshake: - if len(data) == 0 || expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - c.hand.Write(data) - } - - return nil -} - -// retryReadRecord recurses into readRecordOrCCS to drop a non-advancing record, like -// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3. -func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error { - c.retryCount++ - if c.retryCount > maxUselessRecords { - c.sendAlert(alertUnexpectedMessage) - return c.in.setErrorLocked(errors.New("tls: too many ignored records")) - } - return c.readRecordOrCCS(expectChangeCipherSpec) -} - -// atLeastReader reads from R, stopping with EOF once at least N bytes have been -// read. It is different from an io.LimitedReader in that it doesn't cut short -// the last Read call, and in that it considers an early EOF an error. -type atLeastReader struct { - R io.Reader - N int64 -} - -func (r *atLeastReader) Read(p []byte) (int, error) { - if r.N <= 0 { - return 0, io.EOF - } - n, err := r.R.Read(p) - r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809 - if r.N > 0 && err == io.EOF { - return n, io.ErrUnexpectedEOF - } - if r.N <= 0 && err == nil { - return n, io.EOF - } - return n, err -} - -// readFromUntil reads from r into c.rawInput until c.rawInput contains -// at least n bytes or else returns an error. -func (c *Conn) readFromUntil(r io.Reader, n int) error { - if c.rawInput.Len() >= n { - return nil - } - needs := n - c.rawInput.Len() - // There might be extra input waiting on the wire. Make a best effort - // attempt to fetch it so that it can be used in (*Conn).Read to - // "predict" closeNotify alerts. - c.rawInput.Grow(needs + bytes.MinRead) - _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)}) - return err -} - -// sendAlert sends a TLS alert message. -func (c *Conn) sendAlertLocked(err alert) error { - switch err { - case alertNoRenegotiation, alertCloseNotify: - c.tmp[0] = alertLevelWarning - default: - c.tmp[0] = alertLevelError - } - c.tmp[1] = byte(err) - - _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2]) - if err == alertCloseNotify { - // closeNotify is a special case in that it isn't an error. - return writeErr - } - - return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) -} - -// sendAlert sends a TLS alert message. -func (c *Conn) sendAlert(err alert) error { - if c.config.AlternativeRecordLayer != nil { - c.config.AlternativeRecordLayer.SendAlert(uint8(err)) - return &net.OpError{Op: "local error", Err: err} - } - - c.out.Lock() - defer c.out.Unlock() - return c.sendAlertLocked(err) -} - -const ( - // tcpMSSEstimate is a conservative estimate of the TCP maximum segment - // size (MSS). A constant is used, rather than querying the kernel for - // the actual MSS, to avoid complexity. The value here is the IPv6 - // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40 - // bytes) and a TCP header with timestamps (32 bytes). - tcpMSSEstimate = 1208 - - // recordSizeBoostThreshold is the number of bytes of application data - // sent after which the TLS record size will be increased to the - // maximum. - recordSizeBoostThreshold = 128 * 1024 -) - -// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the -// next application data record. There is the following trade-off: -// -// - For latency-sensitive applications, such as web browsing, each TLS -// record should fit in one TCP segment. -// - For throughput-sensitive applications, such as large file transfers, -// larger TLS records better amortize framing and encryption overheads. -// -// A simple heuristic that works well in practice is to use small records for -// the first 1MB of data, then use larger records for subsequent data, and -// reset back to smaller records after the connection becomes idle. See "High -// Performance Web Networking", Chapter 4, or: -// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/ -// -// In the interests of simplicity and determinism, this code does not attempt -// to reset the record size once the connection is idle, however. -func (c *Conn) maxPayloadSizeForWrite(typ recordType) int { - if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData { - return maxPlaintext - } - - if c.bytesSent >= recordSizeBoostThreshold { - return maxPlaintext - } - - // Subtract TLS overheads to get the maximum payload size. - payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen() - if c.out.cipher != nil { - switch ciph := c.out.cipher.(type) { - case cipher.Stream: - payloadBytes -= c.out.mac.Size() - case cipher.AEAD: - payloadBytes -= ciph.Overhead() - case cbcMode: - blockSize := ciph.BlockSize() - // The payload must fit in a multiple of blockSize, with - // room for at least one padding byte. - payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1 - // The MAC is appended before padding so affects the - // payload size directly. - payloadBytes -= c.out.mac.Size() - default: - panic("unknown cipher type") - } - } - if c.vers == VersionTLS13 { - payloadBytes-- // encrypted ContentType - } - - // Allow packet growth in arithmetic progression up to max. - pkt := c.packetsSent - c.packetsSent++ - if pkt > 1000 { - return maxPlaintext // avoid overflow in multiply below - } - - n := payloadBytes * int(pkt+1) - if n > maxPlaintext { - n = maxPlaintext - } - return n -} - -func (c *Conn) write(data []byte) (int, error) { - if c.buffering { - c.sendBuf = append(c.sendBuf, data...) - return len(data), nil - } - - n, err := c.conn.Write(data) - c.bytesSent += int64(n) - return n, err -} - -func (c *Conn) flush() (int, error) { - if len(c.sendBuf) == 0 { - return 0, nil - } - - n, err := c.conn.Write(c.sendBuf) - c.bytesSent += int64(n) - c.sendBuf = nil - c.buffering = false - return n, err -} - -// writeRecordLocked writes a TLS record with the given type and payload to the -// connection and updates the record layer state. -func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { - var n int - for len(data) > 0 { - m := len(data) - if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload { - m = maxPayload - } - - _, c.outBuf = sliceForAppend(c.outBuf[:0], recordHeaderLen) - c.outBuf[0] = byte(typ) - vers := c.vers - if vers == 0 { - // Some TLS servers fail if the record version is - // greater than TLS 1.0 for the initial ClientHello. - vers = VersionTLS10 - } else if vers == VersionTLS13 { - // TLS 1.3 froze the record layer version to 1.2. - // See RFC 8446, Section 5.1. - vers = VersionTLS12 - } - c.outBuf[1] = byte(vers >> 8) - c.outBuf[2] = byte(vers) - c.outBuf[3] = byte(m >> 8) - c.outBuf[4] = byte(m) - - var err error - c.outBuf, err = c.out.encrypt(c.outBuf, data[:m], c.config.rand()) - if err != nil { - return n, err - } - if _, err := c.write(c.outBuf); err != nil { - return n, err - } - n += m - data = data[m:] - } - - if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 { - if err := c.out.changeCipherSpec(); err != nil { - return n, c.sendAlertLocked(err.(alert)) - } - } - - return n, nil -} - -// writeRecord writes a TLS record with the given type and payload to the -// connection and updates the record layer state. -func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) { - if c.config.AlternativeRecordLayer != nil { - if typ == recordTypeChangeCipherSpec { - return len(data), nil - } - return c.config.AlternativeRecordLayer.WriteRecord(data) - } - - c.out.Lock() - defer c.out.Unlock() - - return c.writeRecordLocked(typ, data) -} - -// readHandshake reads the next handshake message from -// the record layer. -func (c *Conn) readHandshake() (interface{}, error) { - var data []byte - if c.config.AlternativeRecordLayer != nil { - var err error - data, err = c.config.AlternativeRecordLayer.ReadHandshakeMessage() - if err != nil { - return nil, err - } - } else { - for c.hand.Len() < 4 { - if err := c.readRecord(); err != nil { - return nil, err - } - } - - data = c.hand.Bytes() - n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - if n > maxHandshake { - c.sendAlertLocked(alertInternalError) - return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)) - } - for c.hand.Len() < 4+n { - if err := c.readRecord(); err != nil { - return nil, err - } - } - data = c.hand.Next(4 + n) - } - var m handshakeMessage - switch data[0] { - case typeHelloRequest: - m = new(helloRequestMsg) - case typeClientHello: - m = new(clientHelloMsg) - case typeServerHello: - m = new(serverHelloMsg) - case typeNewSessionTicket: - if c.vers == VersionTLS13 { - m = new(newSessionTicketMsgTLS13) - } else { - m = new(newSessionTicketMsg) - } - case typeCertificate: - if c.vers == VersionTLS13 { - m = new(certificateMsgTLS13) - } else { - m = new(certificateMsg) - } - case typeCertificateRequest: - if c.vers == VersionTLS13 { - m = new(certificateRequestMsgTLS13) - } else { - m = &certificateRequestMsg{ - hasSignatureAlgorithm: c.vers >= VersionTLS12, - } - } - case typeCertificateStatus: - m = new(certificateStatusMsg) - case typeServerKeyExchange: - m = new(serverKeyExchangeMsg) - case typeServerHelloDone: - m = new(serverHelloDoneMsg) - case typeClientKeyExchange: - m = new(clientKeyExchangeMsg) - case typeCertificateVerify: - m = &certificateVerifyMsg{ - hasSignatureAlgorithm: c.vers >= VersionTLS12, - } - case typeNextProtocol: - m = new(nextProtoMsg) - case typeFinished: - m = new(finishedMsg) - case typeEncryptedExtensions: - m = new(encryptedExtensionsMsg) - case typeEndOfEarlyData: - m = new(endOfEarlyDataMsg) - case typeKeyUpdate: - m = new(keyUpdateMsg) - default: - return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - // The handshake message unmarshalers - // expect to be able to keep references to data, - // so pass in a fresh copy that won't be overwritten. - data = append([]byte(nil), data...) - - if !m.unmarshal(data) { - return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - return m, nil -} - -var ( - errClosed = errors.New("tls: use of closed connection") - errShutdown = errors.New("tls: protocol is shutdown") -) - -// Write writes data to the connection. -func (c *Conn) Write(b []byte) (int, error) { - // interlock with Close below - for { - x := atomic.LoadInt32(&c.activeCall) - if x&1 != 0 { - return 0, errClosed - } - if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) { - defer atomic.AddInt32(&c.activeCall, -2) - break - } - } - - if err := c.Handshake(); err != nil { - return 0, err - } - - c.out.Lock() - defer c.out.Unlock() - - if err := c.out.err; err != nil { - return 0, err - } - - if !c.handshakeComplete() { - return 0, alertInternalError - } - - if c.closeNotifySent { - return 0, errShutdown - } - - // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext - // attack when using block mode ciphers due to predictable IVs. - // This can be prevented by splitting each Application Data - // record into two records, effectively randomizing the IV. - // - // https://www.openssl.org/~bodo/tls-cbc.txt - // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 - // https://www.imperialviolet.org/2012/01/15/beastfollowup.html - - var m int - if len(b) > 1 && c.vers <= VersionTLS10 { - if _, ok := c.out.cipher.(cipher.BlockMode); ok { - n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1]) - if err != nil { - return n, c.out.setErrorLocked(err) - } - m, b = 1, b[1:] - } - } - - n, err := c.writeRecordLocked(recordTypeApplicationData, b) - return n + m, c.out.setErrorLocked(err) -} - -// handleRenegotiation processes a HelloRequest handshake message. -func (c *Conn) handleRenegotiation() error { - if c.vers == VersionTLS13 { - return errors.New("tls: internal error: unexpected renegotiation") - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - helloReq, ok := msg.(*helloRequestMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(helloReq, msg) - } - - if !c.isClient { - return c.sendAlert(alertNoRenegotiation) - } - - switch c.config.Renegotiation { - case RenegotiateNever: - return c.sendAlert(alertNoRenegotiation) - case RenegotiateOnceAsClient: - if c.handshakes > 1 { - return c.sendAlert(alertNoRenegotiation) - } - case RenegotiateFreelyAsClient: - // Ok. - default: - c.sendAlert(alertInternalError) - return errors.New("tls: unknown Renegotiation value") - } - - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - atomic.StoreUint32(&c.handshakeStatus, 0) - if c.handshakeErr = c.clientHandshake(); c.handshakeErr == nil { - c.handshakes++ - } - return c.handshakeErr -} - -func (c *Conn) HandlePostHandshakeMessage() error { - return c.handlePostHandshakeMessage() -} - -// handlePostHandshakeMessage processes a handshake message arrived after the -// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation. -func (c *Conn) handlePostHandshakeMessage() error { - if c.vers != VersionTLS13 { - return c.handleRenegotiation() - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - c.retryCount++ - if c.retryCount > maxUselessRecords { - c.sendAlert(alertUnexpectedMessage) - return c.in.setErrorLocked(errors.New("tls: too many non-advancing records")) - } - - switch msg := msg.(type) { - case *newSessionTicketMsgTLS13: - return c.handleNewSessionTicket(msg) - case *keyUpdateMsg: - return c.handleKeyUpdate(msg) - default: - c.sendAlert(alertUnexpectedMessage) - return fmt.Errorf("tls: received unexpected handshake message of type %T", msg) - } -} - -func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { - cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) - if cipherSuite == nil { - return c.in.setErrorLocked(c.sendAlert(alertInternalError)) - } - - newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret) - c.in.setTrafficSecret(cipherSuite, newSecret) - - if keyUpdate.updateRequested { - c.out.Lock() - defer c.out.Unlock() - - msg := &keyUpdateMsg{} - _, err := c.writeRecordLocked(recordTypeHandshake, msg.marshal()) - if err != nil { - // Surface the error at the next write. - c.out.setErrorLocked(err) - return nil - } - - newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret) - c.out.setTrafficSecret(cipherSuite, newSecret) - } - - return nil -} - -// Read can be made to time out and return a net.Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetReadDeadline. -func (c *Conn) Read(b []byte) (int, error) { - if err := c.Handshake(); err != nil { - return 0, err - } - if len(b) == 0 { - // Put this after Handshake, in case people were calling - // Read(nil) for the side effect of the Handshake. - return 0, nil - } - - c.in.Lock() - defer c.in.Unlock() - - for c.input.Len() == 0 { - if err := c.readRecord(); err != nil { - return 0, err - } - for c.hand.Len() > 0 { - if err := c.handlePostHandshakeMessage(); err != nil { - return 0, err - } - } - } - - n, _ := c.input.Read(b) - - // If a close-notify alert is waiting, read it so that we can return (n, - // EOF) instead of (n, nil), to signal to the HTTP response reading - // goroutine that the connection is now closed. This eliminates a race - // where the HTTP response reading goroutine would otherwise not observe - // the EOF until its next read, by which time a client goroutine might - // have already tried to reuse the HTTP connection for a new request. - // See https://golang.org/cl/76400046 and https://golang.org/issue/3514 - if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 && - recordType(c.rawInput.Bytes()[0]) == recordTypeAlert { - if err := c.readRecord(); err != nil { - return n, err // will be io.EOF on closeNotify - } - } - - return n, nil -} - -// Close closes the connection. -func (c *Conn) Close() error { - // Interlock with Conn.Write above. - var x int32 - for { - x = atomic.LoadInt32(&c.activeCall) - if x&1 != 0 { - return errClosed - } - if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) { - break - } - } - if x != 0 { - // io.Writer and io.Closer should not be used concurrently. - // If Close is called while a Write is currently in-flight, - // interpret that as a sign that this Close is really just - // being used to break the Write and/or clean up resources and - // avoid sending the alertCloseNotify, which may block - // waiting on handshakeMutex or the c.out mutex. - return c.conn.Close() - } - - var alertErr error - - if c.handshakeComplete() { - alertErr = c.closeNotify() - } - - if err := c.conn.Close(); err != nil { - return err - } - return alertErr -} - -var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete") - -// CloseWrite shuts down the writing side of the connection. It should only be -// called once the handshake has completed and does not call CloseWrite on the -// underlying connection. Most callers should just use Close. -func (c *Conn) CloseWrite() error { - if !c.handshakeComplete() { - return errEarlyCloseWrite - } - - return c.closeNotify() -} - -func (c *Conn) closeNotify() error { - c.out.Lock() - defer c.out.Unlock() - - if !c.closeNotifySent { - c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify) - c.closeNotifySent = true - } - return c.closeNotifyErr -} - -// Handshake runs the client or server handshake -// protocol if it has not yet been run. -// Most uses of this package need not call Handshake -// explicitly: the first Read or Write will call it automatically. -func (c *Conn) Handshake() error { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - if err := c.handshakeErr; err != nil { - return err - } - if c.handshakeComplete() { - return nil - } - - c.in.Lock() - defer c.in.Unlock() - - if c.isClient { - c.handshakeErr = c.clientHandshake() - } else { - c.handshakeErr = c.serverHandshake() - } - if c.handshakeErr == nil { - c.handshakes++ - } else { - // If an error occurred during the hadshake try to flush the - // alert that might be left in the buffer. - c.flush() - } - - if c.handshakeErr == nil && !c.handshakeComplete() { - c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") - } - - return c.handshakeErr -} - -// ConnectionState returns basic TLS details about the connection. -func (c *Conn) ConnectionState() ConnectionState { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - var state ConnectionState - state.HandshakeComplete = c.handshakeComplete() - state.ServerName = c.serverName - - if state.HandshakeComplete { - state.Version = c.vers - state.NegotiatedProtocol = c.clientProtocol - state.DidResume = c.didResume - state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback - state.CipherSuite = c.cipherSuite - state.PeerCertificates = c.peerCertificates - state.VerifiedChains = c.verifiedChains - state.SignedCertificateTimestamps = c.scts - state.OCSPResponse = c.ocspResponse - if !c.didResume && c.vers != VersionTLS13 { - if c.clientFinishedIsFirst { - state.TLSUnique = c.clientFinished[:] - } else { - state.TLSUnique = c.serverFinished[:] - } - } - if c.config.Renegotiation != RenegotiateNever { - state.ekm = noExportedKeyingMaterial - } else { - state.ekm = c.ekm - } - } - - return state -} - -// OCSPResponse returns the stapled OCSP response from the TLS server, if -// any. (Only valid for client connections.) -func (c *Conn) OCSPResponse() []byte { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - return c.ocspResponse -} - -// VerifyHostname checks that the peer certificate chain is valid for -// connecting to host. If so, it returns nil; if not, it returns an error -// describing the problem. -func (c *Conn) VerifyHostname(host string) error { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - if !c.isClient { - return errors.New("tls: VerifyHostname called on TLS server connection") - } - if !c.handshakeComplete() { - return errors.New("tls: handshake has not yet been performed") - } - if len(c.verifiedChains) == 0 { - return errors.New("tls: handshake did not verify certificate chain") - } - return c.peerCertificates[0].VerifyHostname(host) -} - -func (c *Conn) handshakeComplete() bool { - return atomic.LoadUint32(&c.handshakeStatus) == 1 -} diff --git a/vendor/github.com/marten-seemann/qtls/go.mod b/vendor/github.com/marten-seemann/qtls/go.mod deleted file mode 100644 index 9cd2ced53..000000000 --- a/vendor/github.com/marten-seemann/qtls/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/marten-seemann/qtls - -go 1.12 - -require ( - golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 - golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e -) diff --git a/vendor/github.com/marten-seemann/qtls/go.sum b/vendor/github.com/marten-seemann/qtls/go.sum deleted file mode 100644 index a47aabd29..000000000 --- a/vendor/github.com/marten-seemann/qtls/go.sum +++ /dev/null @@ -1,5 +0,0 @@ -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= diff --git a/vendor/github.com/marten-seemann/qtls/handshake_client.go b/vendor/github.com/marten-seemann/qtls/handshake_client.go deleted file mode 100644 index 1d2903d56..000000000 --- a/vendor/github.com/marten-seemann/qtls/handshake_client.go +++ /dev/null @@ -1,1023 +0,0 @@ -// Copyright 2009 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 qtls - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "io" - "net" - "strconv" - "strings" - "sync/atomic" - "time" -) - -type clientHandshakeState struct { - c *Conn - serverHello *serverHelloMsg - hello *clientHelloMsg - suite *cipherSuite - finishedHash finishedHash - masterSecret []byte - session *ClientSessionState -} - -func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { - config := c.config - if len(config.ServerName) == 0 && !config.InsecureSkipVerify { - return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") - } - - nextProtosLength := 0 - for _, proto := range config.NextProtos { - if l := len(proto); l == 0 || l > 255 { - return nil, nil, errors.New("tls: invalid NextProtos value") - } else { - nextProtosLength += 1 + l - } - } - if nextProtosLength > 0xffff { - return nil, nil, errors.New("tls: NextProtos values too large") - } - - supportedVersions := config.supportedVersions(true) - if len(supportedVersions) == 0 { - return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") - } - - clientHelloVersion := supportedVersions[0] - // The version at the beginning of the ClientHello was capped at TLS 1.2 - // for compatibility reasons. The supported_versions extension is used - // to negotiate versions now. See RFC 8446, Section 4.2.1. - if clientHelloVersion > VersionTLS12 { - clientHelloVersion = VersionTLS12 - } - - hello := &clientHelloMsg{ - vers: clientHelloVersion, - compressionMethods: []uint8{compressionNone}, - random: make([]byte, 32), - sessionId: make([]byte, 32), - ocspStapling: true, - scts: true, - serverName: hostnameInSNI(config.ServerName), - supportedCurves: config.curvePreferences(), - supportedPoints: []uint8{pointFormatUncompressed}, - nextProtoNeg: len(config.NextProtos) > 0, - secureRenegotiationSupported: true, - alpnProtocols: config.NextProtos, - supportedVersions: supportedVersions, - } - - if c.handshakes > 0 { - hello.secureRenegotiation = c.clientFinished[:] - } - - possibleCipherSuites := config.cipherSuites() - hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) - -NextCipherSuite: - for _, suiteId := range possibleCipherSuites { - for _, suite := range cipherSuites { - if suite.id != suiteId { - continue - } - // Don't advertise TLS 1.2-only cipher suites unless - // we're attempting TLS 1.2. - if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 { - continue - } - hello.cipherSuites = append(hello.cipherSuites, suiteId) - continue NextCipherSuite - } - } - - _, err := io.ReadFull(config.rand(), hello.random) - if err != nil { - return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) - } - - // A random session ID is used to detect when the server accepted a ticket - // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as - // a compatibility measure (see RFC 8446, Section 4.1.2). - if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { - return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) - } - - if hello.vers >= VersionTLS12 { - hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms - } - - var params ecdheParameters - if hello.supportedVersions[0] == VersionTLS13 { - hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13()...) - - curveID := config.curvePreferences()[0] - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err = generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, nil, err - } - hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} - } - - if hello.supportedVersions[0] == VersionTLS13 && config.GetExtensions != nil { - hello.additionalExtensions = config.GetExtensions(typeClientHello) - } - - return hello, params, nil -} - -func (c *Conn) clientHandshake() (err error) { - if c.config == nil { - c.config = defaultConfig() - } - c.setAlternativeRecordLayer() - - // This may be a renegotiation handshake, in which case some fields - // need to be reset. - c.didResume = false - - hello, ecdheParams, err := c.makeClientHello() - if err != nil { - return err - } - - cacheKey, session, earlySecret, binderKey := c.loadSession(hello) - if cacheKey != "" && session != nil { - defer func() { - // If we got a handshake failure when resuming a session, throw away - // the session ticket. See RFC 5077, Section 3.2. - // - // RFC 8446 makes no mention of dropping tickets on failure, but it - // does require servers to abort on invalid binders, so we need to - // delete tickets to recover from a corrupted PSK. - if err != nil { - c.config.ClientSessionCache.Put(cacheKey, nil) - } - }() - } - - if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil { - return err - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - serverHello, ok := msg.(*serverHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverHello, msg) - } - - if err := c.pickTLSVersion(serverHello); err != nil { - return err - } - - if c.vers == VersionTLS13 { - hs := &clientHandshakeStateTLS13{ - c: c, - serverHello: serverHello, - hello: hello, - ecdheParams: ecdheParams, - session: session, - earlySecret: earlySecret, - binderKey: binderKey, - } - - // In TLS 1.3, session tickets are delivered after the handshake. - return hs.handshake() - } - - hs := &clientHandshakeState{ - c: c, - serverHello: serverHello, - hello: hello, - session: session, - } - - if err := hs.handshake(); err != nil { - return err - } - - // If we had a successful handshake and hs.session is different from - // the one already cached - cache a new one. - if cacheKey != "" && hs.session != nil && session != hs.session { - c.config.ClientSessionCache.Put(cacheKey, hs.session) - } - - return nil -} - -func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, - session *ClientSessionState, earlySecret, binderKey []byte) { - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { - return "", nil, nil, nil - } - - hello.ticketSupported = true - - if hello.supportedVersions[0] == VersionTLS13 { - // Require DHE on resumption as it guarantees forward secrecy against - // compromise of the session ticket key. See RFC 8446, Section 4.2.9. - hello.pskModes = []uint8{pskModeDHE} - } - - // Session resumption is not allowed if renegotiating because - // renegotiation is primarily used to allow a client to send a client - // certificate, which would be skipped if session resumption occurred. - if c.handshakes != 0 { - return "", nil, nil, nil - } - - // Try to resume a previously negotiated TLS session, if available. - cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - session, ok := c.config.ClientSessionCache.Get(cacheKey) - if !ok || session == nil { - return cacheKey, nil, nil, nil - } - - // Check that version used for the previous session is still valid. - versOk := false - for _, v := range hello.supportedVersions { - if v == session.vers { - versOk = true - break - } - } - if !versOk { - return cacheKey, nil, nil, nil - } - - // Check that the cached server certificate is not expired, and that it's - // valid for the ServerName. This should be ensured by the cache key, but - // protect the application from a faulty ClientSessionCache implementation. - if !c.config.InsecureSkipVerify { - if len(session.verifiedChains) == 0 { - // The original connection had InsecureSkipVerify, while this doesn't. - return cacheKey, nil, nil, nil - } - serverCert := session.serverCertificates[0] - if c.config.time().After(serverCert.NotAfter) { - // Expired certificate, delete the entry. - c.config.ClientSessionCache.Put(cacheKey, nil) - return cacheKey, nil, nil, nil - } - if err := serverCert.VerifyHostname(c.config.ServerName); err != nil { - return cacheKey, nil, nil, nil - } - } - - if session.vers != VersionTLS13 { - // In TLS 1.2 the cipher suite must match the resumed session. Ensure we - // are still offering it. - if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil { - return cacheKey, nil, nil, nil - } - - hello.sessionTicket = session.sessionTicket - return - } - - // Check that the session ticket is not expired. - if c.config.time().After(session.useBy) { - c.config.ClientSessionCache.Put(cacheKey, nil) - return cacheKey, nil, nil, nil - } - - // In TLS 1.3 the KDF hash must match the resumed session. Ensure we - // offer at least one cipher suite with that hash. - cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) - if cipherSuite == nil { - return cacheKey, nil, nil, nil - } - cipherSuiteOk := false - for _, offeredID := range hello.cipherSuites { - offeredSuite := cipherSuiteTLS13ByID(offeredID) - if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash { - cipherSuiteOk = true - break - } - } - if !cipherSuiteOk { - return cacheKey, nil, nil, nil - } - - // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. - ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond) - identity := pskIdentity{ - label: session.sessionTicket, - obfuscatedTicketAge: ticketAge + session.ageAdd, - } - hello.pskIdentities = []pskIdentity{identity} - hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())} - - // Compute the PSK binders. See RFC 8446, Section 4.2.11.2. - psk := cipherSuite.expandLabel(session.masterSecret, "resumption", - session.nonce, cipherSuite.hash.Size()) - earlySecret = cipherSuite.extract(psk, nil) - binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) - transcript := cipherSuite.hash.New() - transcript.Write(hello.marshalWithoutBinders()) - pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)} - hello.updateBinders(pskBinders) - - return -} - -func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { - peerVersion := serverHello.vers - if serverHello.supportedVersion != 0 { - peerVersion = serverHello.supportedVersion - } - - vers, ok := c.config.mutualVersion(true, []uint16{peerVersion}) - if !ok { - c.sendAlert(alertProtocolVersion) - return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion) - } - - c.vers = vers - c.haveVers = true - c.in.version = vers - c.out.version = vers - - return nil -} - -// Does the handshake, either a full one or resumes old session. Requires hs.c, -// hs.hello, hs.serverHello, and, optionally, hs.session to be set. -func (hs *clientHandshakeState) handshake() error { - c := hs.c - - isResume, err := hs.processServerHello() - if err != nil { - return err - } - - hs.finishedHash = newFinishedHash(c.vers, hs.suite) - - // No signatures of the handshake are needed in a resumption. - // Otherwise, in a full handshake, if we don't have any certificates - // configured then we will never send a CertificateVerify message and - // thus no signatures are needed in that case either. - if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) { - hs.finishedHash.discardHandshakeBuffer() - } - - hs.finishedHash.Write(hs.hello.marshal()) - hs.finishedHash.Write(hs.serverHello.marshal()) - - c.buffering = true - if isResume { - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.readSessionTicket(); err != nil { - return err - } - if err := hs.readFinished(c.serverFinished[:]); err != nil { - return err - } - c.clientFinishedIsFirst = false - if err := hs.sendFinished(c.clientFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - } else { - if err := hs.doFullHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.sendFinished(c.clientFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - c.clientFinishedIsFirst = true - if err := hs.readSessionTicket(); err != nil { - return err - } - if err := hs.readFinished(c.serverFinished[:]); err != nil { - return err - } - } - - c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random) - c.didResume = isResume - atomic.StoreUint32(&c.handshakeStatus, 1) - - return nil -} - -func (hs *clientHandshakeState) pickCipherSuite() error { - if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil { - hs.c.sendAlert(alertHandshakeFailure) - return errors.New("tls: server chose an unconfigured cipher suite") - } - - hs.c.cipherSuite = hs.suite.id - return nil -} - -func (hs *clientHandshakeState) doFullHandshake() error { - c := hs.c - - msg, err := c.readHandshake() - if err != nil { - return err - } - certMsg, ok := msg.(*certificateMsg) - if !ok || len(certMsg.certificates) == 0 { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - hs.finishedHash.Write(certMsg.marshal()) - - if c.handshakes == 0 { - // If this is the first handshake on a connection, process and - // (optionally) verify the server's certificates. - if err := c.verifyServerCertificate(certMsg.certificates); err != nil { - return err - } - } else { - // This is a renegotiation handshake. We require that the - // server's identity (i.e. leaf certificate) is unchanged and - // thus any previous trust decision is still valid. - // - // See https://mitls.org/pages/attacks/3SHAKE for the - // motivation behind this requirement. - if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { - c.sendAlert(alertBadCertificate) - return errors.New("tls: server's identity changed during renegotiation") - } - } - - msg, err = c.readHandshake() - if err != nil { - return err - } - - cs, ok := msg.(*certificateStatusMsg) - if ok { - // RFC4366 on Certificate Status Request: - // The server MAY return a "certificate_status" message. - - if !hs.serverHello.ocspStapling { - // If a server returns a "CertificateStatus" message, then the - // server MUST have included an extension of type "status_request" - // with empty "extension_data" in the extended server hello. - - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: received unexpected CertificateStatus message") - } - hs.finishedHash.Write(cs.marshal()) - - c.ocspResponse = cs.response - - msg, err = c.readHandshake() - if err != nil { - return err - } - } - - keyAgreement := hs.suite.ka(c.vers) - - skx, ok := msg.(*serverKeyExchangeMsg) - if ok { - hs.finishedHash.Write(skx.marshal()) - err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) - if err != nil { - c.sendAlert(alertUnexpectedMessage) - return err - } - - msg, err = c.readHandshake() - if err != nil { - return err - } - } - - var chainToSend *Certificate - var certRequested bool - certReq, ok := msg.(*certificateRequestMsg) - if ok { - certRequested = true - hs.finishedHash.Write(certReq.marshal()) - - cri := certificateRequestInfoFromMsg(certReq) - if chainToSend, err = c.getClientCertificate(cri); err != nil { - c.sendAlert(alertInternalError) - return err - } - - msg, err = c.readHandshake() - if err != nil { - return err - } - } - - shd, ok := msg.(*serverHelloDoneMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(shd, msg) - } - hs.finishedHash.Write(shd.marshal()) - - // If the server requested a certificate then we have to send a - // Certificate message, even if it's empty because we don't have a - // certificate to send. - if certRequested { - certMsg = new(certificateMsg) - certMsg.certificates = chainToSend.Certificate - hs.finishedHash.Write(certMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { - return err - } - } - - preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - if ckx != nil { - hs.finishedHash.Write(ckx.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, ckx.marshal()); err != nil { - return err - } - } - - if chainToSend != nil && len(chainToSend.Certificate) > 0 { - certVerify := &certificateVerifyMsg{ - hasSignatureAlgorithm: c.vers >= VersionTLS12, - } - - key, ok := chainToSend.PrivateKey.(crypto.Signer) - if !ok { - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) - } - - signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, hs.hello.supportedSignatureAlgorithms, c.vers) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - // SignatureAndHashAlgorithm was introduced in TLS 1.2. - if certVerify.hasSignatureAlgorithm { - certVerify.signatureAlgorithm = signatureAlgorithm - } - digest, err := hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - signOpts := crypto.SignerOpts(hashFunc) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc} - } - certVerify.signature, err = key.Sign(c.config.rand(), digest, signOpts) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - hs.finishedHash.Write(certVerify.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certVerify.marshal()); err != nil { - return err - } - } - - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) - if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to write to key log: " + err.Error()) - } - - hs.finishedHash.discardHandshakeBuffer() - - return nil -} - -func (hs *clientHandshakeState) establishKeys() error { - c := hs.c - - clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - var clientCipher, serverCipher interface{} - var clientHash, serverHash macFunction - if hs.suite.cipher != nil { - clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */) - clientHash = hs.suite.mac(c.vers, clientMAC) - serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */) - serverHash = hs.suite.mac(c.vers, serverMAC) - } else { - clientCipher = hs.suite.aead(clientKey, clientIV) - serverCipher = hs.suite.aead(serverKey, serverIV) - } - - c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) - c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) - return nil -} - -func (hs *clientHandshakeState) serverResumedSession() bool { - // If the server responded with the same sessionId then it means the - // sessionTicket is being used to resume a TLS session. - return hs.session != nil && hs.hello.sessionId != nil && - bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId) -} - -func (hs *clientHandshakeState) processServerHello() (bool, error) { - c := hs.c - - if err := hs.pickCipherSuite(); err != nil { - return false, err - } - - if hs.serverHello.compressionMethod != compressionNone { - c.sendAlert(alertUnexpectedMessage) - return false, errors.New("tls: server selected unsupported compression format") - } - - if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported { - c.secureRenegotiation = true - if len(hs.serverHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: initial handshake had non-empty renegotiation extension") - } - } - - if c.handshakes > 0 && c.secureRenegotiation { - var expectedSecureRenegotiation [24]byte - copy(expectedSecureRenegotiation[:], c.clientFinished[:]) - copy(expectedSecureRenegotiation[12:], c.serverFinished[:]) - if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: incorrect renegotiation extension contents") - } - } - - clientDidNPN := hs.hello.nextProtoNeg - clientDidALPN := len(hs.hello.alpnProtocols) > 0 - serverHasNPN := hs.serverHello.nextProtoNeg - serverHasALPN := len(hs.serverHello.alpnProtocol) > 0 - - if !clientDidNPN && serverHasNPN { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server advertised unrequested NPN extension") - } - - if !clientDidALPN && serverHasALPN { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server advertised unrequested ALPN extension") - } - - if serverHasNPN && serverHasALPN { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server advertised both NPN and ALPN extensions") - } - - if serverHasALPN { - c.clientProtocol = hs.serverHello.alpnProtocol - c.clientProtocolFallback = false - } - c.scts = hs.serverHello.scts - - if !hs.serverResumedSession() { - return false, nil - } - - if hs.session.vers != c.vers { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server resumed a session with a different version") - } - - if hs.session.cipherSuite != hs.suite.id { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server resumed a session with a different cipher suite") - } - - // Restore masterSecret and peerCerts from previous state - hs.masterSecret = hs.session.masterSecret - c.peerCertificates = hs.session.serverCertificates - c.verifiedChains = hs.session.verifiedChains - return true, nil -} - -func (hs *clientHandshakeState) readFinished(out []byte) error { - c := hs.c - - if err := c.readChangeCipherSpec(); err != nil { - return err - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - serverFinished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverFinished, msg) - } - - verify := hs.finishedHash.serverSum(hs.masterSecret) - if len(verify) != len(serverFinished.verifyData) || - subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: server's Finished message was incorrect") - } - hs.finishedHash.Write(serverFinished.marshal()) - copy(out, verify) - return nil -} - -func (hs *clientHandshakeState) readSessionTicket() error { - if !hs.serverHello.ticketSupported { - return nil - } - - c := hs.c - msg, err := c.readHandshake() - if err != nil { - return err - } - sessionTicketMsg, ok := msg.(*newSessionTicketMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(sessionTicketMsg, msg) - } - hs.finishedHash.Write(sessionTicketMsg.marshal()) - - hs.session = &ClientSessionState{ - sessionTicket: sessionTicketMsg.ticket, - vers: c.vers, - cipherSuite: hs.suite.id, - masterSecret: hs.masterSecret, - serverCertificates: c.peerCertificates, - verifiedChains: c.verifiedChains, - receivedAt: c.config.time(), - } - - return nil -} - -func (hs *clientHandshakeState) sendFinished(out []byte) error { - c := hs.c - - if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { - return err - } - if hs.serverHello.nextProtoNeg { - nextProto := new(nextProtoMsg) - proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos) - nextProto.proto = proto - c.clientProtocol = proto - c.clientProtocolFallback = fallback - - hs.finishedHash.Write(nextProto.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, nextProto.marshal()); err != nil { - return err - } - } - - finished := new(finishedMsg) - finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) - hs.finishedHash.Write(finished.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { - return err - } - copy(out, finished.verifyData) - return nil -} - -// verifyServerCertificate parses and verifies the provided chain, setting -// c.verifiedChains and c.peerCertificates or sending the appropriate alert. -func (c *Conn) verifyServerCertificate(certificates [][]byte) error { - certs := make([]*x509.Certificate, len(certificates)) - for i, asn1Data := range certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse certificate from server: " + err.Error()) - } - certs[i] = cert - } - - if !c.config.InsecureSkipVerify { - opts := x509.VerifyOptions{ - Roots: c.config.RootCAs, - CurrentTime: c.config.time(), - DNSName: c.config.ServerName, - Intermediates: x509.NewCertPool(), - } - - for i, cert := range certs { - if i == 0 { - continue - } - opts.Intermediates.AddCert(cert) - } - var err error - c.verifiedChains, err = certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - if c.config.VerifyPeerCertificate != nil { - if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - switch certs[0].PublicKey.(type) { - case *rsa.PublicKey, *ecdsa.PublicKey: - break - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) - } - - c.peerCertificates = certs - - return nil -} - -// tls11SignatureSchemes contains the signature schemes that we synthesise for -// a TLS <= 1.1 connection, based on the supported certificate types. -var ( - tls11SignatureSchemes = []SignatureScheme{ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1} - tls11SignatureSchemesECDSA = tls11SignatureSchemes[:3] - tls11SignatureSchemesRSA = tls11SignatureSchemes[3:] -) - -// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS -// <= 1.2 CertificateRequest, making an effort to fill in missing information. -func certificateRequestInfoFromMsg(certReq *certificateRequestMsg) *CertificateRequestInfo { - var rsaAvail, ecdsaAvail bool - for _, certType := range certReq.certificateTypes { - switch certType { - case certTypeRSASign: - rsaAvail = true - case certTypeECDSASign: - ecdsaAvail = true - } - } - - cri := &CertificateRequestInfo{ - AcceptableCAs: certReq.certificateAuthorities, - } - - if !certReq.hasSignatureAlgorithm { - // Prior to TLS 1.2, the signature schemes were not - // included in the certificate request message. In this - // case we use a plausible list based on the acceptable - // certificate types. - switch { - case rsaAvail && ecdsaAvail: - cri.SignatureSchemes = tls11SignatureSchemes - case rsaAvail: - cri.SignatureSchemes = tls11SignatureSchemesRSA - case ecdsaAvail: - cri.SignatureSchemes = tls11SignatureSchemesECDSA - } - return cri - } - - // In TLS 1.2, the signature schemes apply to both the certificate chain and - // the leaf key, while the certificate types only apply to the leaf key. - // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated"). - // Filter the signature schemes based on the certificate type. - cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms)) - for _, sigScheme := range certReq.supportedSignatureAlgorithms { - switch signatureFromSignatureScheme(sigScheme) { - case signatureECDSA: - if ecdsaAvail { - cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) - } - case signatureRSAPSS, signaturePKCS1v15: - if rsaAvail { - cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) - } - } - } - - return cri -} - -func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) { - if c.config.GetClientCertificate != nil { - return c.config.GetClientCertificate(cri) - } - - // We need to search our list of client certs for one - // where SignatureAlgorithm is acceptable to the server and the - // Issuer is in AcceptableCAs. - for i, chain := range c.config.Certificates { - sigOK := false - for _, alg := range signatureSchemesForCertificate(c.vers, &chain) { - if isSupportedSignatureAlgorithm(alg, cri.SignatureSchemes) { - sigOK = true - break - } - } - if !sigOK { - continue - } - - if len(cri.AcceptableCAs) == 0 { - return &chain, nil - } - - for j, cert := range chain.Certificate { - x509Cert := chain.Leaf - // Parse the certificate if this isn't the leaf node, or if - // chain.Leaf was nil. - if j != 0 || x509Cert == nil { - var err error - if x509Cert, err = x509.ParseCertificate(cert); err != nil { - c.sendAlert(alertInternalError) - return nil, errors.New("tls: failed to parse configured certificate chain #" + strconv.Itoa(i) + ": " + err.Error()) - } - } - - for _, ca := range cri.AcceptableCAs { - if bytes.Equal(x509Cert.RawIssuer, ca) { - return &chain, nil - } - } - } - } - - // No acceptable certificate found. Don't send a certificate. - return new(Certificate), nil -} - -// clientSessionCacheKey returns a key used to cache sessionTickets that could -// be used to resume previously negotiated TLS sessions with a server. -func clientSessionCacheKey(serverAddr net.Addr, config *Config) string { - if len(config.ServerName) > 0 { - return config.ServerName - } - return serverAddr.String() -} - -// mutualProtocol finds the mutual Next Protocol Negotiation or ALPN protocol -// given list of possible protocols and a list of the preference order. The -// first list must not be empty. It returns the resulting protocol and flag -// indicating if the fallback case was reached. -func mutualProtocol(protos, preferenceProtos []string) (string, bool) { - for _, s := range preferenceProtos { - for _, c := range protos { - if s == c { - return s, false - } - } - } - - return protos[0], true -} - -// hostnameInSNI converts name into an approriate hostname for SNI. -// Literal IP addresses and absolute FQDNs are not permitted as SNI values. -// See RFC 6066, Section 3. -func hostnameInSNI(name string) string { - host := name - if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { - host = host[1 : len(host)-1] - } - if i := strings.LastIndex(host, "%"); i > 0 { - host = host[:i] - } - if net.ParseIP(host) != nil { - return "" - } - for len(name) > 0 && name[len(name)-1] == '.' { - name = name[:len(name)-1] - } - return name -} diff --git a/vendor/github.com/marten-seemann/qtls/handshake_client_tls13.go b/vendor/github.com/marten-seemann/qtls/handshake_client_tls13.go deleted file mode 100644 index 9d8e0f7be..000000000 --- a/vendor/github.com/marten-seemann/qtls/handshake_client_tls13.go +++ /dev/null @@ -1,680 +0,0 @@ -// 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 qtls - -import ( - "bytes" - "crypto" - "crypto/hmac" - "crypto/rsa" - "errors" - "hash" - "sync/atomic" - "time" -) - -type clientHandshakeStateTLS13 struct { - c *Conn - serverHello *serverHelloMsg - hello *clientHelloMsg - ecdheParams ecdheParameters - - session *ClientSessionState - earlySecret []byte - binderKey []byte - - certReq *certificateRequestMsgTLS13 - usingPSK bool - sentDummyCCS bool - suite *cipherSuiteTLS13 - transcript hash.Hash - masterSecret []byte - trafficSecret []byte // client_application_traffic_secret_0 -} - -// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and, -// optionally, hs.session, hs.earlySecret and hs.binderKey to be set. -func (hs *clientHandshakeStateTLS13) handshake() error { - c := hs.c - - // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, - // sections 4.1.2 and 4.1.3. - if c.handshakes > 0 { - c.sendAlert(alertProtocolVersion) - return errors.New("tls: server selected TLS 1.3 in a renegotiation") - } - - // Consistency check on the presence of a keyShare and its parameters. - if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { - return c.sendAlert(alertInternalError) - } - - if err := hs.checkServerHelloOrHRR(); err != nil { - return err - } - - hs.transcript = hs.suite.hash.New() - hs.transcript.Write(hs.hello.marshal()) - - if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - if err := hs.processHelloRetryRequest(); err != nil { - return err - } - } - - hs.transcript.Write(hs.serverHello.marshal()) - - c.buffering = true - if err := hs.processServerHello(); err != nil { - return err - } - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - if err := hs.establishHandshakeKeys(); err != nil { - return err - } - if err := hs.readServerParameters(); err != nil { - return err - } - if err := hs.readServerCertificate(); err != nil { - return err - } - if err := hs.readServerFinished(); err != nil { - return err - } - if err := hs.sendClientCertificate(); err != nil { - return err - } - if err := hs.sendClientFinished(); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - - atomic.StoreUint32(&c.handshakeStatus, 1) - - return nil -} - -// checkServerHelloOrHRR does validity checks that apply to both ServerHello and -// HelloRetryRequest messages. It sets hs.suite. -func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error { - c := hs.c - - if hs.serverHello.supportedVersion == 0 { - c.sendAlert(alertMissingExtension) - return errors.New("tls: server selected TLS 1.3 using the legacy version field") - } - - if hs.serverHello.supportedVersion != VersionTLS13 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid version after a HelloRetryRequest") - } - - if hs.serverHello.vers != VersionTLS12 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server sent an incorrect legacy version") - } - - if hs.serverHello.nextProtoNeg || - len(hs.serverHello.nextProtos) != 0 || - hs.serverHello.ocspStapling || - hs.serverHello.ticketSupported || - hs.serverHello.secureRenegotiationSupported || - len(hs.serverHello.secureRenegotiation) != 0 || - len(hs.serverHello.alpnProtocol) != 0 || - len(hs.serverHello.scts) != 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3") - } - - if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server did not echo the legacy session ID") - } - - if hs.serverHello.compressionMethod != compressionNone { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported compression format") - } - - selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite) - if hs.suite != nil && selectedSuite != hs.suite { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server changed cipher suite after a HelloRetryRequest") - } - if selectedSuite == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server chose an unconfigured cipher suite") - } - hs.suite = selectedSuite - c.cipherSuite = hs.suite.id - - return nil -} - -// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility -// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. -func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { - if hs.sentDummyCCS { - return nil - } - hs.sentDummyCCS = true - - _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) - return err -} - -// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and -// resends hs.hello, and reads the new ServerHello into hs.serverHello. -func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { - c := hs.c - - // The first ClientHello gets double-hashed into the transcript upon a - // HelloRetryRequest. See RFC 8446, Section 4.4.1. - chHash := hs.transcript.Sum(nil) - hs.transcript.Reset() - hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - hs.transcript.Write(chHash) - hs.transcript.Write(hs.serverHello.marshal()) - - if hs.serverHello.serverShare.group != 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: received malformed key_share extension") - } - - curveID := hs.serverHello.selectedGroup - if curveID == 0 { - c.sendAlert(alertMissingExtension) - return errors.New("tls: received HelloRetryRequest without selected group") - } - curveOK := false - for _, id := range hs.hello.supportedCurves { - if id == curveID { - curveOK = true - break - } - } - if !curveOK { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported group") - } - if hs.ecdheParams.CurveID() == curveID { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server sent an unnecessary HelloRetryRequest message") - } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err := generateECDHEParameters(c.config.rand(), curveID) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.ecdheParams = params - hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} - - hs.hello.cookie = hs.serverHello.cookie - - hs.hello.raw = nil - if len(hs.hello.pskIdentities) > 0 { - pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) - if pskSuite == nil { - return c.sendAlert(alertInternalError) - } - if pskSuite.hash == hs.suite.hash { - // Update binders and obfuscated_ticket_age. - ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond) - hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd - - transcript := hs.suite.hash.New() - transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - transcript.Write(chHash) - transcript.Write(hs.serverHello.marshal()) - transcript.Write(hs.hello.marshalWithoutBinders()) - pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} - hs.hello.updateBinders(pskBinders) - } else { - // Server selected a cipher suite incompatible with the PSK. - hs.hello.pskIdentities = nil - hs.hello.pskBinders = nil - } - } - - hs.transcript.Write(hs.hello.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { - return err - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - serverHello, ok := msg.(*serverHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverHello, msg) - } - hs.serverHello = serverHello - - if err := hs.checkServerHelloOrHRR(); err != nil { - return err - } - - return nil -} - -func (hs *clientHandshakeStateTLS13) processServerHello() error { - c := hs.c - - if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: server sent two HelloRetryRequest messages") - } - - if len(hs.serverHello.cookie) != 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server sent a cookie in a normal ServerHello") - } - - if hs.serverHello.selectedGroup != 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: malformed key_share extension") - } - - if hs.serverHello.serverShare.group == 0 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server did not send a key share") - } - if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported group") - } - - if !hs.serverHello.selectedIdentityPresent { - return nil - } - - if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid PSK") - } - - if len(hs.hello.pskIdentities) != 1 || hs.session == nil { - return c.sendAlert(alertInternalError) - } - pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) - if pskSuite == nil { - return c.sendAlert(alertInternalError) - } - if pskSuite.hash != hs.suite.hash { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid PSK and cipher suite pair") - } - - hs.usingPSK = true - c.didResume = true - c.peerCertificates = hs.session.serverCertificates - c.verifiedChains = hs.session.verifiedChains - return nil -} - -func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { - c := hs.c - - sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) - if sharedKey == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid server key share") - } - - earlySecret := hs.earlySecret - if !hs.usingPSK { - earlySecret = hs.suite.extract(nil, nil) - } - handshakeSecret := hs.suite.extract(sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) - - clientSecret := hs.suite.deriveSecret(handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(hs.suite, clientSecret) - c.out.setTrafficSecret(hs.suite, clientSecret) - serverSecret := hs.suite.deriveSecret(handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(handshakeSecret, "derived", nil)) - - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerParameters() error { - c := hs.c - - msg, err := c.readHandshake() - if err != nil { - return err - } - - encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(encryptedExtensions, msg) - } - if hs.c.config.ReceivedExtensions != nil { - hs.c.config.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions) - } - hs.transcript.Write(encryptedExtensions.marshal()) - - if len(encryptedExtensions.alpnProtocol) != 0 && len(hs.hello.alpnProtocols) == 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server advertised unrequested ALPN extension") - } - c.clientProtocol = encryptedExtensions.alpnProtocol - - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerCertificate() error { - c := hs.c - - // Either a PSK or a certificate is always used, but not both. - // See RFC 8446, Section 4.1.1. - if hs.usingPSK { - return nil - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - certReq, ok := msg.(*certificateRequestMsgTLS13) - if ok { - hs.transcript.Write(certReq.marshal()) - - hs.certReq = certReq - - msg, err = c.readHandshake() - if err != nil { - return err - } - } - - certMsg, ok := msg.(*certificateMsgTLS13) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - if len(certMsg.certificate.Certificate) == 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: received empty certificates message") - } - hs.transcript.Write(certMsg.marshal()) - - c.scts = certMsg.certificate.SignedCertificateTimestamps - c.ocspResponse = certMsg.certificate.OCSPStaple - - if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil { - return err - } - - msg, err = c.readHandshake() - if err != nil { - return err - } - - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid certificate signature algorithm") - } - sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm) - sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm) - if sigType == 0 || err != nil { - c.sendAlert(alertInternalError) - return err - } - if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid certificate signature algorithm") - } - h := sigHash.New() - writeSignedMessage(h, serverSignatureContext, hs.transcript) - if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, - sigHash, h.Sum(nil), certVerify.signature); err != nil { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid certificate signature") - } - - hs.transcript.Write(certVerify.marshal()) - - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerFinished() error { - c := hs.c - - msg, err := c.readHandshake() - if err != nil { - return err - } - - finished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(finished, msg) - } - - expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) - if !hmac.Equal(expectedMAC, finished.verifyData) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid server finished hash") - } - - hs.transcript.Write(finished.marshal()) - - // Derive secrets that take context through the server Finished. - - hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, - clientApplicationTrafficLabel, hs.transcript) - serverSecret := hs.suite.deriveSecret(hs.masterSecret, - serverApplicationTrafficLabel, hs.transcript) - c.in.exportKey(hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) - - err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) - - return nil -} - -func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { - c := hs.c - - if hs.certReq == nil { - return nil - } - - cert, err := c.getClientCertificate(&CertificateRequestInfo{ - AcceptableCAs: hs.certReq.certificateAuthorities, - SignatureSchemes: hs.certReq.supportedSignatureAlgorithms, - }) - if err != nil { - return err - } - - certMsg := new(certificateMsgTLS13) - - certMsg.certificate = *cert - certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0 - certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0 - - hs.transcript.Write(certMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { - return err - } - - // If we sent an empty certificate message, skip the CertificateVerify. - if len(cert.Certificate) == 0 { - return nil - } - - certVerifyMsg := new(certificateVerifyMsg) - certVerifyMsg.hasSignatureAlgorithm = true - - supportedAlgs := signatureSchemesForCertificate(c.vers, cert) - if supportedAlgs == nil { - c.sendAlert(alertInternalError) - return unsupportedCertificateError(cert) - } - // Pick signature scheme in server preference order, as the client - // preference order is not configurable. - for _, preferredAlg := range hs.certReq.supportedSignatureAlgorithms { - if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { - certVerifyMsg.signatureAlgorithm = preferredAlg - break - } - } - if certVerifyMsg.signatureAlgorithm == 0 { - // getClientCertificate returned a certificate incompatible with the - // CertificateRequestInfo supported signature algorithms. - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: server doesn't support selected certificate") - } - - sigType := signatureFromSignatureScheme(certVerifyMsg.signatureAlgorithm) - sigHash, err := hashFromSignatureScheme(certVerifyMsg.signatureAlgorithm) - if sigType == 0 || err != nil { - return c.sendAlert(alertInternalError) - } - h := sigHash.New() - writeSignedMessage(h, clientSignatureContext, hs.transcript) - - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts) - if err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to sign handshake: " + err.Error()) - } - certVerifyMsg.signature = sig - - hs.transcript.Write(certVerifyMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { - return err - } - - return nil -} - -func (hs *clientHandshakeStateTLS13) sendClientFinished() error { - c := hs.c - - finished := &finishedMsg{ - verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), - } - - hs.transcript.Write(finished.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { - return err - } - - c.out.exportKey(hs.suite, hs.trafficSecret) - c.out.setTrafficSecret(hs.suite, hs.trafficSecret) - - if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { - c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) - } - - return nil -} - -func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { - if !c.isClient { - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: received new session ticket from a client") - } - - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { - return nil - } - - // See RFC 8446, Section 4.6.1. - if msg.lifetime == 0 { - return nil - } - lifetime := time.Duration(msg.lifetime) * time.Second - if lifetime > maxSessionTicketLifetime { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: received a session ticket with invalid lifetime") - } - - cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) - if cipherSuite == nil || c.resumptionSecret == nil { - return c.sendAlert(alertInternalError) - } - - // Save the resumption_master_secret and nonce instead of deriving the PSK - // to do the least amount of work on NewSessionTicket messages before we - // know if the ticket will be used. Forward secrecy of resumed connections - // is guaranteed by the requirement for pskModeDHE. - session := &ClientSessionState{ - sessionTicket: msg.label, - vers: c.vers, - cipherSuite: c.cipherSuite, - masterSecret: c.resumptionSecret, - serverCertificates: c.peerCertificates, - verifiedChains: c.verifiedChains, - receivedAt: c.config.time(), - nonce: msg.nonce, - useBy: c.config.time().Add(lifetime), - ageAdd: msg.ageAdd, - } - - cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - c.config.ClientSessionCache.Put(cacheKey, session) - - return nil -} diff --git a/vendor/github.com/marten-seemann/qtls/handshake_messages.go b/vendor/github.com/marten-seemann/qtls/handshake_messages.go deleted file mode 100644 index 9c2ffa1d8..000000000 --- a/vendor/github.com/marten-seemann/qtls/handshake_messages.go +++ /dev/null @@ -1,1900 +0,0 @@ -// Copyright 2009 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 qtls - -import ( - "fmt" - "strings" - - "golang.org/x/crypto/cryptobyte" -) - -// The marshalingFunction type is an adapter to allow the use of ordinary -// functions as cryptobyte.MarshalingValue. -type marshalingFunction func(b *cryptobyte.Builder) error - -func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error { - return f(b) -} - -// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If -// the length of the sequence is not the value specified, it produces an error. -func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) { - b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error { - if len(v) != n { - return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v)) - } - b.AddBytes(v) - return nil - })) -} - -// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder. -func addUint64(b *cryptobyte.Builder, v uint64) { - b.AddUint32(uint32(v >> 32)) - b.AddUint32(uint32(v)) -} - -// readUint64 decodes a big-endian, 64-bit value into out and advances over it. -// It reports whether the read was successful. -func readUint64(s *cryptobyte.String, out *uint64) bool { - var hi, lo uint32 - if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) { - return false - } - *out = uint64(hi)<<32 | uint64(lo) - return true -} - -// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out)) -} - -// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out)) -} - -// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out)) -} - -type clientHelloMsg struct { - raw []byte - vers uint16 - random []byte - sessionId []byte - cipherSuites []uint16 - compressionMethods []uint8 - nextProtoNeg bool - serverName string - ocspStapling bool - supportedCurves []CurveID - supportedPoints []uint8 - ticketSupported bool - sessionTicket []uint8 - supportedSignatureAlgorithms []SignatureScheme - supportedSignatureAlgorithmsCert []SignatureScheme - secureRenegotiationSupported bool - secureRenegotiation []byte - alpnProtocols []string - scts bool - supportedVersions []uint16 - cookie []byte - keyShares []keyShare - earlyData bool - pskModes []uint8 - pskIdentities []pskIdentity - pskBinders [][]byte - additionalExtensions []Extension -} - -func (m *clientHelloMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeClientHello) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.vers) - addBytesWithLength(b, m.random, 32) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.sessionId) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, suite := range m.cipherSuites { - b.AddUint16(suite) - } - }) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.compressionMethods) - }) - - // If extensions aren't present, omit them. - var extensionsPresent bool - bWithoutExtensions := *b - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.nextProtoNeg { - // draft-agl-tls-nextprotoneg-04 - b.AddUint16(extensionNextProtoNeg) - b.AddUint16(0) // empty extension_data - } - if len(m.serverName) > 0 { - // RFC 6066, Section 3 - b.AddUint16(extensionServerName) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(0) // name_type = host_name - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(m.serverName)) - }) - }) - }) - } - if m.ocspStapling { - // RFC 4366, Section 3.6 - b.AddUint16(extensionStatusRequest) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(1) // status_type = ocsp - b.AddUint16(0) // empty responder_id_list - b.AddUint16(0) // empty request_extensions - }) - } - if len(m.supportedCurves) > 0 { - // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 - b.AddUint16(extensionSupportedCurves) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, curve := range m.supportedCurves { - b.AddUint16(uint16(curve)) - } - }) - }) - } - if len(m.supportedPoints) > 0 { - // RFC 4492, Section 5.1.2 - b.AddUint16(extensionSupportedPoints) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.supportedPoints) - }) - }) - } - if m.ticketSupported { - // RFC 5077, Section 3.2 - b.AddUint16(extensionSessionTicket) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.sessionTicket) - }) - } - if len(m.supportedSignatureAlgorithms) > 0 { - // RFC 5246, Section 7.4.1.4.1 - b.AddUint16(extensionSignatureAlgorithms) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithms { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.supportedSignatureAlgorithmsCert) > 0 { - // RFC 8446, Section 4.2.3 - b.AddUint16(extensionSignatureAlgorithmsCert) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if m.secureRenegotiationSupported { - // RFC 5746, Section 3.2 - b.AddUint16(extensionRenegotiationInfo) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.secureRenegotiation) - }) - }) - } - if len(m.alpnProtocols) > 0 { - // RFC 7301, Section 3.1 - b.AddUint16(extensionALPN) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, proto := range m.alpnProtocols { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(proto)) - }) - } - }) - }) - } - if m.scts { - // RFC 6962, Section 3.3.1 - b.AddUint16(extensionSCT) - b.AddUint16(0) // empty extension_data - } - if len(m.supportedVersions) > 0 { - // RFC 8446, Section 4.2.1 - b.AddUint16(extensionSupportedVersions) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - for _, vers := range m.supportedVersions { - b.AddUint16(vers) - } - }) - }) - } - if len(m.cookie) > 0 { - // RFC 8446, Section 4.2.2 - b.AddUint16(extensionCookie) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.cookie) - }) - }) - } - if len(m.keyShares) > 0 { - // RFC 8446, Section 4.2.8 - b.AddUint16(extensionKeyShare) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, ks := range m.keyShares { - b.AddUint16(uint16(ks.group)) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ks.data) - }) - } - }) - }) - } - if m.earlyData { - // RFC 8446, Section 4.2.10 - b.AddUint16(extensionEarlyData) - b.AddUint16(0) // empty extension_data - } - if len(m.pskModes) > 0 { - // RFC 8446, Section 4.2.9 - b.AddUint16(extensionPSKModes) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.pskModes) - }) - }) - } - for _, ext := range m.additionalExtensions { - b.AddUint16(ext.Type) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ext.Data) - }) - } - if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension - // RFC 8446, Section 4.2.11 - b.AddUint16(extensionPreSharedKey) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, psk := range m.pskIdentities { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(psk.label) - }) - b.AddUint32(psk.obfuscatedTicketAge) - } - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, binder := range m.pskBinders { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(binder) - }) - } - }) - }) - } - - extensionsPresent = len(b.BytesOrPanic()) > 2 - }) - - if !extensionsPresent { - *b = bWithoutExtensions - } - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -// marshalWithoutBinders returns the ClientHello through the -// PreSharedKeyExtension.identities field, according to RFC 8446, Section -// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length. -func (m *clientHelloMsg) marshalWithoutBinders() []byte { - bindersLen := 2 // uint16 length prefix - for _, binder := range m.pskBinders { - bindersLen += 1 // uint8 length prefix - bindersLen += len(binder) - } - - fullMessage := m.marshal() - return fullMessage[:len(fullMessage)-bindersLen] -} - -// updateBinders updates the m.pskBinders field, if necessary updating the -// cached marshalled representation. The supplied binders must have the same -// length as the current m.pskBinders. -func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) { - if len(pskBinders) != len(m.pskBinders) { - panic("tls: internal error: pskBinders length mismatch") - } - for i := range m.pskBinders { - if len(pskBinders[i]) != len(m.pskBinders[i]) { - panic("tls: internal error: pskBinders length mismatch") - } - } - m.pskBinders = pskBinders - if m.raw != nil { - lenWithoutBinders := len(m.marshalWithoutBinders()) - // TODO(filippo): replace with NewFixedBuilder once CL 148882 is imported. - b := cryptobyte.NewBuilder(m.raw[:lenWithoutBinders]) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, binder := range m.pskBinders { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(binder) - }) - } - }) - if len(b.BytesOrPanic()) != len(m.raw) { - panic("tls: internal error: failed to update binders") - } - } -} - -func (m *clientHelloMsg) unmarshal(data []byte) bool { - *m = clientHelloMsg{raw: data} - s := cryptobyte.String(data) - - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || - !readUint8LengthPrefixed(&s, &m.sessionId) { - return false - } - - var cipherSuites cryptobyte.String - if !s.ReadUint16LengthPrefixed(&cipherSuites) { - return false - } - m.cipherSuites = []uint16{} - m.secureRenegotiationSupported = false - for !cipherSuites.Empty() { - var suite uint16 - if !cipherSuites.ReadUint16(&suite) { - return false - } - if suite == scsvRenegotiation { - m.secureRenegotiationSupported = true - } - m.cipherSuites = append(m.cipherSuites, suite) - } - - if !readUint8LengthPrefixed(&s, &m.compressionMethods) { - return false - } - - if s.Empty() { - // ClientHello is optionally followed by extension data - return true - } - - var extensions cryptobyte.String - if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - for !extensions.Empty() { - var ext uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&ext) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch ext { - case extensionServerName: - // RFC 6066, Section 3 - var nameList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() { - return false - } - for !nameList.Empty() { - var nameType uint8 - var serverName cryptobyte.String - if !nameList.ReadUint8(&nameType) || - !nameList.ReadUint16LengthPrefixed(&serverName) || - serverName.Empty() { - return false - } - if nameType != 0 { - continue - } - if len(m.serverName) != 0 { - // Multiple names of the same name_type are prohibited. - return false - } - m.serverName = string(serverName) - // An SNI value may not include a trailing dot. - if strings.HasSuffix(m.serverName, ".") { - return false - } - } - case extensionNextProtoNeg: - // draft-agl-tls-nextprotoneg-04 - m.nextProtoNeg = true - case extensionStatusRequest: - // RFC 4366, Section 3.6 - var statusType uint8 - var ignored cryptobyte.String - if !extData.ReadUint8(&statusType) || - !extData.ReadUint16LengthPrefixed(&ignored) || - !extData.ReadUint16LengthPrefixed(&ignored) { - return false - } - m.ocspStapling = statusType == statusTypeOCSP - case extensionSupportedCurves: - // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 - var curves cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() { - return false - } - for !curves.Empty() { - var curve uint16 - if !curves.ReadUint16(&curve) { - return false - } - m.supportedCurves = append(m.supportedCurves, CurveID(curve)) - } - case extensionSupportedPoints: - // RFC 4492, Section 5.1.2 - if !readUint8LengthPrefixed(&extData, &m.supportedPoints) || - len(m.supportedPoints) == 0 { - return false - } - case extensionSessionTicket: - // RFC 5077, Section 3.2 - m.ticketSupported = true - extData.ReadBytes(&m.sessionTicket, len(extData)) - case extensionSignatureAlgorithms: - // RFC 5246, Section 7.4.1.4.1 - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithms = append( - m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) - } - case extensionSignatureAlgorithmsCert: - // RFC 8446, Section 4.2.3 - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithmsCert = append( - m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) - } - case extensionRenegotiationInfo: - // RFC 5746, Section 3.2 - if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { - return false - } - m.secureRenegotiationSupported = true - case extensionALPN: - // RFC 7301, Section 3.1 - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - for !protoList.Empty() { - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() { - return false - } - m.alpnProtocols = append(m.alpnProtocols, string(proto)) - } - case extensionSCT: - // RFC 6962, Section 3.3.1 - m.scts = true - case extensionSupportedVersions: - // RFC 8446, Section 4.2.1 - var versList cryptobyte.String - if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() { - return false - } - for !versList.Empty() { - var vers uint16 - if !versList.ReadUint16(&vers) { - return false - } - m.supportedVersions = append(m.supportedVersions, vers) - } - case extensionCookie: - // RFC 8446, Section 4.2.2 - if !readUint16LengthPrefixed(&extData, &m.cookie) || - len(m.cookie) == 0 { - return false - } - case extensionKeyShare: - // RFC 8446, Section 4.2.8 - var clientShares cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&clientShares) { - return false - } - for !clientShares.Empty() { - var ks keyShare - if !clientShares.ReadUint16((*uint16)(&ks.group)) || - !readUint16LengthPrefixed(&clientShares, &ks.data) || - len(ks.data) == 0 { - return false - } - m.keyShares = append(m.keyShares, ks) - } - case extensionEarlyData: - // RFC 8446, Section 4.2.10 - m.earlyData = true - case extensionPSKModes: - // RFC 8446, Section 4.2.9 - if !readUint8LengthPrefixed(&extData, &m.pskModes) { - return false - } - case extensionPreSharedKey: - // RFC 8446, Section 4.2.11 - if !extensions.Empty() { - return false // pre_shared_key must be the last extension - } - var identities cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() { - return false - } - for !identities.Empty() { - var psk pskIdentity - if !readUint16LengthPrefixed(&identities, &psk.label) || - !identities.ReadUint32(&psk.obfuscatedTicketAge) || - len(psk.label) == 0 { - return false - } - m.pskIdentities = append(m.pskIdentities, psk) - } - var binders cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() { - return false - } - for !binders.Empty() { - var binder []byte - if !readUint8LengthPrefixed(&binders, &binder) || - len(binder) == 0 { - return false - } - m.pskBinders = append(m.pskBinders, binder) - } - default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData}) - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type serverHelloMsg struct { - raw []byte - vers uint16 - random []byte - sessionId []byte - cipherSuite uint16 - compressionMethod uint8 - nextProtoNeg bool - nextProtos []string - ocspStapling bool - ticketSupported bool - secureRenegotiationSupported bool - secureRenegotiation []byte - alpnProtocol string - scts [][]byte - supportedVersion uint16 - serverShare keyShare - selectedIdentityPresent bool - selectedIdentity uint16 - - // HelloRetryRequest extensions - cookie []byte - selectedGroup CurveID -} - -func (m *serverHelloMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeServerHello) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.vers) - addBytesWithLength(b, m.random, 32) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.sessionId) - }) - b.AddUint16(m.cipherSuite) - b.AddUint8(m.compressionMethod) - - // If extensions aren't present, omit them. - var extensionsPresent bool - bWithoutExtensions := *b - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.nextProtoNeg { - b.AddUint16(extensionNextProtoNeg) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, proto := range m.nextProtos { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(proto)) - }) - } - }) - } - if m.ocspStapling { - b.AddUint16(extensionStatusRequest) - b.AddUint16(0) // empty extension_data - } - if m.ticketSupported { - b.AddUint16(extensionSessionTicket) - b.AddUint16(0) // empty extension_data - } - if m.secureRenegotiationSupported { - b.AddUint16(extensionRenegotiationInfo) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.secureRenegotiation) - }) - }) - } - if len(m.alpnProtocol) > 0 { - b.AddUint16(extensionALPN) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(m.alpnProtocol)) - }) - }) - }) - } - if len(m.scts) > 0 { - b.AddUint16(extensionSCT) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sct := range m.scts { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(sct) - }) - } - }) - }) - } - if m.supportedVersion != 0 { - b.AddUint16(extensionSupportedVersions) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.supportedVersion) - }) - } - if m.serverShare.group != 0 { - b.AddUint16(extensionKeyShare) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(uint16(m.serverShare.group)) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.serverShare.data) - }) - }) - } - if m.selectedIdentityPresent { - b.AddUint16(extensionPreSharedKey) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.selectedIdentity) - }) - } - - if len(m.cookie) > 0 { - b.AddUint16(extensionCookie) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.cookie) - }) - }) - } - if m.selectedGroup != 0 { - b.AddUint16(extensionKeyShare) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(uint16(m.selectedGroup)) - }) - } - - extensionsPresent = len(b.BytesOrPanic()) > 2 - }) - - if !extensionsPresent { - *b = bWithoutExtensions - } - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *serverHelloMsg) unmarshal(data []byte) bool { - *m = serverHelloMsg{raw: data} - s := cryptobyte.String(data) - - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || - !readUint8LengthPrefixed(&s, &m.sessionId) || - !s.ReadUint16(&m.cipherSuite) || - !s.ReadUint8(&m.compressionMethod) { - return false - } - - if s.Empty() { - // ServerHello is optionally followed by extension data - return true - } - - var extensions cryptobyte.String - if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch extension { - case extensionNextProtoNeg: - m.nextProtoNeg = true - for !extData.Empty() { - var proto cryptobyte.String - if !extData.ReadUint8LengthPrefixed(&proto) || - proto.Empty() { - return false - } - m.nextProtos = append(m.nextProtos, string(proto)) - } - case extensionStatusRequest: - m.ocspStapling = true - case extensionSessionTicket: - m.ticketSupported = true - case extensionRenegotiationInfo: - if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { - return false - } - m.secureRenegotiationSupported = true - case extensionALPN: - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || - proto.Empty() || !protoList.Empty() { - return false - } - m.alpnProtocol = string(proto) - case extensionSCT: - var sctList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { - return false - } - for !sctList.Empty() { - var sct []byte - if !readUint16LengthPrefixed(&sctList, &sct) || - len(sct) == 0 { - return false - } - m.scts = append(m.scts, sct) - } - case extensionSupportedVersions: - if !extData.ReadUint16(&m.supportedVersion) { - return false - } - case extensionCookie: - if !readUint16LengthPrefixed(&extData, &m.cookie) || - len(m.cookie) == 0 { - return false - } - case extensionKeyShare: - // This extension has different formats in SH and HRR, accept either - // and let the handshake logic decide. See RFC 8446, Section 4.2.8. - if len(extData) == 2 { - if !extData.ReadUint16((*uint16)(&m.selectedGroup)) { - return false - } - } else { - if !extData.ReadUint16((*uint16)(&m.serverShare.group)) || - !readUint16LengthPrefixed(&extData, &m.serverShare.data) { - return false - } - } - case extensionPreSharedKey: - m.selectedIdentityPresent = true - if !extData.ReadUint16(&m.selectedIdentity) { - return false - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type encryptedExtensionsMsg struct { - raw []byte - alpnProtocol string - - additionalExtensions []Extension -} - -func (m *encryptedExtensionsMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeEncryptedExtensions) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if len(m.alpnProtocol) > 0 { - b.AddUint16(extensionALPN) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(m.alpnProtocol)) - }) - }) - }) - } - for _, ext := range m.additionalExtensions { - b.AddUint16(ext.Type) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ext.Data) - }) - } - }) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { - *m = encryptedExtensionsMsg{raw: data} - s := cryptobyte.String(data) - - var extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - for !extensions.Empty() { - var ext uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&ext) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch ext { - case extensionALPN: - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || - proto.Empty() || !protoList.Empty() { - return false - } - m.alpnProtocol = string(proto) - default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData}) - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type endOfEarlyDataMsg struct{} - -func (m *endOfEarlyDataMsg) marshal() []byte { - x := make([]byte, 4) - x[0] = typeEndOfEarlyData - return x -} - -func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} - -type keyUpdateMsg struct { - raw []byte - updateRequested bool -} - -func (m *keyUpdateMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeKeyUpdate) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - if m.updateRequested { - b.AddUint8(1) - } else { - b.AddUint8(0) - } - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *keyUpdateMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - var updateRequested uint8 - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8(&updateRequested) || !s.Empty() { - return false - } - switch updateRequested { - case 0: - m.updateRequested = false - case 1: - m.updateRequested = true - default: - return false - } - return true -} - -type newSessionTicketMsgTLS13 struct { - raw []byte - lifetime uint32 - ageAdd uint32 - nonce []byte - label []byte - maxEarlyData uint32 -} - -func (m *newSessionTicketMsgTLS13) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeNewSessionTicket) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint32(m.lifetime) - b.AddUint32(m.ageAdd) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.nonce) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.label) - }) - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.maxEarlyData > 0 { - b.AddUint16(extensionEarlyData) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint32(m.maxEarlyData) - }) - } - }) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool { - *m = newSessionTicketMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint32(&m.lifetime) || - !s.ReadUint32(&m.ageAdd) || - !readUint8LengthPrefixed(&s, &m.nonce) || - !readUint16LengthPrefixed(&s, &m.label) || - !s.ReadUint16LengthPrefixed(&extensions) || - !s.Empty() { - return false - } - - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch extension { - case extensionEarlyData: - if !extData.ReadUint32(&m.maxEarlyData) { - return false - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type certificateRequestMsgTLS13 struct { - raw []byte - ocspStapling bool - scts bool - supportedSignatureAlgorithms []SignatureScheme - supportedSignatureAlgorithmsCert []SignatureScheme - certificateAuthorities [][]byte -} - -func (m *certificateRequestMsgTLS13) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateRequest) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - // certificate_request_context (SHALL be zero length unless used for - // post-handshake authentication) - b.AddUint8(0) - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.ocspStapling { - b.AddUint16(extensionStatusRequest) - b.AddUint16(0) // empty extension_data - } - if m.scts { - // RFC 8446, Section 4.4.2.1 makes no mention of - // signed_certificate_timestamp in CertificateRequest, but - // "Extensions in the Certificate message from the client MUST - // correspond to extensions in the CertificateRequest message - // from the server." and it appears in the table in Section 4.2. - b.AddUint16(extensionSCT) - b.AddUint16(0) // empty extension_data - } - if len(m.supportedSignatureAlgorithms) > 0 { - b.AddUint16(extensionSignatureAlgorithms) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithms { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.supportedSignatureAlgorithmsCert) > 0 { - b.AddUint16(extensionSignatureAlgorithmsCert) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.certificateAuthorities) > 0 { - b.AddUint16(extensionCertificateAuthorities) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, ca := range m.certificateAuthorities { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ca) - }) - } - }) - }) - } - }) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool { - *m = certificateRequestMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var context, extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || - !s.ReadUint16LengthPrefixed(&extensions) || - !s.Empty() { - return false - } - - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch extension { - case extensionStatusRequest: - m.ocspStapling = true - case extensionSCT: - m.scts = true - case extensionSignatureAlgorithms: - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithms = append( - m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) - } - case extensionSignatureAlgorithmsCert: - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithmsCert = append( - m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) - } - case extensionCertificateAuthorities: - var auths cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() { - return false - } - for !auths.Empty() { - var ca []byte - if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 { - return false - } - m.certificateAuthorities = append(m.certificateAuthorities, ca) - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type certificateMsg struct { - raw []byte - certificates [][]byte -} - -func (m *certificateMsg) marshal() (x []byte) { - if m.raw != nil { - return m.raw - } - - var i int - for _, slice := range m.certificates { - i += len(slice) - } - - length := 3 + 3*len(m.certificates) + i - x = make([]byte, 4+length) - x[0] = typeCertificate - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - certificateOctets := length - 3 - x[4] = uint8(certificateOctets >> 16) - x[5] = uint8(certificateOctets >> 8) - x[6] = uint8(certificateOctets) - - y := x[7:] - for _, slice := range m.certificates { - y[0] = uint8(len(slice) >> 16) - y[1] = uint8(len(slice) >> 8) - y[2] = uint8(len(slice)) - copy(y[3:], slice) - y = y[3+len(slice):] - } - - m.raw = x - return -} - -func (m *certificateMsg) unmarshal(data []byte) bool { - if len(data) < 7 { - return false - } - - m.raw = data - certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]) - if uint32(len(data)) != certsLen+7 { - return false - } - - numCerts := 0 - d := data[7:] - for certsLen > 0 { - if len(d) < 4 { - return false - } - certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) - if uint32(len(d)) < 3+certLen { - return false - } - d = d[3+certLen:] - certsLen -= 3 + certLen - numCerts++ - } - - m.certificates = make([][]byte, numCerts) - d = data[7:] - for i := 0; i < numCerts; i++ { - certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) - m.certificates[i] = d[3 : 3+certLen] - d = d[3+certLen:] - } - - return true -} - -type certificateMsgTLS13 struct { - raw []byte - certificate Certificate - ocspStapling bool - scts bool -} - -func (m *certificateMsgTLS13) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificate) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(0) // certificate_request_context - - certificate := m.certificate - if !m.ocspStapling { - certificate.OCSPStaple = nil - } - if !m.scts { - certificate.SignedCertificateTimestamps = nil - } - marshalCertificate(b, certificate) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) { - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - for i, cert := range certificate.Certificate { - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(cert) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if i > 0 { - // This library only supports OCSP and SCT for leaf certificates. - return - } - if certificate.OCSPStaple != nil { - b.AddUint16(extensionStatusRequest) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(statusTypeOCSP) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(certificate.OCSPStaple) - }) - }) - } - if certificate.SignedCertificateTimestamps != nil { - b.AddUint16(extensionSCT) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sct := range certificate.SignedCertificateTimestamps { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(sct) - }) - } - }) - }) - } - }) - } - }) -} - -func (m *certificateMsgTLS13) unmarshal(data []byte) bool { - *m = certificateMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var context cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || - !unmarshalCertificate(&s, &m.certificate) || - !s.Empty() { - return false - } - - m.scts = m.certificate.SignedCertificateTimestamps != nil - m.ocspStapling = m.certificate.OCSPStaple != nil - - return true -} - -func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool { - var certList cryptobyte.String - if !s.ReadUint24LengthPrefixed(&certList) { - return false - } - for !certList.Empty() { - var cert []byte - var extensions cryptobyte.String - if !readUint24LengthPrefixed(&certList, &cert) || - !certList.ReadUint16LengthPrefixed(&extensions) { - return false - } - certificate.Certificate = append(certificate.Certificate, cert) - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - if len(certificate.Certificate) > 1 { - // This library only supports OCSP and SCT for leaf certificates. - continue - } - - switch extension { - case extensionStatusRequest: - var statusType uint8 - if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP || - !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) || - len(certificate.OCSPStaple) == 0 { - return false - } - case extensionSCT: - var sctList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { - return false - } - for !sctList.Empty() { - var sct []byte - if !readUint16LengthPrefixed(&sctList, &sct) || - len(sct) == 0 { - return false - } - certificate.SignedCertificateTimestamps = append( - certificate.SignedCertificateTimestamps, sct) - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - } - return true -} - -type serverKeyExchangeMsg struct { - raw []byte - key []byte -} - -func (m *serverKeyExchangeMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - length := len(m.key) - x := make([]byte, length+4) - x[0] = typeServerKeyExchange - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - copy(x[4:], m.key) - - m.raw = x - return x -} - -func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { - m.raw = data - if len(data) < 4 { - return false - } - m.key = data[4:] - return true -} - -type certificateStatusMsg struct { - raw []byte - response []byte -} - -func (m *certificateStatusMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateStatus) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(statusTypeOCSP) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.response) - }) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *certificateStatusMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - var statusType uint8 - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8(&statusType) || statusType != statusTypeOCSP || - !readUint24LengthPrefixed(&s, &m.response) || - len(m.response) == 0 || !s.Empty() { - return false - } - return true -} - -type serverHelloDoneMsg struct{} - -func (m *serverHelloDoneMsg) marshal() []byte { - x := make([]byte, 4) - x[0] = typeServerHelloDone - return x -} - -func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} - -type clientKeyExchangeMsg struct { - raw []byte - ciphertext []byte -} - -func (m *clientKeyExchangeMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - length := len(m.ciphertext) - x := make([]byte, length+4) - x[0] = typeClientKeyExchange - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - copy(x[4:], m.ciphertext) - - m.raw = x - return x -} - -func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { - m.raw = data - if len(data) < 4 { - return false - } - l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - if l != len(data)-4 { - return false - } - m.ciphertext = data[4:] - return true -} - -type finishedMsg struct { - raw []byte - verifyData []byte -} - -func (m *finishedMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeFinished) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.verifyData) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *finishedMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - return s.Skip(1) && - readUint24LengthPrefixed(&s, &m.verifyData) && - s.Empty() -} - -type nextProtoMsg struct { - raw []byte - proto string -} - -func (m *nextProtoMsg) marshal() []byte { - if m.raw != nil { - return m.raw - } - l := len(m.proto) - if l > 255 { - l = 255 - } - - padding := 32 - (l+2)%32 - length := l + padding + 2 - x := make([]byte, length+4) - x[0] = typeNextProtocol - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - y := x[4:] - y[0] = byte(l) - copy(y[1:], []byte(m.proto[0:l])) - y = y[1+l:] - y[0] = byte(padding) - - m.raw = x - - return x -} - -func (m *nextProtoMsg) unmarshal(data []byte) bool { - m.raw = data - - if len(data) < 5 { - return false - } - data = data[4:] - protoLen := int(data[0]) - data = data[1:] - if len(data) < protoLen { - return false - } - m.proto = string(data[0:protoLen]) - data = data[protoLen:] - - if len(data) < 1 { - return false - } - paddingLen := int(data[0]) - data = data[1:] - if len(data) != paddingLen { - return false - } - - return true -} - -type certificateRequestMsg struct { - raw []byte - // hasSignatureAlgorithm indicates whether this message includes a list of - // supported signature algorithms. This change was introduced with TLS 1.2. - hasSignatureAlgorithm bool - - certificateTypes []byte - supportedSignatureAlgorithms []SignatureScheme - certificateAuthorities [][]byte -} - -func (m *certificateRequestMsg) marshal() (x []byte) { - if m.raw != nil { - return m.raw - } - - // See RFC 4346, Section 7.4.4. - length := 1 + len(m.certificateTypes) + 2 - casLength := 0 - for _, ca := range m.certificateAuthorities { - casLength += 2 + len(ca) - } - length += casLength - - if m.hasSignatureAlgorithm { - length += 2 + 2*len(m.supportedSignatureAlgorithms) - } - - x = make([]byte, 4+length) - x[0] = typeCertificateRequest - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - x[4] = uint8(len(m.certificateTypes)) - - copy(x[5:], m.certificateTypes) - y := x[5+len(m.certificateTypes):] - - if m.hasSignatureAlgorithm { - n := len(m.supportedSignatureAlgorithms) * 2 - y[0] = uint8(n >> 8) - y[1] = uint8(n) - y = y[2:] - for _, sigAlgo := range m.supportedSignatureAlgorithms { - y[0] = uint8(sigAlgo >> 8) - y[1] = uint8(sigAlgo) - y = y[2:] - } - } - - y[0] = uint8(casLength >> 8) - y[1] = uint8(casLength) - y = y[2:] - for _, ca := range m.certificateAuthorities { - y[0] = uint8(len(ca) >> 8) - y[1] = uint8(len(ca)) - y = y[2:] - copy(y, ca) - y = y[len(ca):] - } - - m.raw = x - return -} - -func (m *certificateRequestMsg) unmarshal(data []byte) bool { - m.raw = data - - if len(data) < 5 { - return false - } - - length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) - if uint32(len(data))-4 != length { - return false - } - - numCertTypes := int(data[4]) - data = data[5:] - if numCertTypes == 0 || len(data) <= numCertTypes { - return false - } - - m.certificateTypes = make([]byte, numCertTypes) - if copy(m.certificateTypes, data) != numCertTypes { - return false - } - - data = data[numCertTypes:] - - if m.hasSignatureAlgorithm { - if len(data) < 2 { - return false - } - sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if sigAndHashLen&1 != 0 { - return false - } - if len(data) < int(sigAndHashLen) { - return false - } - numSigAlgos := sigAndHashLen / 2 - m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos) - for i := range m.supportedSignatureAlgorithms { - m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1]) - data = data[2:] - } - } - - if len(data) < 2 { - return false - } - casLength := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if len(data) < int(casLength) { - return false - } - cas := make([]byte, casLength) - copy(cas, data) - data = data[casLength:] - - m.certificateAuthorities = nil - for len(cas) > 0 { - if len(cas) < 2 { - return false - } - caLen := uint16(cas[0])<<8 | uint16(cas[1]) - cas = cas[2:] - - if len(cas) < int(caLen) { - return false - } - - m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) - cas = cas[caLen:] - } - - return len(data) == 0 -} - -type certificateVerifyMsg struct { - raw []byte - hasSignatureAlgorithm bool // format change introduced in TLS 1.2 - signatureAlgorithm SignatureScheme - signature []byte -} - -func (m *certificateVerifyMsg) marshal() (x []byte) { - if m.raw != nil { - return m.raw - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateVerify) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - if m.hasSignatureAlgorithm { - b.AddUint16(uint16(m.signatureAlgorithm)) - } - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.signature) - }) - }) - - m.raw = b.BytesOrPanic() - return m.raw -} - -func (m *certificateVerifyMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - if !s.Skip(4) { // message type and uint24 length field - return false - } - if m.hasSignatureAlgorithm { - if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) { - return false - } - } - return readUint16LengthPrefixed(&s, &m.signature) && s.Empty() -} - -type newSessionTicketMsg struct { - raw []byte - ticket []byte -} - -func (m *newSessionTicketMsg) marshal() (x []byte) { - if m.raw != nil { - return m.raw - } - - // See RFC 5077, Section 3.3. - ticketLen := len(m.ticket) - length := 2 + 4 + ticketLen - x = make([]byte, 4+length) - x[0] = typeNewSessionTicket - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - x[8] = uint8(ticketLen >> 8) - x[9] = uint8(ticketLen) - copy(x[10:], m.ticket) - - m.raw = x - - return -} - -func (m *newSessionTicketMsg) unmarshal(data []byte) bool { - m.raw = data - - if len(data) < 10 { - return false - } - - length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) - if uint32(len(data))-4 != length { - return false - } - - ticketLen := int(data[8])<<8 + int(data[9]) - if len(data)-10 != ticketLen { - return false - } - - m.ticket = data[10:] - - return true -} - -type helloRequestMsg struct { -} - -func (*helloRequestMsg) marshal() []byte { - return []byte{typeHelloRequest, 0, 0, 0} -} - -func (*helloRequestMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} diff --git a/vendor/github.com/marten-seemann/qtls/handshake_server.go b/vendor/github.com/marten-seemann/qtls/handshake_server.go deleted file mode 100644 index 2f3b41447..000000000 --- a/vendor/github.com/marten-seemann/qtls/handshake_server.go +++ /dev/null @@ -1,822 +0,0 @@ -// Copyright 2009 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 qtls - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "io" - "sync/atomic" -) - -// serverHandshakeState contains details of a server handshake in progress. -// It's discarded once the handshake has completed. -type serverHandshakeState struct { - c *Conn - clientHello *clientHelloMsg - hello *serverHelloMsg - suite *cipherSuite - ellipticOk bool - ecdsaOk bool - rsaDecryptOk bool - rsaSignOk bool - sessionState *sessionState - finishedHash finishedHash - masterSecret []byte - cert *Certificate -} - -// serverHandshake performs a TLS handshake as a server. -func (c *Conn) serverHandshake() error { - // If this is the first server handshake, we generate a random key to - // encrypt the tickets with. - c.config.serverInitOnce.Do(func() { c.config.serverInit(nil) }) - c.setAlternativeRecordLayer() - - clientHello, err := c.readClientHello() - if err != nil { - return err - } - - if c.vers == VersionTLS13 { - hs := serverHandshakeStateTLS13{ - c: c, - clientHello: clientHello, - } - return hs.handshake() - } - - hs := serverHandshakeState{ - c: c, - clientHello: clientHello, - } - return hs.handshake() -} - -func (hs *serverHandshakeState) handshake() error { - c := hs.c - - if err := hs.processClientHello(); err != nil { - return err - } - - // For an overview of TLS handshaking, see RFC 5246, Section 7.3. - c.buffering = true - if hs.checkForResumption() { - // The client has included a session ticket and so we do an abbreviated handshake. - if err := hs.doResumeHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - // ticketSupported is set in a resumption handshake if the - // ticket from the client was encrypted with an old session - // ticket key and thus a refreshed ticket should be sent. - if hs.hello.ticketSupported { - if err := hs.sendSessionTicket(); err != nil { - return err - } - } - if err := hs.sendFinished(c.serverFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - c.clientFinishedIsFirst = false - if err := hs.readFinished(nil); err != nil { - return err - } - c.didResume = true - } else { - // The client didn't include a session ticket, or it wasn't - // valid so we do a full handshake. - if err := hs.pickCipherSuite(); err != nil { - return err - } - if err := hs.doFullHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.readFinished(c.clientFinished[:]); err != nil { - return err - } - c.clientFinishedIsFirst = true - c.buffering = true - if err := hs.sendSessionTicket(); err != nil { - return err - } - if err := hs.sendFinished(nil); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - } - - c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random) - atomic.StoreUint32(&c.handshakeStatus, 1) - - return nil -} - -// readClientHello reads a ClientHello message and selects the protocol version. -func (c *Conn) readClientHello() (*clientHelloMsg, error) { - msg, err := c.readHandshake() - if err != nil { - return nil, err - } - clientHello, ok := msg.(*clientHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return nil, unexpectedMessageError(clientHello, msg) - } - - if c.config.GetConfigForClient != nil { - chi := clientHelloInfo(c, clientHello) - if newConfig, err := c.config.GetConfigForClient(chi); err != nil { - c.sendAlert(alertInternalError) - return nil, err - } else if newConfig != nil { - newConfig.serverInitOnce.Do(func() { newConfig.serverInit(c.config) }) - c.config = newConfig - } - } - - clientVersions := clientHello.supportedVersions - if len(clientHello.supportedVersions) == 0 { - clientVersions = supportedVersionsFromMax(clientHello.vers) - } - c.vers, ok = c.config.mutualVersion(false, clientVersions) - if !ok { - c.sendAlert(alertProtocolVersion) - return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions) - } - c.haveVers = true - c.in.version = c.vers - c.out.version = c.vers - - return clientHello, nil -} - -func (hs *serverHandshakeState) processClientHello() error { - c := hs.c - - hs.hello = new(serverHelloMsg) - hs.hello.vers = c.vers - - supportedCurve := false - preferredCurves := c.config.curvePreferences() -Curves: - for _, curve := range hs.clientHello.supportedCurves { - for _, supported := range preferredCurves { - if supported == curve { - supportedCurve = true - break Curves - } - } - } - - supportedPointFormat := false - for _, pointFormat := range hs.clientHello.supportedPoints { - if pointFormat == pointFormatUncompressed { - supportedPointFormat = true - break - } - } - hs.ellipticOk = supportedCurve && supportedPointFormat - - foundCompression := false - // We only support null compression, so check that the client offered it. - for _, compression := range hs.clientHello.compressionMethods { - if compression == compressionNone { - foundCompression = true - break - } - } - - if !foundCompression { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client does not support uncompressed connections") - } - - hs.hello.random = make([]byte, 32) - serverRandom := hs.hello.random - // Downgrade protection canaries. See RFC 8446, Section 4.1.3. - maxVers := c.config.maxSupportedVersion(false) - if maxVers >= VersionTLS12 && c.vers < maxVers { - if c.vers == VersionTLS12 { - copy(serverRandom[24:], downgradeCanaryTLS12) - } else { - copy(serverRandom[24:], downgradeCanaryTLS11) - } - serverRandom = serverRandom[:24] - } - _, err := io.ReadFull(c.config.rand(), serverRandom) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - if len(hs.clientHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: initial handshake had non-empty renegotiation extension") - } - - hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported - hs.hello.compressionMethod = compressionNone - if len(hs.clientHello.serverName) > 0 { - c.serverName = hs.clientHello.serverName - } - - if len(hs.clientHello.alpnProtocols) > 0 { - if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { - hs.hello.alpnProtocol = selectedProto - c.clientProtocol = selectedProto - } - } else { - // Although sending an empty NPN extension is reasonable, Firefox has - // had a bug around this. Best to send nothing at all if - // c.config.NextProtos is empty. See - // https://golang.org/issue/5445. - if hs.clientHello.nextProtoNeg && len(c.config.NextProtos) > 0 { - hs.hello.nextProtoNeg = true - hs.hello.nextProtos = c.config.NextProtos - } - } - - hs.cert, err = c.config.getCertificate(clientHelloInfo(c, hs.clientHello)) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - if hs.clientHello.scts { - hs.hello.scts = hs.cert.SignedCertificateTimestamps - } - - if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok { - switch priv.Public().(type) { - case *ecdsa.PublicKey: - hs.ecdsaOk = true - case *rsa.PublicKey: - hs.rsaSignOk = true - default: - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public()) - } - } - if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok { - switch priv.Public().(type) { - case *rsa.PublicKey: - hs.rsaDecryptOk = true - default: - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public()) - } - } - - return nil -} - -func (hs *serverHandshakeState) pickCipherSuite() error { - c := hs.c - - var preferenceList, supportedList []uint16 - if c.config.PreferServerCipherSuites { - preferenceList = c.config.cipherSuites() - supportedList = hs.clientHello.cipherSuites - } else { - preferenceList = hs.clientHello.cipherSuites - supportedList = c.config.cipherSuites() - } - - for _, id := range preferenceList { - if hs.setCipherSuite(id, supportedList, c.vers) { - break - } - } - - if hs.suite == nil { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no cipher suite supported by both client and server") - } - - for _, id := range hs.clientHello.cipherSuites { - if id == TLS_FALLBACK_SCSV { - // The client is doing a fallback connection. See RFC 7507. - if hs.clientHello.vers < c.config.maxSupportedVersion(false) { - c.sendAlert(alertInappropriateFallback) - return errors.New("tls: client using inappropriate protocol fallback") - } - break - } - } - - return nil -} - -// checkForResumption reports whether we should perform resumption on this connection. -func (hs *serverHandshakeState) checkForResumption() bool { - c := hs.c - - if c.config.SessionTicketsDisabled { - return false - } - - plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket) - if plaintext == nil { - return false - } - hs.sessionState = &sessionState{usedOldKey: usedOldKey} - ok := hs.sessionState.unmarshal(plaintext) - if !ok { - return false - } - - // Never resume a session for a different TLS version. - if c.vers != hs.sessionState.vers { - return false - } - - cipherSuiteOk := false - // Check that the client is still offering the ciphersuite in the session. - for _, id := range hs.clientHello.cipherSuites { - if id == hs.sessionState.cipherSuite { - cipherSuiteOk = true - break - } - } - if !cipherSuiteOk { - return false - } - - // Check that we also support the ciphersuite from the session. - if !hs.setCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers) { - return false - } - - sessionHasClientCerts := len(hs.sessionState.certificates) != 0 - needClientCerts := requiresClientCert(c.config.ClientAuth) - if needClientCerts && !sessionHasClientCerts { - return false - } - if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { - return false - } - - return true -} - -func (hs *serverHandshakeState) doResumeHandshake() error { - c := hs.c - - hs.hello.cipherSuite = hs.suite.id - // We echo the client's session ID in the ServerHello to let it know - // that we're doing a resumption. - hs.hello.sessionId = hs.clientHello.sessionId - hs.hello.ticketSupported = hs.sessionState.usedOldKey - hs.finishedHash = newFinishedHash(c.vers, hs.suite) - hs.finishedHash.discardHandshakeBuffer() - hs.finishedHash.Write(hs.clientHello.marshal()) - hs.finishedHash.Write(hs.hello.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { - return err - } - - if err := c.processCertsFromClient(Certificate{ - Certificate: hs.sessionState.certificates, - }); err != nil { - return err - } - - hs.masterSecret = hs.sessionState.masterSecret - - return nil -} - -func (hs *serverHandshakeState) doFullHandshake() error { - c := hs.c - - if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { - hs.hello.ocspStapling = true - } - - hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled - hs.hello.cipherSuite = hs.suite.id - - hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) - if c.config.ClientAuth == NoClientCert { - // No need to keep a full record of the handshake if client - // certificates won't be used. - hs.finishedHash.discardHandshakeBuffer() - } - hs.finishedHash.Write(hs.clientHello.marshal()) - hs.finishedHash.Write(hs.hello.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { - return err - } - - certMsg := new(certificateMsg) - certMsg.certificates = hs.cert.Certificate - hs.finishedHash.Write(certMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { - return err - } - - if hs.hello.ocspStapling { - certStatus := new(certificateStatusMsg) - certStatus.response = hs.cert.OCSPStaple - hs.finishedHash.Write(certStatus.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil { - return err - } - } - - keyAgreement := hs.suite.ka(c.vers) - skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello) - if err != nil { - c.sendAlert(alertHandshakeFailure) - return err - } - if skx != nil { - hs.finishedHash.Write(skx.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil { - return err - } - } - - if c.config.ClientAuth >= RequestClientCert { - // Request a client certificate - certReq := new(certificateRequestMsg) - certReq.certificateTypes = []byte{ - byte(certTypeRSASign), - byte(certTypeECDSASign), - } - if c.vers >= VersionTLS12 { - certReq.hasSignatureAlgorithm = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithmsTLS12 - } - - // An empty list of certificateAuthorities signals to - // the client that it may send any certificate in response - // to our request. When we know the CAs we trust, then - // we can send them down, so that the client can choose - // an appropriate certificate to give to us. - if c.config.ClientCAs != nil { - certReq.certificateAuthorities = c.config.ClientCAs.Subjects() - } - hs.finishedHash.Write(certReq.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { - return err - } - } - - helloDone := new(serverHelloDoneMsg) - hs.finishedHash.Write(helloDone.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil { - return err - } - - if _, err := c.flush(); err != nil { - return err - } - - var pub crypto.PublicKey // public key for client auth, if any - - msg, err := c.readHandshake() - if err != nil { - return err - } - - // If we requested a client certificate, then the client must send a - // certificate message, even if it's empty. - if c.config.ClientAuth >= RequestClientCert { - certMsg, ok := msg.(*certificateMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - hs.finishedHash.Write(certMsg.marshal()) - - if err := c.processCertsFromClient(Certificate{ - Certificate: certMsg.certificates, - }); err != nil { - return err - } - if len(certMsg.certificates) != 0 { - pub = c.peerCertificates[0].PublicKey - } - - msg, err = c.readHandshake() - if err != nil { - return err - } - } - - // Get client key exchange - ckx, ok := msg.(*clientKeyExchangeMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(ckx, msg) - } - hs.finishedHash.Write(ckx.marshal()) - - preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers) - if err != nil { - c.sendAlert(alertHandshakeFailure) - return err - } - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) - if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil { - c.sendAlert(alertInternalError) - return err - } - - // If we received a client cert in response to our certificate request message, - // the client will send us a certificateVerifyMsg immediately after the - // clientKeyExchangeMsg. This message is a digest of all preceding - // handshake-layer messages that is signed using the private key corresponding - // to the client's certificate. This allows us to verify that the client is in - // possession of the private key of the certificate. - if len(c.peerCertificates) > 0 { - msg, err = c.readHandshake() - if err != nil { - return err - } - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - // Determine the signature type. - _, sigType, hashFunc, err := pickSignatureAlgorithm(pub, []SignatureScheme{certVerify.signatureAlgorithm}, supportedSignatureAlgorithmsTLS12, c.vers) - if err != nil { - c.sendAlert(alertIllegalParameter) - return err - } - - var digest []byte - if digest, err = hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret); err == nil { - err = verifyHandshakeSignature(sigType, pub, hashFunc, digest, certVerify.signature) - } - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: could not validate signature of connection nonces: " + err.Error()) - } - - hs.finishedHash.Write(certVerify.marshal()) - } - - hs.finishedHash.discardHandshakeBuffer() - - return nil -} - -func (hs *serverHandshakeState) establishKeys() error { - c := hs.c - - clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - - var clientCipher, serverCipher interface{} - var clientHash, serverHash macFunction - - if hs.suite.aead == nil { - clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) - clientHash = hs.suite.mac(c.vers, clientMAC) - serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) - serverHash = hs.suite.mac(c.vers, serverMAC) - } else { - clientCipher = hs.suite.aead(clientKey, clientIV) - serverCipher = hs.suite.aead(serverKey, serverIV) - } - - c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) - c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) - - return nil -} - -func (hs *serverHandshakeState) readFinished(out []byte) error { - c := hs.c - - if err := c.readChangeCipherSpec(); err != nil { - return err - } - - if hs.hello.nextProtoNeg { - msg, err := c.readHandshake() - if err != nil { - return err - } - nextProto, ok := msg.(*nextProtoMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(nextProto, msg) - } - hs.finishedHash.Write(nextProto.marshal()) - c.clientProtocol = nextProto.proto - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - clientFinished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(clientFinished, msg) - } - - verify := hs.finishedHash.clientSum(hs.masterSecret) - if len(verify) != len(clientFinished.verifyData) || - subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client's Finished message is incorrect") - } - - hs.finishedHash.Write(clientFinished.marshal()) - copy(out, verify) - return nil -} - -func (hs *serverHandshakeState) sendSessionTicket() error { - if !hs.hello.ticketSupported { - return nil - } - - c := hs.c - m := new(newSessionTicketMsg) - - var certsFromClient [][]byte - for _, cert := range c.peerCertificates { - certsFromClient = append(certsFromClient, cert.Raw) - } - state := sessionState{ - vers: c.vers, - cipherSuite: hs.suite.id, - masterSecret: hs.masterSecret, - certificates: certsFromClient, - } - var err error - m.ticket, err = c.encryptTicket(state.marshal()) - if err != nil { - return err - } - - hs.finishedHash.Write(m.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeState) sendFinished(out []byte) error { - c := hs.c - - if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { - return err - } - - finished := new(finishedMsg) - finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) - hs.finishedHash.Write(finished.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { - return err - } - - c.cipherSuite = hs.suite.id - copy(out, finished.verifyData) - - return nil -} - -// processCertsFromClient takes a chain of client certificates either from a -// Certificates message or from a sessionState and verifies them. It returns -// the public key of the leaf certificate. -func (c *Conn) processCertsFromClient(certificate Certificate) error { - certificates := certificate.Certificate - certs := make([]*x509.Certificate, len(certificates)) - var err error - for i, asn1Data := range certificates { - if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse client certificate: " + err.Error()) - } - } - - if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) { - c.sendAlert(alertBadCertificate) - return errors.New("tls: client didn't provide a certificate") - } - - if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { - opts := x509.VerifyOptions{ - Roots: c.config.ClientCAs, - CurrentTime: c.config.time(), - Intermediates: x509.NewCertPool(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - - for _, cert := range certs[1:] { - opts.Intermediates.AddCert(cert) - } - - chains, err := certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to verify client's certificate: " + err.Error()) - } - - c.verifiedChains = chains - } - - if c.config.VerifyPeerCertificate != nil { - if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - if len(certs) == 0 { - return nil - } - - switch certs[0].PublicKey.(type) { - case *ecdsa.PublicKey, *rsa.PublicKey: - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey) - } - - c.peerCertificates = certs - c.ocspResponse = certificate.OCSPStaple - c.scts = certificate.SignedCertificateTimestamps - return nil -} - -// setCipherSuite sets a cipherSuite with the given id as the serverHandshakeState -// suite if that cipher suite is acceptable to use. -// It returns a bool indicating if the suite was set. -func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16) bool { - for _, supported := range supportedCipherSuites { - if id == supported { - candidate := cipherSuiteByID(id) - if candidate == nil { - continue - } - // Don't select a ciphersuite which we can't - // support for this client. - if candidate.flags&suiteECDHE != 0 { - if !hs.ellipticOk { - continue - } - if candidate.flags&suiteECDSA != 0 { - if !hs.ecdsaOk { - continue - } - } else if !hs.rsaSignOk { - continue - } - } else if !hs.rsaDecryptOk { - continue - } - if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { - continue - } - hs.suite = candidate - return true - } - } - return false -} - -func clientHelloInfo(c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo { - supportedVersions := clientHello.supportedVersions - if len(clientHello.supportedVersions) == 0 { - supportedVersions = supportedVersionsFromMax(clientHello.vers) - } - - return &ClientHelloInfo{ - CipherSuites: clientHello.cipherSuites, - ServerName: clientHello.serverName, - SupportedCurves: clientHello.supportedCurves, - SupportedPoints: clientHello.supportedPoints, - SignatureSchemes: clientHello.supportedSignatureAlgorithms, - SupportedProtos: clientHello.alpnProtocols, - SupportedVersions: supportedVersions, - Conn: c.conn, - } -} diff --git a/vendor/github.com/marten-seemann/qtls/handshake_server_tls13.go b/vendor/github.com/marten-seemann/qtls/handshake_server_tls13.go deleted file mode 100644 index 97f15091f..000000000 --- a/vendor/github.com/marten-seemann/qtls/handshake_server_tls13.go +++ /dev/null @@ -1,872 +0,0 @@ -// 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 qtls - -import ( - "bytes" - "crypto" - "crypto/hmac" - "crypto/rsa" - "errors" - "hash" - "io" - "sync/atomic" - "time" -) - -// maxClientPSKIdentities is the number of client PSK identities the server will -// attempt to validate. It will ignore the rest not to let cheap ClientHello -// messages cause too much work in session ticket decryption attempts. -const maxClientPSKIdentities = 5 - -type serverHandshakeStateTLS13 struct { - c *Conn - clientHello *clientHelloMsg - hello *serverHelloMsg - sentDummyCCS bool - usingPSK bool - suite *cipherSuiteTLS13 - cert *Certificate - sigAlg SignatureScheme - earlySecret []byte - sharedKey []byte - handshakeSecret []byte - masterSecret []byte - trafficSecret []byte // client_application_traffic_secret_0 - transcript hash.Hash - clientFinished []byte -} - -func (hs *serverHandshakeStateTLS13) handshake() error { - c := hs.c - - // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. - if err := hs.processClientHello(); err != nil { - return err - } - if err := hs.checkForResumption(); err != nil { - return err - } - if err := hs.pickCertificate(); err != nil { - return err - } - c.buffering = true - if err := hs.sendServerParameters(); err != nil { - return err - } - if err := hs.sendServerCertificate(); err != nil { - return err - } - if err := hs.sendServerFinished(); err != nil { - return err - } - // Note that at this point we could start sending application data without - // waiting for the client's second flight, but the application might not - // expect the lack of replay protection of the ClientHello parameters. - if _, err := c.flush(); err != nil { - return err - } - if err := hs.readClientCertificate(); err != nil { - return err - } - if err := hs.readClientFinished(); err != nil { - return err - } - - atomic.StoreUint32(&c.handshakeStatus, 1) - - return nil -} - -func (hs *serverHandshakeStateTLS13) processClientHello() error { - c := hs.c - - hs.hello = new(serverHelloMsg) - - // TLS 1.3 froze the ServerHello.legacy_version field, and uses - // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1. - hs.hello.vers = VersionTLS12 - hs.hello.supportedVersion = c.vers - - if len(hs.clientHello.supportedVersions) == 0 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client used the legacy version field to negotiate TLS 1.3") - } - - // Abort if the client is doing a fallback and landing lower than what we - // support. See RFC 7507, which however does not specify the interaction - // with supported_versions. The only difference is that with - // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4] - // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case, - // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to - // TLS 1.2, because a TLS 1.3 server would abort here. The situation before - // supported_versions was not better because there was just no way to do a - // TLS 1.4 handshake without risking the server selecting TLS 1.3. - for _, id := range hs.clientHello.cipherSuites { - if id == TLS_FALLBACK_SCSV { - // Use c.vers instead of max(supported_versions) because an attacker - // could defeat this by adding an arbitrary high version otherwise. - if c.vers < c.config.maxSupportedVersion(false) { - c.sendAlert(alertInappropriateFallback) - return errors.New("tls: client using inappropriate protocol fallback") - } - break - } - } - - if len(hs.clientHello.compressionMethods) != 1 || - hs.clientHello.compressionMethods[0] != compressionNone { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: TLS 1.3 client supports illegal compression methods") - } - - hs.hello.random = make([]byte, 32) - if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil { - c.sendAlert(alertInternalError) - return err - } - - if len(hs.clientHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: initial handshake had non-empty renegotiation extension") - } - - if hs.clientHello.earlyData { - // See RFC 8446, Section 4.2.10 for the complicated behavior required - // here. The scenario is that a different server at our address offered - // to accept early data in the past, which we can't handle. For now, all - // 0-RTT enabled session tickets need to expire before a Go server can - // replace a server or join a pool. That's the same requirement that - // applies to mixing or replacing with any TLS 1.2 server. - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: client sent unexpected early data") - } - - hs.hello.sessionId = hs.clientHello.sessionId - hs.hello.compressionMethod = compressionNone - - var preferenceList, supportedList []uint16 - if c.config.PreferServerCipherSuites { - preferenceList = defaultCipherSuitesTLS13() - supportedList = hs.clientHello.cipherSuites - } else { - preferenceList = hs.clientHello.cipherSuites - supportedList = defaultCipherSuitesTLS13() - } - for _, suiteID := range preferenceList { - hs.suite = mutualCipherSuiteTLS13(supportedList, suiteID) - if hs.suite != nil { - break - } - } - if hs.suite == nil { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no cipher suite supported by both client and server") - } - c.cipherSuite = hs.suite.id - hs.hello.cipherSuite = hs.suite.id - hs.transcript = hs.suite.hash.New() - - // Pick the ECDHE group in server preference order, but give priority to - // groups with a key share, to avoid a HelloRetryRequest round-trip. - var selectedGroup CurveID - var clientKeyShare *keyShare -GroupSelection: - for _, preferredGroup := range c.config.curvePreferences() { - for _, ks := range hs.clientHello.keyShares { - if ks.group == preferredGroup { - selectedGroup = ks.group - clientKeyShare = &ks - break GroupSelection - } - } - if selectedGroup != 0 { - continue - } - for _, group := range hs.clientHello.supportedCurves { - if group == preferredGroup { - selectedGroup = group - break - } - } - } - if selectedGroup == 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no ECDHE curve supported by both client and server") - } - if clientKeyShare == nil { - if err := hs.doHelloRetryRequest(selectedGroup); err != nil { - return err - } - clientKeyShare = &hs.clientHello.keyShares[0] - } - - if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err := generateECDHEParameters(c.config.rand(), selectedGroup) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} - hs.sharedKey = params.SharedKey(clientKeyShare.data) - if hs.sharedKey == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid client key share") - } - - c.serverName = hs.clientHello.serverName - - if c.config.ReceivedExtensions != nil { - c.config.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions) - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) checkForResumption() error { - c := hs.c - - if c.config.SessionTicketsDisabled { - return nil - } - - modeOK := false - for _, mode := range hs.clientHello.pskModes { - if mode == pskModeDHE { - modeOK = true - break - } - } - if !modeOK { - return nil - } - - if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid or missing PSK binders") - } - if len(hs.clientHello.pskIdentities) == 0 { - return nil - } - - for i, identity := range hs.clientHello.pskIdentities { - if i >= maxClientPSKIdentities { - break - } - - plaintext, _ := c.decryptTicket(identity.label) - if plaintext == nil { - continue - } - sessionState := new(sessionStateTLS13) - if ok := sessionState.unmarshal(plaintext); !ok { - continue - } - - createdAt := time.Unix(int64(sessionState.createdAt), 0) - if c.config.time().Sub(createdAt) > maxSessionTicketLifetime { - continue - } - - // We don't check the obfuscated ticket age because it's affected by - // clock skew and it's only a freshness signal useful for shrinking the - // window for replay attacks, which don't affect us as we don't do 0-RTT. - - pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite) - if pskSuite == nil || pskSuite.hash != hs.suite.hash { - continue - } - - // PSK connections don't re-establish client certificates, but carry - // them over in the session ticket. Ensure the presence of client certs - // in the ticket is consistent with the configured requirements. - sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0 - needClientCerts := requiresClientCert(c.config.ClientAuth) - if needClientCerts && !sessionHasClientCerts { - continue - } - if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { - continue - } - - psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption", - nil, hs.suite.hash.Size()) - hs.earlySecret = hs.suite.extract(psk, nil) - binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil) - // Clone the transcript in case a HelloRetryRequest was recorded. - transcript := cloneHash(hs.transcript, hs.suite.hash) - if transcript == nil { - c.sendAlert(alertInternalError) - return errors.New("tls: internal error: failed to clone hash") - } - transcript.Write(hs.clientHello.marshalWithoutBinders()) - pskBinder := hs.suite.finishedHash(binderKey, transcript) - if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid PSK binder") - } - - if err := c.processCertsFromClient(sessionState.certificate); err != nil { - return err - } - - hs.hello.selectedIdentityPresent = true - hs.hello.selectedIdentity = uint16(i) - hs.usingPSK = true - c.didResume = true - return nil - } - - return nil -} - -// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler -// interfaces implemented by standard library hashes to clone the state of in -// to a new instance of h. It returns nil if the operation fails. -func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash { - // Recreate the interface to avoid importing encoding. - type binaryMarshaler interface { - MarshalBinary() (data []byte, err error) - UnmarshalBinary(data []byte) error - } - marshaler, ok := in.(binaryMarshaler) - if !ok { - return nil - } - state, err := marshaler.MarshalBinary() - if err != nil { - return nil - } - out := h.New() - unmarshaler, ok := out.(binaryMarshaler) - if !ok { - return nil - } - if err := unmarshaler.UnmarshalBinary(state); err != nil { - return nil - } - return out -} - -func (hs *serverHandshakeStateTLS13) pickCertificate() error { - c := hs.c - - // Only one of PSK and certificates are used at a time. - if hs.usingPSK { - return nil - } - - // This implements a very simplistic certificate selection strategy for now: - // getCertificate delegates to the application Config.GetCertificate, or - // selects based on the server_name only. If the selected certificate's - // public key does not match the client signature_algorithms, the handshake - // is aborted. No attention is given to signature_algorithms_cert, and it is - // not passed to the application Config.GetCertificate. This will need to - // improve according to RFC 8446, sections 4.4.2.2 and 4.2.3. - certificate, err := c.config.getCertificate(clientHelloInfo(c, hs.clientHello)) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - supportedAlgs := signatureSchemesForCertificate(c.vers, certificate) - if supportedAlgs == nil { - c.sendAlert(alertInternalError) - return unsupportedCertificateError(certificate) - } - // Pick signature scheme in client preference order, as the server - // preference order is not configurable. - for _, preferredAlg := range hs.clientHello.supportedSignatureAlgorithms { - if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { - hs.sigAlg = preferredAlg - break - } - } - if hs.sigAlg == 0 { - // getCertificate returned a certificate incompatible with the - // ClientHello supported signature algorithms. - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client doesn't support selected certificate") - } - hs.cert = certificate - - return nil -} - -// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility -// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. -func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { - if hs.sentDummyCCS { - return nil - } - hs.sentDummyCCS = true - - _, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) - return err -} - -func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { - c := hs.c - - // The first ClientHello gets double-hashed into the transcript upon a - // HelloRetryRequest. See RFC 8446, Section 4.4.1. - hs.transcript.Write(hs.clientHello.marshal()) - chHash := hs.transcript.Sum(nil) - hs.transcript.Reset() - hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - hs.transcript.Write(chHash) - - helloRetryRequest := &serverHelloMsg{ - vers: hs.hello.vers, - random: helloRetryRequestRandom, - sessionId: hs.hello.sessionId, - cipherSuite: hs.hello.cipherSuite, - compressionMethod: hs.hello.compressionMethod, - supportedVersion: hs.hello.supportedVersion, - selectedGroup: selectedGroup, - } - - hs.transcript.Write(helloRetryRequest.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil { - return err - } - - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - - msg, err := c.readHandshake() - if err != nil { - return err - } - - clientHello, ok := msg.(*clientHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(clientHello, msg) - } - - if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client sent invalid key share in second ClientHello") - } - - if clientHello.earlyData { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client indicated early data in second ClientHello") - } - - if illegalClientHelloChange(clientHello, hs.clientHello) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client illegally modified second ClientHello") - } - - hs.clientHello = clientHello - return nil -} - -// illegalClientHelloChange reports whether the two ClientHello messages are -// different, with the exception of the changes allowed before and after a -// HelloRetryRequest. See RFC 8446, Section 4.1.2. -func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { - if len(ch.supportedVersions) != len(ch1.supportedVersions) || - len(ch.cipherSuites) != len(ch1.cipherSuites) || - len(ch.supportedCurves) != len(ch1.supportedCurves) || - len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) || - len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) || - len(ch.alpnProtocols) != len(ch1.alpnProtocols) { - return true - } - for i := range ch.supportedVersions { - if ch.supportedVersions[i] != ch1.supportedVersions[i] { - return true - } - } - for i := range ch.cipherSuites { - if ch.cipherSuites[i] != ch1.cipherSuites[i] { - return true - } - } - for i := range ch.supportedCurves { - if ch.supportedCurves[i] != ch1.supportedCurves[i] { - return true - } - } - for i := range ch.supportedSignatureAlgorithms { - if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] { - return true - } - } - for i := range ch.supportedSignatureAlgorithmsCert { - if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] { - return true - } - } - for i := range ch.alpnProtocols { - if ch.alpnProtocols[i] != ch1.alpnProtocols[i] { - return true - } - } - return ch.vers != ch1.vers || - !bytes.Equal(ch.random, ch1.random) || - !bytes.Equal(ch.sessionId, ch1.sessionId) || - !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) || - ch.nextProtoNeg != ch1.nextProtoNeg || - ch.serverName != ch1.serverName || - ch.ocspStapling != ch1.ocspStapling || - !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) || - ch.ticketSupported != ch1.ticketSupported || - !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) || - ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported || - !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) || - ch.scts != ch1.scts || - !bytes.Equal(ch.cookie, ch1.cookie) || - !bytes.Equal(ch.pskModes, ch1.pskModes) -} - -func (hs *serverHandshakeStateTLS13) sendServerParameters() error { - c := hs.c - - hs.transcript.Write(hs.clientHello.marshal()) - hs.transcript.Write(hs.hello.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil { - return err - } - - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - - earlySecret := hs.earlySecret - if earlySecret == nil { - earlySecret = hs.suite.extract(nil, nil) - } - hs.handshakeSecret = hs.suite.extract(hs.sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) - - clientSecret := hs.suite.deriveSecret(hs.handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(hs.suite, clientSecret) - c.in.setTrafficSecret(hs.suite, clientSecret) - serverSecret := hs.suite.deriveSecret(hs.handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - encryptedExtensions := new(encryptedExtensionsMsg) - - if len(hs.clientHello.alpnProtocols) > 0 { - if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { - encryptedExtensions.alpnProtocol = selectedProto - c.clientProtocol = selectedProto - } - } - if hs.c.config.GetExtensions != nil { - encryptedExtensions.additionalExtensions = hs.c.config.GetExtensions(typeEncryptedExtensions) - } - - hs.transcript.Write(encryptedExtensions.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) requestClientCert() bool { - return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK -} - -func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { - c := hs.c - - // Only one of PSK and certificates are used at a time. - if hs.usingPSK { - return nil - } - - if hs.requestClientCert() { - // Request a client certificate - certReq := new(certificateRequestMsgTLS13) - certReq.ocspStapling = true - certReq.scts = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms - if c.config.ClientCAs != nil { - certReq.certificateAuthorities = c.config.ClientCAs.Subjects() - } - - hs.transcript.Write(certReq.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { - return err - } - } - - certMsg := new(certificateMsgTLS13) - - certMsg.certificate = *hs.cert - certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0 - certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 - - hs.transcript.Write(certMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { - return err - } - - certVerifyMsg := new(certificateVerifyMsg) - certVerifyMsg.hasSignatureAlgorithm = true - certVerifyMsg.signatureAlgorithm = hs.sigAlg - - sigType := signatureFromSignatureScheme(hs.sigAlg) - sigHash, err := hashFromSignatureScheme(hs.sigAlg) - if sigType == 0 || err != nil { - return c.sendAlert(alertInternalError) - } - h := sigHash.New() - writeSignedMessage(h, serverSignatureContext, hs.transcript) - - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts) - if err != nil { - public := hs.cert.PrivateKey.(crypto.Signer).Public() - if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS && - rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS - c.sendAlert(alertHandshakeFailure) - } else { - c.sendAlert(alertInternalError) - } - return errors.New("tls: failed to sign handshake: " + err.Error()) - } - certVerifyMsg.signature = sig - - hs.transcript.Write(certVerifyMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) sendServerFinished() error { - c := hs.c - - finished := &finishedMsg{ - verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), - } - - hs.transcript.Write(finished.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil { - return err - } - - // Derive secrets that take context through the server Finished. - - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil)) - - hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, - clientApplicationTrafficLabel, hs.transcript) - serverSecret := hs.suite.deriveSecret(hs.masterSecret, - serverApplicationTrafficLabel, hs.transcript) - c.out.exportKey(hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) - - // If we did not request client certificates, at this point we can - // precompute the client finished and roll the transcript forward to send - // session tickets in our first flight. - if !hs.requestClientCert() { - if err := hs.sendSessionTickets(); err != nil { - return err - } - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { - if hs.c.config.SessionTicketsDisabled { - return false - } - - // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9. - for _, pskMode := range hs.clientHello.pskModes { - if pskMode == pskModeDHE { - return true - } - } - return false -} - -func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { - c := hs.c - - hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) - finishedMsg := &finishedMsg{ - verifyData: hs.clientFinished, - } - hs.transcript.Write(finishedMsg.marshal()) - - if !hs.shouldSendSessionTickets() { - return nil - } - - resumptionSecret := hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) - - m := new(newSessionTicketMsgTLS13) - - var certsFromClient [][]byte - for _, cert := range c.peerCertificates { - certsFromClient = append(certsFromClient, cert.Raw) - } - state := sessionStateTLS13{ - cipherSuite: hs.suite.id, - createdAt: uint64(c.config.time().Unix()), - resumptionSecret: resumptionSecret, - certificate: Certificate{ - Certificate: certsFromClient, - OCSPStaple: c.ocspResponse, - SignedCertificateTimestamps: c.scts, - }, - } - var err error - m.label, err = c.encryptTicket(state.marshal()) - if err != nil { - return err - } - m.lifetime = uint32(maxSessionTicketLifetime / time.Second) - - if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) readClientCertificate() error { - c := hs.c - - if !hs.requestClientCert() { - return nil - } - - // If we requested a client certificate, then the client must send a - // certificate message. If it's empty, no CertificateVerify is sent. - - msg, err := c.readHandshake() - if err != nil { - return err - } - - certMsg, ok := msg.(*certificateMsgTLS13) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - hs.transcript.Write(certMsg.marshal()) - - if err := c.processCertsFromClient(certMsg.certificate); err != nil { - return err - } - - if len(certMsg.certificate.Certificate) != 0 { - msg, err = c.readHandshake() - if err != nil { - return err - } - - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid certificate signature algorithm") - } - sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm) - sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm) - if sigType == 0 || err != nil { - c.sendAlert(alertInternalError) - return err - } - if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid certificate signature algorithm") - } - h := sigHash.New() - writeSignedMessage(h, clientSignatureContext, hs.transcript) - if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, - sigHash, h.Sum(nil), certVerify.signature); err != nil { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid certificate signature") - } - - hs.transcript.Write(certVerify.marshal()) - } - - // If we waited until the client certificates to send session tickets, we - // are ready to do it now. - if err := hs.sendSessionTickets(); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) readClientFinished() error { - c := hs.c - - msg, err := c.readHandshake() - if err != nil { - return err - } - - finished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(finished, msg) - } - - if !hmac.Equal(hs.clientFinished, finished.verifyData) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid client finished hash") - } - - c.in.exportKey(hs.suite, hs.trafficSecret) - c.in.setTrafficSecret(hs.suite, hs.trafficSecret) - - return nil -} diff --git a/vendor/github.com/marten-seemann/qtls/key_agreement.go b/vendor/github.com/marten-seemann/qtls/key_agreement.go deleted file mode 100644 index 477aae4a4..000000000 --- a/vendor/github.com/marten-seemann/qtls/key_agreement.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2010 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 qtls - -import ( - "crypto" - "crypto/md5" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "errors" - "io" -) - -var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") -var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message") - -// rsaKeyAgreement implements the standard TLS key agreement where the client -// encrypts the pre-master secret to the server's public key. -type rsaKeyAgreement struct{} - -func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { - return nil, nil -} - -func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { - if len(ckx.ciphertext) < 2 { - return nil, errClientKeyExchange - } - - ciphertext := ckx.ciphertext - if version != VersionSSL30 { - ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) - if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, errClientKeyExchange - } - ciphertext = ckx.ciphertext[2:] - } - priv, ok := cert.PrivateKey.(crypto.Decrypter) - if !ok { - return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter") - } - // Perform constant time RSA PKCS#1 v1.5 decryption - preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48}) - if err != nil { - return nil, err - } - // We don't check the version number in the premaster secret. For one, - // by checking it, we would leak information about the validity of the - // encrypted pre-master secret. Secondly, it provides only a small - // benefit against a downgrade attack and some implementations send the - // wrong version anyway. See the discussion at the end of section - // 7.4.7.1 of RFC 4346. - return preMasterSecret, nil -} - -func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { - return errors.New("tls: unexpected ServerKeyExchange") -} - -func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { - preMasterSecret := make([]byte, 48) - preMasterSecret[0] = byte(clientHello.vers >> 8) - preMasterSecret[1] = byte(clientHello.vers) - _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) - if err != nil { - return nil, nil, err - } - - encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret) - if err != nil { - return nil, nil, err - } - ckx := new(clientKeyExchangeMsg) - ckx.ciphertext = make([]byte, len(encrypted)+2) - ckx.ciphertext[0] = byte(len(encrypted) >> 8) - ckx.ciphertext[1] = byte(len(encrypted)) - copy(ckx.ciphertext[2:], encrypted) - return preMasterSecret, ckx, nil -} - -// sha1Hash calculates a SHA1 hash over the given byte slices. -func sha1Hash(slices [][]byte) []byte { - hsha1 := sha1.New() - for _, slice := range slices { - hsha1.Write(slice) - } - return hsha1.Sum(nil) -} - -// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the -// concatenation of an MD5 and SHA1 hash. -func md5SHA1Hash(slices [][]byte) []byte { - md5sha1 := make([]byte, md5.Size+sha1.Size) - hmd5 := md5.New() - for _, slice := range slices { - hmd5.Write(slice) - } - copy(md5sha1, hmd5.Sum(nil)) - copy(md5sha1[md5.Size:], sha1Hash(slices)) - return md5sha1 -} - -// hashForServerKeyExchange hashes the given slices and returns their digest -// using the given hash function (for >= TLS 1.2) or using a default based on -// the sigType (for earlier TLS versions). -func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) ([]byte, error) { - if version >= VersionTLS12 { - h := hashFunc.New() - for _, slice := range slices { - h.Write(slice) - } - digest := h.Sum(nil) - return digest, nil - } - if sigType == signatureECDSA { - return sha1Hash(slices), nil - } - return md5SHA1Hash(slices), nil -} - -// ecdheKeyAgreement implements a TLS key agreement where the server -// generates an ephemeral EC public/private key pair and signs it. The -// pre-master secret is then calculated using ECDH. The signature may -// either be ECDSA or RSA. -type ecdheKeyAgreement struct { - version uint16 - isRSA bool - params ecdheParameters - - // ckx and preMasterSecret are generated in processServerKeyExchange - // and returned in generateClientKeyExchange. - ckx *clientKeyExchangeMsg - preMasterSecret []byte -} - -func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { - preferredCurves := config.curvePreferences() - - var curveID CurveID -NextCandidate: - for _, candidate := range preferredCurves { - for _, c := range clientHello.supportedCurves { - if candidate == c { - curveID = c - break NextCandidate - } - } - } - - if curveID == 0 { - return nil, errors.New("tls: no supported elliptic curves offered") - } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, errors.New("tls: CurvePreferences includes unsupported curve") - } - - params, err := generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, err - } - ka.params = params - - // See RFC 4492, Section 5.4. - ecdhePublic := params.PublicKey() - serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) - serverECDHParams[0] = 3 // named curve - serverECDHParams[1] = byte(curveID >> 8) - serverECDHParams[2] = byte(curveID) - serverECDHParams[3] = byte(len(ecdhePublic)) - copy(serverECDHParams[4:], ecdhePublic) - - priv, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil, errors.New("tls: certificate private key does not implement crypto.Signer") - } - - signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(priv.Public(), clientHello.supportedSignatureAlgorithms, supportedSignatureAlgorithmsTLS12, ka.version) - if err != nil { - return nil, err - } - if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { - return nil, errors.New("tls: certificate cannot be used with the selected cipher suite") - } - - digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, hello.random, serverECDHParams) - if err != nil { - return nil, err - } - - signOpts := crypto.SignerOpts(hashFunc) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc} - } - sig, err := priv.Sign(config.rand(), digest, signOpts) - if err != nil { - return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error()) - } - - skx := new(serverKeyExchangeMsg) - sigAndHashLen := 0 - if ka.version >= VersionTLS12 { - sigAndHashLen = 2 - } - skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig)) - copy(skx.key, serverECDHParams) - k := skx.key[len(serverECDHParams):] - if ka.version >= VersionTLS12 { - k[0] = byte(signatureAlgorithm >> 8) - k[1] = byte(signatureAlgorithm) - k = k[2:] - } - k[0] = byte(len(sig) >> 8) - k[1] = byte(len(sig)) - copy(k[2:], sig) - - return skx, nil -} - -func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { - if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, errClientKeyExchange - } - - preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:]) - if preMasterSecret == nil { - return nil, errClientKeyExchange - } - - return preMasterSecret, nil -} - -func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { - if len(skx.key) < 4 { - return errServerKeyExchange - } - if skx.key[0] != 3 { // named curve - return errors.New("tls: server selected unsupported curve") - } - curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2]) - - publicLen := int(skx.key[3]) - if publicLen+4 > len(skx.key) { - return errServerKeyExchange - } - serverECDHParams := skx.key[:4+publicLen] - publicKey := serverECDHParams[4:] - - sig := skx.key[4+publicLen:] - if len(sig) < 2 { - return errServerKeyExchange - } - - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return errors.New("tls: server selected unsupported curve") - } - - params, err := generateECDHEParameters(config.rand(), curveID) - if err != nil { - return err - } - ka.params = params - - ka.preMasterSecret = params.SharedKey(publicKey) - if ka.preMasterSecret == nil { - return errServerKeyExchange - } - - ourPublicKey := params.PublicKey() - ka.ckx = new(clientKeyExchangeMsg) - ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey)) - ka.ckx.ciphertext[0] = byte(len(ourPublicKey)) - copy(ka.ckx.ciphertext[1:], ourPublicKey) - - var signatureAlgorithm SignatureScheme - if ka.version >= VersionTLS12 { - // handle SignatureAndHashAlgorithm - signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1]) - sig = sig[2:] - if len(sig) < 2 { - return errServerKeyExchange - } - } - _, sigType, hashFunc, err := pickSignatureAlgorithm(cert.PublicKey, []SignatureScheme{signatureAlgorithm}, clientHello.supportedSignatureAlgorithms, ka.version) - if err != nil { - return err - } - if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { - return errServerKeyExchange - } - - sigLen := int(sig[0])<<8 | int(sig[1]) - if sigLen+2 != len(sig) { - return errServerKeyExchange - } - sig = sig[2:] - - digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, serverHello.random, serverECDHParams) - if err != nil { - return err - } - return verifyHandshakeSignature(sigType, cert.PublicKey, hashFunc, digest, sig) -} - -func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { - if ka.ckx == nil { - return nil, nil, errors.New("tls: missing ServerKeyExchange message") - } - - return ka.preMasterSecret, ka.ckx, nil -} diff --git a/vendor/github.com/marten-seemann/qtls/key_schedule.go b/vendor/github.com/marten-seemann/qtls/key_schedule.go deleted file mode 100644 index 24bae2e68..000000000 --- a/vendor/github.com/marten-seemann/qtls/key_schedule.go +++ /dev/null @@ -1,216 +0,0 @@ -// 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 qtls - -import ( - "crypto" - "crypto/elliptic" - "crypto/hmac" - "errors" - "hash" - "io" - "math/big" - - "golang.org/x/crypto/cryptobyte" - "golang.org/x/crypto/curve25519" - "golang.org/x/crypto/hkdf" -) - -// This file contains the functions necessary to compute the TLS 1.3 key -// schedule. See RFC 8446, Section 7. - -const ( - resumptionBinderLabel = "res binder" - clientHandshakeTrafficLabel = "c hs traffic" - serverHandshakeTrafficLabel = "s hs traffic" - clientApplicationTrafficLabel = "c ap traffic" - serverApplicationTrafficLabel = "s ap traffic" - exporterLabel = "exp master" - resumptionLabel = "res master" - trafficUpdateLabel = "traffic upd" -) - -// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt. -func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte { - if newSecret == nil { - newSecret = make([]byte, hash.Size()) - } - return hkdf.Extract(hash.New, newSecret, currentSecret) -} - -// HkdfExpandLabel HKDF expands a label -func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte { - return hkdfExpandLabel(hash, secret, hashValue, label, L) -} - -func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte { - var hkdfLabel cryptobyte.Builder - hkdfLabel.AddUint16(uint16(length)) - hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte("tls13 ")) - b.AddBytes([]byte(label)) - }) - hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(context) - }) - out := make([]byte, length) - n, err := hkdf.Expand(hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out) - if err != nil || n != length { - panic("tls: HKDF-Expand-Label invocation failed unexpectedly") - } - return out -} - -// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. -func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte { - return hkdfExpandLabel(c.hash, secret, context, label, length) -} - -// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1. -func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte { - if transcript == nil { - transcript = c.hash.New() - } - return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size()) -} - -// extract implements HKDF-Extract with the cipher suite hash. -func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte { - return HkdfExtract(c.hash, newSecret, currentSecret) -} - -// nextTrafficSecret generates the next traffic secret, given the current one, -// according to RFC 8446, Section 7.2. -func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte { - return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size()) -} - -// trafficKey generates traffic keys according to RFC 8446, Section 7.3. -func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { - key = c.expandLabel(trafficSecret, "key", nil, c.keyLen) - iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength) - return -} - -// finishedHash generates the Finished verify_data or PskBinderEntry according -// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey -// selection. -func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte { - finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size()) - verifyData := hmac.New(c.hash.New, finishedKey) - verifyData.Write(transcript.Sum(nil)) - return verifyData.Sum(nil) -} - -// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to -// RFC 8446, Section 7.5. -func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) { - expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript) - return func(label string, context []byte, length int) ([]byte, error) { - secret := c.deriveSecret(expMasterSecret, label, nil) - h := c.hash.New() - h.Write(context) - return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil - } -} - -// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519, -// according to RFC 8446, Section 4.2.8.2. -type ecdheParameters interface { - CurveID() CurveID - PublicKey() []byte - SharedKey(peerPublicKey []byte) []byte -} - -func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) { - if curveID == X25519 { - p := &x25519Parameters{} - if _, err := io.ReadFull(rand, p.privateKey[:]); err != nil { - return nil, err - } - curve25519.ScalarBaseMult(&p.publicKey, &p.privateKey) - return p, nil - } - - curve, ok := curveForCurveID(curveID) - if !ok { - return nil, errors.New("tls: internal error: unsupported curve") - } - - p := &nistParameters{curveID: curveID} - var err error - p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand) - if err != nil { - return nil, err - } - return p, nil -} - -func curveForCurveID(id CurveID) (elliptic.Curve, bool) { - switch id { - case CurveP256: - return elliptic.P256(), true - case CurveP384: - return elliptic.P384(), true - case CurveP521: - return elliptic.P521(), true - default: - return nil, false - } -} - -type nistParameters struct { - privateKey []byte - x, y *big.Int // public key - curveID CurveID -} - -func (p *nistParameters) CurveID() CurveID { - return p.curveID -} - -func (p *nistParameters) PublicKey() []byte { - curve, _ := curveForCurveID(p.curveID) - return elliptic.Marshal(curve, p.x, p.y) -} - -func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte { - curve, _ := curveForCurveID(p.curveID) - // Unmarshal also checks whether the given point is on the curve. - x, y := elliptic.Unmarshal(curve, peerPublicKey) - if x == nil { - return nil - } - - xShared, _ := curve.ScalarMult(x, y, p.privateKey) - sharedKey := make([]byte, (curve.Params().BitSize+7)>>3) - xBytes := xShared.Bytes() - copy(sharedKey[len(sharedKey)-len(xBytes):], xBytes) - - return sharedKey -} - -type x25519Parameters struct { - privateKey [32]byte - publicKey [32]byte -} - -func (p *x25519Parameters) CurveID() CurveID { - return X25519 -} - -func (p *x25519Parameters) PublicKey() []byte { - return p.publicKey[:] -} - -func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte { - if len(peerPublicKey) != 32 { - return nil - } - var theirPublicKey, sharedKey [32]byte - copy(theirPublicKey[:], peerPublicKey) - curve25519.ScalarMult(&sharedKey, &p.privateKey, &theirPublicKey) - return sharedKey[:] -} diff --git a/vendor/github.com/marten-seemann/qtls/prf.go b/vendor/github.com/marten-seemann/qtls/prf.go deleted file mode 100644 index a973d6ad4..000000000 --- a/vendor/github.com/marten-seemann/qtls/prf.go +++ /dev/null @@ -1,385 +0,0 @@ -// Copyright 2009 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 qtls - -import ( - "crypto" - "crypto/hmac" - "crypto/md5" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "errors" - "fmt" - "hash" -) - -// Split a premaster secret in two as specified in RFC 4346, Section 5. -func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { - s1 = secret[0 : (len(secret)+1)/2] - s2 = secret[len(secret)/2:] - return -} - -// pHash implements the P_hash function, as defined in RFC 4346, Section 5. -func pHash(result, secret, seed []byte, hash func() hash.Hash) { - h := hmac.New(hash, secret) - h.Write(seed) - a := h.Sum(nil) - - j := 0 - for j < len(result) { - h.Reset() - h.Write(a) - h.Write(seed) - b := h.Sum(nil) - copy(result[j:], b) - j += len(b) - - h.Reset() - h.Write(a) - a = h.Sum(nil) - } -} - -// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. -func prf10(result, secret, label, seed []byte) { - hashSHA1 := sha1.New - hashMD5 := md5.New - - labelAndSeed := make([]byte, len(label)+len(seed)) - copy(labelAndSeed, label) - copy(labelAndSeed[len(label):], seed) - - s1, s2 := splitPreMasterSecret(secret) - pHash(result, s1, labelAndSeed, hashMD5) - result2 := make([]byte, len(result)) - pHash(result2, s2, labelAndSeed, hashSHA1) - - for i, b := range result2 { - result[i] ^= b - } -} - -// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. -func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) { - return func(result, secret, label, seed []byte) { - labelAndSeed := make([]byte, len(label)+len(seed)) - copy(labelAndSeed, label) - copy(labelAndSeed[len(label):], seed) - - pHash(result, secret, labelAndSeed, hashFunc) - } -} - -// prf30 implements the SSL 3.0 pseudo-random function, as defined in -// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. -func prf30(result, secret, label, seed []byte) { - hashSHA1 := sha1.New() - hashMD5 := md5.New() - - done := 0 - i := 0 - // RFC 5246 section 6.3 says that the largest PRF output needed is 128 - // bytes. Since no more ciphersuites will be added to SSLv3, this will - // remain true. Each iteration gives us 16 bytes so 10 iterations will - // be sufficient. - var b [11]byte - for done < len(result) { - for j := 0; j <= i; j++ { - b[j] = 'A' + byte(i) - } - - hashSHA1.Reset() - hashSHA1.Write(b[:i+1]) - hashSHA1.Write(secret) - hashSHA1.Write(seed) - digest := hashSHA1.Sum(nil) - - hashMD5.Reset() - hashMD5.Write(secret) - hashMD5.Write(digest) - - done += copy(result[done:], hashMD5.Sum(nil)) - i++ - } -} - -const ( - masterSecretLength = 48 // Length of a master secret in TLS 1.1. - finishedVerifyLength = 12 // Length of verify_data in a Finished message. -) - -var masterSecretLabel = []byte("master secret") -var keyExpansionLabel = []byte("key expansion") -var clientFinishedLabel = []byte("client finished") -var serverFinishedLabel = []byte("server finished") - -func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { - switch version { - case VersionSSL30: - return prf30, crypto.Hash(0) - case VersionTLS10, VersionTLS11: - return prf10, crypto.Hash(0) - case VersionTLS12: - if suite.flags&suiteSHA384 != 0 { - return prf12(sha512.New384), crypto.SHA384 - } - return prf12(sha256.New), crypto.SHA256 - default: - panic("unknown version") - } -} - -func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { - prf, _ := prfAndHashForVersion(version, suite) - return prf -} - -// masterFromPreMasterSecret generates the master secret from the pre-master -// secret. See RFC 5246, Section 8.1. -func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte { - seed := make([]byte, 0, len(clientRandom)+len(serverRandom)) - seed = append(seed, clientRandom...) - seed = append(seed, serverRandom...) - - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed) - return masterSecret -} - -// keysFromMasterSecret generates the connection keys from the master -// secret, given the lengths of the MAC key, cipher key and IV, as defined in -// RFC 2246, Section 6.3. -func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { - seed := make([]byte, 0, len(serverRandom)+len(clientRandom)) - seed = append(seed, serverRandom...) - seed = append(seed, clientRandom...) - - n := 2*macLen + 2*keyLen + 2*ivLen - keyMaterial := make([]byte, n) - prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed) - clientMAC = keyMaterial[:macLen] - keyMaterial = keyMaterial[macLen:] - serverMAC = keyMaterial[:macLen] - keyMaterial = keyMaterial[macLen:] - clientKey = keyMaterial[:keyLen] - keyMaterial = keyMaterial[keyLen:] - serverKey = keyMaterial[:keyLen] - keyMaterial = keyMaterial[keyLen:] - clientIV = keyMaterial[:ivLen] - keyMaterial = keyMaterial[ivLen:] - serverIV = keyMaterial[:ivLen] - return -} - -// hashFromSignatureScheme returns the corresponding crypto.Hash for a given -// hash from a TLS SignatureScheme. -func hashFromSignatureScheme(signatureAlgorithm SignatureScheme) (crypto.Hash, error) { - switch signatureAlgorithm { - case PKCS1WithSHA1, ECDSAWithSHA1: - return crypto.SHA1, nil - case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256: - return crypto.SHA256, nil - case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384: - return crypto.SHA384, nil - case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512: - return crypto.SHA512, nil - default: - return 0, fmt.Errorf("tls: unsupported signature algorithm: %#04x", signatureAlgorithm) - } -} - -func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { - var buffer []byte - if version == VersionSSL30 || version >= VersionTLS12 { - buffer = []byte{} - } - - prf, hash := prfAndHashForVersion(version, cipherSuite) - if hash != 0 { - return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf} - } - - return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf} -} - -// A finishedHash calculates the hash of a set of handshake messages suitable -// for including in a Finished message. -type finishedHash struct { - client hash.Hash - server hash.Hash - - // Prior to TLS 1.2, an additional MD5 hash is required. - clientMD5 hash.Hash - serverMD5 hash.Hash - - // In TLS 1.2, a full buffer is sadly required. - buffer []byte - - version uint16 - prf func(result, secret, label, seed []byte) -} - -func (h *finishedHash) Write(msg []byte) (n int, err error) { - h.client.Write(msg) - h.server.Write(msg) - - if h.version < VersionTLS12 { - h.clientMD5.Write(msg) - h.serverMD5.Write(msg) - } - - if h.buffer != nil { - h.buffer = append(h.buffer, msg...) - } - - return len(msg), nil -} - -func (h finishedHash) Sum() []byte { - if h.version >= VersionTLS12 { - return h.client.Sum(nil) - } - - out := make([]byte, 0, md5.Size+sha1.Size) - out = h.clientMD5.Sum(out) - return h.client.Sum(out) -} - -// finishedSum30 calculates the contents of the verify_data member of a SSLv3 -// Finished message given the MD5 and SHA1 hashes of a set of handshake -// messages. -func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic []byte) []byte { - md5.Write(magic) - md5.Write(masterSecret) - md5.Write(ssl30Pad1[:]) - md5Digest := md5.Sum(nil) - - md5.Reset() - md5.Write(masterSecret) - md5.Write(ssl30Pad2[:]) - md5.Write(md5Digest) - md5Digest = md5.Sum(nil) - - sha1.Write(magic) - sha1.Write(masterSecret) - sha1.Write(ssl30Pad1[:40]) - sha1Digest := sha1.Sum(nil) - - sha1.Reset() - sha1.Write(masterSecret) - sha1.Write(ssl30Pad2[:40]) - sha1.Write(sha1Digest) - sha1Digest = sha1.Sum(nil) - - ret := make([]byte, len(md5Digest)+len(sha1Digest)) - copy(ret, md5Digest) - copy(ret[len(md5Digest):], sha1Digest) - return ret -} - -var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} -var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} - -// clientSum returns the contents of the verify_data member of a client's -// Finished message. -func (h finishedHash) clientSum(masterSecret []byte) []byte { - if h.version == VersionSSL30 { - return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic[:]) - } - - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) - return out -} - -// serverSum returns the contents of the verify_data member of a server's -// Finished message. -func (h finishedHash) serverSum(masterSecret []byte) []byte { - if h.version == VersionSSL30 { - return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic[:]) - } - - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) - return out -} - -// hashForClientCertificate returns a digest over the handshake messages so far, -// suitable for signing by a TLS client certificate. -func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) ([]byte, error) { - if (h.version == VersionSSL30 || h.version >= VersionTLS12) && h.buffer == nil { - panic("a handshake hash for a client-certificate was requested after discarding the handshake buffer") - } - - if h.version == VersionSSL30 { - if sigType != signaturePKCS1v15 { - return nil, errors.New("tls: unsupported signature type for client certificate") - } - - md5Hash := md5.New() - md5Hash.Write(h.buffer) - sha1Hash := sha1.New() - sha1Hash.Write(h.buffer) - return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), nil - } - if h.version >= VersionTLS12 { - hash := hashAlg.New() - hash.Write(h.buffer) - return hash.Sum(nil), nil - } - - if sigType == signatureECDSA { - return h.server.Sum(nil), nil - } - - return h.Sum(), nil -} - -// discardHandshakeBuffer is called when there is no more need to -// buffer the entirety of the handshake messages. -func (h *finishedHash) discardHandshakeBuffer() { - h.buffer = nil -} - -// noExportedKeyingMaterial is used as a value of -// ConnectionState.ekm when renegotation is enabled and thus -// we wish to fail all key-material export requests. -func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { - return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") -} - -// ekmFromMasterSecret generates exported keying material as defined in RFC 5705. -func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { - return func(label string, context []byte, length int) ([]byte, error) { - switch label { - case "client finished", "server finished", "master secret", "key expansion": - // These values are reserved and may not be used. - return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label) - } - - seedLen := len(serverRandom) + len(clientRandom) - if context != nil { - seedLen += 2 + len(context) - } - seed := make([]byte, 0, seedLen) - - seed = append(seed, clientRandom...) - seed = append(seed, serverRandom...) - - if context != nil { - if len(context) >= 1<<16 { - return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long") - } - seed = append(seed, byte(len(context)>>8), byte(len(context))) - seed = append(seed, context...) - } - - keyMaterial := make([]byte, length) - prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) - return keyMaterial, nil - } -} diff --git a/vendor/github.com/marten-seemann/qtls/ticket.go b/vendor/github.com/marten-seemann/qtls/ticket.go deleted file mode 100644 index 989c9787d..000000000 --- a/vendor/github.com/marten-seemann/qtls/ticket.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2012 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 qtls - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/sha256" - "crypto/subtle" - "errors" - "io" - - "golang.org/x/crypto/cryptobyte" -) - -// sessionState contains the information that is serialized into a session -// ticket in order to later resume a connection. -type sessionState struct { - vers uint16 - cipherSuite uint16 - masterSecret []byte - certificates [][]byte - // usedOldKey is true if the ticket from which this session came from - // was encrypted with an older key and thus should be refreshed. - usedOldKey bool -} - -func (s *sessionState) marshal() []byte { - length := 2 + 2 + 2 + len(s.masterSecret) + 2 - for _, cert := range s.certificates { - length += 4 + len(cert) - } - - ret := make([]byte, length) - x := ret - x[0] = byte(s.vers >> 8) - x[1] = byte(s.vers) - x[2] = byte(s.cipherSuite >> 8) - x[3] = byte(s.cipherSuite) - x[4] = byte(len(s.masterSecret) >> 8) - x[5] = byte(len(s.masterSecret)) - x = x[6:] - copy(x, s.masterSecret) - x = x[len(s.masterSecret):] - - x[0] = byte(len(s.certificates) >> 8) - x[1] = byte(len(s.certificates)) - x = x[2:] - - for _, cert := range s.certificates { - x[0] = byte(len(cert) >> 24) - x[1] = byte(len(cert) >> 16) - x[2] = byte(len(cert) >> 8) - x[3] = byte(len(cert)) - copy(x[4:], cert) - x = x[4+len(cert):] - } - - return ret -} - -func (s *sessionState) unmarshal(data []byte) bool { - if len(data) < 8 { - return false - } - - s.vers = uint16(data[0])<<8 | uint16(data[1]) - s.cipherSuite = uint16(data[2])<<8 | uint16(data[3]) - masterSecretLen := int(data[4])<<8 | int(data[5]) - data = data[6:] - if len(data) < masterSecretLen { - return false - } - - s.masterSecret = data[:masterSecretLen] - data = data[masterSecretLen:] - - if len(data) < 2 { - return false - } - - numCerts := int(data[0])<<8 | int(data[1]) - data = data[2:] - - s.certificates = make([][]byte, numCerts) - for i := range s.certificates { - if len(data) < 4 { - return false - } - certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - data = data[4:] - if certLen < 0 { - return false - } - if len(data) < certLen { - return false - } - s.certificates[i] = data[:certLen] - data = data[certLen:] - } - - return len(data) == 0 -} - -// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first -// version (revision = 0) doesn't carry any of the information needed for 0-RTT -// validation and the nonce is always empty. -type sessionStateTLS13 struct { - // uint8 version = 0x0304; - // uint8 revision = 0; - cipherSuite uint16 - createdAt uint64 - resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>; - certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; -} - -func (m *sessionStateTLS13) marshal() []byte { - var b cryptobyte.Builder - b.AddUint16(VersionTLS13) - b.AddUint8(0) // revision - b.AddUint16(m.cipherSuite) - addUint64(&b, m.createdAt) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.resumptionSecret) - }) - marshalCertificate(&b, m.certificate) - return b.BytesOrPanic() -} - -func (m *sessionStateTLS13) unmarshal(data []byte) bool { - *m = sessionStateTLS13{} - s := cryptobyte.String(data) - var version uint16 - var revision uint8 - return s.ReadUint16(&version) && - version == VersionTLS13 && - s.ReadUint8(&revision) && - revision == 0 && - s.ReadUint16(&m.cipherSuite) && - readUint64(&s, &m.createdAt) && - readUint8LengthPrefixed(&s, &m.resumptionSecret) && - len(m.resumptionSecret) != 0 && - unmarshalCertificate(&s, &m.certificate) && - s.Empty() -} - -func (c *Conn) encryptTicket(state []byte) ([]byte, error) { - encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size) - keyName := encrypted[:ticketKeyNameLen] - iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] - macBytes := encrypted[len(encrypted)-sha256.Size:] - - if _, err := io.ReadFull(c.config.rand(), iv); err != nil { - return nil, err - } - key := c.config.ticketKeys()[0] - copy(keyName, key.keyName[:]) - block, err := aes.NewCipher(key.aesKey[:]) - if err != nil { - return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) - } - cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state) - - mac := hmac.New(sha256.New, key.hmacKey[:]) - mac.Write(encrypted[:len(encrypted)-sha256.Size]) - mac.Sum(macBytes[:0]) - - return encrypted, nil -} - -func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) { - if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size { - return nil, false - } - - keyName := encrypted[:ticketKeyNameLen] - iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] - macBytes := encrypted[len(encrypted)-sha256.Size:] - ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size] - - keys := c.config.ticketKeys() - keyIndex := -1 - for i, candidateKey := range keys { - if bytes.Equal(keyName, candidateKey.keyName[:]) { - keyIndex = i - break - } - } - - if keyIndex == -1 { - return nil, false - } - key := &keys[keyIndex] - - mac := hmac.New(sha256.New, key.hmacKey[:]) - mac.Write(encrypted[:len(encrypted)-sha256.Size]) - expected := mac.Sum(nil) - - if subtle.ConstantTimeCompare(macBytes, expected) != 1 { - return nil, false - } - - block, err := aes.NewCipher(key.aesKey[:]) - if err != nil { - return nil, false - } - plaintext = make([]byte, len(ciphertext)) - cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) - - return plaintext, keyIndex > 0 -} diff --git a/vendor/github.com/marten-seemann/qtls/tls.go b/vendor/github.com/marten-seemann/qtls/tls.go deleted file mode 100644 index 818997d3e..000000000 --- a/vendor/github.com/marten-seemann/qtls/tls.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2009 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 qtls partially implements TLS 1.2, as specified in RFC 5246, -// and TLS 1.3, as specified in RFC 8446. -// -// TLS 1.3 is available only on an opt-in basis in Go 1.12. To enable -// it, set the GODEBUG environment variable (comma-separated key=value -// options) such that it includes "tls13=1". To enable it from within -// the process, set the environment variable before any use of TLS: -// -// func init() { -// os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1") -// } -package qtls - -// BUG(agl): The crypto/tls package only implements some countermeasures -// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 -// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and -// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "net" - "strings" - "time" -) - -// Server returns a new TLS server side connection -// using conn as the underlying transport. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func Server(conn net.Conn, config *Config) *Conn { - return &Conn{conn: conn, config: config} -} - -// Client returns a new TLS client side connection -// using conn as the underlying transport. -// The config cannot be nil: users must set either ServerName or -// InsecureSkipVerify in the config. -func Client(conn net.Conn, config *Config) *Conn { - return &Conn{conn: conn, config: config, isClient: true} -} - -// A listener implements a network listener (net.Listener) for TLS connections. -type listener struct { - net.Listener - config *Config -} - -// Accept waits for and returns the next incoming TLS connection. -// The returned connection is of type *Conn. -func (l *listener) Accept() (net.Conn, error) { - c, err := l.Listener.Accept() - if err != nil { - return nil, err - } - return Server(c, l.config), nil -} - -// NewListener creates a Listener which accepts connections from an inner -// Listener and wraps each connection with Server. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func NewListener(inner net.Listener, config *Config) net.Listener { - l := new(listener) - l.Listener = inner - l.config = config - return l -} - -// Listen creates a TLS listener accepting connections on the -// given network address using net.Listen. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func Listen(network, laddr string, config *Config) (net.Listener, error) { - if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) { - return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config") - } - l, err := net.Listen(network, laddr) - if err != nil { - return nil, err - } - return NewListener(l, config), nil -} - -type timeoutError struct{} - -func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } -func (timeoutError) Timeout() bool { return true } -func (timeoutError) Temporary() bool { return true } - -// DialWithDialer connects to the given network address using dialer.Dial and -// then initiates a TLS handshake, returning the resulting TLS connection. Any -// timeout or deadline given in the dialer apply to connection and TLS -// handshake as a whole. -// -// DialWithDialer interprets a nil configuration as equivalent to the zero -// configuration; see the documentation of Config for the defaults. -func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) { - // We want the Timeout and Deadline values from dialer to cover the - // whole process: TCP connection and TLS handshake. This means that we - // also need to start our own timers now. - timeout := dialer.Timeout - - if !dialer.Deadline.IsZero() { - deadlineTimeout := time.Until(dialer.Deadline) - if timeout == 0 || deadlineTimeout < timeout { - timeout = deadlineTimeout - } - } - - var errChannel chan error - - if timeout != 0 { - errChannel = make(chan error, 2) - time.AfterFunc(timeout, func() { - errChannel <- timeoutError{} - }) - } - - rawConn, err := dialer.Dial(network, addr) - if err != nil { - return nil, err - } - - colonPos := strings.LastIndex(addr, ":") - if colonPos == -1 { - colonPos = len(addr) - } - hostname := addr[:colonPos] - - if config == nil { - config = defaultConfig() - } - // If no ServerName is set, infer the ServerName - // from the hostname we're connecting to. - if config.ServerName == "" { - // Make a copy to avoid polluting argument or default. - c := config.Clone() - c.ServerName = hostname - config = c - } - - conn := Client(rawConn, config) - - if timeout == 0 { - err = conn.Handshake() - } else { - go func() { - errChannel <- conn.Handshake() - }() - - err = <-errChannel - } - - if err != nil { - rawConn.Close() - return nil, err - } - - return conn, nil -} - -// Dial connects to the given network address using net.Dial -// and then initiates a TLS handshake, returning the resulting -// TLS connection. -// Dial interprets a nil configuration as equivalent to -// the zero configuration; see the documentation of Config -// for the defaults. -func Dial(network, addr string, config *Config) (*Conn, error) { - return DialWithDialer(new(net.Dialer), network, addr, config) -} - -// LoadX509KeyPair reads and parses a public/private key pair from a pair -// of files. The files must contain PEM encoded data. The certificate file -// may contain intermediate certificates following the leaf certificate to -// form a certificate chain. On successful return, Certificate.Leaf will -// be nil because the parsed form of the certificate is not retained. -func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { - certPEMBlock, err := ioutil.ReadFile(certFile) - if err != nil { - return Certificate{}, err - } - keyPEMBlock, err := ioutil.ReadFile(keyFile) - if err != nil { - return Certificate{}, err - } - return X509KeyPair(certPEMBlock, keyPEMBlock) -} - -// X509KeyPair parses a public/private key pair from a pair of -// PEM encoded data. On successful return, Certificate.Leaf will be nil because -// the parsed form of the certificate is not retained. -func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { - fail := func(err error) (Certificate, error) { return Certificate{}, err } - - var cert Certificate - var skippedBlockTypes []string - for { - var certDERBlock *pem.Block - certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) - if certDERBlock == nil { - break - } - if certDERBlock.Type == "CERTIFICATE" { - cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) - } else { - skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type) - } - } - - if len(cert.Certificate) == 0 { - if len(skippedBlockTypes) == 0 { - return fail(errors.New("tls: failed to find any PEM data in certificate input")) - } - if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") { - return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched")) - } - return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) - } - - skippedBlockTypes = skippedBlockTypes[:0] - var keyDERBlock *pem.Block - for { - keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) - if keyDERBlock == nil { - if len(skippedBlockTypes) == 0 { - return fail(errors.New("tls: failed to find any PEM data in key input")) - } - if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" { - return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key")) - } - return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) - } - if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { - break - } - skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) - } - - // We don't need to parse the public key for TLS, but we so do anyway - // to check that it looks sane and matches the private key. - x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - return fail(err) - } - - cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) - if err != nil { - return fail(err) - } - - switch pub := x509Cert.PublicKey.(type) { - case *rsa.PublicKey: - priv, ok := cert.PrivateKey.(*rsa.PrivateKey) - if !ok { - return fail(errors.New("tls: private key type does not match public key type")) - } - if pub.N.Cmp(priv.N) != 0 { - return fail(errors.New("tls: private key does not match public key")) - } - case *ecdsa.PublicKey: - priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) - if !ok { - return fail(errors.New("tls: private key type does not match public key type")) - } - if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { - return fail(errors.New("tls: private key does not match public key")) - } - default: - return fail(errors.New("tls: unknown public key algorithm")) - } - - return cert, nil -} - -// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates -// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. -// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. -func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { - if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { - return key, nil - } - if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { - switch key := key.(type) { - case *rsa.PrivateKey, *ecdsa.PrivateKey: - return key, nil - default: - return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") - } - } - if key, err := x509.ParseECPrivateKey(der); err == nil { - return key, nil - } - - return nil, errors.New("tls: failed to parse private key") -} diff --git a/vendor/github.com/mholt/caddy/.gitattributes b/vendor/github.com/mholt/caddy/.gitattributes deleted file mode 100644 index 2e5077856..000000000 --- a/vendor/github.com/mholt/caddy/.gitattributes +++ /dev/null @@ -1,19 +0,0 @@ -# shell scripts should not use tabs to indent! -*.bash text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.sh text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 - -# files for systemd (shell-similar) -*.path text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.service text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.timer text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 - -# go fmt will enforce this, but in case a user has not called "go fmt" allow GIT to catch this: -*.go text eol=lf core.whitespace whitespace=indent-with-non-tab,trailing-space,tabwidth=4 - -*.txt text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.tpl text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.htm text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.html text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.md text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -*.yml text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2 -.git* text eol=auto core.whitespace whitespace=trailing-space diff --git a/vendor/github.com/mholt/caddy/.gitignore b/vendor/github.com/mholt/caddy/.gitignore deleted file mode 100644 index d996add1e..000000000 --- a/vendor/github.com/mholt/caddy/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -.DS_Store -Thumbs.db -_gitignore/ -Vagrantfile -.vagrant/ -/.idea - -dist/builds/ -dist/release/ - -error.log -access.log - -/*.conf -Caddyfile -!caddyfile/ - -og_static/ - -.vscode/ - -*.bat diff --git a/vendor/github.com/mholt/caddy/README.md b/vendor/github.com/mholt/caddy/README.md deleted file mode 100644 index 0acd13657..000000000 --- a/vendor/github.com/mholt/caddy/README.md +++ /dev/null @@ -1,225 +0,0 @@ -

- Caddy -

-

Every Site on HTTPS

-

Caddy is a general-purpose HTTP/2 web server that serves HTTPS by default.

-

- - - -
- @caddyserver on Twitter - Caddy Forum - Caddy on Sourcegraph -

-

- Download · - Documentation · - Community -

- ---- - -Caddy is a **production-ready** open-source web server that is fast, easy to use, and makes you more productive. - -Available for Windows, Mac, Linux, BSD, Solaris, and [Android](https://github.com/mholt/caddy/wiki/Running-Caddy-on-Android). - -

- Thanks to our special sponsor: -

- Relica - Cross-platform file backup to the cloud, local disks, or other computers -

- -## Menu - -- [Features](#features) -- [Install](#install) -- [Quick Start](#quick-start) -- [Running in Production](#running-in-production) -- [Contributing](#contributing) -- [Donors](#donors) -- [About the Project](#about-the-project) - -## Features - -- **Easy configuration** with the Caddyfile -- **Automatic HTTPS** on by default (via [Let's Encrypt](https://letsencrypt.org)) -- **HTTP/2** by default -- **Virtual hosting** so multiple sites just work -- Experimental **QUIC support** for cutting-edge transmissions -- TLS session ticket **key rotation** for more secure connections -- **Extensible with plugins** because a convenient web server is a helpful one -- **Runs anywhere** with **no external dependencies** (not even libc) - -[See a more complete list of features built into Caddy.](https://caddyserver.com/features) On top of all those, Caddy does even more with plugins: choose which plugins you want at [download](https://caddyserver.com/download). - -Altogether, Caddy can do things other web servers simply cannot do. Its features and plugins save you time and mistakes, and will cheer you up. Your Caddy instance takes care of the details for you! - - -

- Powered by -
- CertMagic -

- - -## Install - -Caddy binaries have no dependencies and are available for every platform. Get Caddy any of these ways: - -- **[Download page](https://caddyserver.com/download)** (RECOMMENDED) allows you to customize your build in the browser -- **[Latest release](https://github.com/mholt/caddy/releases/latest)** for pre-built, vanilla binaries -- **[AWS Marketplace](https://aws.amazon.com/marketplace/pp/B07J1WNK75?qid=1539015041932&sr=0-1&ref_=srh_res_product_title&cl_spe=C)** makes it easy to deploy directly to your cloud environment. -Get Caddy on the AWS Marketplace - - -## Build - -To build from source you need **[Git](https://git-scm.com/downloads)** and **[Go](https://golang.org/doc/install)** (1.12 or newer). - -**To build Caddy without plugins:** - - -1. Set the transitional environment variable for Go modules: `export GO111MODULE=on` -2. Run `go get github.com/mholt/caddy/caddy` - -Caddy will be installed to your `$GOPATH/bin` folder. - -With these instructions, the binary will not have embedded version information (see [golang/go#29228](https://github.com/golang/go/issues/29228)), but it is fine for a quick start. - -**To build Caddy with plugins (and with version information):** - -There is no need to modify the Caddy code to build it with plugins. We will create a simple Go module with our own `main()` that you can use to make custom Caddy builds. - - -1. Set the transitional environment variable for Go modules: `export GO111MODULE=on` -2. Create a new folder anywhere, and put this Go file into it, then import the plugins you want to include: -```go -package main - -import ( - "github.com/mholt/caddy/caddy/caddymain" - - // plug in plugins here, for example: - // _ "import/path/here" -) - -func main() { - // optional: disable telemetry - // caddymain.EnableTelemetry = false - caddymain.Run() -} -``` -3. `go mod init caddy` -4. Run `go get github.com/mholt/caddy` -5. `go install` will then create your binary at `$GOPATH/bin`, or `go build` will put it in the current directory. - -**To install Caddy's source code for development:** - - -1. Set the transitional environment variable for Go modules: `export GO111MODULE=on` -2. Run `git clone https://github.com/mholt/caddy.git` in any folder (doesn't have to be in GOPATH). - -You can make changes to the source code from that clone and checkout any commit or tag you wish to develop on. - -When building from source, telemetry is enabled by default. You can disable it by changing `caddymain.EnableTelemetry = false` in run.go, or use the `-disabled-metrics` flag at runtime to disable only certain metrics. - - -## Quick Start - -To serve static files from the current working directory, run: - -``` -caddy -``` - -Caddy's default port is 2015, so open your browser to [http://localhost:2015](http://localhost:2015). - -### Go from 0 to HTTPS in 5 seconds - -If the `caddy` binary has permission to bind to low ports and your domain name's DNS records point to the machine you're on: - -``` -caddy -host example.com -``` - -This command serves static files from the current directory over HTTPS. Certificates are automatically obtained and renewed for you! Caddy is also automatically configuring ports 80 and 443 for you, and redirecting HTTP to HTTPS. Cool, huh? - -### Customizing your site - -To customize how your site is served, create a file named Caddyfile by your site and paste this into it: - -```plain -localhost - -push -browse -websocket /echo cat -ext .html -log /var/log/access.log -proxy /api 127.0.0.1:7005 -header /api Access-Control-Allow-Origin * -``` - -When you run `caddy` in that directory, it will automatically find and use that Caddyfile. - -This simple file enables server push (via Link headers), allows directory browsing (for folders without an index file), hosts a WebSocket echo server at /echo, serves clean URLs, logs requests to an access log, proxies all API requests to a backend on port 7005, and adds the coveted `Access-Control-Allow-Origin: *` header for all responses from the API. - -Wow! Caddy can do a lot with just a few lines. - -### Doing more with Caddy - -To host multiple sites and do more with the Caddyfile, please see the [Caddyfile tutorial](https://caddyserver.com/tutorial/caddyfile). - -Sites with qualifying hostnames are served over [HTTPS by default](https://caddyserver.com/docs/automatic-https). - -Caddy has a nice little command line interface. Run `caddy -h` to view basic help or see the [CLI documentation](https://caddyserver.com/docs/cli) for details. - - -## Running in Production - -Caddy is production-ready if you find it to be a good fit for your site and workflow. - -**Running as root:** We advise against this. You can still listen on ports < 1024 on Linux using setcap like so: `sudo setcap cap_net_bind_service=+ep ./caddy` - -The Caddy project does not officially maintain any system-specific integrations nor suggest how to administer your own system. But your download file includes [unofficial resources](https://github.com/mholt/caddy/tree/master/dist/init) contributed by the community that you may find helpful for running Caddy in production. - -How you choose to run Caddy is up to you. Many users are satisfied with `nohup caddy &`. Others use `screen`. Users who need Caddy to come back up after reboots either do so in the script that caused the reboot, add a command to an init script, or configure a service with their OS. - -If you have questions or concerns about Caddy' underlying crypto implementations, consult Go's [crypto packages](https://golang.org/pkg/crypto), starting with their documentation, then issues, then the code itself; as Caddy uses mainly those libraries. - - -## Contributing - -**[Join our forum](https://caddy.community) where you can chat with other Caddy users and developers!** To get familiar with the code base, try [Caddy code search on Sourcegraph](https://sourcegraph.com/github.com/mholt/caddy/)! - -Please see our [contributing guidelines](https://github.com/mholt/caddy/blob/master/.github/CONTRIBUTING.md) for instructions. If you want to write a plugin, check out the [developer wiki](https://github.com/mholt/caddy/wiki). - -We use GitHub issues and pull requests only for discussing bug reports and the development of specific changes. We welcome all other topics on the [forum](https://caddy.community)! - -If you want to contribute to the documentation, please [submit an issue](https://github.com/mholt/caddy/issues/new) describing the change that should be made. - -### Good First Issue - -If you are looking for somewhere to start and would like to help out by working on an existing issue, take a look at our [`Good First Issue`](https://github.com/mholt/caddy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag - -Thanks for making Caddy -- and the Web -- better! - - -## Donors - -- [DigitalOcean](https://m.do.co/c/6d7bdafccf96) is hosting the Caddy project. -- [DNSimple](https://dnsimple.link/resolving-caddy) provides DNS services for Caddy's sites. -- [DNS Spy](https://dnsspy.io) keeps an eye on Caddy's DNS properties. - -We thank them for their services. **If you want to help keep Caddy free, please [become a sponsor](https://caddyserver.com/pricing)!** - - -## About the Project - -Caddy was born out of the need for a "batteries-included" web server that runs anywhere and doesn't have to take its configuration with it. Caddy took inspiration from [spark](https://github.com/rif/spark), [nginx](https://github.com/nginx/nginx), lighttpd, -[Websocketd](https://github.com/joewalnes/websocketd) and [Vagrant](https://www.vagrantup.com/), which provides a pleasant mixture of features from each of them. - -**The name "Caddy" is trademarked:** The name of the software is "Caddy", not "Caddy Server" or "CaddyServer". Please call it "Caddy" or, if you wish to clarify, "the Caddy web server". See [brand guidelines](https://caddyserver.com/brand). Caddy is a registered trademark of Light Code Labs, LLC. - -*Author on Twitter: [@mholt6](https://twitter.com/mholt6)* diff --git a/vendor/github.com/mholt/caddy/assets.go b/vendor/github.com/mholt/caddy/assets.go deleted file mode 100644 index 893653fef..000000000 --- a/vendor/github.com/mholt/caddy/assets.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "os" - "path/filepath" - "runtime" -) - -// AssetsPath returns the path to the folder -// where the application may store data. If -// CADDYPATH env variable is set, that value -// is used. Otherwise, the path is the result -// of evaluating "$HOME/.caddy". -func AssetsPath() string { - if caddyPath := os.Getenv("CADDYPATH"); caddyPath != "" { - return caddyPath - } - return filepath.Join(userHomeDir(), ".caddy") -} - -// userHomeDir returns the user's home directory according to -// environment variables. -// -// Credit: http://stackoverflow.com/a/7922977/1048862 -func userHomeDir() string { - if runtime.GOOS == "windows" { - home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - if home == "" { - home = os.Getenv("USERPROFILE") - } - return home - } - return os.Getenv("HOME") -} diff --git a/vendor/github.com/mholt/caddy/azure-pipelines.yml b/vendor/github.com/mholt/caddy/azure-pipelines.yml deleted file mode 100644 index 72796a51b..000000000 --- a/vendor/github.com/mholt/caddy/azure-pipelines.yml +++ /dev/null @@ -1,88 +0,0 @@ -# Mutilated beyond recognition from the example at: -# https://docs.microsoft.com/azure/devops/pipelines/languages/go - -trigger: -- master - -strategy: - matrix: - linux: - imageName: ubuntu-16.04 - gorootDir: /usr/local - mac: - imageName: macos-10.13 - gorootDir: /usr/local - windows: - imageName: windows-2019 - gorootDir: C:\ - -pool: - vmImage: $(imageName) - -variables: - GOROOT: $(gorootDir)/go - GOPATH: $(system.defaultWorkingDirectory)/gopath - GOBIN: $(GOPATH)/bin - modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' - # TODO: modules should be the default in Go 1.13, so this won't be needed - GO111MODULE: on - -steps: -- bash: | - latestGo=$(curl "https://golang.org/VERSION?m=text") - echo "##vso[task.setvariable variable=LATEST_GO]$latestGo" - echo "Latest Go version: $latestGo" - displayName: "Get latest Go version" - -- bash: | - sudo rm -f $(which go) - echo '##vso[task.prependpath]$(GOBIN)' - echo '##vso[task.prependpath]$(GOROOT)/bin' - mkdir -p '$(modulePath)' - shopt -s extglob - shopt -s dotglob - mv !(gopath) '$(modulePath)' - displayName: Remove old Go, set GOBIN/GOROOT, and move project into GOPATH - -# Install Go (this varies by platform) - -- bash: | - wget "https://dl.google.com/go/$(LATEST_GO).linux-amd64.tar.gz" - sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).linux-amd64.tar.gz" - condition: eq( variables['Agent.OS'], 'Linux' ) - displayName: Install Go on Linux - -- bash: | - wget "https://dl.google.com/go/$(LATEST_GO).darwin-amd64.tar.gz" - sudo tar -C $(gorootDir) -xzf "$(LATEST_GO).darwin-amd64.tar.gz" - condition: eq( variables['Agent.OS'], 'Darwin' ) - displayName: Install Go on macOS - -- powershell: | - Write-Host "Downloading Go... (please be patient, I am very slow)" - (New-Object System.Net.WebClient).DownloadFile("https://dl.google.com/go/$(LATEST_GO).windows-amd64.zip", "$(LATEST_GO).windows-amd64.zip") - Write-Host "Extracting Go... (I'm slow too)" - Expand-Archive "$(LATEST_GO).windows-amd64.zip" -DestinationPath "$(gorootDir)" - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - displayName: Install Go on Windows - -# TODO: When this issue is fixed, replace with installer script: -# https://github.com/golangci/golangci-lint/issues/472 -- script: go get -v github.com/golangci/golangci-lint/cmd/golangci-lint - displayName: Install golangci-lint - -- bash: | - printf "Using go at: $(which go)\n" - printf "Go version: $(go version)\n" - printf "\n\nGo environment:\n\n" - go env - printf "\n\nSystem environment:\n\n" - env - displayName: Print Go version and environment - -- script: | - go get -v -t -d ./... - golangci-lint run -E gofmt -E goimports -E misspell - go test -race ./... - workingDirectory: '$(modulePath)' - displayName: Run tests diff --git a/vendor/github.com/mholt/caddy/caddy.go b/vendor/github.com/mholt/caddy/caddy.go deleted file mode 100644 index 2aafd3758..000000000 --- a/vendor/github.com/mholt/caddy/caddy.go +++ /dev/null @@ -1,1026 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy implements the Caddy server manager. -// -// To use this package: -// -// 1. Set the AppName and AppVersion variables. -// 2. Call LoadCaddyfile() to get the Caddyfile. -// Pass in the name of the server type (like "http"). -// Make sure the server type's package is imported -// (import _ "github.com/mholt/caddy/caddyhttp"). -// 3. Call caddy.Start() to start Caddy. You get back -// an Instance, on which you can call Restart() to -// restart it or Stop() to stop it. -// -// You should call Wait() on your instance to wait for -// all servers to quit before your process exits. -package caddy - -import ( - "bytes" - "encoding/gob" - "fmt" - "io" - "io/ioutil" - "log" - "net" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/mholt/caddy/caddyfile" - "github.com/mholt/caddy/telemetry" -) - -// Configurable application parameters -var ( - // AppName is the name of the application. - AppName string - - // AppVersion is the version of the application. - AppVersion string - - // Quiet mode will not show any informative output on initialization. - Quiet bool - - // PidFile is the path to the pidfile to create. - PidFile string - - // GracefulTimeout is the maximum duration of a graceful shutdown. - GracefulTimeout time.Duration - - // isUpgrade will be set to true if this process - // was started as part of an upgrade, where a parent - // Caddy process started this one. - isUpgrade = os.Getenv("CADDY__UPGRADE") == "1" - - // started will be set to true when the first - // instance is started; it never gets set to - // false after that. - started bool - - // mu protects the variables 'isUpgrade' and 'started'. - mu sync.Mutex -) - -func init() { - OnProcessExit = append(OnProcessExit, func() { - if PidFile != "" { - os.Remove(PidFile) - } - }) -} - -// Instance contains the state of servers created as a result of -// calling Start and can be used to access or control those servers. -// It is literally an instance of a server type. Instance values -// should NOT be copied. Use *Instance for safety. -type Instance struct { - // serverType is the name of the instance's server type - serverType string - - // caddyfileInput is the input configuration text used for this process - caddyfileInput Input - - // wg is used to wait for all servers to shut down - wg *sync.WaitGroup - - // context is the context created for this instance, - // used to coordinate the setting up of the server type - context Context - - // servers is the list of servers with their listeners - servers []ServerListener - - // these callbacks execute when certain events occur - OnFirstStartup []func() error // starting, not as part of a restart - OnStartup []func() error // starting, even as part of a restart - OnRestart []func() error // before restart commences - OnRestartFailed []func() error // if restart failed - OnShutdown []func() error // stopping, even as part of a restart - OnFinalShutdown []func() error // stopping, not as part of a restart - - // storing values on an instance is preferable to - // global state because these will get garbage- - // collected after in-process reloads when the - // old instances are destroyed; use StorageMu - // to access this value safely - Storage map[interface{}]interface{} - StorageMu sync.RWMutex -} - -// Instances returns the list of instances. -func Instances() []*Instance { - return instances -} - -// Servers returns the ServerListeners in i. -func (i *Instance) Servers() []ServerListener { return i.servers } - -// Stop stops all servers contained in i. It does NOT -// execute shutdown callbacks. -func (i *Instance) Stop() error { - // stop the servers - for _, s := range i.servers { - if gs, ok := s.server.(GracefulServer); ok { - if err := gs.Stop(); err != nil { - log.Printf("[ERROR] Stopping %s: %v", gs.Address(), err) - } - } - } - - // splice i out of instance list, causing it to be garbage-collected - instancesMu.Lock() - for j, other := range instances { - if other == i { - instances = append(instances[:j], instances[j+1:]...) - break - } - } - instancesMu.Unlock() - - return nil -} - -// ShutdownCallbacks executes all the shutdown callbacks of i, -// including ones that are scheduled only for the final shutdown -// of i. An error returned from one does not stop execution of -// the rest. All the non-nil errors will be returned. -func (i *Instance) ShutdownCallbacks() []error { - var errs []error - for _, shutdownFunc := range i.OnShutdown { - err := shutdownFunc() - if err != nil { - errs = append(errs, err) - } - } - for _, finalShutdownFunc := range i.OnFinalShutdown { - err := finalShutdownFunc() - if err != nil { - errs = append(errs, err) - } - } - return errs -} - -// Restart replaces the servers in i with new servers created from -// executing the newCaddyfile. Upon success, it returns the new -// instance to replace i. Upon failure, i will not be replaced. -func (i *Instance) Restart(newCaddyfile Input) (*Instance, error) { - log.Println("[INFO] Reloading") - - i.wg.Add(1) - defer i.wg.Done() - - var err error - // if something went wrong on restart then run onRestartFailed callbacks - defer func() { - r := recover() - if err != nil || r != nil { - for _, fn := range i.OnRestartFailed { - if err := fn(); err != nil { - log.Printf("[ERROR] Restart failed callback returned error: %v", err) - } - } - if err != nil { - log.Printf("[ERROR] Restart failed: %v", err) - } - if r != nil { - log.Printf("[PANIC] Restart: %v", r) - } - } - }() - - // run restart callbacks - for _, fn := range i.OnRestart { - err = fn() - if err != nil { - return i, err - } - } - - if newCaddyfile == nil { - newCaddyfile = i.caddyfileInput - } - - // Add file descriptors of all the sockets that are capable of it - restartFds := make(map[string]restartTriple) - for _, s := range i.servers { - gs, srvOk := s.server.(GracefulServer) - ln, lnOk := s.listener.(Listener) - pc, pcOk := s.packet.(PacketConn) - if srvOk { - if lnOk && pcOk { - restartFds[gs.Address()] = restartTriple{server: gs, listener: ln, packet: pc} - continue - } - if lnOk { - restartFds[gs.Address()] = restartTriple{server: gs, listener: ln} - continue - } - if pcOk { - restartFds[gs.Address()] = restartTriple{server: gs, packet: pc} - continue - } - } - } - - // create new instance; if the restart fails, it is simply discarded - newInst := &Instance{serverType: newCaddyfile.ServerType(), wg: i.wg, Storage: make(map[interface{}]interface{})} - - // attempt to start new instance - err = startWithListenerFds(newCaddyfile, newInst, restartFds) - if err != nil { - return i, fmt.Errorf("starting with listener file descriptors: %v", err) - } - - // success! stop the old instance - err = i.Stop() - if err != nil { - return i, err - } - for _, shutdownFunc := range i.OnShutdown { - err = shutdownFunc() - if err != nil { - return i, err - } - } - - // Execute instantiation events - EmitEvent(InstanceStartupEvent, newInst) - - log.Println("[INFO] Reloading complete") - - return newInst, nil -} - -// SaveServer adds s and its associated listener ln to the -// internally-kept list of servers that is running. For -// saved servers, graceful restarts will be provided. -func (i *Instance) SaveServer(s Server, ln net.Listener) { - i.servers = append(i.servers, ServerListener{server: s, listener: ln}) -} - -// TCPServer is a type that can listen and serve connections. -// A TCPServer must associate with exactly zero or one net.Listeners. -type TCPServer interface { - // Listen starts listening by creating a new listener - // and returning it. It does not start accepting - // connections. For UDP-only servers, this method - // can be a no-op that returns (nil, nil). - Listen() (net.Listener, error) - - // Serve starts serving using the provided listener. - // Serve must start the server loop nearly immediately, - // or at least not return any errors before the server - // loop begins. Serve blocks indefinitely, or in other - // words, until the server is stopped. For UDP-only - // servers, this method can be a no-op that returns nil. - Serve(net.Listener) error -} - -// UDPServer is a type that can listen and serve packets. -// A UDPServer must associate with exactly zero or one net.PacketConns. -type UDPServer interface { - // ListenPacket starts listening by creating a new packetconn - // and returning it. It does not start accepting connections. - // TCP-only servers may leave this method blank and return - // (nil, nil). - ListenPacket() (net.PacketConn, error) - - // ServePacket starts serving using the provided packetconn. - // ServePacket must start the server loop nearly immediately, - // or at least not return any errors before the server - // loop begins. ServePacket blocks indefinitely, or in other - // words, until the server is stopped. For TCP-only servers, - // this method can be a no-op that returns nil. - ServePacket(net.PacketConn) error -} - -// Server is a type that can listen and serve. It supports both -// TCP and UDP, although the UDPServer interface can be used -// for more than just UDP. -// -// If the server uses TCP, it should implement TCPServer completely. -// If it uses UDP or some other protocol, it should implement -// UDPServer completely. If it uses both, both interfaces should be -// fully implemented. Any unimplemented methods should be made as -// no-ops that simply return nil values. -type Server interface { - TCPServer - UDPServer -} - -// Stopper is a type that can stop serving. The stop -// does not necessarily have to be graceful. -type Stopper interface { - // Stop stops the server. It blocks until the - // server is completely stopped. - Stop() error -} - -// GracefulServer is a Server and Stopper, the stopping -// of which is graceful (whatever that means for the kind -// of server being implemented). It must be able to return -// the address it is configured to listen on so that its -// listener can be paired with it upon graceful restarts. -// The net.Listener that a GracefulServer creates must -// implement the Listener interface for restarts to be -// graceful (assuming the listener is for TCP). -type GracefulServer interface { - Server - Stopper - - // Address returns the address the server should - // listen on; it is used to pair the server to - // its listener during a graceful/zero-downtime - // restart. Thus when implementing this method, - // you must not access a listener to get the - // address; you must store the address the - // server is to serve on some other way. - Address() string - - // WrapListener wraps a listener with the - // listener middlewares configured for this - // server, if any. - WrapListener(net.Listener) net.Listener -} - -// Listener is a net.Listener with an underlying file descriptor. -// A server's listener should implement this interface if it is -// to support zero-downtime reloads. -type Listener interface { - net.Listener - File() (*os.File, error) -} - -// PacketConn is a net.PacketConn with an underlying file descriptor. -// A server's packetconn should implement this interface if it is -// to support zero-downtime reloads (in sofar this holds true for datagram -// connections). -type PacketConn interface { - net.PacketConn - File() (*os.File, error) -} - -// AfterStartup is an interface that can be implemented -// by a server type that wants to run some code after all -// servers for the same Instance have started. -type AfterStartup interface { - OnStartupComplete() -} - -// LoadCaddyfile loads a Caddyfile by calling the plugged in -// Caddyfile loader methods. An error is returned if more than -// one loader returns a non-nil Caddyfile input. If no loaders -// load a Caddyfile, the default loader is used. If no default -// loader is registered or it returns nil, the server type's -// default Caddyfile is loaded. If the server type does not -// specify any default Caddyfile value, then an empty Caddyfile -// is returned. Consequently, this function never returns a nil -// value as long as there are no errors. -func LoadCaddyfile(serverType string) (Input, error) { - // If we are finishing an upgrade, we must obtain the Caddyfile - // from our parent process, regardless of configured loaders. - if IsUpgrade() { - err := gob.NewDecoder(os.Stdin).Decode(&loadedGob) - if err != nil { - return nil, err - } - return loadedGob.Caddyfile, nil - } - - // Ask plugged-in loaders for a Caddyfile - cdyfile, err := loadCaddyfileInput(serverType) - if err != nil { - return nil, err - } - - // Otherwise revert to default - if cdyfile == nil { - cdyfile = DefaultInput(serverType) - } - - // Still nil? Geez. - if cdyfile == nil { - cdyfile = CaddyfileInput{ServerTypeName: serverType} - } - - return cdyfile, nil -} - -// Wait blocks until all of i's servers have stopped. -func (i *Instance) Wait() { - i.wg.Wait() -} - -// CaddyfileFromPipe loads the Caddyfile input from f if f is -// not interactive input. f is assumed to be a pipe or stream, -// such as os.Stdin. If f is not a pipe, no error is returned -// but the Input value will be nil. An error is only returned -// if there was an error reading the pipe, even if the length -// of what was read is 0. -func CaddyfileFromPipe(f *os.File, serverType string) (Input, error) { - fi, err := f.Stat() - if err == nil && fi.Mode()&os.ModeCharDevice == 0 { - // Note that a non-nil error is not a problem. Windows - // will not create a stdin if there is no pipe, which - // produces an error when calling Stat(). But Unix will - // make one either way, which is why we also check that - // bitmask. - // NOTE: Reading from stdin after this fails (e.g. for the let's encrypt email address) (OS X) - confBody, err := ioutil.ReadAll(f) - if err != nil { - return nil, err - } - return CaddyfileInput{ - Contents: confBody, - Filepath: f.Name(), - ServerTypeName: serverType, - }, nil - } - - // not having input from the pipe is not itself an error, - // just means no input to return. - return nil, nil -} - -// Caddyfile returns the Caddyfile used to create i. -func (i *Instance) Caddyfile() Input { - return i.caddyfileInput -} - -// Start starts Caddy with the given Caddyfile. -// -// This function blocks until all the servers are listening. -func Start(cdyfile Input) (*Instance, error) { - inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})} - err := startWithListenerFds(cdyfile, inst, nil) - if err != nil { - return inst, err - } - signalSuccessToParent() - if pidErr := writePidFile(); pidErr != nil { - log.Printf("[ERROR] Could not write pidfile: %v", pidErr) - } - - // Execute instantiation events - EmitEvent(InstanceStartupEvent, inst) - - return inst, nil -} - -func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error { - // save this instance in the list now so that - // plugins can access it if need be, for example - // the caddytls package, so it can perform cert - // renewals while starting up; we just have to - // remove the instance from the list later if - // it fails - instancesMu.Lock() - instances = append(instances, inst) - instancesMu.Unlock() - var err error - defer func() { - if err != nil { - instancesMu.Lock() - for i, otherInst := range instances { - if otherInst == inst { - instances = append(instances[:i], instances[i+1:]...) - break - } - } - instancesMu.Unlock() - } - }() - - if cdyfile == nil { - cdyfile = CaddyfileInput{} - } - - err = ValidateAndExecuteDirectives(cdyfile, inst, false) - if err != nil { - return err - } - - slist, err := inst.context.MakeServers() - if err != nil { - return err - } - - // run startup callbacks - if !IsUpgrade() && restartFds == nil { - // first startup means not a restart or upgrade - for _, firstStartupFunc := range inst.OnFirstStartup { - err = firstStartupFunc() - if err != nil { - return err - } - } - } - for _, startupFunc := range inst.OnStartup { - err = startupFunc() - if err != nil { - return err - } - } - - err = startServers(slist, inst, restartFds) - if err != nil { - return err - } - - // run any AfterStartup callbacks if this is not - // part of a restart; then show file descriptor notice - if restartFds == nil { - for _, srvln := range inst.servers { - if srv, ok := srvln.server.(AfterStartup); ok { - srv.OnStartupComplete() - } - } - if !Quiet { - for _, srvln := range inst.servers { - // only show FD notice if the listener is not nil. - // This can happen when only serving UDP or TCP - if srvln.listener == nil { - continue - } - if !IsLoopback(srvln.listener.Addr().String()) { - checkFdlimit() - break - } - } - } - } - - mu.Lock() - started = true - mu.Unlock() - - return nil -} - -// ValidateAndExecuteDirectives will load the server blocks from cdyfile -// by parsing it, then execute the directives configured by it and store -// the resulting server blocks into inst. If justValidate is true, parse -// callbacks will not be executed between directives, since the purpose -// is only to check the input for valid syntax. -func ValidateAndExecuteDirectives(cdyfile Input, inst *Instance, justValidate bool) error { - // If parsing only inst will be nil, create an instance for this function call only. - if justValidate { - inst = &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})} - } - - stypeName := cdyfile.ServerType() - - stype, err := getServerType(stypeName) - if err != nil { - return err - } - - inst.caddyfileInput = cdyfile - - sblocks, err := loadServerBlocks(stypeName, cdyfile.Path(), bytes.NewReader(cdyfile.Body())) - if err != nil { - return err - } - - inst.context = stype.NewContext(inst) - if inst.context == nil { - return fmt.Errorf("server type %s produced a nil Context", stypeName) - } - - sblocks, err = inst.context.InspectServerBlocks(cdyfile.Path(), sblocks) - if err != nil { - return fmt.Errorf("error inspecting server blocks: %v", err) - } - - telemetry.Set("num_server_blocks", len(sblocks)) - - return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate) -} - -func executeDirectives(inst *Instance, filename string, - directives []string, sblocks []caddyfile.ServerBlock, justValidate bool) error { - // map of server block ID to map of directive name to whatever. - storages := make(map[int]map[string]interface{}) - - // It is crucial that directives are executed in the proper order. - // We loop with the directives on the outer loop so we execute - // a directive for all server blocks before going to the next directive. - // This is important mainly due to the parsing callbacks (below). - for _, dir := range directives { - for i, sb := range sblocks { - var once sync.Once - if _, ok := storages[i]; !ok { - storages[i] = make(map[string]interface{}) - } - - for j, key := range sb.Keys { - // Execute directive if it is in the server block - if tokens, ok := sb.Tokens[dir]; ok { - controller := &Controller{ - instance: inst, - Key: key, - Dispenser: caddyfile.NewDispenserTokens(filename, tokens), - OncePerServerBlock: func(f func() error) error { - var err error - once.Do(func() { - err = f() - }) - return err - }, - ServerBlockIndex: i, - ServerBlockKeyIndex: j, - ServerBlockKeys: sb.Keys, - ServerBlockStorage: storages[i][dir], - } - - setup, err := DirectiveAction(inst.serverType, dir) - if err != nil { - return err - } - - err = setup(controller) - if err != nil { - return err - } - - storages[i][dir] = controller.ServerBlockStorage // persist for this server block - } - } - } - - if !justValidate { - // See if there are any callbacks to execute after this directive - if allCallbacks, ok := parsingCallbacks[inst.serverType]; ok { - callbacks := allCallbacks[dir] - for _, callback := range callbacks { - if err := callback(inst.context); err != nil { - return err - } - } - } - } - } - - return nil -} - -func startServers(serverList []Server, inst *Instance, restartFds map[string]restartTriple) error { - errChan := make(chan error, len(serverList)) - - // used for signaling to error logging goroutine to terminate - stopChan := make(chan struct{}) - // used to track termination of servers - stopWg := &sync.WaitGroup{} - - for _, s := range serverList { - var ( - ln net.Listener - pc net.PacketConn - err error - ) - - // if performing an upgrade, obtain listener file descriptors - // from parent process - if IsUpgrade() { - if gs, ok := s.(GracefulServer); ok { - addr := gs.Address() - if fdIndex, ok := loadedGob.ListenerFds["tcp"+addr]; ok { - file := os.NewFile(fdIndex, "") - ln, err = net.FileListener(file) - if err != nil { - return fmt.Errorf("making listener from file: %v", err) - } - err = file.Close() - if err != nil { - return fmt.Errorf("closing copy of listener file: %v", err) - } - } - if fdIndex, ok := loadedGob.ListenerFds["udp"+addr]; ok { - file := os.NewFile(fdIndex, "") - pc, err = net.FilePacketConn(file) - if err != nil { - return fmt.Errorf("making packet connection from file: %v", err) - } - err = file.Close() - if err != nil { - return fmt.Errorf("closing copy of packet connection file: %v", err) - } - } - ln = gs.WrapListener(ln) - } - } - - // If this is a reload and s is a GracefulServer, - // reuse the listener for a graceful restart. - if gs, ok := s.(GracefulServer); ok && restartFds != nil { - addr := gs.Address() - if old, ok := restartFds[addr]; ok { - // listener - if old.listener != nil { - file, err := old.listener.File() - if err != nil { - return fmt.Errorf("getting old listener file: %v", err) - } - ln, err = net.FileListener(file) - if err != nil { - return fmt.Errorf("getting file listener: %v", err) - } - err = file.Close() - if err != nil { - return fmt.Errorf("closing copy of listener file: %v", err) - } - } - // packetconn - if old.packet != nil { - file, err := old.packet.File() - if err != nil { - return fmt.Errorf("getting old packet file: %v", err) - } - pc, err = net.FilePacketConn(file) - if err != nil { - return fmt.Errorf("getting file packet connection: %v", err) - } - err = file.Close() - if err != nil { - return fmt.Errorf("close copy of packet file: %v", err) - } - } - ln = gs.WrapListener(ln) - } - } - - if ln == nil { - ln, err = s.Listen() - if err != nil { - return fmt.Errorf("Listen: %v", err) - } - } - if pc == nil { - pc, err = s.ListenPacket() - if err != nil { - return fmt.Errorf("ListenPacket: %v", err) - } - } - - inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc}) - } - - for _, s := range inst.servers { - inst.wg.Add(2) - stopWg.Add(2) - func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) { - go func() { - defer func() { - inst.wg.Done() - stopWg.Done() - }() - errChan <- s.Serve(ln) - }() - - go func() { - defer func() { - inst.wg.Done() - stopWg.Done() - }() - errChan <- s.ServePacket(pc) - }() - }(s.server, s.listener, s.packet, inst) - } - - // Log errors that may be returned from Serve() calls, - // these errors should only be occurring in the server loop. - go func() { - for { - select { - case err := <-errChan: - if err != nil { - if !strings.Contains(err.Error(), "use of closed network connection") { - // this error is normal when closing the listener; see https://github.com/golang/go/issues/4373 - log.Println(err) - } - } - case <-stopChan: - return - } - } - }() - - go func() { - stopWg.Wait() - stopChan <- struct{}{} - }() - - return nil -} - -func getServerType(serverType string) (ServerType, error) { - stype, ok := serverTypes[serverType] - if ok { - return stype, nil - } - if len(serverTypes) == 0 { - return ServerType{}, fmt.Errorf("no server types plugged in") - } - if serverType == "" { - if len(serverTypes) == 1 { - for _, stype := range serverTypes { - return stype, nil - } - } - return ServerType{}, fmt.Errorf("multiple server types available; must choose one") - } - return ServerType{}, fmt.Errorf("unknown server type '%s'", serverType) -} - -func loadServerBlocks(serverType, filename string, input io.Reader) ([]caddyfile.ServerBlock, error) { - validDirectives := ValidDirectives(serverType) - serverBlocks, err := caddyfile.Parse(filename, input, validDirectives) - if err != nil { - return nil, err - } - if len(serverBlocks) == 0 && serverTypes[serverType].DefaultInput != nil { - newInput := serverTypes[serverType].DefaultInput() - serverBlocks, err = caddyfile.Parse(newInput.Path(), - bytes.NewReader(newInput.Body()), validDirectives) - if err != nil { - return nil, err - } - } - return serverBlocks, nil -} - -// Stop stops ALL servers. It blocks until they are all stopped. -// It does NOT execute shutdown callbacks, and it deletes all -// instances after stopping is completed. Do not re-use any -// references to old instances after calling Stop. -func Stop() error { - // This awkward for loop is to avoid a deadlock since - // inst.Stop() also acquires the instancesMu lock. - for { - instancesMu.Lock() - if len(instances) == 0 { - instancesMu.Unlock() - break - } - inst := instances[0] - instancesMu.Unlock() - if err := inst.Stop(); err != nil { - log.Printf("[ERROR] Stopping %s: %v", inst.serverType, err) - } - } - return nil -} - -// IsLoopback returns true if the hostname of addr looks -// explicitly like a common local hostname. addr must only -// be a host or a host:port combination. -func IsLoopback(addr string) bool { - host, _, err := net.SplitHostPort(strings.ToLower(addr)) - if err != nil { - host = addr // happens if the addr is just a hostname - } - return host == "localhost" || - strings.Trim(host, "[]") == "::1" || - strings.HasPrefix(host, "127.") -} - -// IsInternal returns true if the IP of addr -// belongs to a private network IP range. addr must only -// be an IP or an IP:port combination. -// Loopback addresses are considered false. -func IsInternal(addr string) bool { - privateNetworks := []string{ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - "fc00::/7", - } - - host, _, err := net.SplitHostPort(addr) - if err != nil { - host = addr // happens if the addr is just a hostname, missing port - // if we encounter an error, the brackets need to be stripped - // because SplitHostPort didn't do it for us - host = strings.Trim(host, "[]") - } - ip := net.ParseIP(host) - if ip == nil { - return false - } - for _, privateNetwork := range privateNetworks { - _, ipnet, _ := net.ParseCIDR(privateNetwork) - if ipnet.Contains(ip) { - return true - } - } - return false -} - -// Started returns true if at least one instance has been -// started by this package. It never gets reset to false -// once it is set to true. -func Started() bool { - mu.Lock() - defer mu.Unlock() - return started -} - -// CaddyfileInput represents a Caddyfile as input -// and is simply a convenient way to implement -// the Input interface. -type CaddyfileInput struct { - Filepath string - Contents []byte - ServerTypeName string -} - -// Body returns c.Contents. -func (c CaddyfileInput) Body() []byte { return c.Contents } - -// Path returns c.Filepath. -func (c CaddyfileInput) Path() string { return c.Filepath } - -// ServerType returns c.ServerType. -func (c CaddyfileInput) ServerType() string { return c.ServerTypeName } - -// Input represents a Caddyfile; its contents and file path -// (which should include the file name at the end of the path). -// If path does not apply (e.g. piped input) you may use -// any understandable value. The path is mainly used for logging, -// error messages, and debugging. -type Input interface { - // Gets the Caddyfile contents - Body() []byte - - // Gets the path to the origin file - Path() string - - // The type of server this input is intended for - ServerType() string -} - -// DefaultInput returns the default Caddyfile input -// to use when it is otherwise empty or missing. -// It uses the default host and port (depends on -// host, e.g. localhost is 2015, otherwise 443) and -// root. -func DefaultInput(serverType string) Input { - if _, ok := serverTypes[serverType]; !ok { - return nil - } - if serverTypes[serverType].DefaultInput == nil { - return nil - } - return serverTypes[serverType].DefaultInput() -} - -// writePidFile writes the process ID to the file at PidFile. -// It does nothing if PidFile is not set. -func writePidFile() error { - if PidFile == "" { - return nil - } - pid := []byte(strconv.Itoa(os.Getpid()) + "\n") - return ioutil.WriteFile(PidFile, pid, 0644) -} - -type restartTriple struct { - server GracefulServer - listener Listener - packet PacketConn -} - -var ( - // instances is the list of running Instances. - instances []*Instance - - // instancesMu protects instances. - instancesMu sync.Mutex -) - -var ( - // DefaultConfigFile is the name of the configuration file that is loaded - // by default if no other file is specified. - DefaultConfigFile = "Caddyfile" -) - -// CtxKey is a value type for use with context.WithValue. -type CtxKey string diff --git a/vendor/github.com/mholt/caddy/caddy/caddymain/run.go b/vendor/github.com/mholt/caddy/caddy/caddymain/run.go deleted file mode 100644 index e9ce689bb..000000000 --- a/vendor/github.com/mholt/caddy/caddy/caddymain/run.go +++ /dev/null @@ -1,601 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddymain - -import ( - "bufio" - "errors" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "runtime" - "runtime/debug" - "strconv" - "strings" - - "github.com/google/uuid" - "github.com/klauspost/cpuid" - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyfile" - "github.com/mholt/caddy/caddytls" - "github.com/mholt/caddy/telemetry" - "github.com/mholt/certmagic" - lumberjack "gopkg.in/natefinch/lumberjack.v2" - - _ "github.com/mholt/caddy/caddyhttp" // plug in the HTTP server type - // This is where other plugins get plugged in (imported) -) - -func init() { - caddy.TrapSignals() - - flag.BoolVar(&certmagic.Default.Agreed, "agree", false, "Agree to the CA's Subscriber Agreement") - flag.StringVar(&certmagic.Default.CA, "ca", certmagic.Default.CA, "URL to certificate authority's ACME server directory") - flag.StringVar(&certmagic.Default.DefaultServerName, "default-sni", certmagic.Default.DefaultServerName, "If a ClientHello ServerName is empty, use this ServerName to choose a TLS certificate") - flag.BoolVar(&certmagic.Default.DisableHTTPChallenge, "disable-http-challenge", certmagic.Default.DisableHTTPChallenge, "Disable the ACME HTTP challenge") - flag.BoolVar(&certmagic.Default.DisableTLSALPNChallenge, "disable-tls-alpn-challenge", certmagic.Default.DisableTLSALPNChallenge, "Disable the ACME TLS-ALPN challenge") - flag.StringVar(&disabledMetrics, "disabled-metrics", "", "Comma-separated list of telemetry metrics to disable") - flag.StringVar(&conf, "conf", "", "Caddyfile to load (default \""+caddy.DefaultConfigFile+"\")") - flag.StringVar(&cpu, "cpu", "100%", "CPU cap") - flag.BoolVar(&printEnv, "env", false, "Enable to print environment variables") - flag.StringVar(&envFile, "envfile", "", "Path to file with environment variables to load in KEY=VALUE format") - flag.BoolVar(&fromJSON, "json-to-caddyfile", false, "From JSON stdin to Caddyfile stdout") - flag.BoolVar(&plugins, "plugins", false, "List installed plugins") - flag.StringVar(&certmagic.Default.Email, "email", "", "Default ACME CA account email address") - flag.DurationVar(&certmagic.HTTPTimeout, "catimeout", certmagic.HTTPTimeout, "Default ACME CA HTTP timeout") - flag.StringVar(&logfile, "log", "", "Process log file") - flag.IntVar(&logRollMB, "log-roll-mb", 100, "Roll process log when it reaches this many megabytes (0 to disable rolling)") - flag.BoolVar(&logRollCompress, "log-roll-compress", true, "Gzip-compress rolled process log files") - flag.StringVar(&caddy.PidFile, "pidfile", "", "Path to write pid file") - flag.BoolVar(&caddy.Quiet, "quiet", false, "Quiet mode (no initialization output)") - flag.StringVar(&revoke, "revoke", "", "Hostname for which to revoke the certificate") - flag.StringVar(&serverType, "type", "http", "Type of server to run") - flag.BoolVar(&toJSON, "caddyfile-to-json", false, "From Caddyfile stdin to JSON stdout") - flag.BoolVar(&version, "version", false, "Show version") - flag.BoolVar(&validate, "validate", false, "Parse the Caddyfile but do not start the server") - - caddy.RegisterCaddyfileLoader("flag", caddy.LoaderFunc(confLoader)) - caddy.SetDefaultCaddyfileLoader("default", caddy.LoaderFunc(defaultLoader)) -} - -// Run is Caddy's main() function. -func Run() { - flag.Parse() - - module := getBuildModule() - cleanModVersion := strings.TrimPrefix(module.Version, "v") - - caddy.AppName = appName - caddy.AppVersion = module.Version - certmagic.UserAgent = appName + "/" + cleanModVersion - - // Set up process log before anything bad happens - switch logfile { - case "stdout": - log.SetOutput(os.Stdout) - case "stderr": - log.SetOutput(os.Stderr) - case "": - log.SetOutput(ioutil.Discard) - default: - if logRollMB > 0 { - log.SetOutput(&lumberjack.Logger{ - Filename: logfile, - MaxSize: logRollMB, - MaxAge: 14, - MaxBackups: 10, - Compress: logRollCompress, - }) - } else { - err := os.MkdirAll(filepath.Dir(logfile), 0755) - if err != nil { - mustLogFatalf("%v", err) - } - f, err := os.OpenFile(logfile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - mustLogFatalf("%v", err) - } - // don't close file; log should be writeable for duration of process - log.SetOutput(f) - } - } - - // load all additional envs as soon as possible - if err := LoadEnvFromFile(envFile); err != nil { - mustLogFatalf("%v", err) - } - - if printEnv { - for _, v := range os.Environ() { - fmt.Println(v) - } - } - - // initialize telemetry client - if EnableTelemetry { - err := initTelemetry() - if err != nil { - mustLogFatalf("[ERROR] Initializing telemetry: %v", err) - } - } else if disabledMetrics != "" { - mustLogFatalf("[ERROR] Cannot disable specific metrics because telemetry is disabled") - } - - // Check for one-time actions - if revoke != "" { - err := caddytls.Revoke(revoke) - if err != nil { - mustLogFatalf("%v", err) - } - fmt.Printf("Revoked certificate for %s\n", revoke) - os.Exit(0) - } - if version { - if module.Sum != "" { - // a build with a known version will also have a checksum - fmt.Printf("Caddy %s (%s)\n", module.Version, module.Sum) - } else { - fmt.Println(module.Version) - } - os.Exit(0) - } - if plugins { - fmt.Println(caddy.DescribePlugins()) - os.Exit(0) - } - - // Check if we just need to do a Caddyfile Convert and exit - checkJSONCaddyfile() - - // Set CPU cap - err := setCPU(cpu) - if err != nil { - mustLogFatalf("%v", err) - } - - // Executes Startup events - caddy.EmitEvent(caddy.StartupEvent, nil) - - // Get Caddyfile input - caddyfileinput, err := caddy.LoadCaddyfile(serverType) - if err != nil { - mustLogFatalf("%v", err) - } - - if validate { - err := caddy.ValidateAndExecuteDirectives(caddyfileinput, nil, true) - if err != nil { - mustLogFatalf("%v", err) - } - msg := "Caddyfile is valid" - fmt.Println(msg) - log.Printf("[INFO] %s", msg) - os.Exit(0) - } - - // Start your engines - instance, err := caddy.Start(caddyfileinput) - if err != nil { - mustLogFatalf("%v", err) - } - - // Begin telemetry (these are no-ops if telemetry disabled) - telemetry.Set("caddy_version", module.Version) - telemetry.Set("num_listeners", len(instance.Servers())) - telemetry.Set("server_type", serverType) - telemetry.Set("os", runtime.GOOS) - telemetry.Set("arch", runtime.GOARCH) - telemetry.Set("cpu", struct { - BrandName string `json:"brand_name,omitempty"` - NumLogical int `json:"num_logical,omitempty"` - AESNI bool `json:"aes_ni,omitempty"` - }{ - BrandName: cpuid.CPU.BrandName, - NumLogical: runtime.NumCPU(), - AESNI: cpuid.CPU.AesNi(), - }) - if containerized := detectContainer(); containerized { - telemetry.Set("container", containerized) - } - telemetry.StartEmitting() - - // Twiddle your thumbs - instance.Wait() -} - -// mustLogFatalf wraps log.Fatalf() in a way that ensures the -// output is always printed to stderr so the user can see it -// if the user is still there, even if the process log was not -// enabled. If this process is an upgrade, however, and the user -// might not be there anymore, this just logs to the process -// log and exits. -func mustLogFatalf(format string, args ...interface{}) { - if !caddy.IsUpgrade() { - log.SetOutput(os.Stderr) - } - log.Fatalf(format, args...) -} - -// confLoader loads the Caddyfile using the -conf flag. -func confLoader(serverType string) (caddy.Input, error) { - if conf == "" { - return nil, nil - } - - if conf == "stdin" { - return caddy.CaddyfileFromPipe(os.Stdin, serverType) - } - - var contents []byte - if strings.Contains(conf, "*") { - // Let caddyfile.doImport logic handle the globbed path - contents = []byte("import " + conf) - } else { - var err error - contents, err = ioutil.ReadFile(conf) - if err != nil { - return nil, err - } - } - - return caddy.CaddyfileInput{ - Contents: contents, - Filepath: conf, - ServerTypeName: serverType, - }, nil -} - -// defaultLoader loads the Caddyfile from the current working directory. -func defaultLoader(serverType string) (caddy.Input, error) { - contents, err := ioutil.ReadFile(caddy.DefaultConfigFile) - if err != nil { - if os.IsNotExist(err) { - return nil, nil - } - return nil, err - } - return caddy.CaddyfileInput{ - Contents: contents, - Filepath: caddy.DefaultConfigFile, - ServerTypeName: serverType, - }, nil -} - -// getBuildModule returns the build info of Caddy -// from debug.BuildInfo (requires Go modules). If -// no version information is available, a non-nil -// value will still be returned, but with an -// unknown version. -func getBuildModule() *debug.Module { - bi, ok := debug.ReadBuildInfo() - if ok { - // The recommended way to build Caddy involves - // creating a separate main module, which - // preserves caddy a read-only dependency - // TODO: track related Go issue: https://github.com/golang/go/issues/29228 - for _, mod := range bi.Deps { - if mod.Path == "github.com/mholt/caddy" { - return mod - } - } - } - return &debug.Module{Version: "unknown"} -} - -func checkJSONCaddyfile() { - if fromJSON { - jsonBytes, err := ioutil.ReadAll(os.Stdin) - if err != nil { - fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err) - os.Exit(1) - } - caddyfileBytes, err := caddyfile.FromJSON(jsonBytes) - if err != nil { - fmt.Fprintf(os.Stderr, "Converting from JSON failed: %v", err) - os.Exit(2) - } - fmt.Println(string(caddyfileBytes)) - os.Exit(0) - } - if toJSON { - caddyfileBytes, err := ioutil.ReadAll(os.Stdin) - if err != nil { - fmt.Fprintf(os.Stderr, "Read stdin failed: %v", err) - os.Exit(1) - } - jsonBytes, err := caddyfile.ToJSON(caddyfileBytes) - if err != nil { - fmt.Fprintf(os.Stderr, "Converting to JSON failed: %v", err) - os.Exit(2) - } - fmt.Println(string(jsonBytes)) - os.Exit(0) - } -} - -// setCPU parses string cpu and sets GOMAXPROCS -// according to its value. It accepts either -// a number (e.g. 3) or a percent (e.g. 50%). -// If the percent resolves to less than a single -// GOMAXPROCS, it rounds it up to GOMAXPROCS=1. -func setCPU(cpu string) error { - var numCPU int - - availCPU := runtime.NumCPU() - - if strings.HasSuffix(cpu, "%") { - // Percent - var percent float32 - pctStr := cpu[:len(cpu)-1] - pctInt, err := strconv.Atoi(pctStr) - if err != nil || pctInt < 1 || pctInt > 100 { - return errors.New("invalid CPU value: percentage must be between 1-100") - } - percent = float32(pctInt) / 100 - numCPU = int(float32(availCPU) * percent) - if numCPU < 1 { - numCPU = 1 - } - } else { - // Number - num, err := strconv.Atoi(cpu) - if err != nil || num < 1 { - return errors.New("invalid CPU value: provide a number or percent greater than 0") - } - numCPU = num - } - - if numCPU > availCPU { - numCPU = availCPU - } - - runtime.GOMAXPROCS(numCPU) - return nil -} - -// detectContainer attempts to determine whether the process is -// being run inside a container. References: -// https://tuhrig.de/how-to-know-you-are-inside-a-docker-container/ -// https://stackoverflow.com/a/20012536/1048862 -// https://gist.github.com/anantkamath/623ce7f5432680749e087cf8cfba9b69 -func detectContainer() bool { - if runtime.GOOS != "linux" { - return false - } - - file, err := os.Open("/proc/1/cgroup") - if err != nil { - return false - } - defer file.Close() - - i := 0 - scanner := bufio.NewScanner(file) - for scanner.Scan() { - i++ - if i > 1000 { - return false - } - - line := scanner.Text() - parts := strings.SplitN(line, ":", 3) - if len(parts) < 3 { - continue - } - - if strings.Contains(parts[2], "docker") || - strings.Contains(parts[2], "lxc") || - strings.Contains(parts[2], "moby") { - return true - } - } - - return false -} - -// initTelemetry initializes the telemetry engine. -func initTelemetry() error { - uuidFilename := filepath.Join(caddy.AssetsPath(), "uuid") - if customUUIDFile := os.Getenv("CADDY_UUID_FILE"); customUUIDFile != "" { - uuidFilename = customUUIDFile - } - - newUUID := func() uuid.UUID { - id := uuid.New() - err := os.MkdirAll(caddy.AssetsPath(), 0700) - if err != nil { - log.Printf("[ERROR] Persisting instance UUID: %v", err) - return id - } - err = ioutil.WriteFile(uuidFilename, []byte(id.String()), 0600) // human-readable as a string - if err != nil { - log.Printf("[ERROR] Persisting instance UUID: %v", err) - } - return id - } - - var id uuid.UUID - - // load UUID from storage, or create one if we don't have one - if uuidFile, err := os.Open(uuidFilename); os.IsNotExist(err) { - // no UUID exists yet; create a new one and persist it - id = newUUID() - } else if err != nil { - log.Printf("[ERROR] Loading persistent UUID: %v", err) - id = newUUID() - } else { - defer uuidFile.Close() - uuidBytes, err := ioutil.ReadAll(uuidFile) - if err != nil { - log.Printf("[ERROR] Reading persistent UUID: %v", err) - id = newUUID() - } else { - id, err = uuid.ParseBytes(uuidBytes) - if err != nil { - log.Printf("[ERROR] Parsing UUID: %v", err) - id = newUUID() - } - } - } - - // parse and check the list of disabled metrics - var disabledMetricsSlice []string - if len(disabledMetrics) > 0 { - if len(disabledMetrics) > 1024 { - // mitigate disk space exhaustion at the collection endpoint - return fmt.Errorf("too many metrics to disable") - } - disabledMetricsSlice = splitTrim(disabledMetrics, ",") - for _, metric := range disabledMetricsSlice { - if metric == "instance_id" || metric == "timestamp" || metric == "disabled_metrics" { - return fmt.Errorf("instance_id, timestamp, and disabled_metrics cannot be disabled") - } - } - } - - // initialize telemetry - telemetry.Init(id, disabledMetricsSlice) - - // if any metrics were disabled, report which ones (so we know how representative the data is) - if len(disabledMetricsSlice) > 0 { - telemetry.Set("disabled_metrics", disabledMetricsSlice) - log.Printf("[NOTICE] The following telemetry metrics are disabled: %s", disabledMetrics) - } - - return nil -} - -// Split string s into all substrings separated by sep and returns a slice of -// the substrings between those separators. -// -// If s does not contain sep and sep is not empty, Split returns a -// slice of length 1 whose only element is s. -// -// If sep is empty, Split splits after each UTF-8 sequence. If both s -// and sep are empty, Split returns an empty slice. -// -// Each item that in result is trim space and not empty string -func splitTrim(s string, sep string) []string { - splitItems := strings.Split(s, sep) - trimItems := make([]string, 0, len(splitItems)) - for _, item := range splitItems { - if item = strings.TrimSpace(item); item != "" { - trimItems = append(trimItems, item) - } - } - return trimItems -} - -// LoadEnvFromFile loads additional envs if file provided and exists -// Envs in file should be in KEY=VALUE format -func LoadEnvFromFile(envFile string) error { - if envFile == "" { - return nil - } - - file, err := os.Open(envFile) - if err != nil { - return err - } - defer file.Close() - - envMap, err := ParseEnvFile(file) - if err != nil { - return err - } - - for k, v := range envMap { - if err := os.Setenv(k, v); err != nil { - return err - } - } - - return nil -} - -// ParseEnvFile implements parse logic for environment files -func ParseEnvFile(envInput io.Reader) (map[string]string, error) { - envMap := make(map[string]string) - - scanner := bufio.NewScanner(envInput) - var line string - lineNumber := 0 - - for scanner.Scan() { - line = strings.TrimSpace(scanner.Text()) - lineNumber++ - - // skip lines starting with comment - if strings.HasPrefix(line, "#") { - continue - } - - // skip empty line - if len(line) == 0 { - continue - } - - fields := strings.SplitN(line, "=", 2) - if len(fields) != 2 { - return nil, fmt.Errorf("Can't parse line %d; line should be in KEY=VALUE format", lineNumber) - } - - if strings.Contains(fields[0], " ") { - return nil, fmt.Errorf("Can't parse line %d; KEY contains whitespace", lineNumber) - } - - key := fields[0] - val := fields[1] - - if key == "" { - return nil, fmt.Errorf("Can't parse line %d; KEY can't be empty string", lineNumber) - } - envMap[key] = val - } - - if err := scanner.Err(); err != nil { - return nil, err - } - - return envMap, nil -} - -const appName = "Caddy" - -// Flags that control program flow or startup -var ( - serverType string - conf string - cpu string - envFile string - fromJSON bool - logfile string - logRollMB int - logRollCompress bool - revoke string - toJSON bool - version bool - plugins bool - printEnv bool - validate bool - disabledMetrics string -) - -// EnableTelemetry defines whether telemetry is enabled in Run. -var EnableTelemetry = true diff --git a/vendor/github.com/mholt/caddy/caddyfile/dispenser.go b/vendor/github.com/mholt/caddy/caddyfile/dispenser.go deleted file mode 100644 index c7b3f4c12..000000000 --- a/vendor/github.com/mholt/caddy/caddyfile/dispenser.go +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddyfile - -import ( - "errors" - "fmt" - "io" - "strings" -) - -// Dispenser is a type that dispenses tokens, similarly to a lexer, -// except that it can do so with some notion of structure and has -// some really convenient methods. -type Dispenser struct { - filename string - tokens []Token - cursor int - nesting int -} - -// NewDispenser returns a Dispenser, ready to use for parsing the given input. -func NewDispenser(filename string, input io.Reader) Dispenser { - tokens, _ := allTokens(input) // ignoring error because nothing to do with it - return Dispenser{ - filename: filename, - tokens: tokens, - cursor: -1, - } -} - -// NewDispenserTokens returns a Dispenser filled with the given tokens. -func NewDispenserTokens(filename string, tokens []Token) Dispenser { - return Dispenser{ - filename: filename, - tokens: tokens, - cursor: -1, - } -} - -// Next loads the next token. Returns true if a token -// was loaded; false otherwise. If false, all tokens -// have been consumed. -func (d *Dispenser) Next() bool { - if d.cursor < len(d.tokens)-1 { - d.cursor++ - return true - } - return false -} - -// NextArg loads the next token if it is on the same -// line. Returns true if a token was loaded; false -// otherwise. If false, all tokens on the line have -// been consumed. It handles imported tokens correctly. -func (d *Dispenser) NextArg() bool { - if d.cursor < 0 { - d.cursor++ - return true - } - if d.cursor >= len(d.tokens) { - return false - } - if d.cursor < len(d.tokens)-1 && - d.tokens[d.cursor].File == d.tokens[d.cursor+1].File && - d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].Line { - d.cursor++ - return true - } - return false -} - -// NextLine loads the next token only if it is not on the same -// line as the current token, and returns true if a token was -// loaded; false otherwise. If false, there is not another token -// or it is on the same line. It handles imported tokens correctly. -func (d *Dispenser) NextLine() bool { - if d.cursor < 0 { - d.cursor++ - return true - } - if d.cursor >= len(d.tokens) { - return false - } - if d.cursor < len(d.tokens)-1 && - (d.tokens[d.cursor].File != d.tokens[d.cursor+1].File || - d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].Line) { - d.cursor++ - return true - } - return false -} - -// NextBlock can be used as the condition of a for loop -// to load the next token as long as it opens a block or -// is already in a block. It returns true if a token was -// loaded, or false when the block's closing curly brace -// was loaded and thus the block ended. Nested blocks are -// not supported. -func (d *Dispenser) NextBlock() bool { - if d.nesting > 0 { - d.Next() - if d.Val() == "}" { - d.nesting-- - return false - } - return true - } - if !d.NextArg() { // block must open on same line - return false - } - if d.Val() != "{" { - d.cursor-- // roll back if not opening brace - return false - } - d.Next() - if d.Val() == "}" { - // Open and then closed right away - return false - } - d.nesting++ - return true -} - -// Val gets the text of the current token. If there is no token -// loaded, it returns empty string. -func (d *Dispenser) Val() string { - if d.cursor < 0 || d.cursor >= len(d.tokens) { - return "" - } - return d.tokens[d.cursor].Text -} - -// Line gets the line number of the current token. If there is no token -// loaded, it returns 0. -func (d *Dispenser) Line() int { - if d.cursor < 0 || d.cursor >= len(d.tokens) { - return 0 - } - return d.tokens[d.cursor].Line -} - -// File gets the filename of the current token. If there is no token loaded, -// it returns the filename originally given when parsing started. -func (d *Dispenser) File() string { - if d.cursor < 0 || d.cursor >= len(d.tokens) { - return d.filename - } - if tokenFilename := d.tokens[d.cursor].File; tokenFilename != "" { - return tokenFilename - } - return d.filename -} - -// Args is a convenience function that loads the next arguments -// (tokens on the same line) into an arbitrary number of strings -// pointed to in targets. If there are fewer tokens available -// than string pointers, the remaining strings will not be changed -// and false will be returned. If there were enough tokens available -// to fill the arguments, then true will be returned. -func (d *Dispenser) Args(targets ...*string) bool { - enough := true - for i := 0; i < len(targets); i++ { - if !d.NextArg() { - enough = false - break - } - *targets[i] = d.Val() - } - return enough -} - -// RemainingArgs loads any more arguments (tokens on the same line) -// into a slice and returns them. Open curly brace tokens also indicate -// the end of arguments, and the curly brace is not included in -// the return value nor is it loaded. -func (d *Dispenser) RemainingArgs() []string { - var args []string - - for d.NextArg() { - if d.Val() == "{" { - d.cursor-- - break - } - args = append(args, d.Val()) - } - - return args -} - -// ArgErr returns an argument error, meaning that another -// argument was expected but not found. In other words, -// a line break or open curly brace was encountered instead of -// an argument. -func (d *Dispenser) ArgErr() error { - if d.Val() == "{" { - return d.Err("Unexpected token '{', expecting argument") - } - return d.Errf("Wrong argument count or unexpected line ending after '%s'", d.Val()) -} - -// SyntaxErr creates a generic syntax error which explains what was -// found and what was expected. -func (d *Dispenser) SyntaxErr(expected string) error { - msg := fmt.Sprintf("%s:%d - Syntax error: Unexpected token '%s', expecting '%s'", d.File(), d.Line(), d.Val(), expected) - return errors.New(msg) -} - -// EOFErr returns an error indicating that the dispenser reached -// the end of the input when searching for the next token. -func (d *Dispenser) EOFErr() error { - return d.Errf("Unexpected EOF") -} - -// Err generates a custom parse-time error with a message of msg. -func (d *Dispenser) Err(msg string) error { - msg = fmt.Sprintf("%s:%d - Error during parsing: %s", d.File(), d.Line(), msg) - return errors.New(msg) -} - -// Errf is like Err, but for formatted error messages -func (d *Dispenser) Errf(format string, args ...interface{}) error { - return d.Err(fmt.Sprintf(format, args...)) -} - -// numLineBreaks counts how many line breaks are in the token -// value given by the token index tknIdx. It returns 0 if the -// token does not exist or there are no line breaks. -func (d *Dispenser) numLineBreaks(tknIdx int) int { - if tknIdx < 0 || tknIdx >= len(d.tokens) { - return 0 - } - return strings.Count(d.tokens[tknIdx].Text, "\n") -} - -// isNewLine determines whether the current token is on a different -// line (higher line number) than the previous token. It handles imported -// tokens correctly. If there isn't a previous token, it returns true. -func (d *Dispenser) isNewLine() bool { - if d.cursor < 1 { - return true - } - if d.cursor > len(d.tokens)-1 { - return false - } - return d.tokens[d.cursor-1].File != d.tokens[d.cursor].File || - d.tokens[d.cursor-1].Line+d.numLineBreaks(d.cursor-1) < d.tokens[d.cursor].Line -} diff --git a/vendor/github.com/mholt/caddy/caddyfile/json.go b/vendor/github.com/mholt/caddy/caddyfile/json.go deleted file mode 100644 index 0d37e8e98..000000000 --- a/vendor/github.com/mholt/caddy/caddyfile/json.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddyfile - -import ( - "bytes" - "encoding/json" - "fmt" - "sort" - "strconv" - "strings" -) - -const filename = "Caddyfile" - -// ToJSON converts caddyfile to its JSON representation. -func ToJSON(caddyfile []byte) ([]byte, error) { - var j EncodedCaddyfile - - serverBlocks, err := Parse(filename, bytes.NewReader(caddyfile), nil) - if err != nil { - return nil, err - } - - for _, sb := range serverBlocks { - block := EncodedServerBlock{ - Keys: sb.Keys, - Body: [][]interface{}{}, - } - - // Extract directives deterministically by sorting them - var directives = make([]string, len(sb.Tokens)) - for dir := range sb.Tokens { - directives = append(directives, dir) - } - sort.Strings(directives) - - // Convert each directive's tokens into our JSON structure - for _, dir := range directives { - disp := NewDispenserTokens(filename, sb.Tokens[dir]) - for disp.Next() { - block.Body = append(block.Body, constructLine(&disp)) - } - } - - // tack this block onto the end of the list - j = append(j, block) - } - - result, err := json.Marshal(j) - if err != nil { - return nil, err - } - - return result, nil -} - -// constructLine transforms tokens into a JSON-encodable structure; -// but only one line at a time, to be used at the top-level of -// a server block only (where the first token on each line is a -// directive) - not to be used at any other nesting level. -func constructLine(d *Dispenser) []interface{} { - var args []interface{} - - args = append(args, d.Val()) - - for d.NextArg() { - if d.Val() == "{" { - args = append(args, constructBlock(d)) - continue - } - args = append(args, d.Val()) - } - - return args -} - -// constructBlock recursively processes tokens into a -// JSON-encodable structure. To be used in a directive's -// block. Goes to end of block. -func constructBlock(d *Dispenser) [][]interface{} { - block := [][]interface{}{} - - for d.Next() { - if d.Val() == "}" { - break - } - block = append(block, constructLine(d)) - } - - return block -} - -// FromJSON converts JSON-encoded jsonBytes to Caddyfile text -func FromJSON(jsonBytes []byte) ([]byte, error) { - var j EncodedCaddyfile - var result string - - err := json.Unmarshal(jsonBytes, &j) - if err != nil { - return nil, err - } - - for sbPos, sb := range j { - if sbPos > 0 { - result += "\n\n" - } - for i, key := range sb.Keys { - if i > 0 { - result += ", " - } - //result += standardizeScheme(key) - result += key - } - result += jsonToText(sb.Body, 1) - } - - return []byte(result), nil -} - -// jsonToText recursively transforms a scope of JSON into plain -// Caddyfile text. -func jsonToText(scope interface{}, depth int) string { - var result string - - switch val := scope.(type) { - case string: - if strings.ContainsAny(val, "\" \n\t\r") { - result += `"` + strings.Replace(val, "\"", "\\\"", -1) + `"` - } else { - result += val - } - case int: - result += strconv.Itoa(val) - case float64: - result += fmt.Sprintf("%v", val) - case bool: - result += fmt.Sprintf("%t", val) - case [][]interface{}: - result += " {\n" - for _, arg := range val { - result += strings.Repeat("\t", depth) + jsonToText(arg, depth+1) + "\n" - } - result += strings.Repeat("\t", depth-1) + "}" - case []interface{}: - for i, v := range val { - if block, ok := v.([]interface{}); ok { - result += "{\n" - for _, arg := range block { - result += strings.Repeat("\t", depth) + jsonToText(arg, depth+1) + "\n" - } - result += strings.Repeat("\t", depth-1) + "}" - continue - } - result += jsonToText(v, depth) - if i < len(val)-1 { - result += " " - } - } - } - - return result -} - -// TODO: Will this function come in handy somewhere else? -/* -// standardizeScheme turns an address like host:https into https://host, -// or "host:" into "host". -func standardizeScheme(addr string) string { - if hostname, port, err := net.SplitHostPort(addr); err == nil { - if port == "http" || port == "https" { - addr = port + "://" + hostname - } - } - return strings.TrimSuffix(addr, ":") -} -*/ - -// EncodedCaddyfile encapsulates a slice of EncodedServerBlocks. -type EncodedCaddyfile []EncodedServerBlock - -// EncodedServerBlock represents a server block ripe for encoding. -type EncodedServerBlock struct { - Keys []string `json:"keys"` - Body [][]interface{} `json:"body"` -} diff --git a/vendor/github.com/mholt/caddy/caddyfile/lexer.go b/vendor/github.com/mholt/caddy/caddyfile/lexer.go deleted file mode 100644 index 2b38627be..000000000 --- a/vendor/github.com/mholt/caddy/caddyfile/lexer.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddyfile - -import ( - "bufio" - "io" - "unicode" -) - -type ( - // lexer is a utility which can get values, token by - // token, from a Reader. A token is a word, and tokens - // are separated by whitespace. A word can be enclosed - // in quotes if it contains whitespace. - lexer struct { - reader *bufio.Reader - token Token - line int - } - - // Token represents a single parsable unit. - Token struct { - File string - Line int - Text string - } -) - -// load prepares the lexer to scan an input for tokens. -// It discards any leading byte order mark. -func (l *lexer) load(input io.Reader) error { - l.reader = bufio.NewReader(input) - l.line = 1 - - // discard byte order mark, if present - firstCh, _, err := l.reader.ReadRune() - if err != nil { - return err - } - if firstCh != 0xFEFF { - err := l.reader.UnreadRune() - if err != nil { - return err - } - } - - return nil -} - -// next loads the next token into the lexer. -// A token is delimited by whitespace, unless -// the token starts with a quotes character (") -// in which case the token goes until the closing -// quotes (the enclosing quotes are not included). -// Inside quoted strings, quotes may be escaped -// with a preceding \ character. No other chars -// may be escaped. The rest of the line is skipped -// if a "#" character is read in. Returns true if -// a token was loaded; false otherwise. -func (l *lexer) next() bool { - var val []rune - var comment, quoted, escaped bool - - makeToken := func() bool { - l.token.Text = string(val) - return true - } - - for { - ch, _, err := l.reader.ReadRune() - if err != nil { - if len(val) > 0 { - return makeToken() - } - if err == io.EOF { - return false - } - panic(err) - } - - if quoted { - if !escaped { - if ch == '\\' { - escaped = true - continue - } else if ch == '"' { - quoted = false - return makeToken() - } - } - if ch == '\n' { - l.line++ - } - if escaped { - // only escape quotes - if ch != '"' { - val = append(val, '\\') - } - } - val = append(val, ch) - escaped = false - continue - } - - if unicode.IsSpace(ch) { - if ch == '\r' { - continue - } - if ch == '\n' { - l.line++ - comment = false - } - if len(val) > 0 { - return makeToken() - } - continue - } - - if ch == '#' { - comment = true - } - - if comment { - continue - } - - if len(val) == 0 { - l.token = Token{Line: l.line} - if ch == '"' { - quoted = true - continue - } - } - - val = append(val, ch) - } -} diff --git a/vendor/github.com/mholt/caddy/caddyfile/parse.go b/vendor/github.com/mholt/caddy/caddyfile/parse.go deleted file mode 100644 index eb5512cbb..000000000 --- a/vendor/github.com/mholt/caddy/caddyfile/parse.go +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddyfile - -import ( - "io" - "log" - "os" - "path/filepath" - "strings" - - "github.com/mholt/caddy/telemetry" -) - -// Parse parses the input just enough to group tokens, in -// order, by server block. No further parsing is performed. -// Server blocks are returned in the order in which they appear. -// Directives that do not appear in validDirectives will cause -// an error. If you do not want to check for valid directives, -// pass in nil instead. -func Parse(filename string, input io.Reader, validDirectives []string) ([]ServerBlock, error) { - p := parser{Dispenser: NewDispenser(filename, input), validDirectives: validDirectives} - return p.parseAll() -} - -// allTokens lexes the entire input, but does not parse it. -// It returns all the tokens from the input, unstructured -// and in order. -func allTokens(input io.Reader) ([]Token, error) { - l := new(lexer) - err := l.load(input) - if err != nil { - return nil, err - } - var tokens []Token - for l.next() { - tokens = append(tokens, l.token) - } - return tokens, nil -} - -type parser struct { - Dispenser - block ServerBlock // current server block being parsed - validDirectives []string // a directive must be valid or it's an error - eof bool // if we encounter a valid EOF in a hard place - definedSnippets map[string][]Token -} - -func (p *parser) parseAll() ([]ServerBlock, error) { - var blocks []ServerBlock - - for p.Next() { - err := p.parseOne() - if err != nil { - return blocks, err - } - if len(p.block.Keys) > 0 { - blocks = append(blocks, p.block) - } - } - - return blocks, nil -} - -func (p *parser) parseOne() error { - p.block = ServerBlock{Tokens: make(map[string][]Token)} - - return p.begin() -} - -func (p *parser) begin() error { - if len(p.tokens) == 0 { - return nil - } - - err := p.addresses() - - if err != nil { - return err - } - - if p.eof { - // this happens if the Caddyfile consists of only - // a line of addresses and nothing else - return nil - } - - if ok, name := p.isSnippet(); ok { - if p.definedSnippets == nil { - p.definedSnippets = map[string][]Token{} - } - if _, found := p.definedSnippets[name]; found { - return p.Errf("redeclaration of previously declared snippet %s", name) - } - // consume all tokens til matched close brace - tokens, err := p.snippetTokens() - if err != nil { - return err - } - p.definedSnippets[name] = tokens - // empty block keys so we don't save this block as a real server. - p.block.Keys = nil - return nil - } - - return p.blockContents() -} - -func (p *parser) addresses() error { - var expectingAnother bool - - for { - tkn := replaceEnvVars(p.Val()) - - // special case: import directive replaces tokens during parse-time - if tkn == "import" && p.isNewLine() { - err := p.doImport() - if err != nil { - return err - } - continue - } - - // Open brace definitely indicates end of addresses - if tkn == "{" { - if expectingAnother { - return p.Errf("Expected another address but had '%s' - check for extra comma", tkn) - } - break - } - - if tkn != "" { // empty token possible if user typed "" - // Trailing comma indicates another address will follow, which - // may possibly be on the next line - if tkn[len(tkn)-1] == ',' { - tkn = tkn[:len(tkn)-1] - expectingAnother = true - } else { - expectingAnother = false // but we may still see another one on this line - } - - p.block.Keys = append(p.block.Keys, tkn) - } - - // Advance token and possibly break out of loop or return error - hasNext := p.Next() - if expectingAnother && !hasNext { - return p.EOFErr() - } - if !hasNext { - p.eof = true - break // EOF - } - if !expectingAnother && p.isNewLine() { - break - } - } - - return nil -} - -func (p *parser) blockContents() error { - errOpenCurlyBrace := p.openCurlyBrace() - if errOpenCurlyBrace != nil { - // single-server configs don't need curly braces - p.cursor-- - } - - err := p.directives() - if err != nil { - return err - } - - // Only look for close curly brace if there was an opening - if errOpenCurlyBrace == nil { - err = p.closeCurlyBrace() - if err != nil { - return err - } - } - - return nil -} - -// directives parses through all the lines for directives -// and it expects the next token to be the first -// directive. It goes until EOF or closing curly brace -// which ends the server block. -func (p *parser) directives() error { - for p.Next() { - // end of server block - if p.Val() == "}" { - break - } - - // special case: import directive replaces tokens during parse-time - if p.Val() == "import" { - err := p.doImport() - if err != nil { - return err - } - p.cursor-- // cursor is advanced when we continue, so roll back one more - continue - } - - // normal case: parse a directive on this line - if err := p.directive(); err != nil { - return err - } - } - return nil -} - -// doImport swaps out the import directive and its argument -// (a total of 2 tokens) with the tokens in the specified file -// or globbing pattern. When the function returns, the cursor -// is on the token before where the import directive was. In -// other words, call Next() to access the first token that was -// imported. -func (p *parser) doImport() error { - // syntax checks - if !p.NextArg() { - return p.ArgErr() - } - importPattern := replaceEnvVars(p.Val()) - if importPattern == "" { - return p.Err("Import requires a non-empty filepath") - } - if p.NextArg() { - return p.Err("Import takes only one argument (glob pattern or file)") - } - // splice out the import directive and its argument (2 tokens total) - tokensBefore := p.tokens[:p.cursor-1] - tokensAfter := p.tokens[p.cursor+1:] - var importedTokens []Token - - // first check snippets. That is a simple, non-recursive replacement - if p.definedSnippets != nil && p.definedSnippets[importPattern] != nil { - importedTokens = p.definedSnippets[importPattern] - } else { - // make path relative to the file of the _token_ being processed rather - // than current working directory (issue #867) and then use glob to get - // list of matching filenames - absFile, err := filepath.Abs(p.Dispenser.File()) - if err != nil { - return p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err) - } - - var matches []string - var globPattern string - if !filepath.IsAbs(importPattern) { - globPattern = filepath.Join(filepath.Dir(absFile), importPattern) - } else { - globPattern = importPattern - } - if strings.Count(globPattern, "*") > 1 || strings.Count(globPattern, "?") > 1 || - (strings.Contains(globPattern, "[") && strings.Contains(globPattern, "]")) { - // See issue #2096 - a pattern with many glob expansions can hang for too long - return p.Errf("Glob pattern may only contain one wildcard (*), but has others: %s", globPattern) - } - matches, err = filepath.Glob(globPattern) - - if err != nil { - return p.Errf("Failed to use import pattern %s: %v", importPattern, err) - } - if len(matches) == 0 { - if strings.ContainsAny(globPattern, "*?[]") { - log.Printf("[WARNING] No files matching import glob pattern: %s", importPattern) - } else { - return p.Errf("File to import not found: %s", importPattern) - } - } - - // collect all the imported tokens - - for _, importFile := range matches { - newTokens, err := p.doSingleImport(importFile) - if err != nil { - return err - } - importedTokens = append(importedTokens, newTokens...) - } - } - - // splice the imported tokens in the place of the import statement - // and rewind cursor so Next() will land on first imported token - p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...) - p.cursor-- - - return nil -} - -// doSingleImport lexes the individual file at importFile and returns -// its tokens or an error, if any. -func (p *parser) doSingleImport(importFile string) ([]Token, error) { - file, err := os.Open(importFile) - if err != nil { - return nil, p.Errf("Could not import %s: %v", importFile, err) - } - defer file.Close() - - if info, err := file.Stat(); err != nil { - return nil, p.Errf("Could not import %s: %v", importFile, err) - } else if info.IsDir() { - return nil, p.Errf("Could not import %s: is a directory", importFile) - } - - importedTokens, err := allTokens(file) - if err != nil { - return nil, p.Errf("Could not read tokens while importing %s: %v", importFile, err) - } - - // Tack the file path onto these tokens so errors show the imported file's name - // (we use full, absolute path to avoid bugs: issue #1892) - filename, err := filepath.Abs(importFile) - if err != nil { - return nil, p.Errf("Failed to get absolute path of file: %s: %v", p.Dispenser.filename, err) - } - for i := 0; i < len(importedTokens); i++ { - importedTokens[i].File = filename - } - - return importedTokens, nil -} - -// directive collects tokens until the directive's scope -// closes (either end of line or end of curly brace block). -// It expects the currently-loaded token to be a directive -// (or } that ends a server block). The collected tokens -// are loaded into the current server block for later use -// by directive setup functions. -func (p *parser) directive() error { - dir := replaceEnvVars(p.Val()) - nesting := 0 - - // TODO: More helpful error message ("did you mean..." or "maybe you need to install its server type") - if !p.validDirective(dir) { - return p.Errf("Unknown directive '%s'", dir) - } - - // The directive itself is appended as a relevant token - p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) - telemetry.AppendUnique("directives", dir) - - for p.Next() { - if p.Val() == "{" { - nesting++ - } else if p.isNewLine() && nesting == 0 { - p.cursor-- // read too far - break - } else if p.Val() == "}" && nesting > 0 { - nesting-- - } else if p.Val() == "}" && nesting == 0 { - return p.Err("Unexpected '}' because no matching opening brace") - } else if p.Val() == "import" && p.isNewLine() { - if err := p.doImport(); err != nil { - return err - } - p.cursor-- // cursor is advanced when we continue, so roll back one more - continue - } - p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text) - p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) - } - - if nesting > 0 { - return p.EOFErr() - } - return nil -} - -// openCurlyBrace expects the current token to be an -// opening curly brace. This acts like an assertion -// because it returns an error if the token is not -// a opening curly brace. It does NOT advance the token. -func (p *parser) openCurlyBrace() error { - if p.Val() != "{" { - return p.SyntaxErr("{") - } - return nil -} - -// closeCurlyBrace expects the current token to be -// a closing curly brace. This acts like an assertion -// because it returns an error if the token is not -// a closing curly brace. It does NOT advance the token. -func (p *parser) closeCurlyBrace() error { - if p.Val() != "}" { - return p.SyntaxErr("}") - } - return nil -} - -// validDirective returns true if dir is in p.validDirectives. -func (p *parser) validDirective(dir string) bool { - if p.validDirectives == nil { - return true - } - for _, d := range p.validDirectives { - if d == dir { - return true - } - } - return false -} - -// replaceEnvVars replaces environment variables that appear in the token -// and understands both the $UNIX and %WINDOWS% syntaxes. -func replaceEnvVars(s string) string { - s = replaceEnvReferences(s, "{%", "%}") - s = replaceEnvReferences(s, "{$", "}") - return s -} - -// replaceEnvReferences performs the actual replacement of env variables -// in s, given the placeholder start and placeholder end strings. -func replaceEnvReferences(s, refStart, refEnd string) string { - index := strings.Index(s, refStart) - for index != -1 { - endIndex := strings.Index(s[index:], refEnd) - if endIndex == -1 { - break - } - - endIndex += index - if endIndex > index+len(refStart) { - ref := s[index : endIndex+len(refEnd)] - s = strings.Replace(s, ref, os.Getenv(ref[len(refStart):len(ref)-len(refEnd)]), -1) - } else { - return s - } - index = strings.Index(s, refStart) - } - return s -} - -// ServerBlock associates any number of keys (usually addresses -// of some sort) with tokens (grouped by directive name). -type ServerBlock struct { - Keys []string - Tokens map[string][]Token -} - -func (p *parser) isSnippet() (bool, string) { - keys := p.block.Keys - // A snippet block is a single key with parens. Nothing else qualifies. - if len(keys) == 1 && strings.HasPrefix(keys[0], "(") && strings.HasSuffix(keys[0], ")") { - return true, strings.TrimSuffix(keys[0][1:], ")") - } - return false, "" -} - -// read and store everything in a block for later replay. -func (p *parser) snippetTokens() ([]Token, error) { - // TODO: disallow imports in snippets for simplicity at import time - // snippet must have curlies. - err := p.openCurlyBrace() - if err != nil { - return nil, err - } - count := 1 - tokens := []Token{} - for p.Next() { - if p.Val() == "}" { - count-- - if count == 0 { - break - } - } - if p.Val() == "{" { - count++ - } - tokens = append(tokens, p.tokens[p.cursor]) - } - // make sure we're matched up - if count != 0 { - return nil, p.SyntaxErr("}") - } - return tokens, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/basicauth/basicauth.go b/vendor/github.com/mholt/caddy/caddyhttp/basicauth/basicauth.go deleted file mode 100644 index 93e79b97b..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/basicauth/basicauth.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 basicauth implements HTTP Basic Authentication for Caddy. -// -// This is useful for simple protections on a website, like requiring -// a password to access an admin interface. This package assumes a -// fairly small threat model. -package basicauth - -import ( - "bufio" - "context" - "crypto/sha1" - "crypto/subtle" - "fmt" - "io" - "log" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - - "github.com/jimstudt/http-authentication/basic" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// BasicAuth is middleware to protect resources with a username and password. -// Note that HTTP Basic Authentication is not secure by itself and should -// not be used to protect important assets without HTTPS. Even then, the -// security of HTTP Basic Auth is disputed. Use discretion when deciding -// what to protect with BasicAuth. -type BasicAuth struct { - Next httpserver.Handler - SiteRoot string - Rules []Rule -} - -// ServeHTTP implements the httpserver.Handler interface. -func (a BasicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - var protected, isAuthenticated bool - var realm string - var username string - var password string - var ok bool - - // do not check for basic auth on OPTIONS call - if r.Method == http.MethodOptions { - // Pass-through when no paths match - return a.Next.ServeHTTP(w, r) - } - - for _, rule := range a.Rules { - for _, res := range rule.Resources { - if !httpserver.Path(r.URL.Path).Matches(res) { - continue - } - - // path matches; this endpoint is protected - protected = true - realm = rule.Realm - - // parse auth header - username, password, ok = r.BasicAuth() - - // check credentials - if !ok || - username != rule.Username || - !rule.Password(password) { - continue - } - - // by this point, authentication was successful - isAuthenticated = true - - // let upstream middleware (e.g. fastcgi and cgi) know about authenticated - // user; this replaces the request with a wrapped instance - r = r.WithContext(context.WithValue(r.Context(), - httpserver.RemoteUserCtxKey, username)) - - // Provide username to be used in log by replacer - repl := httpserver.NewReplacer(r, nil, "-") - repl.Set("user", username) - } - } - - if protected && !isAuthenticated { - // browsers show a message that says something like: - // "The website says: " - // which is kinda dumb, but whatever. - if realm == "" { - realm = "Restricted" - } - w.Header().Set("WWW-Authenticate", "Basic realm=\""+realm+"\"") - - // Get a replacer so we can provide basic info for the authentication error. - repl := httpserver.NewReplacer(r, nil, "-") - repl.Set("user", username) - errstr := repl.Replace("BasicAuth: user \"{user}\" was not found or password was incorrect. {remote} {host} {uri} {proto}") - err := fmt.Errorf("%s", errstr) - return http.StatusUnauthorized, err - } - - // Pass-through when no paths match - return a.Next.ServeHTTP(w, r) -} - -// Rule represents a BasicAuth rule. A username and password -// combination protect the associated resources, which are -// file or directory paths. -type Rule struct { - Username string - Password func(string) bool - Resources []string - Realm string // See RFC 1945 and RFC 2617, default: "Restricted" -} - -// PasswordMatcher determines whether a password matches a rule. -type PasswordMatcher func(pw string) bool - -var ( - htpasswords map[string]map[string]PasswordMatcher - htpasswordsMu sync.Mutex -) - -// GetHtpasswdMatcher matches password rules. -func GetHtpasswdMatcher(filename, username, siteRoot string) (PasswordMatcher, error) { - filename = filepath.Join(siteRoot, filename) - htpasswordsMu.Lock() - if htpasswords == nil { - htpasswords = make(map[string]map[string]PasswordMatcher) - } - pm := htpasswords[filename] - if pm == nil { - fh, err := os.Open(filename) - if err != nil { - return nil, fmt.Errorf("open %q: %v", filename, err) - } - defer fh.Close() - pm = make(map[string]PasswordMatcher) - if err = parseHtpasswd(pm, fh); err != nil { - return nil, fmt.Errorf("parsing htpasswd %q: %v", fh.Name(), err) - } - htpasswords[filename] = pm - } - htpasswordsMu.Unlock() - if pm[username] == nil { - return nil, fmt.Errorf("username %q not found in %q", username, filename) - } - return pm[username], nil -} - -func parseHtpasswd(pm map[string]PasswordMatcher, r io.Reader) error { - scanner := bufio.NewScanner(r) - for scanner.Scan() { - line := strings.TrimSpace(scanner.Text()) - if line == "" || strings.IndexByte(line, '#') == 0 { - continue - } - i := strings.IndexByte(line, ':') - if i <= 0 { - return fmt.Errorf("malformed line, no color: %q", line) - } - user, encoded := line[:i], line[i+1:] - for _, p := range basic.DefaultSystems { - matcher, err := p(encoded) - if err != nil { - return err - } - if matcher != nil { - pm[user] = matcher.MatchesPassword - break - } - } - } - return scanner.Err() -} - -// PlainMatcher returns a PasswordMatcher that does a constant-time -// byte comparison against the password passw. -func PlainMatcher(passw string) PasswordMatcher { - // compare hashes of equal length instead of actual password - // to avoid leaking password length - passwHash := sha1.New() - if _, err := passwHash.Write([]byte(passw)); err != nil { - log.Printf("[ERROR] unable to write password hash: %v", err) - } - passwSum := passwHash.Sum(nil) - return func(pw string) bool { - pwHash := sha1.New() - if _, err := pwHash.Write([]byte(pw)); err != nil { - log.Printf("[ERROR] unable to write password hash: %v", err) - } - pwSum := pwHash.Sum(nil) - return subtle.ConstantTimeCompare([]byte(pwSum), []byte(passwSum)) == 1 - } -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/basicauth/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/basicauth/setup.go deleted file mode 100644 index 87627f179..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/basicauth/setup.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 basicauth - -import ( - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("basicauth", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new BasicAuth middleware instance. -func setup(c *caddy.Controller) error { - cfg := httpserver.GetConfig(c) - root := cfg.Root - - rules, err := basicAuthParse(c) - if err != nil { - return err - } - - basic := BasicAuth{Rules: rules} - - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - basic.Next = next - basic.SiteRoot = root - return basic - }) - - return nil -} - -func basicAuthParse(c *caddy.Controller) ([]Rule, error) { - var rules []Rule - cfg := httpserver.GetConfig(c) - - var err error - for c.Next() { - var rule Rule - - args := c.RemainingArgs() - - switch len(args) { - case 2: - rule.Username = args[0] - if rule.Password, err = passwordMatcher(rule.Username, args[1], cfg.Root); err != nil { - return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err) - } - case 3: - rule.Resources = append(rule.Resources, args[0]) - rule.Username = args[1] - if rule.Password, err = passwordMatcher(rule.Username, args[2], cfg.Root); err != nil { - return rules, c.Errf("Get password matcher from %s: %v", c.Val(), err) - } - default: - return rules, c.ArgErr() - } - - // If nested block is present, process it here - for c.NextBlock() { - val := c.Val() - args = c.RemainingArgs() - switch len(args) { - case 0: - // Assume single argument is path resource - rule.Resources = append(rule.Resources, val) - case 1: - if val == "realm" { - if rule.Realm == "" { - rule.Realm = strings.Replace(args[0], `"`, `\"`, -1) - } else { - return rules, c.Errf("\"realm\" subdirective can only be specified once") - } - } else { - return rules, c.Errf("expecting \"realm\", got \"%s\"", val) - } - default: - return rules, c.ArgErr() - } - } - - rules = append(rules, rule) - } - - return rules, nil -} - -func passwordMatcher(username, passw, siteRoot string) (PasswordMatcher, error) { - htpasswdPrefix := "htpasswd=" - if !strings.HasPrefix(passw, htpasswdPrefix) { - return PlainMatcher(passw), nil - } - return GetHtpasswdMatcher(passw[len(htpasswdPrefix):], username, siteRoot) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/bind/bind.go b/vendor/github.com/mholt/caddy/caddyhttp/bind/bind.go deleted file mode 100644 index 79489fe4a..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/bind/bind.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 bind - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("bind", caddy.Plugin{ - ServerType: "http", - Action: setupBind, - }) -} - -func setupBind(c *caddy.Controller) error { - config := httpserver.GetConfig(c) - for c.Next() { - if !c.Args(&config.ListenHost) { - return c.ArgErr() - } - config.TLS.Manager.ListenHost = config.ListenHost // necessary for ACME challenges, see issue #309 - } - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/browse/browse.go b/vendor/github.com/mholt/caddy/caddyhttp/browse/browse.go deleted file mode 100644 index fd09ad659..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/browse/browse.go +++ /dev/null @@ -1,527 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 browse provides middleware for listing files in a directory -// when directory path is requested instead of a specific file. -package browse - -import ( - "bytes" - "encoding/json" - "net/http" - "net/url" - "os" - "path" - "sort" - "strconv" - "strings" - "text/template" - "time" - - "github.com/dustin/go-humanize" - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddyhttp/staticfiles" -) - -const ( - sortByName = "name" - sortByNameDirFirst = "namedirfirst" - sortBySize = "size" - sortByTime = "time" -) - -// Browse is an http.Handler that can show a file listing when -// directories in the given paths are specified. -type Browse struct { - Next httpserver.Handler - Configs []Config - IgnoreIndexes bool -} - -// Config is a configuration for browsing in a particular path. -type Config struct { - PathScope string // the base path the URL must match to enable browsing - Fs staticfiles.FileServer - Variables interface{} - Template *template.Template -} - -// A Listing is the context used to fill out a template. -type Listing struct { - // The name of the directory (the last element of the path). - Name string - - // The full path of the request. - Path string - - // Whether the parent directory is browse-able. - CanGoUp bool - - // The items (files and folders) in the path. - Items []FileInfo - - // The number of directories in the listing. - NumDirs int - - // The number of files (items that aren't directories) in the listing. - NumFiles int - - // Which sorting order is used. - Sort string - - // And which order. - Order string - - // If ≠0 then Items have been limited to that many elements. - ItemsLimitedTo int - - // Optional custom variables for use in browse templates. - User interface{} - - httpserver.Context -} - -// Crumb represents part of a breadcrumb menu. -type Crumb struct { - Link, Text string -} - -// Breadcrumbs returns l.Path where every element maps -// the link to the text to display. -func (l Listing) Breadcrumbs() []Crumb { - var result []Crumb - - if len(l.Path) == 0 { - return result - } - - // skip trailing slash - lpath := l.Path - if lpath[len(lpath)-1] == '/' { - lpath = lpath[:len(lpath)-1] - } - - parts := strings.Split(lpath, "/") - for i := range parts { - txt := parts[i] - if i == 0 && parts[i] == "" { - txt = "/" - } - result = append(result, Crumb{Link: strings.Repeat("../", len(parts)-i-1), Text: txt}) - } - - return result -} - -// FileInfo is the info about a particular file or directory -type FileInfo struct { - Name string - Size int64 - URL string - ModTime time.Time - Mode os.FileMode - IsDir bool - IsSymlink bool -} - -// HumanSize returns the size of the file as a human-readable string -// in IEC format (i.e. power of 2 or base 1024). -func (fi FileInfo) HumanSize() string { - return humanize.IBytes(uint64(fi.Size)) -} - -// HumanModTime returns the modified time of the file as a human-readable string. -func (fi FileInfo) HumanModTime(format string) string { - return fi.ModTime.Format(format) -} - -// Implement sorting for Listing -type byName Listing -type byNameDirFirst Listing -type bySize Listing -type byTime Listing - -// By Name -func (l byName) Len() int { return len(l.Items) } -func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } - -// Treat upper and lower case equally -func (l byName) Less(i, j int) bool { - return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) -} - -// By Name Dir First -func (l byNameDirFirst) Len() int { return len(l.Items) } -func (l byNameDirFirst) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } - -// Treat upper and lower case equally -func (l byNameDirFirst) Less(i, j int) bool { - - // if both are dir or file sort normally - if l.Items[i].IsDir == l.Items[j].IsDir { - return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) - } - - // always sort dir ahead of file - return l.Items[i].IsDir -} - -// By Size -func (l bySize) Len() int { return len(l.Items) } -func (l bySize) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } - -const directoryOffset = -1 << 31 // = math.MinInt32 -func (l bySize) Less(i, j int) bool { - iSize, jSize := l.Items[i].Size, l.Items[j].Size - - // Directory sizes depend on the filesystem implementation, - // which is opaque to a visitor, and should indeed does not change if the operator chooses to change the fs. - // For a consistent user experience directories are pulled to the front… - if l.Items[i].IsDir { - iSize = directoryOffset - } - if l.Items[j].IsDir { - jSize = directoryOffset - } - // … and sorted by name. - if l.Items[i].IsDir && l.Items[j].IsDir { - return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name) - } - - return iSize < jSize -} - -// By Time -func (l byTime) Len() int { return len(l.Items) } -func (l byTime) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] } -func (l byTime) Less(i, j int) bool { return l.Items[i].ModTime.Before(l.Items[j].ModTime) } - -// Add sorting method to "Listing" -// it will apply what's in ".Sort" and ".Order" -func (l Listing) applySort() { - // Check '.Order' to know how to sort - if l.Order == "desc" { - switch l.Sort { - case sortByName: - sort.Sort(sort.Reverse(byName(l))) - case sortByNameDirFirst: - sort.Sort(sort.Reverse(byNameDirFirst(l))) - case sortBySize: - sort.Sort(sort.Reverse(bySize(l))) - case sortByTime: - sort.Sort(sort.Reverse(byTime(l))) - default: - // If not one of the above, do nothing - return - } - } else { // If we had more Orderings we could add them here - switch l.Sort { - case sortByName: - sort.Sort(byName(l)) - case sortByNameDirFirst: - sort.Sort(byNameDirFirst(l)) - case sortBySize: - sort.Sort(bySize(l)) - case sortByTime: - sort.Sort(byTime(l)) - default: - // If not one of the above, do nothing - return - } - } -} - -func directoryListing(files []os.FileInfo, canGoUp bool, urlPath string, config *Config) (Listing, bool) { - var ( - fileInfos []FileInfo - dirCount, fileCount int - hasIndexFile bool - ) - - for _, f := range files { - name := f.Name() - - for _, indexName := range config.Fs.IndexPages { - if name == indexName { - hasIndexFile = true - break - } - } - - isDir := f.IsDir() || isSymlinkTargetDir(f, urlPath, config) - - if isDir { - name += "/" - dirCount++ - } else { - fileCount++ - } - - if config.Fs.IsHidden(f) { - continue - } - - u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name - - fileInfos = append(fileInfos, FileInfo{ - IsDir: isDir, - IsSymlink: isSymlink(f), - Name: f.Name(), - Size: f.Size(), - URL: u.String(), - ModTime: f.ModTime().UTC(), - Mode: f.Mode(), - }) - } - - return Listing{ - Name: path.Base(urlPath), - Path: urlPath, - CanGoUp: canGoUp, - Items: fileInfos, - NumDirs: dirCount, - NumFiles: fileCount, - }, hasIndexFile -} - -// isSymlink return true if f is a symbolic link -func isSymlink(f os.FileInfo) bool { - return f.Mode()&os.ModeSymlink != 0 -} - -// isSymlinkTargetDir return true if f's symbolic link target -// is a directory. Return false if not a symbolic link. -func isSymlinkTargetDir(f os.FileInfo, urlPath string, config *Config) bool { - if !isSymlink(f) { - return false - } - - // a bit strange, but we want Stat thru the jailed filesystem to be safe - target, err := config.Fs.Root.Open(path.Join(urlPath, f.Name())) - if err != nil { - return false - } - defer target.Close() - targetInfo, err := target.Stat() - if err != nil { - return false - } - - return targetInfo.IsDir() -} - -// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met. -// If so, control is handed over to ServeListing. -func (b Browse) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // See if there's a browse configuration to match the path - var bc *Config - for i := range b.Configs { - if httpserver.Path(r.URL.Path).Matches(b.Configs[i].PathScope) { - bc = &b.Configs[i] - break - } - } - if bc == nil { - return b.Next.ServeHTTP(w, r) - } - - // Browse works on existing directories; delegate everything else - requestedFilepath, err := bc.Fs.Root.Open(r.URL.Path) - if err != nil { - switch { - case os.IsPermission(err): - return http.StatusForbidden, err - case os.IsExist(err): - return http.StatusNotFound, err - default: - return b.Next.ServeHTTP(w, r) - } - } - defer requestedFilepath.Close() - - info, err := requestedFilepath.Stat() - if err != nil { - switch { - case os.IsPermission(err): - return http.StatusForbidden, err - case os.IsExist(err): - return http.StatusGone, err - default: - return b.Next.ServeHTTP(w, r) - } - } - if !info.IsDir() { - return b.Next.ServeHTTP(w, r) - } - - // Do not reply to anything else because it might be nonsensical - switch r.Method { - case http.MethodGet, http.MethodHead: - // proceed, noop - case "PROPFIND", http.MethodOptions: - return http.StatusNotImplemented, nil - default: - return b.Next.ServeHTTP(w, r) - } - - // Browsing navigation gets messed up if browsing a directory - // that doesn't end in "/" (which it should, anyway) - u := *r.URL - if u.Path == "" { - u.Path = "/" - } - if u.Path[len(u.Path)-1] != '/' { - u.Path += "/" - http.Redirect(w, r, u.String(), http.StatusMovedPermanently) - return http.StatusMovedPermanently, nil - } - - return b.ServeListing(w, r, requestedFilepath, bc) -} - -func (b Browse) loadDirectoryContents(requestedFilepath http.File, urlPath string, config *Config) (*Listing, bool, error) { - files, err := requestedFilepath.Readdir(-1) - if err != nil { - return nil, false, err - } - - // Determine if user can browse up another folder - var canGoUp bool - curPathDir := path.Dir(strings.TrimSuffix(urlPath, "/")) - for _, other := range b.Configs { - if strings.HasPrefix(curPathDir, other.PathScope) { - canGoUp = true - break - } - } - - // Assemble listing of directory contents - listing, hasIndex := directoryListing(files, canGoUp, urlPath, config) - - return &listing, hasIndex, nil -} - -// handleSortOrder gets and stores for a Listing the 'sort' and 'order', -// and reads 'limit' if given. The latter is 0 if not given. -// -// This sets Cookies. -func (b Browse) handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, limit int, err error) { - sort, order, limitQuery := r.URL.Query().Get("sort"), r.URL.Query().Get("order"), r.URL.Query().Get("limit") - - // If the query 'sort' or 'order' is empty, use defaults or any values previously saved in Cookies - switch sort { - case "": - sort = sortByNameDirFirst - if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil { - sort = sortCookie.Value - } - case sortByName, sortByNameDirFirst, sortBySize, sortByTime: - http.SetCookie(w, &http.Cookie{Name: "sort", Value: sort, Path: scope, Secure: r.TLS != nil}) - } - - switch order { - case "": - order = "asc" - if orderCookie, orderErr := r.Cookie("order"); orderErr == nil { - order = orderCookie.Value - } - case "asc", "desc": - http.SetCookie(w, &http.Cookie{Name: "order", Value: order, Path: scope, Secure: r.TLS != nil}) - } - - if limitQuery != "" { - limit, err = strconv.Atoi(limitQuery) - if err != nil { // if the 'limit' query can't be interpreted as a number, return err - return - } - } - - return -} - -// ServeListing returns a formatted view of 'requestedFilepath' contents'. -func (b Browse) ServeListing(w http.ResponseWriter, r *http.Request, requestedFilepath http.File, bc *Config) (int, error) { - listing, containsIndex, err := b.loadDirectoryContents(requestedFilepath, r.URL.Path, bc) - if err != nil { - switch { - case os.IsPermission(err): - return http.StatusForbidden, err - case os.IsExist(err): - return http.StatusGone, err - default: - return http.StatusInternalServerError, err - } - } - if containsIndex && !b.IgnoreIndexes { // directory isn't browsable - return b.Next.ServeHTTP(w, r) - } - listing.Context = httpserver.Context{ - Root: bc.Fs.Root, - Req: r, - URL: r.URL, - } - listing.User = bc.Variables - - // Copy the query values into the Listing struct - var limit int - listing.Sort, listing.Order, limit, err = b.handleSortOrder(w, r, bc.PathScope) - if err != nil { - return http.StatusBadRequest, err - } - - listing.applySort() - - if limit > 0 && limit <= len(listing.Items) { - listing.Items = listing.Items[:limit] - listing.ItemsLimitedTo = limit - } - - var buf *bytes.Buffer - acceptHeader := strings.ToLower(strings.Join(r.Header["Accept"], ",")) - switch { - case strings.Contains(acceptHeader, "application/json"): - if buf, err = b.formatAsJSON(listing, bc); err != nil { - return http.StatusInternalServerError, err - } - w.Header().Set("Content-Type", "application/json; charset=utf-8") - - default: // There's no 'application/json' in the 'Accept' header; browse normally - if buf, err = b.formatAsHTML(listing, bc); err != nil { - return http.StatusInternalServerError, err - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - - } - - _, _ = buf.WriteTo(w) - - return http.StatusOK, nil -} - -func (b Browse) formatAsJSON(listing *Listing, bc *Config) (*bytes.Buffer, error) { - marsh, err := json.Marshal(listing.Items) - if err != nil { - return nil, err - } - - buf := new(bytes.Buffer) - _, err = buf.Write(marsh) - return buf, err -} - -func (b Browse) formatAsHTML(listing *Listing, bc *Config) (*bytes.Buffer, error) { - buf := new(bytes.Buffer) - err := bc.Template.Execute(buf, listing) - return buf, err -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/browse/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/browse/setup.go deleted file mode 100644 index 90b1646d1..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/browse/setup.go +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 browse - -import ( - "fmt" - "io/ioutil" - "net/http" - "text/template" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddyhttp/staticfiles" -) - -func init() { - caddy.RegisterPlugin("browse", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Browse middleware instance. -func setup(c *caddy.Controller) error { - configs, err := browseParse(c) - if err != nil { - return err - } - - b := Browse{ - Configs: configs, - IgnoreIndexes: false, - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - b.Next = next - return b - }) - - return nil -} - -func browseParse(c *caddy.Controller) ([]Config, error) { - var configs []Config - - cfg := httpserver.GetConfig(c) - - appendCfg := func(bc Config) error { - for _, c := range configs { - if c.PathScope == bc.PathScope { - return fmt.Errorf("duplicate browsing config for %s", c.PathScope) - } - } - configs = append(configs, bc) - return nil - } - - for c.Next() { - var bc Config - - // First argument is directory to allow browsing; default is site root - if c.NextArg() { - bc.PathScope = c.Val() - } else { - bc.PathScope = "/" - } - - bc.Fs = staticfiles.FileServer{ - Root: http.Dir(cfg.Root), - Hide: cfg.HiddenFiles, - IndexPages: cfg.IndexPages, - } - - // Second argument would be the template file to use - var tplText string - if c.NextArg() { - tplBytes, err := ioutil.ReadFile(c.Val()) - if err != nil { - return configs, err - } - tplText = string(tplBytes) - } else { - tplText = defaultTemplate - } - - // Build the template - tpl, err := template.New("listing").Parse(tplText) - if err != nil { - return configs, err - } - bc.Template = tpl - - // Save configuration - err = appendCfg(bc) - if err != nil { - return configs, err - } - } - - return configs, nil -} - -// The default template to use when serving up directory listings -const defaultTemplate = ` - - - {{html .Name}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- {{range $i, $crumb := .Breadcrumbs}}{{html $crumb.Text}}{{if ne $i 0}}/{{end}}{{end}} -

-
-
-
-
- {{.NumDirs}} director{{if eq 1 .NumDirs}}y{{else}}ies{{end}} - {{.NumFiles}} file{{if ne 1 .NumFiles}}s{{end}} - {{- if ne 0 .ItemsLimitedTo}} - (of which only {{.ItemsLimitedTo}} are displayed) - {{- end}} - -
-
-
- - - - - - - - - - - - {{- if .CanGoUp}} - - - - - - - - {{- end}} - {{- range .Items}} - - - - {{- if .IsDir}} - - {{- else}} - - {{- end}} - - - - {{- end}} - -
- {{- if and (eq .Sort "namedirfirst") (ne .Order "desc")}} - - {{- else if and (eq .Sort "namedirfirst") (ne .Order "asc")}} - - {{- else}} - - {{- end}} - - {{- if and (eq .Sort "name") (ne .Order "desc")}} - Name - {{- else if and (eq .Sort "name") (ne .Order "asc")}} - Name - {{- else}} - Name - {{- end}} - - {{- if and (eq .Sort "size") (ne .Order "desc")}} - Size - {{- else if and (eq .Sort "size") (ne .Order "asc")}} - Size - {{- else}} - Size - {{- end}} - - {{- if and (eq .Sort "time") (ne .Order "desc")}} - Modified - {{- else if and (eq .Sort "time") (ne .Order "asc")}} - Modified - {{- else}} - Modified - {{- end}} -
- - Go up - -
- - {{- if .IsDir}} - - {{- else}} - - {{- end}} - {{html .Name}} - - {{.HumanSize}}
-
-
- - - -` diff --git a/vendor/github.com/mholt/caddy/caddyhttp/caddyhttp.go b/vendor/github.com/mholt/caddy/caddyhttp/caddyhttp.go deleted file mode 100644 index d9d97e9d0..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/caddyhttp.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddyhttp - -import ( - // plug in the server - _ "github.com/mholt/caddy/caddyhttp/httpserver" - - // plug in the standard directives - _ "github.com/mholt/caddy/caddyhttp/basicauth" - _ "github.com/mholt/caddy/caddyhttp/bind" - _ "github.com/mholt/caddy/caddyhttp/browse" - _ "github.com/mholt/caddy/caddyhttp/errors" - _ "github.com/mholt/caddy/caddyhttp/expvar" - _ "github.com/mholt/caddy/caddyhttp/extensions" - _ "github.com/mholt/caddy/caddyhttp/fastcgi" - _ "github.com/mholt/caddy/caddyhttp/gzip" - _ "github.com/mholt/caddy/caddyhttp/header" - _ "github.com/mholt/caddy/caddyhttp/index" - _ "github.com/mholt/caddy/caddyhttp/internalsrv" - _ "github.com/mholt/caddy/caddyhttp/limits" - _ "github.com/mholt/caddy/caddyhttp/log" - _ "github.com/mholt/caddy/caddyhttp/markdown" - _ "github.com/mholt/caddy/caddyhttp/mime" - _ "github.com/mholt/caddy/caddyhttp/pprof" - _ "github.com/mholt/caddy/caddyhttp/proxy" - _ "github.com/mholt/caddy/caddyhttp/push" - _ "github.com/mholt/caddy/caddyhttp/redirect" - _ "github.com/mholt/caddy/caddyhttp/requestid" - _ "github.com/mholt/caddy/caddyhttp/rewrite" - _ "github.com/mholt/caddy/caddyhttp/root" - _ "github.com/mholt/caddy/caddyhttp/status" - _ "github.com/mholt/caddy/caddyhttp/templates" - _ "github.com/mholt/caddy/caddyhttp/timeouts" - _ "github.com/mholt/caddy/caddyhttp/websocket" - _ "github.com/mholt/caddy/onevent" -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/errors/errors.go b/vendor/github.com/mholt/caddy/caddyhttp/errors/errors.go deleted file mode 100644 index fd09f8fb1..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/errors/errors.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 errors implements an HTTP error handling middleware. -package errors - -import ( - "fmt" - "io" - "net/http" - "os" - "runtime" - "strings" - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("errors", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// ErrorHandler handles HTTP errors (and errors from other middleware). -type ErrorHandler struct { - Next httpserver.Handler - GenericErrorPage string // default error page filename - ErrorPages map[int]string // map of status code to filename - Log *httpserver.Logger - Debug bool // if true, errors are written out to client rather than to a log -} - -func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - defer h.recovery(w, r) - - status, err := h.Next.ServeHTTP(w, r) - - if err != nil { - errMsg := fmt.Sprintf("%s [ERROR %d %s] %v", time.Now().Format(timeFormat), status, r.URL.Path, err) - if h.Debug { - // Write error to response instead of to log - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.WriteHeader(status) - fmt.Fprintln(w, errMsg) - return 0, err // returning 0 signals that a response has been written - } - h.Log.Println(errMsg) - } - - if status >= 400 { - h.errorPage(w, r, status) - return 0, err - } - - return status, err -} - -// errorPage serves a static error page to w according to the status -// code. If there is an error serving the error page, a plaintext error -// message is written instead, and the extra error is logged. -func (h ErrorHandler) errorPage(w http.ResponseWriter, r *http.Request, code int) { - // See if an error page for this status code was specified - if pagePath, ok := h.findErrorPage(code); ok { - // Try to open it - errorPage, err := os.Open(pagePath) - if err != nil { - // An additional error handling an error... - h.Log.Printf("%s [NOTICE %d %s] could not load error page: %v", - time.Now().Format(timeFormat), code, r.URL.String(), err) - httpserver.DefaultErrorFunc(w, r, code) - return - } - defer errorPage.Close() - - // Copy the page body into the response - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.WriteHeader(code) - _, err = io.Copy(w, errorPage) - - if err != nil { - // Epic fail... sigh. - h.Log.Printf("%s [NOTICE %d %s] could not respond with %s: %v", - time.Now().Format(timeFormat), code, r.URL.String(), pagePath, err) - httpserver.DefaultErrorFunc(w, r, code) - } - - return - } - - // Default error response - httpserver.DefaultErrorFunc(w, r, code) -} - -func (h ErrorHandler) findErrorPage(code int) (string, bool) { - if pagePath, ok := h.ErrorPages[code]; ok { - return pagePath, true - } - - if h.GenericErrorPage != "" { - return h.GenericErrorPage, true - } - - return "", false -} - -func (h ErrorHandler) recovery(w http.ResponseWriter, r *http.Request) { - rec := recover() - if rec == nil { - return - } - - // Obtain source of panic - // From: https://gist.github.com/swdunlop/9629168 - var name, file string // function name, file name - var line int - var pc [16]uintptr - n := runtime.Callers(3, pc[:]) - for _, pc := range pc[:n] { - fn := runtime.FuncForPC(pc) - if fn == nil { - continue - } - file, line = fn.FileLine(pc) - name = fn.Name() - if !strings.HasPrefix(name, "runtime.") { - break - } - } - - // Trim file path - delim := "/github.com/mholt/caddy/" - pkgPathPos := strings.Index(file, delim) - if pkgPathPos > -1 && len(file) > pkgPathPos+len(delim) { - file = file[pkgPathPos+len(delim):] - } - - panicMsg := fmt.Sprintf("%s [PANIC %s] %s:%d - %v", time.Now().Format(timeFormat), r.URL.String(), file, line, rec) - if h.Debug { - // Write error and stack trace to the response rather than to a log - var stackBuf [4096]byte - stack := stackBuf[:runtime.Stack(stackBuf[:], false)] - httpserver.WriteTextResponse(w, http.StatusInternalServerError, fmt.Sprintf("%s\n\n%s", panicMsg, stack)) - } else { - // Currently we don't use the function name, since file:line is more conventional - h.Log.Printf(panicMsg) - h.errorPage(w, r, http.StatusInternalServerError) - } -} - -const timeFormat = "02/Jan/2006:15:04:05 -0700" diff --git a/vendor/github.com/mholt/caddy/caddyhttp/errors/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/errors/setup.go deleted file mode 100644 index bcda1c8b3..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/errors/setup.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 errors - -import ( - "log" - "os" - "path/filepath" - "strconv" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// setup configures a new errors middleware instance. -func setup(c *caddy.Controller) error { - handler, err := errorsParse(c) - - if err != nil { - return err - } - - handler.Log.Attach(c) - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - handler.Next = next - return handler - }) - - return nil -} - -func errorsParse(c *caddy.Controller) (*ErrorHandler, error) { - - // Very important that we make a pointer because the startup - // function that opens the log file must have access to the - // same instance of the handler, not a copy. - handler := &ErrorHandler{ - ErrorPages: make(map[int]string), - Log: &httpserver.Logger{}, - } - - cfg := httpserver.GetConfig(c) - - optionalBlock := func() error { - for c.NextBlock() { - - what := c.Val() - where := c.RemainingArgs() - - if httpserver.IsLogRollerSubdirective(what) { - var err error - err = httpserver.ParseRoller(handler.Log.Roller, what, where...) - if err != nil { - return err - } - } else { - if len(where) != 1 { - return c.ArgErr() - } - where := where[0] - - // Error page; ensure it exists - if !filepath.IsAbs(where) { - where = filepath.Join(cfg.Root, where) - } - - f, err := os.Open(where) - if err != nil { - log.Printf("[WARNING] Unable to open error page '%s': %v", where, err) - } - f.Close() - - if what == "*" { - if handler.GenericErrorPage != "" { - return c.Errf("Duplicate status code entry: %s", what) - } - handler.GenericErrorPage = where - } else { - whatInt, err := strconv.Atoi(what) - if err != nil { - return c.Err("Expecting a numeric status code or '*', got '" + what + "'") - } - - if _, exists := handler.ErrorPages[whatInt]; exists { - return c.Errf("Duplicate status code entry: %s", what) - } - - handler.ErrorPages[whatInt] = where - } - } - } - return nil - } - - for c.Next() { - // weird hack to avoid having the handler values overwritten. - if c.Val() == "}" { - continue - } - - args := c.RemainingArgs() - - if len(args) == 1 { - switch args[0] { - case "visible": - handler.Debug = true - default: - handler.Log.Output = args[0] - handler.Log.Roller = httpserver.DefaultLogRoller() - } - } - - if len(args) > 1 { - return handler, c.Errf("Only 1 Argument expected for errors directive") - } - - // Configuration may be in a block - err := optionalBlock() - if err != nil { - return handler, err - } - } - - return handler, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/expvar/expvar.go b/vendor/github.com/mholt/caddy/caddyhttp/expvar/expvar.go deleted file mode 100644 index efbfa32df..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/expvar/expvar.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 expvar - -import ( - "expvar" - "fmt" - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// ExpVar is a simple struct to hold expvar's configuration -type ExpVar struct { - Next httpserver.Handler - Resource Resource -} - -// ServeHTTP handles requests to expvar's configured entry point with -// expvar, or passes all other requests up the chain. -func (e ExpVar) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if httpserver.Path(r.URL.Path).Matches(string(e.Resource)) { - expvarHandler(w, r) - return 0, nil - } - return e.Next.ServeHTTP(w, r) -} - -// expvarHandler returns a JSON object will all the published variables. -// -// This is lifted straight from the expvar package. -func expvarHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintf(w, "{\n") - first := true - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintf(w, ",\n") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintf(w, "\n}\n") -} - -// Resource contains the path to the expvar entry point -type Resource string diff --git a/vendor/github.com/mholt/caddy/caddyhttp/expvar/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/expvar/setup.go deleted file mode 100644 index 79f2763b5..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/expvar/setup.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 expvar - -import ( - "expvar" - "runtime" - "sync" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("expvar", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new ExpVar middleware instance. -func setup(c *caddy.Controller) error { - resource, err := expVarParse(c) - if err != nil { - return err - } - - // publish any extra information/metrics we may want to capture - publishExtraVars() - - ev := ExpVar{Resource: resource} - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - ev.Next = next - return ev - }) - - return nil -} - -func expVarParse(c *caddy.Controller) (Resource, error) { - var resource Resource - var err error - - for c.Next() { - args := c.RemainingArgs() - switch len(args) { - case 0: - resource = Resource(defaultExpvarPath) - case 1: - resource = Resource(args[0]) - default: - return resource, c.ArgErr() - } - } - - return resource, err -} - -func publishExtraVars() { - // By using sync.Once instead of an init() function, we don't clutter - // the app's expvar export unnecessarily, or risk colliding with it. - publishOnce.Do(func() { - expvar.Publish("Goroutines", expvar.Func(func() interface{} { - return runtime.NumGoroutine() - })) - }) -} - -var publishOnce sync.Once // publishing variables should only be done once -var defaultExpvarPath = "/debug/vars" diff --git a/vendor/github.com/mholt/caddy/caddyhttp/extensions/ext.go b/vendor/github.com/mholt/caddy/caddyhttp/extensions/ext.go deleted file mode 100644 index b78f5792c..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/extensions/ext.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 extensions contains middleware for clean URLs. -// -// The root path of the site is passed in as well as possible extensions -// to try internally for paths requested that don't match an existing -// resource. The first path+ext combination that matches a valid file -// will be used. -package extensions - -import ( - "net/http" - "os" - "path" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Ext can assume an extension from clean URLs. -// It tries extensions in the order listed in Extensions. -type Ext struct { - // Next handler in the chain - Next httpserver.Handler - - // Path to site root - Root string - - // List of extensions to try - Extensions []string -} - -// ServeHTTP implements the httpserver.Handler interface. -func (e Ext) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - urlpath := strings.TrimSuffix(r.URL.Path, "/") - if len(r.URL.Path) > 0 && path.Ext(urlpath) == "" && r.URL.Path[len(r.URL.Path)-1] != '/' { - for _, ext := range e.Extensions { - _, err := os.Stat(httpserver.SafePath(e.Root, urlpath) + ext) - if err == nil { - r.URL.Path = urlpath + ext - break - } - } - } - return e.Next.ServeHTTP(w, r) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/extensions/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/extensions/setup.go deleted file mode 100644 index 1261c49ee..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/extensions/setup.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 extensions - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("ext", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new instance of 'extensions' middleware for clean URLs. -func setup(c *caddy.Controller) error { - cfg := httpserver.GetConfig(c) - root := cfg.Root - - exts, err := extParse(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Ext{ - Next: next, - Extensions: exts, - Root: root, - } - }) - - return nil -} - -// extParse sets up an instance of extension middleware -// from a middleware controller and returns a list of extensions. -func extParse(c *caddy.Controller) ([]string, error) { - var exts []string - - for c.Next() { - // At least one extension is required - if !c.NextArg() { - return exts, c.ArgErr() - } - exts = append(exts, c.Val()) - - // Tack on any other extensions that may have been listed - exts = append(exts, c.RemainingArgs()...) - } - - return exts, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fastcgi.go b/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fastcgi.go deleted file mode 100644 index f5c80dc56..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fastcgi.go +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 fastcgi has middleware that acts as a FastCGI client. Requests -// that get forwarded to FastCGI stop the middleware execution chain. -// The most common use for this package is to serve PHP websites via php-fpm. -package fastcgi - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "strconv" - "strings" - "sync/atomic" - "time" - - "crypto/tls" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddytls" -) - -// Handler is a middleware type that can handle requests as a FastCGI client. -type Handler struct { - Next httpserver.Handler - Rules []Rule - Root string - FileSys http.FileSystem - - // These are sent to CGI scripts in env variables - SoftwareName string - SoftwareVersion string - ServerName string - ServerPort string -} - -// ServeHTTP satisfies the httpserver.Handler interface. -func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - for _, rule := range h.Rules { - // First requirement: Base path must match request path. If it doesn't, - // we check to make sure the leading slash is not missing, and if so, - // we check again with it prepended. This is in case people forget - // a leading slash when performing rewrites, and we don't want to expose - // the contents of the (likely PHP) script. See issue #1645. - hpath := httpserver.Path(r.URL.Path) - if !hpath.Matches(rule.Path) { - if strings.HasPrefix(string(hpath), "/") { - // this is a normal-looking path, and it doesn't match; try next rule - continue - } - hpath = httpserver.Path("/" + string(hpath)) // prepend leading slash - if !hpath.Matches(rule.Path) { - // even after fixing the request path, it still doesn't match; try next rule - continue - } - } - // The path must also be allowed (not ignored). - if !rule.AllowedPath(r.URL.Path) { - continue - } - - // In addition to matching the path, a request must meet some - // other criteria before being proxied as FastCGI. For example, - // we probably want to exclude static assets (CSS, JS, images...) - // but we also want to be flexible for the script we proxy to. - - fpath := r.URL.Path - - if idx, ok := httpserver.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok { - fpath = idx - // Index file present. - // If request path cannot be split, return error. - if !rule.canSplit(fpath) { - return http.StatusInternalServerError, ErrIndexMissingSplit - } - } else { - // No index file present. - // If request path cannot be split, ignore request. - if !rule.canSplit(fpath) { - continue - } - } - - // These criteria work well in this order for PHP sites - if !h.exists(fpath) || fpath[len(fpath)-1] == '/' || strings.HasSuffix(fpath, rule.Ext) { - - // Create environment for CGI script - env, err := h.buildEnv(r, rule, fpath) - if err != nil { - return http.StatusInternalServerError, err - } - - // Connect to FastCGI gateway - address, err := rule.Address() - if err != nil { - return http.StatusBadGateway, err - } - network, address := parseAddress(address) - - ctx := context.Background() - if rule.ConnectTimeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, rule.ConnectTimeout) - defer cancel() - } - - fcgiBackend, err := DialContext(ctx, network, address) - if err != nil { - return http.StatusBadGateway, err - } - defer fcgiBackend.Close() - - // read/write timeouts - if err := fcgiBackend.SetReadTimeout(rule.ReadTimeout); err != nil { - return http.StatusInternalServerError, err - } - if err := fcgiBackend.SetSendTimeout(rule.SendTimeout); err != nil { - return http.StatusInternalServerError, err - } - - var resp *http.Response - - var contentLength int64 - // if ContentLength is already set - if r.ContentLength > 0 { - contentLength = r.ContentLength - } else { - contentLength, _ = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64) - } - switch r.Method { - case "HEAD": - resp, err = fcgiBackend.Head(env) - case "GET": - resp, err = fcgiBackend.Get(env, r.Body, contentLength) - case "OPTIONS": - resp, err = fcgiBackend.Options(env) - default: - resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength) - } - - if resp != nil && resp.Body != nil { - defer resp.Body.Close() - } - - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return http.StatusGatewayTimeout, err - } else if err != io.EOF { - return http.StatusBadGateway, err - } - } - - // Write response header - writeHeader(w, resp) - - // Write the response body - _, err = io.Copy(w, resp.Body) - if err != nil { - return http.StatusBadGateway, err - } - - // Log any stderr output from upstream - if fcgiBackend.stderr.Len() != 0 { - // Remove trailing newline, error logger already does this. - err = LogError(strings.TrimSuffix(fcgiBackend.stderr.String(), "\n")) - } - - // Normally we would return the status code if it is an error status (>= 400), - // however, upstream FastCGI apps don't know about our contract and have - // probably already written an error page. So we just return 0, indicating - // that the response body is already written. However, we do return any - // error value so it can be logged. - // Note that the proxy middleware works the same way, returning status=0. - return 0, err - } - } - - return h.Next.ServeHTTP(w, r) -} - -// parseAddress returns the network and address of fcgiAddress. -// The first string is the network, "tcp" or "unix", implied from the scheme and address. -// The second string is fcgiAddress, with scheme prefixes removed. -// The two returned strings can be used as parameters to the Dial() function. -func parseAddress(fcgiAddress string) (string, string) { - // check if address has tcp scheme explicitly set - if strings.HasPrefix(fcgiAddress, "tcp://") { - return "tcp", fcgiAddress[len("tcp://"):] - } - // check if address has fastcgi scheme explicitly set - if strings.HasPrefix(fcgiAddress, "fastcgi://") { - return "tcp", fcgiAddress[len("fastcgi://"):] - } - // check if unix socket - if trim := strings.HasPrefix(fcgiAddress, "unix"); strings.HasPrefix(fcgiAddress, "/") || trim { - if trim { - return "unix", fcgiAddress[len("unix:"):] - } - return "unix", fcgiAddress - } - // default case, a plain tcp address with no scheme - return "tcp", fcgiAddress -} - -func writeHeader(w http.ResponseWriter, r *http.Response) { - for key, vals := range r.Header { - for _, val := range vals { - w.Header().Add(key, val) - } - } - w.WriteHeader(r.StatusCode) -} - -func (h Handler) exists(path string) bool { - if _, err := os.Stat(h.Root + path); err == nil { - return true - } - return false -} - -// buildEnv returns a set of CGI environment variables for the request. -func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) { - var env map[string]string - - // Separate remote IP and port; more lenient than net.SplitHostPort - var ip, port string - if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 { - ip = r.RemoteAddr[:idx] - port = r.RemoteAddr[idx+1:] - } else { - ip = r.RemoteAddr - } - - // Remove [] from IPv6 addresses - ip = strings.Replace(ip, "[", "", 1) - ip = strings.Replace(ip, "]", "", 1) - - // Split path in preparation for env variables. - // Previous rule.canSplit checks ensure this can never be -1. - splitPos := rule.splitPos(fpath) - - // Request has the extension; path was split successfully - docURI := fpath[:splitPos+len(rule.SplitPath)] - pathInfo := fpath[splitPos+len(rule.SplitPath):] - scriptName := fpath - - // Strip PATH_INFO from SCRIPT_NAME - scriptName = strings.TrimSuffix(scriptName, pathInfo) - - // SCRIPT_FILENAME is the absolute path of SCRIPT_NAME - scriptFilename := filepath.Join(rule.Root, scriptName) - - // Add vhost path prefix to scriptName. Otherwise, some PHP software will - // have difficulty discovering its URL. - pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string) - scriptName = path.Join(pathPrefix, scriptName) - - // Get the request URI from context. The context stores the original URI in case - // it was changed by a middleware such as rewrite. By default, we pass the - // original URI in as the value of REQUEST_URI (the user can overwrite this - // if desired). Most PHP apps seem to want the original URI. Besides, this is - // how nginx defaults: http://stackoverflow.com/a/12485156/1048862 - reqURL, _ := r.Context().Value(httpserver.OriginalURLCtxKey).(url.URL) - - // Retrieve name of remote user that was set by some downstream middleware such as basicauth. - remoteUser, _ := r.Context().Value(httpserver.RemoteUserCtxKey).(string) - - requestScheme := "http" - if r.TLS != nil { - requestScheme = "https" - } - - // Some variables are unused but cleared explicitly to prevent - // the parent environment from interfering. - env = map[string]string{ - // Variables defined in CGI 1.1 spec - "AUTH_TYPE": "", // Not used - "CONTENT_LENGTH": r.Header.Get("Content-Length"), - "CONTENT_TYPE": r.Header.Get("Content-Type"), - "GATEWAY_INTERFACE": "CGI/1.1", - "PATH_INFO": pathInfo, - "QUERY_STRING": r.URL.RawQuery, - "REMOTE_ADDR": ip, - "REMOTE_HOST": ip, // For speed, remote host lookups disabled - "REMOTE_PORT": port, - "REMOTE_IDENT": "", // Not used - "REMOTE_USER": remoteUser, - "REQUEST_METHOD": r.Method, - "REQUEST_SCHEME": requestScheme, - "SERVER_NAME": h.ServerName, - "SERVER_PORT": h.ServerPort, - "SERVER_PROTOCOL": r.Proto, - "SERVER_SOFTWARE": h.SoftwareName + "/" + h.SoftwareVersion, - - // Other variables - "DOCUMENT_ROOT": rule.Root, - "DOCUMENT_URI": docURI, - "HTTP_HOST": r.Host, // added here, since not always part of headers - "REQUEST_URI": reqURL.RequestURI(), - "SCRIPT_FILENAME": scriptFilename, - "SCRIPT_NAME": scriptName, - } - - // compliance with the CGI specification requires that - // PATH_TRANSLATED should only exist if PATH_INFO is defined. - // Info: https://www.ietf.org/rfc/rfc3875 Page 14 - if env["PATH_INFO"] != "" { - env["PATH_TRANSLATED"] = filepath.Join(rule.Root, pathInfo) // Info: http://www.oreilly.com/openbook/cgi/ch02_04.html - } - - // Some web apps rely on knowing HTTPS or not - if r.TLS != nil { - env["HTTPS"] = "on" - // and pass the protocol details in a manner compatible with apache's mod_ssl - // (which is why they have a SSL_ prefix and not TLS_). - v, ok := tlsProtocolStringToMap[r.TLS.Version] - if ok { - env["SSL_PROTOCOL"] = v - } - // and pass the cipher suite in a manner compatible with apache's mod_ssl - for k, v := range caddytls.SupportedCiphersMap { - if v == r.TLS.CipherSuite { - env["SSL_CIPHER"] = k - break - } - } - } - - // Add env variables from config (with support for placeholders in values) - replacer := httpserver.NewReplacer(r, nil, "") - for _, envVar := range rule.EnvVars { - env[envVar[0]] = replacer.Replace(envVar[1]) - } - - // Add all HTTP headers to env variables - for field, val := range r.Header { - header := strings.ToUpper(field) - header = headerNameReplacer.Replace(header) - env["HTTP_"+header] = strings.Join(val, ", ") - } - return env, nil -} - -// Rule represents a FastCGI handling rule. -// It is parsed from the fastcgi directive in the Caddyfile, see setup.go. -type Rule struct { - // The base path to match. Required. - Path string - - // upstream load balancer - balancer - - // Always process files with this extension with fastcgi. - Ext string - - // Use this directory as the fastcgi root directory. Defaults to the root - // directory of the parent virtual host. - Root string - - // The path in the URL will be split into two, with the first piece ending - // with the value of SplitPath. The first piece will be assumed as the - // actual resource (CGI script) name, and the second piece will be set to - // PATH_INFO for the CGI script to use. - SplitPath string - - // If the URL ends with '/' (which indicates a directory), these index - // files will be tried instead. - IndexFiles []string - - // Environment Variables - EnvVars [][2]string - - // Ignored paths - IgnoredSubPaths []string - - // The duration used to set a deadline when connecting to an upstream. - ConnectTimeout time.Duration - - // The duration used to set a deadline when reading from the FastCGI server. - ReadTimeout time.Duration - - // The duration used to set a deadline when sending to the FastCGI server. - SendTimeout time.Duration -} - -// balancer is a fastcgi upstream load balancer. -type balancer interface { - // Address picks an upstream address from the - // underlying load balancer. - Address() (string, error) -} - -// roundRobin is a round robin balancer for fastcgi upstreams. -type roundRobin struct { - // Known Go bug: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - // must be first field for 64 bit alignment - // on x86 and arm. - index int64 - addresses []string -} - -func (r *roundRobin) Address() (string, error) { - index := atomic.AddInt64(&r.index, 1) % int64(len(r.addresses)) - return r.addresses[index], nil -} - -// srvResolver is a private interface used to abstract -// the DNS resolver. It is mainly used to facilitate testing. -type srvResolver interface { - LookupSRV(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) -} - -// srv is a service locator for fastcgi upstreams -type srv struct { - resolver srvResolver - service string -} - -// Address looks up the service and returns the address:port -// from first result in resolved list. -// No explicit balancing is required because net.LookupSRV -// sorts the results by priority and randomizes within priority. -func (s *srv) Address() (string, error) { - _, addrs, err := s.resolver.LookupSRV(context.Background(), "", "", s.service) - if err != nil { - return "", err - } - - return fmt.Sprintf("%s:%d", strings.TrimRight(addrs[0].Target, "."), addrs[0].Port), nil -} - -// canSplit checks if path can split into two based on rule.SplitPath. -func (r Rule) canSplit(path string) bool { - return r.splitPos(path) >= 0 -} - -// splitPos returns the index where path should be split -// based on rule.SplitPath. -func (r Rule) splitPos(path string) int { - if httpserver.CaseSensitivePath { - return strings.Index(path, r.SplitPath) - } - return strings.Index(strings.ToLower(path), strings.ToLower(r.SplitPath)) -} - -// AllowedPath checks if requestPath is not an ignored path. -func (r Rule) AllowedPath(requestPath string) bool { - for _, ignoredSubPath := range r.IgnoredSubPaths { - if httpserver.Path(path.Clean(requestPath)).Matches(path.Join(r.Path, ignoredSubPath)) { - return false - } - } - return true -} - -var ( - headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_") - // ErrIndexMissingSplit describes an index configuration error. - ErrIndexMissingSplit = errors.New("configured index file(s) must include split value") -) - -// LogError is a non fatal error that allows requests to go through. -type LogError string - -// Error satisfies error interface. -func (l LogError) Error() string { - return string(l) -} - -// Map of supported protocols to Apache ssl_mod format -// Note that these are slightly different from SupportedProtocols in caddytls/config.go's -var tlsProtocolStringToMap = map[uint16]string{ - tls.VersionTLS10: "TLSv1", - tls.VersionTLS11: "TLSv1.1", - tls.VersionTLS12: "TLSv1.2", -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgi_test.php b/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgi_test.php deleted file mode 100644 index 3f5e5f2db..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgi_test.php +++ /dev/null @@ -1,79 +0,0 @@ - $val) { - $md5 = md5($val); - - if ($key != $md5) { - $stat = "FAILED"; - echo "server:err ".$md5." != ".$key."\n"; - } - - $length += strlen($key) + strlen($val); - - $ret .= $key."(".strlen($key).") "; - } - $ret .= "] ["; - foreach ($_FILES as $k0 => $val) { - - $error = $val["error"]; - if ($error == UPLOAD_ERR_OK) { - $tmp_name = $val["tmp_name"]; - $name = $val["name"]; - $datafile = "/tmp/test.go"; - move_uploaded_file($tmp_name, $datafile); - $md5 = md5_file($datafile); - - if ($k0 != $md5) { - $stat = "FAILED"; - echo "server:err ".$md5." != ".$key."\n"; - } - - $length += strlen($k0) + filesize($datafile); - - unlink($datafile); - $ret .= $k0."(".strlen($k0).") "; - } - else{ - $stat = "FAILED"; - echo "server:file err ".file_upload_error_message($error)."\n"; - } - } - $ret .= "]"; - echo "server:got data length " .$length."\n"; -} - - -echo "-{$stat}-POST(".count($_POST).") FILE(".count($_FILES).")\n"; - -function file_upload_error_message($error_code) { - switch ($error_code) { - case UPLOAD_ERR_INI_SIZE: - return 'The uploaded file exceeds the upload_max_filesize directive in php.ini'; - case UPLOAD_ERR_FORM_SIZE: - return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'; - case UPLOAD_ERR_PARTIAL: - return 'The uploaded file was only partially uploaded'; - case UPLOAD_ERR_NO_FILE: - return 'No file was uploaded'; - case UPLOAD_ERR_NO_TMP_DIR: - return 'Missing a temporary folder'; - case UPLOAD_ERR_CANT_WRITE: - return 'Failed to write file to disk'; - case UPLOAD_ERR_EXTENSION: - return 'File upload stopped by extension'; - default: - return 'Unknown upload error'; - } -} \ No newline at end of file diff --git a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgiclient.go b/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgiclient.go deleted file mode 100644 index 45e82526e..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/fcgiclient.go +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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. - -// Forked Jan. 2015 from http://bitbucket.org/PinIdea/fcgi_client -// (which is forked from https://code.google.com/p/go-fastcgi-client/) - -// This fork contains several fixes and improvements by Matt Holt and -// other contributors to this project. - -// Copyright 2012 Junqing Tan and The Go Authors -// Use of this source code is governed by a BSD-style -// Part of source code is from Go fcgi package - -package fastcgi - -import ( - "bufio" - "bytes" - "context" - "encoding/binary" - "errors" - "io" - "io/ioutil" - "mime/multipart" - "net" - "net/http" - "net/http/httputil" - "net/textproto" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" -) - -// FCGIListenSockFileno describes listen socket file number. -const FCGIListenSockFileno uint8 = 0 - -// FCGIHeaderLen describes header length. -const FCGIHeaderLen uint8 = 8 - -// Version1 describes the version. -const Version1 uint8 = 1 - -// FCGINullRequestID describes the null request ID. -const FCGINullRequestID uint8 = 0 - -// FCGIKeepConn describes keep connection mode. -const FCGIKeepConn uint8 = 1 - -const ( - // BeginRequest is the begin request flag. - BeginRequest uint8 = iota + 1 - // AbortRequest is the abort request flag. - AbortRequest - // EndRequest is the end request flag. - EndRequest - // Params is the parameters flag. - Params - // Stdin is the standard input flag. - Stdin - // Stdout is the standard output flag. - Stdout - // Stderr is the standard error flag. - Stderr - // Data is the data flag. - Data - // GetValues is the get values flag. - GetValues - // GetValuesResult is the get values result flag. - GetValuesResult - // UnknownType is the unknown type flag. - UnknownType - // MaxType is the maximum type flag. - MaxType = UnknownType -) - -const ( - // Responder is the responder flag. - Responder uint8 = iota + 1 - // Authorizer is the authorizer flag. - Authorizer - // Filter is the filter flag. - Filter -) - -const ( - // RequestComplete is the completed request flag. - RequestComplete uint8 = iota - // CantMultiplexConns is the multiplexed connections flag. - CantMultiplexConns - // Overloaded is the overloaded flag. - Overloaded - // UnknownRole is the unknown role flag. - UnknownRole -) - -const ( - // MaxConns is the maximum connections flag. - MaxConns string = "MAX_CONNS" - // MaxRequests is the maximum requests flag. - MaxRequests string = "MAX_REQS" - // MultiplexConns is the multiplex connections flag. - MultiplexConns string = "MPXS_CONNS" -) - -const ( - maxWrite = 65500 // 65530 may work, but for compatibility - maxPad = 255 -) - -type header struct { - Version uint8 - Type uint8 - ID uint16 - ContentLength uint16 - PaddingLength uint8 - Reserved uint8 -} - -// for padding so we don't have to allocate all the time -// not synchronized because we don't care what the contents are -var pad [maxPad]byte - -func (h *header) init(recType uint8, reqID uint16, contentLength int) { - h.Version = 1 - h.Type = recType - h.ID = reqID - h.ContentLength = uint16(contentLength) - h.PaddingLength = uint8(-contentLength & 7) -} - -type record struct { - h header - rbuf []byte -} - -func (rec *record) read(r io.Reader) (buf []byte, err error) { - if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { - return - } - if rec.h.Version != 1 { - err = errors.New("fcgi: invalid header version") - return - } - if rec.h.Type == EndRequest { - err = io.EOF - return - } - n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) - if len(rec.rbuf) < n { - rec.rbuf = make([]byte, n) - } - if _, err = io.ReadFull(r, rec.rbuf[:n]); err != nil { - return - } - buf = rec.rbuf[:int(rec.h.ContentLength)] - - return -} - -// FCGIClient implements a FastCGI client, which is a standard for -// interfacing external applications with Web servers. -type FCGIClient struct { - mutex sync.Mutex - rwc io.ReadWriteCloser - h header - buf bytes.Buffer - stderr bytes.Buffer - keepAlive bool - reqID uint16 -} - -// DialWithDialerContext connects to the fcgi responder at the specified network address, using custom net.Dialer -// and a context. -// See func net.Dial for a description of the network and address parameters. -func DialWithDialerContext(ctx context.Context, network, address string, dialer net.Dialer) (fcgi *FCGIClient, err error) { - var conn net.Conn - conn, err = dialer.DialContext(ctx, network, address) - if err != nil { - return - } - - fcgi = &FCGIClient{ - rwc: conn, - keepAlive: false, - reqID: 1, - } - - return -} - -// DialContext is like Dial but passes ctx to dialer.Dial. -func DialContext(ctx context.Context, network, address string) (fcgi *FCGIClient, err error) { - return DialWithDialerContext(ctx, network, address, net.Dialer{}) -} - -// Dial connects to the fcgi responder at the specified network address, using default net.Dialer. -// See func net.Dial for a description of the network and address parameters. -func Dial(network, address string) (fcgi *FCGIClient, err error) { - return DialContext(context.Background(), network, address) -} - -// Close closes fcgi connection -func (c *FCGIClient) Close() { - c.rwc.Close() -} - -func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) { - c.mutex.Lock() - defer c.mutex.Unlock() - c.buf.Reset() - c.h.init(recType, c.reqID, len(content)) - if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { - return err - } - if _, err := c.buf.Write(content); err != nil { - return err - } - if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { - return err - } - _, err = c.rwc.Write(c.buf.Bytes()) - return err -} - -func (c *FCGIClient) writeBeginRequest(role uint16, flags uint8) error { - b := [8]byte{byte(role >> 8), byte(role), flags} - return c.writeRecord(BeginRequest, b[:]) -} - -func (c *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) error { - b := make([]byte, 8) - binary.BigEndian.PutUint32(b, uint32(appStatus)) - b[4] = protocolStatus - return c.writeRecord(EndRequest, b) -} - -func (c *FCGIClient) writePairs(recType uint8, pairs map[string]string) error { - w := newWriter(c, recType) - b := make([]byte, 8) - nn := 0 - for k, v := range pairs { - m := 8 + len(k) + len(v) - if m > maxWrite { - // param data size exceed 65535 bytes" - vl := maxWrite - 8 - len(k) - v = v[:vl] - } - n := encodeSize(b, uint32(len(k))) - n += encodeSize(b[n:], uint32(len(v))) - m = n + len(k) + len(v) - if (nn + m) > maxWrite { - w.Flush() - nn = 0 - } - nn += m - if _, err := w.Write(b[:n]); err != nil { - return err - } - if _, err := w.WriteString(k); err != nil { - return err - } - if _, err := w.WriteString(v); err != nil { - return err - } - } - w.Close() - return nil -} - -func encodeSize(b []byte, size uint32) int { - if size > 127 { - size |= 1 << 31 - binary.BigEndian.PutUint32(b, size) - return 4 - } - b[0] = byte(size) - return 1 -} - -// bufWriter encapsulates bufio.Writer but also closes the underlying stream when -// Closed. -type bufWriter struct { - closer io.Closer - *bufio.Writer -} - -func (w *bufWriter) Close() error { - if err := w.Writer.Flush(); err != nil { - w.closer.Close() - return err - } - return w.closer.Close() -} - -func newWriter(c *FCGIClient, recType uint8) *bufWriter { - s := &streamWriter{c: c, recType: recType} - w := bufio.NewWriterSize(s, maxWrite) - return &bufWriter{s, w} -} - -// streamWriter abstracts out the separation of a stream into discrete records. -// It only writes maxWrite bytes at a time. -type streamWriter struct { - c *FCGIClient - recType uint8 -} - -func (w *streamWriter) Write(p []byte) (int, error) { - nn := 0 - for len(p) > 0 { - n := len(p) - if n > maxWrite { - n = maxWrite - } - if err := w.c.writeRecord(w.recType, p[:n]); err != nil { - return nn, err - } - nn += n - p = p[n:] - } - return nn, nil -} - -func (w *streamWriter) Close() error { - // send empty record to close the stream - return w.c.writeRecord(w.recType, nil) -} - -type streamReader struct { - c *FCGIClient - buf []byte -} - -func (w *streamReader) Read(p []byte) (n int, err error) { - - if len(p) > 0 { - if len(w.buf) == 0 { - - // filter outputs for error log - for { - rec := &record{} - var buf []byte - buf, err = rec.read(w.c.rwc) - if err != nil { - return - } - // standard error output - if rec.h.Type == Stderr { - w.c.stderr.Write(buf) - continue - } - w.buf = buf - break - } - } - - n = len(p) - if n > len(w.buf) { - n = len(w.buf) - } - copy(p, w.buf[:n]) - w.buf = w.buf[n:] - } - - return -} - -// Do made the request and returns a io.Reader that translates the data read -// from fcgi responder out of fcgi packet before returning it. -func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) { - err = c.writeBeginRequest(uint16(Responder), 0) - if err != nil { - return - } - - err = c.writePairs(Params, p) - if err != nil { - return - } - - body := newWriter(c, Stdin) - if req != nil { - _, _ = io.Copy(body, req) - } - body.Close() - - r = &streamReader{c: c} - return -} - -// clientCloser is a io.ReadCloser. It wraps a io.Reader with a Closer -// that closes FCGIClient connection. -type clientCloser struct { - *FCGIClient - io.Reader -} - -func (f clientCloser) Close() error { return f.rwc.Close() } - -// Request returns a HTTP Response with Header and Body -// from fcgi responder -func (c *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) { - r, err := c.Do(p, req) - if err != nil { - return - } - - rb := bufio.NewReader(r) - tp := textproto.NewReader(rb) - resp = new(http.Response) - - // Parse the response headers. - mimeHeader, err := tp.ReadMIMEHeader() - if err != nil && err != io.EOF { - return - } - resp.Header = http.Header(mimeHeader) - - if resp.Header.Get("Status") != "" { - statusParts := strings.SplitN(resp.Header.Get("Status"), " ", 2) - resp.StatusCode, err = strconv.Atoi(statusParts[0]) - if err != nil { - return - } - if len(statusParts) > 1 { - resp.Status = statusParts[1] - } - - } else { - resp.StatusCode = http.StatusOK - } - - // TODO: fixTransferEncoding ? - resp.TransferEncoding = resp.Header["Transfer-Encoding"] - resp.ContentLength, _ = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64) - - if chunked(resp.TransferEncoding) { - resp.Body = clientCloser{c, httputil.NewChunkedReader(rb)} - } else { - resp.Body = clientCloser{c, ioutil.NopCloser(rb)} - } - return -} - -// Get issues a GET request to the fcgi responder. -func (c *FCGIClient) Get(p map[string]string, body io.Reader, l int64) (resp *http.Response, err error) { - - p["REQUEST_METHOD"] = "GET" - p["CONTENT_LENGTH"] = strconv.FormatInt(l, 10) - - return c.Request(p, body) -} - -// Head issues a HEAD request to the fcgi responder. -func (c *FCGIClient) Head(p map[string]string) (resp *http.Response, err error) { - - p["REQUEST_METHOD"] = "HEAD" - p["CONTENT_LENGTH"] = "0" - - return c.Request(p, nil) -} - -// Options issues an OPTIONS request to the fcgi responder. -func (c *FCGIClient) Options(p map[string]string) (resp *http.Response, err error) { - - p["REQUEST_METHOD"] = "OPTIONS" - p["CONTENT_LENGTH"] = "0" - - return c.Request(p, nil) -} - -// Post issues a POST request to the fcgi responder. with request body -// in the format that bodyType specified -func (c *FCGIClient) Post(p map[string]string, method string, bodyType string, body io.Reader, l int64) (resp *http.Response, err error) { - if p == nil { - p = make(map[string]string) - } - - p["REQUEST_METHOD"] = strings.ToUpper(method) - - if len(p["REQUEST_METHOD"]) == 0 || p["REQUEST_METHOD"] == "GET" { - p["REQUEST_METHOD"] = "POST" - } - - p["CONTENT_LENGTH"] = strconv.FormatInt(l, 10) - if len(bodyType) > 0 { - p["CONTENT_TYPE"] = bodyType - } else { - p["CONTENT_TYPE"] = "application/x-www-form-urlencoded" - } - - return c.Request(p, body) -} - -// PostForm issues a POST to the fcgi responder, with form -// as a string key to a list values (url.Values) -func (c *FCGIClient) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) { - body := bytes.NewReader([]byte(data.Encode())) - return c.Post(p, "POST", "application/x-www-form-urlencoded", body, int64(body.Len())) -} - -// PostFile issues a POST to the fcgi responder in multipart(RFC 2046) standard, -// with form as a string key to a list values (url.Values), -// and/or with file as a string key to a list file path. -func (c *FCGIClient) PostFile(p map[string]string, data url.Values, file map[string]string) (resp *http.Response, err error) { - buf := &bytes.Buffer{} - writer := multipart.NewWriter(buf) - bodyType := writer.FormDataContentType() - - for key, val := range data { - for _, v0 := range val { - err = writer.WriteField(key, v0) - if err != nil { - return - } - } - } - - for key, val := range file { - fd, e := os.Open(val) - if e != nil { - return nil, e - } - defer fd.Close() - - part, e := writer.CreateFormFile(key, filepath.Base(val)) - if e != nil { - return nil, e - } - _, err = io.Copy(part, fd) - if err != nil { - return - } - } - - err = writer.Close() - if err != nil { - return - } - - return c.Post(p, "POST", bodyType, buf, int64(buf.Len())) -} - -// SetReadTimeout sets the read timeout for future calls that read from the -// fcgi responder. A zero value for t means no timeout will be set. -func (c *FCGIClient) SetReadTimeout(t time.Duration) error { - if conn, ok := c.rwc.(net.Conn); ok && t != 0 { - return conn.SetReadDeadline(time.Now().Add(t)) - } - return nil -} - -// SetSendTimeout sets the read timeout for future calls that send data to -// the fcgi responder. A zero value for t means no timeout will be set. -func (c *FCGIClient) SetSendTimeout(t time.Duration) error { - if conn, ok := c.rwc.(net.Conn); ok && t != 0 { - return conn.SetWriteDeadline(time.Now().Add(t)) - } - return nil -} - -// Checks whether chunked is part of the encodings stack -func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } diff --git a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/setup.go deleted file mode 100644 index 8034c6410..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/fastcgi/setup.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 fastcgi - -import ( - "errors" - "fmt" - "net" - "net/http" - "path/filepath" - "strings" - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -var defaultTimeout = 60 * time.Second - -func init() { - caddy.RegisterPlugin("fastcgi", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new FastCGI middleware instance. -func setup(c *caddy.Controller) error { - cfg := httpserver.GetConfig(c) - - rules, err := fastcgiParse(c) - if err != nil { - return err - } - - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Handler{ - Next: next, - Rules: rules, - Root: cfg.Root, - FileSys: http.Dir(cfg.Root), - SoftwareName: caddy.AppName, - SoftwareVersion: caddy.AppVersion, - ServerName: cfg.Addr.Host, - ServerPort: cfg.Addr.Port, - } - }) - - return nil -} - -func fastcgiParse(c *caddy.Controller) ([]Rule, error) { - var rules []Rule - - cfg := httpserver.GetConfig(c) - absRoot, err := filepath.Abs(cfg.Root) - if err != nil { - return nil, err - } - - for c.Next() { - args := c.RemainingArgs() - - if len(args) < 2 || len(args) > 3 { - return rules, c.ArgErr() - } - - rule := Rule{ - Root: absRoot, - Path: args[0], - ConnectTimeout: defaultTimeout, - ReadTimeout: defaultTimeout, - SendTimeout: defaultTimeout, - } - - upstreams := []string{args[1]} - - srvUpstream := false - if strings.HasPrefix(upstreams[0], "srv://") { - srvUpstream = true - } - - if len(args) == 3 { - if err := fastcgiPreset(args[2], &rule); err != nil { - return rules, err - } - } - - var err error - - for c.NextBlock() { - switch c.Val() { - case "root": - if !c.NextArg() { - return rules, c.ArgErr() - } - rule.Root = c.Val() - - case "ext": - if !c.NextArg() { - return rules, c.ArgErr() - } - rule.Ext = c.Val() - case "split": - if !c.NextArg() { - return rules, c.ArgErr() - } - rule.SplitPath = c.Val() - case "index": - args := c.RemainingArgs() - if len(args) == 0 { - return rules, c.ArgErr() - } - rule.IndexFiles = args - - case "upstream": - if srvUpstream { - return rules, c.Err("additional upstreams are not supported with SRV upstream") - } - - args := c.RemainingArgs() - - if len(args) != 1 { - return rules, c.ArgErr() - } - - upstreams = append(upstreams, args[0]) - case "env": - envArgs := c.RemainingArgs() - if len(envArgs) < 2 { - return rules, c.ArgErr() - } - rule.EnvVars = append(rule.EnvVars, [2]string{envArgs[0], envArgs[1]}) - case "except": - ignoredPaths := c.RemainingArgs() - if len(ignoredPaths) == 0 { - return rules, c.ArgErr() - } - rule.IgnoredSubPaths = ignoredPaths - - case "connect_timeout": - if !c.NextArg() { - return rules, c.ArgErr() - } - rule.ConnectTimeout, err = time.ParseDuration(c.Val()) - if err != nil { - return rules, err - } - case "read_timeout": - if !c.NextArg() { - return rules, c.ArgErr() - } - readTimeout, err := time.ParseDuration(c.Val()) - if err != nil { - return rules, err - } - rule.ReadTimeout = readTimeout - case "send_timeout": - if !c.NextArg() { - return rules, c.ArgErr() - } - sendTimeout, err := time.ParseDuration(c.Val()) - if err != nil { - return rules, err - } - rule.SendTimeout = sendTimeout - } - } - - if srvUpstream { - balancer, err := parseSRV(upstreams[0]) - if err != nil { - return rules, c.Err("malformed service locator string: " + err.Error()) - } - rule.balancer = balancer - } else { - rule.balancer = &roundRobin{addresses: upstreams, index: -1} - } - - rules = append(rules, rule) - } - return rules, nil -} - -func parseSRV(locator string) (*srv, error) { - if locator[6:] == "" { - return nil, fmt.Errorf("%s does not include the host", locator) - } - - return &srv{ - service: locator[6:], - resolver: &net.Resolver{}, - }, nil -} - -// fastcgiPreset configures rule according to name. It returns an error if -// name is not a recognized preset name. -func fastcgiPreset(name string, rule *Rule) error { - switch name { - case "php": - rule.Ext = ".php" - rule.SplitPath = ".php" - rule.IndexFiles = []string{"index.php"} - default: - return errors.New(name + " is not a valid preset name") - } - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/gzip/gzip.go b/vendor/github.com/mholt/caddy/caddyhttp/gzip/gzip.go deleted file mode 100644 index 619a1d579..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/gzip/gzip.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 gzip provides a middleware layer that performs -// gzip compression on the response. -package gzip - -import ( - "compress/gzip" - "io" - "net/http" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("gzip", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) - - initWriterPool() -} - -// Gzip is a middleware type which gzips HTTP responses. It is -// imperative that any handler which writes to a gzipped response -// specifies the Content-Type, otherwise some clients will assume -// application/x-gzip and try to download a file. -type Gzip struct { - Next httpserver.Handler - Configs []Config -} - -// Config holds the configuration for Gzip middleware -type Config struct { - RequestFilters []RequestFilter - ResponseFilters []ResponseFilter - Level int // Compression level -} - -// ServeHTTP serves a gzipped response if the client supports it. -func (g Gzip) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { - return g.Next.ServeHTTP(w, r) - } -outer: - for _, c := range g.Configs { - - // Check request filters to determine if gzipping is permitted for this request - for _, filter := range c.RequestFilters { - if !filter.ShouldCompress(r) { - continue outer - } - } - - // In order to avoid unused memory allocation, gzip.putWriter only be called when gzip compression happened. - // see https://github.com/mholt/caddy/issues/2395 - gz := &gzipResponseWriter{ - ResponseWriterWrapper: &httpserver.ResponseWriterWrapper{ResponseWriter: w}, - newWriter: func() io.Writer { - // gzipWriter modifies underlying writer at init, - // use a discard writer instead to leave ResponseWriter in - // original form. - return getWriter(c.Level) - }, - } - - defer func() { - if gzWriter, ok := gz.internalWriter.(*gzip.Writer); ok { - putWriter(c.Level, gzWriter) - } - }() - - var rw http.ResponseWriter - // if no response filter is used - if len(c.ResponseFilters) == 0 { - // replace discard writer with ResponseWriter - if gzWriter, ok := gz.Writer().(*gzip.Writer); ok { - gzWriter.Reset(w) - } - rw = gz - } else { - // wrap gzip writer with ResponseFilterWriter - rw = NewResponseFilterWriter(c.ResponseFilters, gz) - } - - // Any response in forward middleware will now be compressed - status, err := g.Next.ServeHTTP(rw, r) - - // If there was an error that remained unhandled, we need - // to send something back before gzipWriter gets closed at - // the return of this method! - if status >= 400 { - httpserver.DefaultErrorFunc(w, r, status) - return 0, err - } - return status, err - } - - // no matching filter - return g.Next.ServeHTTP(w, r) -} - -// gzipResponseWriter wraps the underlying Write method -// with a gzip.Writer to compress the output. -type gzipResponseWriter struct { - internalWriter io.Writer - *httpserver.ResponseWriterWrapper - statusCodeWritten bool - newWriter func() io.Writer -} - -// WriteHeader wraps the underlying WriteHeader method to prevent -// problems with conflicting headers from proxied backends. For -// example, a backend system that calculates Content-Length would -// be wrong because it doesn't know it's being gzipped. -func (w *gzipResponseWriter) WriteHeader(code int) { - w.Header().Del("Content-Length") - w.Header().Set("Content-Encoding", "gzip") - w.Header().Add("Vary", "Accept-Encoding") - originalEtag := w.Header().Get("ETag") - if originalEtag != "" && !strings.HasPrefix(originalEtag, "W/") { - w.Header().Set("ETag", "W/"+originalEtag) - } - w.ResponseWriterWrapper.WriteHeader(code) - w.statusCodeWritten = true -} - -// Write wraps the underlying Write method to do compression. -func (w *gzipResponseWriter) Write(b []byte) (int, error) { - if w.Header().Get("Content-Type") == "" { - w.Header().Set("Content-Type", http.DetectContentType(b)) - } - if !w.statusCodeWritten { - w.WriteHeader(http.StatusOK) - } - n, err := w.Writer().Write(b) - return n, err -} - -//Writer use a lazy way to initialize Writer -func (w *gzipResponseWriter) Writer() io.Writer { - if w.internalWriter == nil { - w.internalWriter = w.newWriter() - } - return w.internalWriter -} - -// Interface guards -var _ httpserver.HTTPInterfaces = (*gzipResponseWriter)(nil) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/gzip/requestfilter.go b/vendor/github.com/mholt/caddy/caddyhttp/gzip/requestfilter.go deleted file mode 100644 index 696c051d4..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/gzip/requestfilter.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 gzip - -import ( - "net/http" - "path" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// RequestFilter determines if a request should be gzipped. -type RequestFilter interface { - // ShouldCompress tells if gzip compression - // should be done on the request. - ShouldCompress(*http.Request) bool -} - -// defaultExtensions is the list of default extensions for which to enable gzipping. -var defaultExtensions = []string{"", ".txt", ".htm", ".html", ".css", ".php", ".js", ".json", - ".md", ".mdown", ".xml", ".svg", ".go", ".cgi", ".py", ".pl", ".aspx", ".asp", ".m3u", ".m3u8"} - -// DefaultExtFilter creates an ExtFilter with default extensions. -func DefaultExtFilter() ExtFilter { - m := ExtFilter{Exts: make(Set)} - for _, extension := range defaultExtensions { - m.Exts.Add(extension) - } - return m -} - -// ExtFilter is RequestFilter for file name extensions. -type ExtFilter struct { - // Exts is the file name extensions to accept - Exts Set -} - -// ExtWildCard is the wildcard for extensions. -const ExtWildCard = "*" - -// ShouldCompress checks if the request file extension matches any -// of the registered extensions. It returns true if the extension is -// found and false otherwise. -func (e ExtFilter) ShouldCompress(r *http.Request) bool { - ext := path.Ext(r.URL.Path) - return e.Exts.Contains(ExtWildCard) || e.Exts.Contains(ext) -} - -// PathFilter is RequestFilter for request path. -type PathFilter struct { - // IgnoredPaths is the paths to ignore - IgnoredPaths Set -} - -// ShouldCompress checks if the request path matches any of the -// registered paths to ignore. It returns false if an ignored path -// is found and true otherwise. -func (p PathFilter) ShouldCompress(r *http.Request) bool { - return !p.IgnoredPaths.ContainsFunc(func(value string) bool { - return httpserver.Path(r.URL.Path).Matches(value) - }) -} - -// Set stores distinct strings. -type Set map[string]struct{} - -// Add adds an element to the set. -func (s Set) Add(value string) { - s[value] = struct{}{} -} - -// Remove removes an element from the set. -func (s Set) Remove(value string) { - delete(s, value) -} - -// Contains check if the set contains value. -func (s Set) Contains(value string) bool { - _, ok := s[value] - return ok -} - -// ContainsFunc is similar to Contains. It iterates all the -// elements in the set and passes each to f. It returns true -// on the first call to f that returns true and false otherwise. -func (s Set) ContainsFunc(f func(string) bool) bool { - for k := range s { - if f(k) { - return true - } - } - return false -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/gzip/responsefilter.go b/vendor/github.com/mholt/caddy/caddyhttp/gzip/responsefilter.go deleted file mode 100644 index 82e10e9c9..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/gzip/responsefilter.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 gzip - -import ( - "compress/gzip" - "net/http" - "strconv" -) - -// ResponseFilter determines if the response should be gzipped. -type ResponseFilter interface { - ShouldCompress(http.ResponseWriter) bool -} - -// LengthFilter is ResponseFilter for minimum content length. -type LengthFilter int64 - -// ShouldCompress returns if content length is greater than or -// equals to minimum length. -func (l LengthFilter) ShouldCompress(w http.ResponseWriter) bool { - contentLength := w.Header().Get("Content-Length") - length, err := strconv.ParseInt(contentLength, 10, 64) - if err != nil || length == 0 { - return false - } - return l != 0 && int64(l) <= length -} - -// SkipCompressedFilter is ResponseFilter that will discard already compressed responses -type SkipCompressedFilter struct{} - -// ShouldCompress returns true if served file is not already compressed -// encodings via https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding -func (n SkipCompressedFilter) ShouldCompress(w http.ResponseWriter) bool { - switch w.Header().Get("Content-Encoding") { - case "gzip", "compress", "deflate", "br": - return false - default: - return true - } -} - -// ResponseFilterWriter validates ResponseFilters. It writes -// gzip compressed data if ResponseFilters are satisfied or -// uncompressed data otherwise. -type ResponseFilterWriter struct { - filters []ResponseFilter - shouldCompress bool - statusCodeWritten bool - *gzipResponseWriter -} - -// NewResponseFilterWriter creates and initializes a new ResponseFilterWriter. -func NewResponseFilterWriter(filters []ResponseFilter, gz *gzipResponseWriter) *ResponseFilterWriter { - return &ResponseFilterWriter{filters: filters, gzipResponseWriter: gz} -} - -// WriteHeader wraps underlying WriteHeader method and -// compresses if filters are satisfied. -func (r *ResponseFilterWriter) WriteHeader(code int) { - // Determine if compression should be used or not. - r.shouldCompress = true - for _, filter := range r.filters { - if !filter.ShouldCompress(r) { - r.shouldCompress = false - break - } - } - - if r.shouldCompress { - // replace discard writer with ResponseWriter - if gzWriter, ok := r.gzipResponseWriter.Writer().(*gzip.Writer); ok { - gzWriter.Reset(r.ResponseWriter) - } - // use gzip WriteHeader to include and delete - // necessary headers - r.gzipResponseWriter.WriteHeader(code) - } else { - r.ResponseWriter.WriteHeader(code) - } - r.statusCodeWritten = true -} - -// Write wraps underlying Write method and compresses if filters -// are satisfied -func (r *ResponseFilterWriter) Write(b []byte) (int, error) { - if !r.statusCodeWritten { - r.WriteHeader(http.StatusOK) - } - if r.shouldCompress { - return r.gzipResponseWriter.Write(b) - } - return r.ResponseWriter.Write(b) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/gzip/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/gzip/setup.go deleted file mode 100644 index 275955c1d..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/gzip/setup.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 gzip - -import ( - "compress/gzip" - "fmt" - "io/ioutil" - "strconv" - "strings" - "sync" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// setup configures a new gzip middleware instance. -func setup(c *caddy.Controller) error { - configs, err := gzipParse(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Gzip{Next: next, Configs: configs} - }) - - return nil -} - -func gzipParse(c *caddy.Controller) ([]Config, error) { - var configs []Config - - for c.Next() { - config := Config{} - - // Request Filters - pathFilter := PathFilter{IgnoredPaths: make(Set)} - extFilter := ExtFilter{Exts: make(Set)} - - // Response Filters - lengthFilter := LengthFilter(0) - - // No extra args expected - if len(c.RemainingArgs()) > 0 { - return configs, c.ArgErr() - } - - for c.NextBlock() { - switch c.Val() { - case "ext": - exts := c.RemainingArgs() - if len(exts) == 0 { - return configs, c.ArgErr() - } - for _, e := range exts { - if !strings.HasPrefix(e, ".") && e != ExtWildCard && e != "" { - return configs, fmt.Errorf(`gzip: invalid extension "%v" (must start with dot)`, e) - } - extFilter.Exts.Add(e) - } - case "not": - paths := c.RemainingArgs() - if len(paths) == 0 { - return configs, c.ArgErr() - } - for _, p := range paths { - if p == "/" { - return configs, fmt.Errorf(`gzip: cannot exclude path "/" - remove directive entirely instead`) - } - if !strings.HasPrefix(p, "/") { - return configs, fmt.Errorf(`gzip: invalid path "%v" (must start with /)`, p) - } - pathFilter.IgnoredPaths.Add(p) - } - case "level": - if !c.NextArg() { - return configs, c.ArgErr() - } - level, _ := strconv.Atoi(c.Val()) - config.Level = level - case "min_length": - if !c.NextArg() { - return configs, c.ArgErr() - } - length, err := strconv.ParseInt(c.Val(), 10, 64) - if err != nil { - return configs, err - } else if length == 0 { - return configs, fmt.Errorf(`gzip: min_length must be greater than 0`) - } - lengthFilter = LengthFilter(length) - default: - return configs, c.ArgErr() - } - } - - // Request Filters - config.RequestFilters = []RequestFilter{} - - // If ignored paths are specified, put in front to filter with path first - if len(pathFilter.IgnoredPaths) > 0 { - config.RequestFilters = []RequestFilter{pathFilter} - } - - // Then, if extensions are specified, use those to filter. - // Otherwise, use default extensions filter. - if len(extFilter.Exts) > 0 { - config.RequestFilters = append(config.RequestFilters, extFilter) - } else { - config.RequestFilters = append(config.RequestFilters, DefaultExtFilter()) - } - - config.ResponseFilters = append(config.ResponseFilters, SkipCompressedFilter{}) - - // Response Filters - // If min_length is specified, use it. - if int64(lengthFilter) != 0 { - config.ResponseFilters = append(config.ResponseFilters, lengthFilter) - } - - configs = append(configs, config) - } - - return configs, nil -} - -// pool gzip.Writer according to compress level -// so we can reuse allocations over time -var ( - writerPool = map[int]*sync.Pool{} - defaultWriterPoolIndex int -) - -func initWriterPool() { - var i int - newWriterPool := func(level int) *sync.Pool { - return &sync.Pool{ - New: func() interface{} { - w, _ := gzip.NewWriterLevel(ioutil.Discard, level) - return w - }, - } - } - for i = gzip.BestSpeed; i <= gzip.BestCompression; i++ { - writerPool[i] = newWriterPool(i) - } - - // add default writer pool - defaultWriterPoolIndex = i - writerPool[defaultWriterPoolIndex] = newWriterPool(gzip.DefaultCompression) -} - -func getWriter(level int) *gzip.Writer { - index := defaultWriterPoolIndex - if level >= gzip.BestSpeed && level <= gzip.BestCompression { - index = level - } - w := writerPool[index].Get().(*gzip.Writer) - w.Reset(ioutil.Discard) - return w -} - -func putWriter(level int, w *gzip.Writer) { - index := defaultWriterPoolIndex - if level >= gzip.BestSpeed && level <= gzip.BestCompression { - index = level - } - w.Close() - writerPool[index].Put(w) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/header/header.go b/vendor/github.com/mholt/caddy/caddyhttp/header/header.go deleted file mode 100644 index 884c5e403..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/header/header.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 header provides middleware that appends headers to -// requests based on a set of configuration rules that define -// which routes receive which headers. -package header - -import ( - "net/http" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Headers is middleware that adds headers to the responses -// for requests matching a certain path. -type Headers struct { - Next httpserver.Handler - Rules []Rule -} - -// ServeHTTP implements the httpserver.Handler interface and serves requests, -// setting headers on the response according to the configured rules. -func (h Headers) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - replacer := httpserver.NewReplacer(r, nil, "") - rww := &responseWriterWrapper{ - ResponseWriterWrapper: &httpserver.ResponseWriterWrapper{ResponseWriter: w}, - } - for _, rule := range h.Rules { - if httpserver.Path(r.URL.Path).Matches(rule.Path) { - for name := range rule.Headers { - - // One can either delete a header, add multiple values to a header, or simply - // set a header. - - if strings.HasPrefix(name, "-") { - rww.delHeader(strings.TrimLeft(name, "-")) - } else if strings.HasPrefix(name, "+") { - for _, value := range rule.Headers[name] { - rww.Header().Add(strings.TrimLeft(name, "+"), replacer.Replace(value)) - } - } else { - for _, value := range rule.Headers[name] { - rww.Header().Set(name, replacer.Replace(value)) - } - } - } - } - } - return h.Next.ServeHTTP(rww, r) -} - -type ( - // Rule groups a slice of HTTP headers by a URL pattern. - Rule struct { - Path string - Headers http.Header - } -) - -// headerOperation represents an operation on the header -type headerOperation func(http.Header) - -// responseWriterWrapper wraps the real ResponseWriter. -// It defers header operations until writeHeader -type responseWriterWrapper struct { - *httpserver.ResponseWriterWrapper - ops []headerOperation - wroteHeader bool -} - -func (rww *responseWriterWrapper) Header() http.Header { - return rww.ResponseWriterWrapper.Header() -} - -func (rww *responseWriterWrapper) Write(d []byte) (int, error) { - if !rww.wroteHeader { - rww.WriteHeader(http.StatusOK) - } - return rww.ResponseWriterWrapper.Write(d) -} - -func (rww *responseWriterWrapper) WriteHeader(status int) { - if rww.wroteHeader { - return - } - rww.wroteHeader = true - // capture the original headers - h := rww.Header() - - // perform our revisions - for _, op := range rww.ops { - op(h) - } - - rww.ResponseWriterWrapper.WriteHeader(status) -} - -// delHeader deletes the existing header according to the key -// Also it will delete that header added later. -func (rww *responseWriterWrapper) delHeader(key string) { - // remove the existing one if any - rww.Header().Del(key) - - // register a future deletion - rww.ops = append(rww.ops, func(h http.Header) { - h.Del(key) - }) -} - -// Interface guards -var _ httpserver.HTTPInterfaces = (*responseWriterWrapper)(nil) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/header/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/header/setup.go deleted file mode 100644 index 99c173fb9..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/header/setup.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 header - -import ( - "net/http" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("header", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Headers middleware instance. -func setup(c *caddy.Controller) error { - rules, err := headersParse(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Headers{Next: next, Rules: rules} - }) - - return nil -} - -func headersParse(c *caddy.Controller) ([]Rule, error) { - var rules []Rule - - for c.NextLine() { - var head Rule - head.Headers = http.Header{} - var isNewPattern bool - - if !c.NextArg() { - return rules, c.ArgErr() - } - pattern := c.Val() - - // See if we already have a definition for this Path pattern... - for _, h := range rules { - if h.Path == pattern { - head = h - break - } - } - - // ...otherwise, this is a new pattern - if head.Path == "" { - head.Path = pattern - isNewPattern = true - } - - for c.NextBlock() { - // A block of headers was opened... - name := c.Val() - value := "" - - args := c.RemainingArgs() - - if len(args) > 1 { - return rules, c.ArgErr() - } else if len(args) == 1 { - value = args[0] - } - - head.Headers.Add(name, value) - } - if c.NextArg() { - // ... or single header was defined as an argument instead. - - name := c.Val() - value := c.Val() - - if c.NextArg() { - value = c.Val() - } - - head.Headers.Add(name, value) - } - - if isNewPattern { - rules = append(rules, head) - } else { - for i := 0; i < len(rules); i++ { - if rules[i].Path == pattern { - rules[i] = head - break - } - } - } - } - - return rules, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/condition.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/condition.go deleted file mode 100644 index 0b329092b..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/condition.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "fmt" - "net/http" - "regexp" - "strings" - - "github.com/mholt/caddy" -) - -// SetupIfMatcher parses `if` or `if_op` in the current dispenser block. -// It returns a RequestMatcher and an error if any. -func SetupIfMatcher(controller *caddy.Controller) (RequestMatcher, error) { - var c = controller.Dispenser // copy the dispenser - var matcher IfMatcher - for c.NextBlock() { - switch c.Val() { - case "if": - args1 := c.RemainingArgs() - if len(args1) != 3 { - return matcher, c.ArgErr() - } - ifc, err := newIfCond(args1[0], args1[1], args1[2]) - if err != nil { - return matcher, err - } - matcher.ifs = append(matcher.ifs, ifc) - matcher.Enabled = true - case "if_op": - if !c.NextArg() { - return matcher, c.ArgErr() - } - switch c.Val() { - case "and": - matcher.isOr = false - case "or": - matcher.isOr = true - default: - return matcher, c.ArgErr() - } - } - } - return matcher, nil -} - -// operators -const ( - isOp = "is" - notOp = "not" - hasOp = "has" - startsWithOp = "starts_with" - endsWithOp = "ends_with" - matchOp = "match" -) - -// ifCondition is a 'if' condition. -type ifFunc func(a, b string) bool - -// ifCond is statement for a IfMatcher condition. -type ifCond struct { - a string - op string - b string - neg bool - rex *regexp.Regexp - f ifFunc -} - -// newIfCond creates a new If condition. -func newIfCond(a, op, b string) (ifCond, error) { - i := ifCond{a: a, op: op, b: b} - if strings.HasPrefix(op, "not_") { - i.neg = true - i.op = op[4:] - } - - switch i.op { - case isOp: - // It checks for equality. - i.f = i.isFunc - case notOp: - // It checks for inequality. - i.f = i.notFunc - case hasOp: - // It checks if b is a substring of a. - i.f = strings.Contains - case startsWithOp: - // It checks if b is a prefix of a. - i.f = strings.HasPrefix - case endsWithOp: - // It checks if b is a suffix of a. - i.f = strings.HasSuffix - case matchOp: - // It does regexp matching of a against pattern in b and returns if they match. - var err error - if i.rex, err = regexp.Compile(i.b); err != nil { - return ifCond{}, fmt.Errorf("Invalid regular expression: '%s', %v", i.b, err) - } - i.f = i.matchFunc - default: - return ifCond{}, fmt.Errorf("Invalid operator %v", i.op) - } - - return i, nil -} - -// isFunc is condition for Is operator. -func (i ifCond) isFunc(a, b string) bool { - return a == b -} - -// notFunc is condition for Not operator. -func (i ifCond) notFunc(a, b string) bool { - return a != b -} - -// matchFunc is condition for Match operator. -func (i ifCond) matchFunc(a, b string) bool { - return i.rex.MatchString(a) -} - -// True returns true if the condition is true and false otherwise. -// If r is not nil, it replaces placeholders before comparison. -func (i ifCond) True(r *http.Request) bool { - if i.f != nil { - a, b := i.a, i.b - if r != nil { - replacer := NewReplacer(r, nil, "") - a = replacer.Replace(i.a) - if i.op != matchOp { - b = replacer.Replace(i.b) - } - } - if i.neg { - return !i.f(a, b) - } - return i.f(a, b) - } - return i.neg // false if not negated, true otherwise -} - -// IfMatcher is a RequestMatcher for 'if' conditions. -type IfMatcher struct { - Enabled bool // if true, matcher has been configured; otherwise it's no-op - ifs []ifCond // list of If - isOr bool // if true, conditions are 'or' instead of 'and' -} - -// Match satisfies RequestMatcher interface. -// It returns true if the conditions in m are true. -func (m IfMatcher) Match(r *http.Request) bool { - if m.isOr { - return m.Or(r) - } - return m.And(r) -} - -// And returns true if all conditions in m are true. -func (m IfMatcher) And(r *http.Request) bool { - for _, i := range m.ifs { - if !i.True(r) { - return false - } - } - return true -} - -// Or returns true if any of the conditions in m is true. -func (m IfMatcher) Or(r *http.Request) bool { - for _, i := range m.ifs { - if i.True(r) { - return true - } - } - return false -} - -// IfMatcherKeyword checks if the next value in the dispenser is a keyword for 'if' config block. -// If true, remaining arguments in the dispenser are cleared to keep the dispenser valid for use. -func IfMatcherKeyword(c *caddy.Controller) bool { - if c.Val() == "if" || c.Val() == "if_op" { - // clear remaining args - c.RemainingArgs() - return true - } - return false -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/error.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/error.go deleted file mode 100644 index 85bc6e136..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/error.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "fmt" -) - -var ( - _ error = NonHijackerError{} - _ error = NonFlusherError{} - _ error = NonCloseNotifierError{} - _ error = NonPusherError{} -) - -// NonHijackerError is more descriptive error caused by a non hijacker -type NonHijackerError struct { - // underlying type which doesn't implement Hijack - Underlying interface{} -} - -// Implement Error -func (h NonHijackerError) Error() string { - return fmt.Sprintf("%T is not a hijacker", h.Underlying) -} - -// NonFlusherError is more descriptive error caused by a non flusher -type NonFlusherError struct { - // underlying type which doesn't implement Flush - Underlying interface{} -} - -// Implement Error -func (f NonFlusherError) Error() string { - return fmt.Sprintf("%T is not a flusher", f.Underlying) -} - -// NonCloseNotifierError is more descriptive error caused by a non closeNotifier -type NonCloseNotifierError struct { - // underlying type which doesn't implement CloseNotify - Underlying interface{} -} - -// Implement Error -func (c NonCloseNotifierError) Error() string { - return fmt.Sprintf("%T is not a closeNotifier", c.Underlying) -} - -// NonPusherError is more descriptive error caused by a non pusher -type NonPusherError struct { - // underlying type which doesn't implement pusher - Underlying interface{} -} - -// Implement Error -func (c NonPusherError) Error() string { - return fmt.Sprintf("%T is not a pusher", c.Underlying) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/https.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/https.go deleted file mode 100644 index 24b18b8a8..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/https.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "fmt" - "net" - "net/http" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddytls" - "github.com/mholt/certmagic" -) - -func activateHTTPS(cctx caddy.Context) error { - operatorPresent := !caddy.Started() - - if !caddy.Quiet && operatorPresent { - fmt.Print("Activating privacy features... ") - } - - ctx := cctx.(*httpContext) - - // pre-screen each config and earmark the ones that qualify for managed TLS - markQualifiedForAutoHTTPS(ctx.siteConfigs) - - // place certificates and keys on disk - for _, c := range ctx.siteConfigs { - if !c.TLS.Managed { - continue - } - if c.TLS.Manager.OnDemand != nil { - continue // obtain these certificates on-demand instead - } - err := c.TLS.Manager.ObtainCert(c.TLS.Hostname, operatorPresent) - if err != nil { - return err - } - } - - // update TLS configurations - err := enableAutoHTTPS(ctx.siteConfigs, true) - if err != nil { - return err - } - - // set up redirects - ctx.siteConfigs = makePlaintextRedirects(ctx.siteConfigs) - - // renew all relevant certificates that need renewal. this is important - // to do right away so we guarantee that renewals aren't missed, and - // also the user can respond to any potential errors that occur. - // (skip if upgrading, because the parent process is likely already listening - // on the ports we'd need to do ACME before we finish starting; parent process - // already running renewal ticker, so renewal won't be missed anyway.) - if !caddy.IsUpgrade() { - ctx.instance.StorageMu.RLock() - certCache, ok := ctx.instance.Storage[caddytls.CertCacheInstStorageKey].(*certmagic.Cache) - ctx.instance.StorageMu.RUnlock() - if ok && certCache != nil { - err = certCache.RenewManagedCertificates(operatorPresent) - if err != nil { - return err - } - } - } - - if !caddy.Quiet && operatorPresent { - fmt.Println("done.") - } - - return nil -} - -// markQualifiedForAutoHTTPS scans each config and, if it -// qualifies for managed TLS, it sets the Managed field of -// the TLS config to true. -func markQualifiedForAutoHTTPS(configs []*SiteConfig) { - for _, cfg := range configs { - if caddytls.QualifiesForManagedTLS(cfg) && cfg.Addr.Scheme != "http" { - cfg.TLS.Managed = true - } - } -} - -// enableAutoHTTPS configures each config to use TLS according to default settings. -// It will only change configs that are marked as managed but not on-demand, and -// assumes that certificates and keys are already on disk. If loadCertificates is -// true, the certificates will be loaded from disk into the cache for this process -// to use. If false, TLS will still be enabled and configured with default settings, -// but no certificates will be parsed loaded into the cache, and the returned error -// value will always be nil. -func enableAutoHTTPS(configs []*SiteConfig, loadCertificates bool) error { - for _, cfg := range configs { - if cfg == nil || cfg.TLS == nil || !cfg.TLS.Managed || - cfg.TLS.Manager == nil || cfg.TLS.Manager.OnDemand != nil { - continue - } - cfg.TLS.Enabled = true - cfg.Addr.Scheme = "https" - if loadCertificates && certmagic.HostQualifies(cfg.TLS.Hostname) { - _, err := cfg.TLS.Manager.CacheManagedCertificate(cfg.TLS.Hostname) - if err != nil { - return err - } - } - - // Make sure any config values not explicitly set are set to default - caddytls.SetDefaultTLSParams(cfg.TLS) - - // Set default port of 443 if not explicitly set - if cfg.Addr.Port == "" && - cfg.TLS.Enabled && - (!cfg.TLS.Manual || cfg.TLS.Manager.OnDemand != nil) && - cfg.Addr.Host != "localhost" { - cfg.Addr.Port = HTTPSPort - } - } - return nil -} - -// makePlaintextRedirects sets up redirects from port 80 to the relevant HTTPS -// hosts. You must pass in all configs, not just configs that qualify, since -// we must know whether the same host already exists on port 80, and those would -// not be in a list of configs that qualify for automatic HTTPS. This function will -// only set up redirects for configs that qualify. It returns the updated list of -// all configs. -func makePlaintextRedirects(allConfigs []*SiteConfig) []*SiteConfig { - for i, cfg := range allConfigs { - if cfg.TLS.Managed && - !hostHasOtherPort(allConfigs, i, HTTPPort) && - (cfg.Addr.Port == HTTPSPort || !hostHasOtherPort(allConfigs, i, HTTPSPort)) { - allConfigs = append(allConfigs, redirPlaintextHost(cfg)) - } - } - return allConfigs -} - -// hostHasOtherPort returns true if there is another config in the list with the same -// hostname that has port otherPort, or false otherwise. All the configs are checked -// against the hostname of allConfigs[thisConfigIdx]. -func hostHasOtherPort(allConfigs []*SiteConfig, thisConfigIdx int, otherPort string) bool { - for i, otherCfg := range allConfigs { - if i == thisConfigIdx { - continue // has to be a config OTHER than the one we're comparing against - } - if otherCfg.Addr.Host == allConfigs[thisConfigIdx].Addr.Host && - otherCfg.Addr.Port == otherPort { - return true - } - } - return false -} - -// redirPlaintextHost returns a new plaintext HTTP configuration for -// a virtualHost that simply redirects to cfg, which is assumed to -// be the HTTPS configuration. The returned configuration is set -// to listen on HTTPPort. The TLS field of cfg must not be nil. -func redirPlaintextHost(cfg *SiteConfig) *SiteConfig { - redirPort := cfg.Addr.Port - if redirPort == HTTPSPort { - // By default, HTTPSPort should be DefaultHTTPSPort, - // which of course doesn't need to be explicitly stated - // in the Location header. Even if HTTPSPort is changed - // so that it is no longer DefaultHTTPSPort, we shouldn't - // append it to the URL in the Location because changing - // the HTTPS port is assumed to be an internal-only change - // (in other words, we assume port forwarding is going on); - // but redirects go back to a presumably-external client. - // (If redirect clients are also internal, that is more - // advanced, and the user should configure HTTP->HTTPS - // redirects themselves.) - redirPort = "" - } - - redirMiddleware := func(next Handler) Handler { - return HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { - // Construct the URL to which to redirect. Note that the Host in a - // request might contain a port, but we just need the hostname from - // it; and we'll set the port if needed. - toURL := "https://" - requestHost, _, err := net.SplitHostPort(r.Host) - if err != nil { - requestHost = r.Host // Host did not contain a port, so use the whole value - } - if redirPort == "" { - toURL += requestHost - } else { - toURL += net.JoinHostPort(requestHost, redirPort) - } - - toURL += r.URL.RequestURI() - - w.Header().Set("Connection", "close") - http.Redirect(w, r, toURL, http.StatusMovedPermanently) - return 0, nil - }) - } - - host := cfg.Addr.Host - port := HTTPPort - addr := net.JoinHostPort(host, port) - - return &SiteConfig{ - Addr: Address{Original: addr, Host: host, Port: port}, - ListenHost: cfg.ListenHost, - middleware: []Middleware{redirMiddleware}, - TLS: &caddytls.Config{Manager: cfg.TLS.Manager}, - Timeouts: cfg.Timeouts, - } -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/logger.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/logger.go deleted file mode 100644 index cd2c26674..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/logger.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bytes" - "io" - "log" - "net" - "os" - "strings" - "sync" - - gsyslog "github.com/hashicorp/go-syslog" - "github.com/mholt/caddy" -) - -var remoteSyslogPrefixes = map[string]string{ - "syslog+tcp://": "tcp", - "syslog+udp://": "udp", - "syslog://": "udp", -} - -// Logger is shared between errors and log plugins and supports both logging to -// a file (with an optional file roller), local and remote syslog servers. -type Logger struct { - Output string - *log.Logger - Roller *LogRoller - writer io.Writer - fileMu *sync.RWMutex - V4ipMask net.IPMask - V6ipMask net.IPMask - IPMaskExists bool - Exceptions []string -} - -// NewTestLogger creates logger suitable for testing purposes -func NewTestLogger(buffer *bytes.Buffer) *Logger { - return &Logger{ - Logger: log.New(buffer, "", 0), - fileMu: new(sync.RWMutex), - } -} - -// Println wraps underlying logger with mutex -func (l Logger) Println(args ...interface{}) { - l.fileMu.RLock() - l.Logger.Println(args...) - l.fileMu.RUnlock() -} - -// Printf wraps underlying logger with mutex -func (l Logger) Printf(format string, args ...interface{}) { - l.fileMu.RLock() - l.Logger.Printf(format, args...) - l.fileMu.RUnlock() -} - -func (l Logger) MaskIP(ip string) string { - var reqIP net.IP - // If unable to parse, simply return IP as provided. - reqIP = net.ParseIP(ip) - if reqIP == nil { - return ip - } - - if reqIP.To4() != nil { - return reqIP.Mask(l.V4ipMask).String() - } else { - return reqIP.Mask(l.V6ipMask).String() - } - -} - -// ShouldLog returns true if the path is not exempted from -// being logged (i.e. it is not found in l.Exceptions). -func (l Logger) ShouldLog(path string) bool { - for _, exc := range l.Exceptions { - if Path(path).Matches(exc) { - return false - } - } - return true -} - -// Attach binds logger Start and Close functions to -// controller's OnStartup and OnShutdown hooks. -func (l *Logger) Attach(controller *caddy.Controller) { - if controller != nil { - // Opens file or connect to local/remote syslog - controller.OnStartup(l.Start) - - // Closes file or disconnects from local/remote syslog - controller.OnShutdown(l.Close) - } -} - -type syslogAddress struct { - network string - address string -} - -func parseSyslogAddress(location string) *syslogAddress { - for prefix, network := range remoteSyslogPrefixes { - if strings.HasPrefix(location, prefix) { - return &syslogAddress{ - network: network, - address: strings.TrimPrefix(location, prefix), - } - } - } - - return nil -} - -// Start initializes logger opening files or local/remote syslog connections -func (l *Logger) Start() error { - // initialize mutex on start - l.fileMu = new(sync.RWMutex) - - var err error - -selectwriter: - switch l.Output { - case "", "stderr": - l.writer = os.Stderr - case "stdout": - l.writer = os.Stdout - case "syslog": - l.writer, err = gsyslog.NewLogger(gsyslog.LOG_ERR, "LOCAL0", "caddy") - if err != nil { - return err - } - default: - if address := parseSyslogAddress(l.Output); address != nil { - l.writer, err = gsyslog.DialLogger(address.network, address.address, gsyslog.LOG_ERR, "LOCAL0", "caddy") - - if err != nil { - return err - } - - break selectwriter - } - - var file *os.File - - file, err = os.OpenFile(l.Output, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) - if err != nil { - return err - } - - if l.Roller != nil && !l.Roller.Disabled { - file.Close() - l.Roller.Filename = l.Output - l.writer = l.Roller.GetLogWriter() - } else { - l.writer = file - } - } - - l.Logger = log.New(l.writer, "", 0) - - return nil - -} - -// Close closes open log files or connections to syslog. -func (l *Logger) Close() error { - // don't close stdout or stderr - if l.writer == os.Stdout || l.writer == os.Stderr { - return nil - } - - // Will close local/remote syslog connections too :) - if closer, ok := l.writer.(io.WriteCloser); ok { - l.fileMu.Lock() - err := closer.Close() - l.fileMu.Unlock() - return err - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/middleware.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/middleware.go deleted file mode 100644 index d470811a9..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/middleware.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "fmt" - "net/http" - "os" - "path" - "time" - - "github.com/mholt/caddy" -) - -func init() { - initCaseSettings() -} - -type ( - // Middleware is the middle layer which represents the traditional - // idea of middleware: it chains one Handler to the next by being - // passed the next Handler in the chain. - Middleware func(Handler) Handler - - // ListenerMiddleware is similar to the Middleware type, except it - // chains one net.Listener to the next. - ListenerMiddleware func(caddy.Listener) caddy.Listener - - // Handler is like http.Handler except ServeHTTP may return a status - // code and/or error. - // - // If ServeHTTP writes the response header, it should return a status - // code of 0. This signals to other handlers before it that the response - // is already handled, and that they should not write to it also. Keep - // in mind that writing to the response body writes the header, too. - // - // If ServeHTTP encounters an error, it should return the error value - // so it can be logged by designated error-handling middleware. - // - // If writing a response after calling the next ServeHTTP method, the - // returned status code SHOULD be used when writing the response. - // - // If handling errors after calling the next ServeHTTP method, the - // returned error value SHOULD be logged or handled accordingly. - // - // Otherwise, return values should be propagated down the middleware - // chain by returning them unchanged. - Handler interface { - ServeHTTP(http.ResponseWriter, *http.Request) (int, error) - } - - // HandlerFunc is a convenience type like http.HandlerFunc, except - // ServeHTTP returns a status code and an error. See Handler - // documentation for more information. - HandlerFunc func(http.ResponseWriter, *http.Request) (int, error) - - // RequestMatcher checks to see if current request should be handled - // by underlying handler. - RequestMatcher interface { - Match(r *http.Request) bool - } - - // HandlerConfig is a middleware configuration. - // This makes it possible for middlewares to have a common - // configuration interface. - // - // TODO The long term plan is to get all middleware implement this - // interface for configurations. - HandlerConfig interface { - RequestMatcher - BasePath() string - } - - // ConfigSelector selects a configuration. - ConfigSelector []HandlerConfig -) - -// ServeHTTP implements the Handler interface. -func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - return f(w, r) -} - -// Select selects a Config. -// This chooses the config with the longest length. -func (c ConfigSelector) Select(r *http.Request) (config HandlerConfig) { - for i := range c { - if !c[i].Match(r) { - continue - } - if config == nil || len(c[i].BasePath()) > len(config.BasePath()) { - config = c[i] - } - } - return config -} - -// IndexFile looks for a file in /root/fpath/indexFile for each string -// in indexFiles. If an index file is found, it returns the root-relative -// path to the file and true. If no index file is found, empty string -// and false is returned. fpath must end in a forward slash '/' -// otherwise no index files will be tried (directory paths must end -// in a forward slash according to HTTP). -// -// All paths passed into and returned from this function use '/' as the -// path separator, just like URLs. IndexFle handles path manipulation -// internally for systems that use different path separators. -func IndexFile(root http.FileSystem, fpath string, indexFiles []string) (string, bool) { - if fpath[len(fpath)-1] != '/' || root == nil { - return "", false - } - for _, indexFile := range indexFiles { - // func (http.FileSystem).Open wants all paths separated by "/", - // regardless of operating system convention, so use - // path.Join instead of filepath.Join - fp := path.Join(fpath, indexFile) - f, err := root.Open(fp) - if err == nil { - f.Close() - return fp, true - } - } - return "", false -} - -// SetLastModifiedHeader checks if the provided modTime is valid and if it is sets it -// as a Last-Modified header to the ResponseWriter. If the modTime is in the future -// the current time is used instead. -func SetLastModifiedHeader(w http.ResponseWriter, modTime time.Time) { - if modTime.IsZero() || modTime.Equal(time.Unix(0, 0)) { - // the time does not appear to be valid. Don't put it in the response - return - } - - // RFC 2616 - Section 14.29 - Last-Modified: - // An origin server MUST NOT send a Last-Modified date which is later than the - // server's time of message origination. In such cases, where the resource's last - // modification would indicate some time in the future, the server MUST replace - // that date with the message origination date. - now := currentTime() - if modTime.After(now) { - modTime = now - } - - w.Header().Set("Last-Modified", modTime.UTC().Format(http.TimeFormat)) -} - -// CaseSensitivePath determines if paths should be case sensitive. -// This is configurable via CASE_SENSITIVE_PATH environment variable. -var CaseSensitivePath = false - -const caseSensitivePathEnv = "CASE_SENSITIVE_PATH" - -// initCaseSettings loads case sensitivity config from environment variable. -// -// This could have been in init, but init cannot be called from tests. -func initCaseSettings() { - switch os.Getenv(caseSensitivePathEnv) { - case "1", "true": - CaseSensitivePath = true - default: - CaseSensitivePath = false - } -} - -// MergeRequestMatchers merges multiple RequestMatchers into one. -// This allows a middleware to use multiple RequestMatchers. -func MergeRequestMatchers(matchers ...RequestMatcher) RequestMatcher { - return requestMatchers(matchers) -} - -type requestMatchers []RequestMatcher - -// Match satisfies RequestMatcher interface. -func (m requestMatchers) Match(r *http.Request) bool { - for _, matcher := range m { - if !matcher.Match(r) { - return false - } - } - return true -} - -// currentTime, as it is defined here, returns time.Now(). -// It's defined as a variable for mocking time in tests. -var currentTime = func() time.Time { return time.Now() } - -// EmptyNext is a no-op function that can be passed into -// Middleware functions so that the assignment to the -// Next field of the Handler can be tested. -// -// Used primarily for testing but needs to be exported so -// plugins can use this as a convenience. -var EmptyNext = HandlerFunc(func(w http.ResponseWriter, r *http.Request) (int, error) { return 0, nil }) - -// SameNext does a pointer comparison between next1 and next2. -// -// Used primarily for testing but needs to be exported so -// plugins can use this as a convenience. -func SameNext(next1, next2 Handler) bool { - return fmt.Sprintf("%v", next1) == fmt.Sprintf("%v", next2) -} - -// Context key constants. -const ( - // ReplacerCtxKey is the context key for a per-request replacer. - ReplacerCtxKey caddy.CtxKey = "replacer" - - // RemoteUserCtxKey is the key for the remote user of the request, if any (basicauth). - RemoteUserCtxKey caddy.CtxKey = "remote_user" - - // MitmCtxKey is the key for the result of MITM detection - MitmCtxKey caddy.CtxKey = "mitm" - - // RequestIDCtxKey is the key for the U4 UUID value - RequestIDCtxKey caddy.CtxKey = "request_id" -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/mitm.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/mitm.go deleted file mode 100644 index 6736610e9..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/mitm.go +++ /dev/null @@ -1,780 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bytes" - "context" - "crypto/tls" - "io" - "net" - "net/http" - "strconv" - "strings" - "sync" - - "github.com/mholt/caddy/caddytls" - "github.com/mholt/caddy/telemetry" -) - -// tlsHandler is a http.Handler that will inject a value -// into the request context indicating if the TLS -// connection is likely being intercepted. -type tlsHandler struct { - next http.Handler - listener *tlsHelloListener - closeOnMITM bool // whether to close connection on MITM; TODO: expose through new directive -} - -// ServeHTTP checks the User-Agent. For the four main browsers (Chrome, -// Edge, Firefox, and Safari) indicated by the User-Agent, the properties -// of the TLS Client Hello will be compared. The context value "mitm" will -// be set to a value indicating if it is likely that the underlying TLS -// connection is being intercepted. -// -// Note that due to Microsoft's decision to intentionally make IE/Edge -// user agents obscure (and look like other browsers), this may offer -// less accuracy for IE/Edge clients. -// -// This MITM detection capability is based on research done by Durumeric, -// Halderman, et. al. in "The Security Impact of HTTPS Interception" (NDSS '17): -// https://jhalderm.com/pub/papers/interception-ndss17.pdf -func (h *tlsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // TODO: one request per connection, we should report UA in connection with - // handshake (reported in caddytls package) and our MITM assessment - - if h.listener == nil { - h.next.ServeHTTP(w, r) - return - } - - h.listener.helloInfosMu.RLock() - info := h.listener.helloInfos[r.RemoteAddr] - h.listener.helloInfosMu.RUnlock() - - ua := r.Header.Get("User-Agent") - uaHash := telemetry.FastHash([]byte(ua)) - - // report this request's UA in connection with this ClientHello - go telemetry.AppendUnique("tls_client_hello_ua:"+caddytls.ClientHelloInfo(info).Key(), uaHash) - - var checked, mitm bool - if r.Header.Get("X-BlueCoat-Via") != "" || // Blue Coat (masks User-Agent header to generic values) - r.Header.Get("X-FCCKV2") != "" || // Fortinet - info.advertisesHeartbeatSupport() { // no major browsers have ever implemented Heartbeat - // TODO: Move the heartbeat check into each "looksLike" function... - checked = true - mitm = true - } else if strings.Contains(ua, "Edge") || strings.Contains(ua, "MSIE") || - strings.Contains(ua, "Trident") { - checked = true - mitm = !info.looksLikeEdge() - } else if strings.Contains(ua, "Chrome") { - checked = true - mitm = !info.looksLikeChrome() - } else if strings.Contains(ua, "CriOS") { - // Chrome on iOS sometimes uses iOS-provided TLS stack (which looks exactly like Safari) - // but for connections that don't render a web page (favicon, etc.) it uses its own... - checked = true - mitm = !info.looksLikeChrome() && !info.looksLikeSafari() - } else if strings.Contains(ua, "Firefox") { - checked = true - if strings.Contains(ua, "Windows") { - ver := getVersion(ua, "Firefox") - if ver == 45.0 || ver == 52.0 { - mitm = !info.looksLikeTor() - } else { - mitm = !info.looksLikeFirefox() - } - } else { - mitm = !info.looksLikeFirefox() - } - } else if strings.Contains(ua, "Safari") { - checked = true - mitm = !info.looksLikeSafari() - } - - if checked { - r = r.WithContext(context.WithValue(r.Context(), MitmCtxKey, mitm)) - if mitm { - go telemetry.AppendUnique("http_mitm", "likely") - } else { - go telemetry.AppendUnique("http_mitm", "unlikely") - } - } else { - go telemetry.AppendUnique("http_mitm", "unknown") - } - - if mitm && h.closeOnMITM { - // TODO: This termination might need to happen later in the middleware - // chain in order to be picked up by the log directive, in case the site - // owner still wants to log this event. It'll probably require a new - // directive. If this feature is useful, we can finish implementing this. - r.Close = true - return - } - - h.next.ServeHTTP(w, r) -} - -// getVersion returns a (possibly simplified) representation of the version string -// from a UserAgent string. It returns a float, so it can represent major and minor -// versions; the rest of the version is just tacked on behind the decimal point. -// The purpose of this is to stay simple while allowing for basic, fast comparisons. -// If the version for softwareName is not found in ua, -1 is returned. -func getVersion(ua, softwareName string) float64 { - search := softwareName + "/" - start := strings.Index(ua, search) - if start < 0 { - return -1 - } - start += len(search) - end := strings.Index(ua[start:], " ") - if end < 0 { - end = len(ua) - } else { - end += start - } - strVer := strings.Replace(ua[start:end], "-", "", -1) - firstDot := strings.Index(strVer, ".") - if firstDot >= 0 { - strVer = strVer[:firstDot+1] + strings.Replace(strVer[firstDot+1:], ".", "", -1) - } - ver, err := strconv.ParseFloat(strVer, 64) - if err != nil { - return -1 - } - return ver -} - -// clientHelloConn reads the ClientHello -// and stores it in the attached listener. -type clientHelloConn struct { - net.Conn - listener *tlsHelloListener - readHello bool // whether ClientHello has been read - buf *bytes.Buffer -} - -// Read reads from c.Conn (by letting the standard library -// do the reading off the wire), with the exception of -// getting a copy of the ClientHello so it can parse it. -func (c *clientHelloConn) Read(b []byte) (n int, err error) { - // if we've already read the ClientHello, pass thru - if c.readHello { - return c.Conn.Read(b) - } - - // we let the standard lib read off the wire for us, and - // tee that into our buffer so we can read the ClientHello - tee := io.TeeReader(c.Conn, c.buf) - n, err = tee.Read(b) - if err != nil { - return - } - if c.buf.Len() < 5 { - return // need to read more bytes for header - } - - // read the header bytes - hdr := make([]byte, 5) - _, err = io.ReadFull(c.buf, hdr) - if err != nil { - return // this would be highly unusual and sad - } - - // get length of the ClientHello message and read it - length := int(uint16(hdr[3])<<8 | uint16(hdr[4])) - if c.buf.Len() < length { - return // need to read more bytes - } - hello := make([]byte, length) - _, err = io.ReadFull(c.buf, hello) - if err != nil { - return - } - bufpool.Put(c.buf) // buffer no longer needed - - // parse the ClientHello and store it in the map - rawParsed := parseRawClientHello(hello) - c.listener.helloInfosMu.Lock() - c.listener.helloInfos[c.Conn.RemoteAddr().String()] = rawParsed - c.listener.helloInfosMu.Unlock() - - // report this ClientHello to telemetry - chKey := caddytls.ClientHelloInfo(rawParsed).Key() - go telemetry.SetNested("tls_client_hello", chKey, rawParsed) - go telemetry.AppendUnique("tls_client_hello_count", chKey) - - c.readHello = true - return -} - -// parseRawClientHello parses data which contains the raw -// TLS Client Hello message. It extracts relevant information -// into info. Any error reading the Client Hello (such as -// insufficient length or invalid length values) results in -// a silent error and an incomplete info struct, since there -// is no good way to handle an error like this during Accept(). -// The data is expected to contain the whole ClientHello and -// ONLY the ClientHello. -// -// The majority of this code is borrowed from the Go standard -// library, which is (c) The Go Authors. It has been modified -// to fit this use case. -func parseRawClientHello(data []byte) (info rawHelloInfo) { - if len(data) < 42 { - return - } - info.Version = uint16(data[4])<<8 | uint16(data[5]) - sessionIDLen := int(data[38]) - if sessionIDLen > 32 || len(data) < 39+sessionIDLen { - return - } - data = data[39+sessionIDLen:] - if len(data) < 2 { - return - } - // cipherSuiteLen is the number of bytes of cipher suite numbers. Since - // they are uint16s, the number must be even. - cipherSuiteLen := int(data[0])<<8 | int(data[1]) - if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { - return - } - numCipherSuites := cipherSuiteLen / 2 - // read in the cipher suites - info.CipherSuites = make([]uint16, numCipherSuites) - for i := 0; i < numCipherSuites; i++ { - info.CipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) - } - data = data[2+cipherSuiteLen:] - if len(data) < 1 { - return - } - // read in the compression methods - compressionMethodsLen := int(data[0]) - if len(data) < 1+compressionMethodsLen { - return - } - info.CompressionMethods = data[1 : 1+compressionMethodsLen] - - data = data[1+compressionMethodsLen:] - - // ClientHello is optionally followed by extension data - if len(data) < 2 { - return - } - extensionsLength := int(data[0])<<8 | int(data[1]) - data = data[2:] - if extensionsLength != len(data) { - return - } - - // read in each extension, and extract any relevant information - // from extensions we care about - for len(data) != 0 { - if len(data) < 4 { - return - } - extension := uint16(data[0])<<8 | uint16(data[1]) - length := int(data[2])<<8 | int(data[3]) - data = data[4:] - if len(data) < length { - return - } - - // record that the client advertised support for this extension - info.Extensions = append(info.Extensions, extension) - - switch extension { - case extensionSupportedCurves: - // http://tools.ietf.org/html/rfc4492#section-5.5.1 - if length < 2 { - return - } - l := int(data[0])<<8 | int(data[1]) - if l%2 == 1 || length != l+2 { - return - } - numCurves := l / 2 - info.Curves = make([]tls.CurveID, numCurves) - d := data[2:] - for i := 0; i < numCurves; i++ { - info.Curves[i] = tls.CurveID(d[0])<<8 | tls.CurveID(d[1]) - d = d[2:] - } - case extensionSupportedPoints: - // http://tools.ietf.org/html/rfc4492#section-5.5.2 - if length < 1 { - return - } - l := int(data[0]) - if length != l+1 { - return - } - info.Points = make([]uint8, l) - copy(info.Points, data[1:]) - } - - data = data[length:] - } - - return -} - -// newTLSListener returns a new tlsHelloListener that wraps ln. -func newTLSListener(ln net.Listener, config *tls.Config) *tlsHelloListener { - return &tlsHelloListener{ - Listener: ln, - config: config, - helloInfos: make(map[string]rawHelloInfo), - } -} - -// tlsHelloListener is a TLS listener that is specially designed -// to read the ClientHello manually so we can extract necessary -// information from it. Each ClientHello message is mapped by -// the remote address of the client, which must be removed when -// the connection is closed (use ConnState). -type tlsHelloListener struct { - net.Listener - config *tls.Config - helloInfos map[string]rawHelloInfo - helloInfosMu sync.RWMutex -} - -// Accept waits for and returns the next connection to the listener. -// After it accepts the underlying connection, it reads the -// ClientHello message and stores the parsed data into a map on l. -func (l *tlsHelloListener) Accept() (net.Conn, error) { - conn, err := l.Listener.Accept() - if err != nil { - return nil, err - } - buf := bufpool.Get().(*bytes.Buffer) - buf.Reset() - helloConn := &clientHelloConn{Conn: conn, listener: l, buf: buf} - return tls.Server(helloConn, l.config), nil -} - -// rawHelloInfo contains the "raw" data parsed from the TLS -// Client Hello. No interpretation is done on the raw data. -// -// The methods on this type implement heuristics described -// by Durumeric, Halderman, et. al. in -// "The Security Impact of HTTPS Interception": -// https://jhalderm.com/pub/papers/interception-ndss17.pdf -type rawHelloInfo caddytls.ClientHelloInfo - -// advertisesHeartbeatSupport returns true if info indicates -// that the client supports the Heartbeat extension. -func (info rawHelloInfo) advertisesHeartbeatSupport() bool { - for _, ext := range info.Extensions { - if ext == extensionHeartbeat { - return true - } - } - return false -} - -// looksLikeFirefox returns true if info looks like a handshake -// from a modern version of Firefox. -func (info rawHelloInfo) looksLikeFirefox() bool { - // "To determine whether a Firefox session has been - // intercepted, we check for the presence and order - // of extensions, cipher suites, elliptic curves, - // EC point formats, and handshake compression methods." (early 2016) - - // We check for the presence and order of the extensions. - // Note: Sometimes 0x15 (21, padding) is present, sometimes not. - // Note: Firefox 51+ does not advertise 0x3374 (13172, NPN). - // Note: Firefox doesn't advertise 0x0 (0, SNI) when connecting to IP addresses. - // Note: Firefox 55+ doesn't appear to advertise 0xFF03 (65283, short headers). It used to be between 5 and 13. - // Note: Firefox on Fedora (or RedHat) doesn't include ECC suites because of patent liability. - requiredExtensionsOrder := []uint16{23, 65281, 10, 11, 35, 16, 5, 13} - if !assertPresenceAndOrdering(requiredExtensionsOrder, info.Extensions, true) { - return false - } - - // We check for both presence of curves and their ordering. - requiredCurves := []tls.CurveID{29, 23, 24, 25} - if len(info.Curves) < len(requiredCurves) { - return false - } - for i := range requiredCurves { - if info.Curves[i] != requiredCurves[i] { - return false - } - } - if len(info.Curves) > len(requiredCurves) { - // newer Firefox (55 Nightly?) may have additional curves at end of list - allowedCurves := []tls.CurveID{256, 257} - for i := range allowedCurves { - if info.Curves[len(requiredCurves)+i] != allowedCurves[i] { - return false - } - } - } - - if hasGreaseCiphers(info.CipherSuites) { - return false - } - - // We check for order of cipher suites but not presence, since - // according to the paper, cipher suites may be not be added - // or reordered by the user, but they may be disabled. - expectedCipherSuiteOrder := []uint16{ - TLS_AES_128_GCM_SHA256, // 0x1301 - TLS_CHACHA20_POLY1305_SHA256, // 0x1303 - TLS_AES_256_GCM_SHA384, // 0x1302 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // 0xc02b - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // 0xc02f - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, // 0xcca9 - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, // 0xcca8 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // 0xc02c - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // 0xc030 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // 0xc00a - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // 0xc009 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // 0xc013 - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // 0xc014 - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, // 0x33 - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, // 0x39 - tls.TLS_RSA_WITH_AES_128_CBC_SHA, // 0x2f - tls.TLS_RSA_WITH_AES_256_CBC_SHA, // 0x35 - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // 0xa - } - return assertPresenceAndOrdering(expectedCipherSuiteOrder, info.CipherSuites, false) -} - -// looksLikeChrome returns true if info looks like a handshake -// from a modern version of Chrome. -func (info rawHelloInfo) looksLikeChrome() bool { - // "We check for ciphers and extensions that Chrome is known - // to not support, but do not check for the inclusion of - // specific ciphers or extensions, nor do we validate their - // order. When appropriate, we check the presence and order - // of elliptic curves, compression methods, and EC point formats." (early 2016) - - // Not in Chrome 56, but present in Safari 10 (Feb. 2017): - // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024) - // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023) - // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) - // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) - // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028) - // TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027) - // TLS_RSA_WITH_AES_256_CBC_SHA256 (0x3d) - // TLS_RSA_WITH_AES_128_CBC_SHA256 (0x3c) - - // Not in Chrome 56, but present in Firefox 51 (Feb. 2017): - // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a) - // TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009) - // TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x33) - // TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39) - - // Selected ciphers present in Chrome mobile (Feb. 2017): - // 0xc00a, 0xc014, 0xc009, 0x9c, 0x9d, 0x2f, 0x35, 0xa - - chromeCipherExclusions := map[uint16]struct{}{ - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: {}, // 0xc024 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: {}, // 0xc023 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: {}, // 0xc028 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: {}, // 0xc027 - TLS_RSA_WITH_AES_256_CBC_SHA256: {}, // 0x3d - tls.TLS_RSA_WITH_AES_128_CBC_SHA256: {}, // 0x3c - TLS_DHE_RSA_WITH_AES_128_CBC_SHA: {}, // 0x33 - TLS_DHE_RSA_WITH_AES_256_CBC_SHA: {}, // 0x39 - } - for _, ext := range info.CipherSuites { - if _, ok := chromeCipherExclusions[ext]; ok { - return false - } - } - - // Chrome does not include curve 25 (CurveP521) (as of Chrome 56, Feb. 2017). - for _, curve := range info.Curves { - if curve == 25 { - return false - } - } - - if !hasGreaseCiphers(info.CipherSuites) { - return false - } - - return true -} - -// looksLikeEdge returns true if info looks like a handshake -// from a modern version of MS Edge. -func (info rawHelloInfo) looksLikeEdge() bool { - // "SChannel connections can by uniquely identified because SChannel - // is the only TLS library we tested that includes the OCSP status - // request extension before the supported groups and EC point formats - // extensions." (early 2016) - // - // More specifically, the OCSP status request extension appears - // *directly* before the other two extensions, which occur in that - // order. (I contacted the authors for clarification and verified it.) - for i, ext := range info.Extensions { - if ext == extensionOCSPStatusRequest { - if len(info.Extensions) <= i+2 { - return false - } - if info.Extensions[i+1] != extensionSupportedCurves || - info.Extensions[i+2] != extensionSupportedPoints { - return false - } - } - } - - for _, cs := range info.CipherSuites { - // As of Feb. 2017, Edge does not have 0xff, but Avast adds it - if cs == scsvRenegotiation { - return false - } - // Edge and modern IE do not have 0x4 or 0x5, but Blue Coat does - if cs == TLS_RSA_WITH_RC4_128_MD5 || cs == tls.TLS_RSA_WITH_RC4_128_SHA { - return false - } - } - - if hasGreaseCiphers(info.CipherSuites) { - return false - } - - return true -} - -// looksLikeSafari returns true if info looks like a handshake -// from a modern version of MS Safari. -func (info rawHelloInfo) looksLikeSafari() bool { - // "One unique aspect of Secure Transport is that it includes - // the TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0xff) cipher first, - // whereas the other libraries we investigated include the - // cipher last. Similar to Microsoft, Apple has changed - // TLS behavior in minor OS updates, which are not indicated - // in the HTTP User-Agent header. We allow for any of the - // updates when validating handshakes, and we check for the - // presence and ordering of ciphers, extensions, elliptic - // curves, and compression methods." (early 2016) - - // Note that any C lib (e.g. curl) compiled on macOS - // will probably use Secure Transport which will also - // share the TLS handshake characteristics of Safari. - - // We check for the presence and order of the extensions. - requiredExtensionsOrder := []uint16{10, 11, 13, 13172, 16, 5, 18, 23} - if !assertPresenceAndOrdering(requiredExtensionsOrder, info.Extensions, true) { - // Safari on iOS 11 (beta) uses different set/ordering of extensions - requiredExtensionsOrderiOS11 := []uint16{65281, 0, 23, 13, 5, 13172, 18, 16, 11, 10} - if !assertPresenceAndOrdering(requiredExtensionsOrderiOS11, info.Extensions, true) { - return false - } - } else { - // For these versions of Safari, expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV first. - if len(info.CipherSuites) < 1 { - return false - } - if info.CipherSuites[0] != scsvRenegotiation { - return false - } - } - - if hasGreaseCiphers(info.CipherSuites) { - return false - } - - // We check for order and presence of cipher suites - expectedCipherSuiteOrder := []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // 0xc02c - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // 0xc02b - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, // 0xc024 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, // 0xc023 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // 0xc00a - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // 0xc009 - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // 0xc030 - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // 0xc02f - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, // 0xc028 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, // 0xc027 - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // 0xc014 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // 0xc013 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, // 0x9d - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // 0x9c - TLS_RSA_WITH_AES_256_CBC_SHA256, // 0x3d - tls.TLS_RSA_WITH_AES_128_CBC_SHA256, // 0x3c - tls.TLS_RSA_WITH_AES_256_CBC_SHA, // 0x35 - tls.TLS_RSA_WITH_AES_128_CBC_SHA, // 0x2f - } - return assertPresenceAndOrdering(expectedCipherSuiteOrder, info.CipherSuites, true) -} - -// looksLikeTor returns true if the info looks like a ClientHello from Tor browser -// (based on Firefox). -func (info rawHelloInfo) looksLikeTor() bool { - requiredExtensionsOrder := []uint16{10, 11, 16, 5, 13} - if !assertPresenceAndOrdering(requiredExtensionsOrder, info.Extensions, true) { - return false - } - - // check for session tickets support; Tor doesn't support them to prevent tracking - for _, ext := range info.Extensions { - if ext == 35 { - return false - } - } - - // We check for both presence of curves and their ordering, including - // an optional curve at the beginning (for Tor based on Firefox 52) - infoCurves := info.Curves - if len(info.Curves) == 4 { - if info.Curves[0] != 29 { - return false - } - infoCurves = info.Curves[1:] - } - requiredCurves := []tls.CurveID{23, 24, 25} - if len(infoCurves) < len(requiredCurves) { - return false - } - for i := range requiredCurves { - if infoCurves[i] != requiredCurves[i] { - return false - } - } - - if hasGreaseCiphers(info.CipherSuites) { - return false - } - - // We check for order of cipher suites but not presence, since - // according to the paper, cipher suites may be not be added - // or reordered by the user, but they may be disabled. - expectedCipherSuiteOrder := []uint16{ - TLS_AES_128_GCM_SHA256, // 0x1301 - TLS_CHACHA20_POLY1305_SHA256, // 0x1303 - TLS_AES_256_GCM_SHA384, // 0x1302 - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // 0xc02b - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // 0xc02f - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, // 0xcca9 - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, // 0xcca8 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, // 0xc02c - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // 0xc030 - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, // 0xc00a - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, // 0xc009 - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, // 0xc013 - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, // 0xc014 - TLS_DHE_RSA_WITH_AES_128_CBC_SHA, // 0x33 - TLS_DHE_RSA_WITH_AES_256_CBC_SHA, // 0x39 - tls.TLS_RSA_WITH_AES_128_CBC_SHA, // 0x2f - tls.TLS_RSA_WITH_AES_256_CBC_SHA, // 0x35 - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, // 0xa - } - return assertPresenceAndOrdering(expectedCipherSuiteOrder, info.CipherSuites, false) -} - -// assertPresenceAndOrdering will return true if candidateList contains -// the items in requiredItems in the same order as requiredItems. -// -// If requiredIsSubset is true, then all items in requiredItems must be -// present in candidateList. If requiredIsSubset is false, then requiredItems -// may contain items that are not in candidateList. -// -// In all cases, the order of requiredItems is enforced. -func assertPresenceAndOrdering(requiredItems, candidateList []uint16, requiredIsSubset bool) bool { - superset := requiredItems - subset := candidateList - if requiredIsSubset { - superset = candidateList - subset = requiredItems - } - - var j int - for _, item := range subset { - var found bool - for j < len(superset) { - if superset[j] == item { - found = true - break - } - j++ - } - if j == len(superset) && !found { - return false - } - } - return true -} - -func hasGreaseCiphers(cipherSuites []uint16) bool { - for _, cipher := range cipherSuites { - if _, ok := greaseCiphers[cipher]; ok { - return true - } - } - return false -} - -// pool buffers so we can reuse allocations over time -var bufpool = sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, -} - -var greaseCiphers = map[uint16]struct{}{ - 0x0A0A: {}, - 0x1A1A: {}, - 0x2A2A: {}, - 0x3A3A: {}, - 0x4A4A: {}, - 0x5A5A: {}, - 0x6A6A: {}, - 0x7A7A: {}, - 0x8A8A: {}, - 0x9A9A: {}, - 0xAAAA: {}, - 0xBABA: {}, - 0xCACA: {}, - 0xDADA: {}, - 0xEAEA: {}, - 0xFAFA: {}, -} - -// Define variables used for TLS communication -const ( - extensionOCSPStatusRequest = 5 - extensionSupportedCurves = 10 // also called "SupportedGroups" - extensionSupportedPoints = 11 - extensionHeartbeat = 15 - - scsvRenegotiation = 0xff - - // cipher suites missing from the crypto/tls package, - // in no particular order here - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xc024 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xc028 - TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x3d - TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x33 - TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x39 - TLS_RSA_WITH_RC4_128_MD5 = 0x4 - - // new PSK ciphers introduced by TLS 1.3, not (yet) in crypto/tls - // https://tlswg.github.io/tls13-spec/#rfc.appendix.A.4) - TLS_AES_128_GCM_SHA256 = 0x1301 - TLS_AES_256_GCM_SHA384 = 0x1302 - TLS_CHACHA20_POLY1305_SHA256 = 0x1303 - TLS_AES_128_CCM_SHA256 = 0x1304 - TLS_AES_128_CCM_8_SHA256 = 0x1305 -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/path.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/path.go deleted file mode 100644 index 524bb0252..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/path.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "net/http" - "path" - "strings" -) - -// Path represents a URI path. It should usually be -// set to the value of a request path. -type Path string - -// Matches checks to see if base matches p. The correct -// usage of this method sets p as the request path, and -// base as a Caddyfile (user-defined) rule path. -// -// Path matching will probably not always be a direct -// comparison; this method assures that paths can be -// easily and consistently matched. -// -// Multiple slashes are collapsed/merged. See issue #1859. -func (p Path) Matches(base string) bool { - if base == "/" || base == "" { - return true - } - - // sanitize the paths for comparison, very important - // (slightly lossy if the base path requires multiple - // consecutive forward slashes, since those will be merged) - pHasTrailingSlash := strings.HasSuffix(string(p), "/") - baseHasTrailingSlash := strings.HasSuffix(base, "/") - p = Path(path.Clean(string(p))) - base = path.Clean(base) - if pHasTrailingSlash { - p += "/" - } - if baseHasTrailingSlash { - base += "/" - } - - if CaseSensitivePath { - return strings.HasPrefix(string(p), base) - } - return strings.HasPrefix(strings.ToLower(string(p)), strings.ToLower(base)) -} - -// PathMatcher is a Path RequestMatcher. -type PathMatcher string - -// Match satisfies RequestMatcher. -func (p PathMatcher) Match(r *http.Request) bool { - return Path(r.URL.Path).Matches(string(p)) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go deleted file mode 100644 index 58dbfd50d..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "crypto/tls" - "flag" - "fmt" - "log" - "net" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyfile" - "github.com/mholt/caddy/caddyhttp/staticfiles" - "github.com/mholt/caddy/caddytls" - "github.com/mholt/caddy/telemetry" - "github.com/mholt/certmagic" -) - -const serverType = "http" - -func init() { - flag.StringVar(&HTTPPort, "http-port", HTTPPort, "Default port to use for HTTP") - flag.StringVar(&HTTPSPort, "https-port", HTTPSPort, "Default port to use for HTTPS") - flag.StringVar(&Host, "host", DefaultHost, "Default host") - flag.StringVar(&Port, "port", DefaultPort, "Default port") - flag.StringVar(&Root, "root", DefaultRoot, "Root path of default site") - flag.DurationVar(&GracefulTimeout, "grace", 5*time.Second, "Maximum duration of graceful shutdown") - flag.BoolVar(&HTTP2, "http2", true, "Use HTTP/2") - flag.BoolVar(&QUIC, "quic", false, "Use experimental QUIC") - - caddy.RegisterServerType(serverType, caddy.ServerType{ - Directives: func() []string { return directives }, - DefaultInput: func() caddy.Input { - if Port == DefaultPort && Host != "" { - // by leaving the port blank in this case we give auto HTTPS - // a chance to set the port to 443 for us - return caddy.CaddyfileInput{ - Contents: []byte(fmt.Sprintf("%s\nroot %s", Host, Root)), - ServerTypeName: serverType, - } - } - return caddy.CaddyfileInput{ - Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, Port, Root)), - ServerTypeName: serverType, - } - }, - NewContext: newContext, - }) - caddy.RegisterCaddyfileLoader("short", caddy.LoaderFunc(shortCaddyfileLoader)) - caddy.RegisterParsingCallback(serverType, "root", hideCaddyfile) - caddy.RegisterParsingCallback(serverType, "tls", activateHTTPS) - caddytls.RegisterConfigGetter(serverType, func(c *caddy.Controller) *caddytls.Config { return GetConfig(c).TLS }) - - // disable the caddytls package reporting ClientHellos - // to telemetry, since our MITM detector does this but - // with more information than the standard lib provides - // (as of May 2018) - caddytls.ClientHelloTelemetry = false -} - -// hideCaddyfile hides the source/origin Caddyfile if it is within the -// site root. This function should be run after parsing the root directive. -func hideCaddyfile(cctx caddy.Context) error { - ctx := cctx.(*httpContext) - for _, cfg := range ctx.siteConfigs { - // if no Caddyfile exists exit. - if cfg.originCaddyfile == "" { - return nil - } - absRoot, err := filepath.Abs(cfg.Root) - if err != nil { - return err - } - absOriginCaddyfile, err := filepath.Abs(cfg.originCaddyfile) - if err != nil { - return err - } - if strings.HasPrefix(absOriginCaddyfile, absRoot) { - cfg.HiddenFiles = append(cfg.HiddenFiles, filepath.ToSlash(strings.TrimPrefix(absOriginCaddyfile, absRoot))) - } - } - return nil -} - -func newContext(inst *caddy.Instance) caddy.Context { - return &httpContext{instance: inst, keysToSiteConfigs: make(map[string]*SiteConfig)} -} - -type httpContext struct { - instance *caddy.Instance - - // keysToSiteConfigs maps an address at the top of a - // server block (a "key") to its SiteConfig. Not all - // SiteConfigs will be represented here, only ones - // that appeared in the Caddyfile. - keysToSiteConfigs map[string]*SiteConfig - - // siteConfigs is the master list of all site configs. - siteConfigs []*SiteConfig -} - -func (h *httpContext) saveConfig(key string, cfg *SiteConfig) { - h.siteConfigs = append(h.siteConfigs, cfg) - h.keysToSiteConfigs[key] = cfg -} - -// InspectServerBlocks make sure that everything checks out before -// executing directives and otherwise prepares the directives to -// be parsed and executed. -func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) { - siteAddrs := make(map[string]string) - - // For each address in each server block, make a new config - for _, sb := range serverBlocks { - for _, key := range sb.Keys { - addr, err := standardizeAddress(key) - if err != nil { - return serverBlocks, err - } - - addr = addr.Normalize() - key = addr.Key() - if _, dup := h.keysToSiteConfigs[key]; dup { - return serverBlocks, fmt.Errorf("duplicate site key: %s", key) - } - - // Fill in address components from command line so that middleware - // have access to the correct information during setup - if addr.Host == "" && Host != DefaultHost { - addr.Host = Host - } - if addr.Port == "" && Port != DefaultPort { - addr.Port = Port - } - - // Make sure the adjusted site address is distinct - addrCopy := addr // make copy so we don't disturb the original, carefully-parsed address struct - if addrCopy.Port == "" && Port == DefaultPort { - addrCopy.Port = Port - } - addrStr := addrCopy.String() - if otherSiteKey, dup := siteAddrs[addrStr]; dup { - err := fmt.Errorf("duplicate site address: %s", addrStr) - if (addrCopy.Host == Host && Host != DefaultHost) || - (addrCopy.Port == Port && Port != DefaultPort) { - err = fmt.Errorf("site defined as %s is a duplicate of %s because of modified "+ - "default host and/or port values (usually via -host or -port flags)", key, otherSiteKey) - } - return serverBlocks, err - } - siteAddrs[addrStr] = key - - // If default HTTP or HTTPS ports have been customized, - // make sure the ACME challenge ports match - var altHTTPPort, altTLSALPNPort int - if HTTPPort != DefaultHTTPPort { - portInt, err := strconv.Atoi(HTTPPort) - if err != nil { - return nil, err - } - altHTTPPort = portInt - } - if HTTPSPort != DefaultHTTPSPort { - portInt, err := strconv.Atoi(HTTPSPort) - if err != nil { - return nil, err - } - altTLSALPNPort = portInt - } - - // Make our caddytls.Config, which has a pointer to the - // instance's certificate cache and enough information - // to use automatic HTTPS when the time comes - caddytlsConfig, err := caddytls.NewConfig(h.instance) - if err != nil { - return nil, fmt.Errorf("creating new caddytls configuration: %v", err) - } - caddytlsConfig.Hostname = addr.Host - caddytlsConfig.Manager.AltHTTPPort = altHTTPPort - caddytlsConfig.Manager.AltTLSALPNPort = altTLSALPNPort - - // Save the config to our master list, and key it for lookups - cfg := &SiteConfig{ - Addr: addr, - Root: Root, - TLS: caddytlsConfig, - originCaddyfile: sourceFile, - IndexPages: staticfiles.DefaultIndexPages, - } - h.saveConfig(key, cfg) - } - } - - // For sites that have gzip (which gets chained in - // before the error handler) we should ensure that the - // errors directive also appears so error pages aren't - // written after the gzip writer is closed. See #616. - for _, sb := range serverBlocks { - _, hasGzip := sb.Tokens["gzip"] - _, hasErrors := sb.Tokens["errors"] - if hasGzip && !hasErrors { - sb.Tokens["errors"] = []caddyfile.Token{{Text: "errors"}} - } - } - - return serverBlocks, nil -} - -// MakeServers uses the newly-created siteConfigs to -// create and return a list of server instances. -func (h *httpContext) MakeServers() ([]caddy.Server, error) { - // make a rough estimate as to whether we're in a "production - // environment/system" - start by assuming that most production - // servers will set their default CA endpoint to a public, - // trusted CA (obviously not a perfect heuristic) - var looksLikeProductionCA bool - for _, publicCAEndpoint := range caddytls.KnownACMECAs { - if strings.Contains(certmagic.Default.CA, publicCAEndpoint) { - looksLikeProductionCA = true - break - } - } - - // Iterate each site configuration and make sure that: - // 1) TLS is disabled for explicitly-HTTP sites (necessary - // when an HTTP address shares a block containing tls) - // 2) if QUIC is enabled, TLS ClientAuth is not, because - // currently, QUIC does not support ClientAuth (TODO: - // revisit this when our QUIC implementation supports it) - // 3) if TLS ClientAuth is used, StrictHostMatching is on - var atLeastOneSiteLooksLikeProduction bool - for _, cfg := range h.siteConfigs { - // see if all the addresses (both sites and - // listeners) are loopback to help us determine - // if this is a "production" instance or not - if !atLeastOneSiteLooksLikeProduction { - if !caddy.IsLoopback(cfg.Addr.Host) && - !caddy.IsLoopback(cfg.ListenHost) && - (caddytls.QualifiesForManagedTLS(cfg) || - certmagic.HostQualifies(cfg.Addr.Host)) { - atLeastOneSiteLooksLikeProduction = true - } - } - - // make sure TLS is disabled for explicitly-HTTP sites - // (necessary when HTTP address shares a block containing tls) - if !cfg.TLS.Enabled { - continue - } - if cfg.Addr.Port == HTTPPort || cfg.Addr.Scheme == "http" { - cfg.TLS.Enabled = false - log.Printf("[WARNING] TLS disabled for %s", cfg.Addr) - } else if cfg.Addr.Scheme == "" { - // set scheme to https ourselves, since TLS is enabled - // and it was not explicitly set to something else. this - // makes it appear as "https" when we print the list of - // running sites; otherwise "http" would be assumed which - // is incorrect for this site. - cfg.Addr.Scheme = "https" - } - if cfg.Addr.Port == "" && ((!cfg.TLS.Manual && !cfg.TLS.SelfSigned) || cfg.TLS.Manager.OnDemand != nil) { - // this is vital, otherwise the function call below that - // sets the listener address will use the default port - // instead of 443 because it doesn't know about TLS. - cfg.Addr.Port = HTTPSPort - } - if cfg.TLS.ClientAuth != tls.NoClientCert { - if QUIC { - return nil, fmt.Errorf("cannot enable TLS client authentication with QUIC, because QUIC does not yet support it") - } - // this must be enabled so that a client cannot connect - // using SNI for another site on this listener that - // does NOT require ClientAuth, and then send HTTP - // requests with the Host header of this site which DOES - // require client auth, thus bypassing it... - cfg.StrictHostMatching = true - } - } - - // we must map (group) each config to a bind address - groups, err := groupSiteConfigsByListenAddr(h.siteConfigs) - if err != nil { - return nil, err - } - - // then we create a server for each group - var servers []caddy.Server - for addr, group := range groups { - s, err := NewServer(addr, group) - if err != nil { - return nil, err - } - servers = append(servers, s) - } - - // NOTE: This value is only a "good guess". Quite often, development - // environments will use internal DNS or a local hosts file to serve - // real-looking domains in local development. We can't easily tell - // which without doing a DNS lookup, so this guess is definitely naive, - // and if we ever want a better guess, we will have to do DNS lookups. - deploymentGuess := "dev" - if looksLikeProductionCA && atLeastOneSiteLooksLikeProduction { - deploymentGuess = "prod" - } - telemetry.Set("http_deployment_guess", deploymentGuess) - telemetry.Set("http_num_sites", len(h.siteConfigs)) - - return servers, nil -} - -// normalizedKey returns "normalized" key representation: -// scheme and host names are lowered, everything else stays the same -func normalizedKey(key string) string { - addr, err := standardizeAddress(key) - if err != nil { - return key - } - return addr.Normalize().Key() -} - -// GetConfig gets the SiteConfig that corresponds to c. -// If none exist (should only happen in tests), then a -// new, empty one will be created. -func GetConfig(c *caddy.Controller) *SiteConfig { - ctx := c.Context().(*httpContext) - key := normalizedKey(c.Key) - if cfg, ok := ctx.keysToSiteConfigs[key]; ok { - return cfg - } - // we should only get here during tests because directive - // actions typically skip the server blocks where we make - // the configs - cfg := &SiteConfig{ - Root: Root, - TLS: &caddytls.Config{Manager: certmagic.NewDefault()}, - IndexPages: staticfiles.DefaultIndexPages, - } - ctx.saveConfig(key, cfg) - return cfg -} - -// shortCaddyfileLoader loads a Caddyfile if positional arguments are -// detected, or, in other words, if un-named arguments are provided to -// the program. A "short Caddyfile" is one in which each argument -// is a line of the Caddyfile. The default host and port are prepended -// according to the Host and Port values. -func shortCaddyfileLoader(serverType string) (caddy.Input, error) { - if flag.NArg() > 0 && serverType == "http" { - confBody := fmt.Sprintf("%s:%s\n%s", Host, Port, strings.Join(flag.Args(), "\n")) - return caddy.CaddyfileInput{ - Contents: []byte(confBody), - Filepath: "args", - ServerTypeName: serverType, - }, nil - } - return nil, nil -} - -// groupSiteConfigsByListenAddr groups site configs by their listen -// (bind) address, so sites that use the same listener can be served -// on the same server instance. The return value maps the listen -// address (what you pass into net.Listen) to the list of site configs. -// This function does NOT vet the configs to ensure they are compatible. -func groupSiteConfigsByListenAddr(configs []*SiteConfig) (map[string][]*SiteConfig, error) { - groups := make(map[string][]*SiteConfig) - - for _, conf := range configs { - // We would add a special case here so that localhost addresses - // bind to 127.0.0.1 if conf.ListenHost is not already set, which - // would prevent outsiders from even connecting; but that was problematic: - // https://caddy.community/t/wildcard-virtual-domains-with-wildcard-roots/221/5?u=matt - - if conf.Addr.Port == "" { - conf.Addr.Port = Port - } - addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.ListenHost, conf.Addr.Port)) - if err != nil { - return nil, err - } - addrstr := addr.String() - groups[addrstr] = append(groups[addrstr], conf) - } - - return groups, nil -} - -// Address represents a site address. It contains -// the original input value, and the component -// parts of an address. The component parts may be -// updated to the correct values as setup proceeds, -// but the original value should never be changed. -// -// The Host field must be in a normalized form. -type Address struct { - Original, Scheme, Host, Port, Path string -} - -// String returns a human-friendly print of the address. -func (a Address) String() string { - if a.Host == "" && a.Port == "" { - return "" - } - scheme := a.Scheme - if scheme == "" { - if a.Port == HTTPSPort { - scheme = "https" - } else { - scheme = "http" - } - } - s := scheme - if s != "" { - s += "://" - } - if a.Port != "" && - ((scheme == "https" && a.Port != DefaultHTTPSPort) || - (scheme == "http" && a.Port != DefaultHTTPPort)) { - s += net.JoinHostPort(a.Host, a.Port) - } else { - s += a.Host - } - if a.Path != "" { - s += a.Path - } - return s -} - -// VHost returns a sensible concatenation of Host:Port/Path from a. -// It's basically the a.Original but without the scheme. -func (a Address) VHost() string { - if idx := strings.Index(a.Original, "://"); idx > -1 { - return a.Original[idx+3:] - } - return a.Original -} - -// Normalize normalizes URL: turn scheme and host names into lower case -func (a Address) Normalize() Address { - path := a.Path - if !CaseSensitivePath { - path = strings.ToLower(path) - } - - // ensure host is normalized if it's an IP address - host := a.Host - if ip := net.ParseIP(host); ip != nil { - host = ip.String() - } - - return Address{ - Original: a.Original, - Scheme: strings.ToLower(a.Scheme), - Host: strings.ToLower(host), - Port: a.Port, - Path: path, - } -} - -// Key is similar to String, just replaces scheme and host values with modified values. -// Unlike String it doesn't add anything default (scheme, port, etc) -func (a Address) Key() string { - res := "" - if a.Scheme != "" { - res += a.Scheme + "://" - } - if a.Host != "" { - res += a.Host - } - if a.Port != "" { - if strings.HasPrefix(a.Original[len(res):], ":"+a.Port) { - // insert port only if the original has its own explicit port - res += ":" + a.Port - } - } - if a.Path != "" { - res += a.Path - } - return res -} - -// standardizeAddress parses an address string into a structured format with separate -// scheme, host, port, and path portions, as well as the original input string. -func standardizeAddress(str string) (Address, error) { - input := str - - // Split input into components (prepend with // to assert host by default) - if !strings.Contains(str, "//") && !strings.HasPrefix(str, "/") { - str = "//" + str - } - u, err := url.Parse(str) - if err != nil { - return Address{}, err - } - - // separate host and port - host, port, err := net.SplitHostPort(u.Host) - if err != nil { - host, port, err = net.SplitHostPort(u.Host + ":") - if err != nil { - host = u.Host - } - } - - // see if we can set port based off scheme - if port == "" { - if u.Scheme == "http" { - port = HTTPPort - } else if u.Scheme == "https" { - port = HTTPSPort - } - } - - // repeated or conflicting scheme is confusing, so error - if u.Scheme != "" && (port == "http" || port == "https") { - return Address{}, fmt.Errorf("[%s] scheme specified twice in address", input) - } - - // error if scheme and port combination violate convention - if (u.Scheme == "http" && port == HTTPSPort) || (u.Scheme == "https" && port == HTTPPort) { - return Address{}, fmt.Errorf("[%s] scheme and port violate convention", input) - } - - // standardize http and https ports to their respective port numbers - if port == "http" { - u.Scheme = "http" - port = HTTPPort - } else if port == "https" { - u.Scheme = "https" - port = HTTPSPort - } - - return Address{Original: input, Scheme: u.Scheme, Host: host, Port: port, Path: u.Path}, err -} - -// RegisterDevDirective splices name into the list of directives -// immediately before another directive. This function is ONLY -// for plugin development purposes! NEVER use it for a plugin -// that you are not currently building. If before is empty, -// the directive will be appended to the end of the list. -// -// It is imperative that directives execute in the proper -// order, and hard-coding the list of directives guarantees -// a correct, absolute order every time. This function is -// convenient when developing a plugin, but it does not -// guarantee absolute ordering. Multiple plugins registering -// directives with this function will lead to non- -// deterministic builds and buggy software. -// -// Directive names must be lower-cased and unique. Any errors -// here are fatal, and even successful calls print a message -// to stdout as a reminder to use it only in development. -func RegisterDevDirective(name, before string) { - if name == "" { - fmt.Println("[FATAL] Cannot register empty directive name") - os.Exit(1) - } - if strings.ToLower(name) != name { - fmt.Printf("[FATAL] %s: directive name must be lowercase\n", name) - os.Exit(1) - } - for _, dir := range directives { - if dir == name { - fmt.Printf("[FATAL] %s: directive name already exists\n", name) - os.Exit(1) - } - } - if before == "" { - directives = append(directives, name) - } else { - var found bool - for i, dir := range directives { - if dir == before { - directives = append(directives[:i], append([]string{name}, directives[i:]...)...) - found = true - break - } - } - if !found { - fmt.Printf("[FATAL] %s: directive not found\n", before) - os.Exit(1) - } - } - msg := fmt.Sprintf("Registered directive '%s' ", name) - if before == "" { - msg += "at end of list" - } else { - msg += fmt.Sprintf("before '%s'", before) - } - fmt.Printf("[DEV NOTICE] %s\n", msg) -} - -// directives is the list of all directives known to exist for the -// http server type, including non-standard (3rd-party) directives. -// The ordering of this list is important. -var directives = []string{ - // primitive actions that set up the fundamental vitals of each config - "root", - "index", - "bind", - "limits", - "timeouts", - "tls", - - // services/utilities, or other directives that don't necessarily inject handlers - "startup", // TODO: Deprecate this directive - "shutdown", // TODO: Deprecate this directive - "on", - "supervisor", // github.com/lucaslorentz/caddy-supervisor - "request_id", - "realip", // github.com/captncraig/caddy-realip - "git", // github.com/abiosoft/caddy-git - - // directives that add listener middleware to the stack - "proxyprotocol", // github.com/mastercactapus/caddy-proxyprotocol - - // directives that add middleware to the stack - "locale", // github.com/simia-tech/caddy-locale - "log", - "cache", // github.com/nicolasazrak/caddy-cache - "rewrite", - "ext", - "minify", // github.com/hacdias/caddy-minify - "gzip", - "header", - "geoip", // github.com/kodnaplakal/caddy-geoip - "errors", - "authz", // github.com/casbin/caddy-authz - "filter", // github.com/echocat/caddy-filter - "ipfilter", // github.com/pyed/ipfilter - "ratelimit", // github.com/xuqingfeng/caddy-rate-limit - "expires", // github.com/epicagency/caddy-expires - "forwardproxy", // github.com/caddyserver/forwardproxy - "basicauth", - "redir", - "status", - "cors", // github.com/captncraig/cors/caddy - "s3browser", // github.com/techknowlogick/caddy-s3browser - "nobots", // github.com/Xumeiquer/nobots - "mime", - "login", // github.com/tarent/loginsrv/caddy - "reauth", // github.com/freman/caddy-reauth - "extauth", // github.com/BTBurke/caddy-extauth - "jwt", // github.com/BTBurke/caddy-jwt - "jsonp", // github.com/pschlump/caddy-jsonp - "upload", // blitznote.com/src/caddy.upload - "multipass", // github.com/namsral/multipass/caddy - "internal", - "pprof", - "expvar", - "push", - "datadog", // github.com/payintech/caddy-datadog - "prometheus", // github.com/miekg/caddy-prometheus - "templates", - "proxy", - "fastcgi", - "cgi", // github.com/jung-kurt/caddy-cgi - "websocket", - "filebrowser", // github.com/filebrowser/caddy - "webdav", // github.com/hacdias/caddy-webdav - "markdown", - "browse", - "mailout", // github.com/SchumacherFM/mailout - "awses", // github.com/miquella/caddy-awses - "awslambda", // github.com/coopernurse/caddy-awslambda - "grpc", // github.com/pieterlouw/caddy-grpc - "gopkg", // github.com/zikes/gopkg - "restic", // github.com/restic/caddy - "wkd", // github.com/emersion/caddy-wkd - "dyndns", // github.com/linkonoid/caddy-dyndns -} - -const ( - // DefaultHost is the default host. - DefaultHost = "" - // DefaultPort is the default port. - DefaultPort = "2015" - // DefaultRoot is the default root folder. - DefaultRoot = "." - // DefaultHTTPPort is the default port for HTTP. - DefaultHTTPPort = "80" - // DefaultHTTPSPort is the default port for HTTPS. - DefaultHTTPSPort = "443" -) - -// These "soft defaults" are configurable by -// command line flags, etc. -var ( - // Root is the site root - Root = DefaultRoot - - // Host is the site host - Host = DefaultHost - - // Port is the site port - Port = DefaultPort - - // GracefulTimeout is the maximum duration of a graceful shutdown. - GracefulTimeout time.Duration - - // HTTP2 indicates whether HTTP2 is enabled or not. - HTTP2 bool - - // QUIC indicates whether QUIC is enabled or not. - QUIC bool - - // HTTPPort is the port to use for HTTP. - HTTPPort = DefaultHTTPPort - - // HTTPSPort is the port to use for HTTPS. - HTTPSPort = DefaultHTTPSPort -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/recorder.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/recorder.go deleted file mode 100644 index e59b5c288..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/recorder.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bytes" - "io" - "net/http" - "sync" - "time" -) - -// ResponseRecorder is a type of http.ResponseWriter that captures -// the status code written to it and also the size of the body -// written in the response. A status code does not have -// to be written, however, in which case 200 must be assumed. -// It is best to have the constructor initialize this type -// with that default status code. -// -// Setting the Replacer field allows middlewares to type-assert -// the http.ResponseWriter to ResponseRecorder and set their own -// placeholder values for logging utilities to use. -// -// Beware when accessing the Replacer value; it may be nil! -type ResponseRecorder struct { - *ResponseWriterWrapper - Replacer Replacer - status int - size int - start time.Time -} - -// NewResponseRecorder makes and returns a new ResponseRecorder. -// Because a status is not set unless WriteHeader is called -// explicitly, this constructor initializes with a status code -// of 200 to cover the default case. -func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder { - return &ResponseRecorder{ - ResponseWriterWrapper: &ResponseWriterWrapper{ResponseWriter: w}, - status: http.StatusOK, - start: time.Now(), - } -} - -// WriteHeader records the status code and calls the -// underlying ResponseWriter's WriteHeader method. -func (r *ResponseRecorder) WriteHeader(status int) { - r.status = status - r.ResponseWriterWrapper.WriteHeader(status) -} - -// Write is a wrapper that records the size of the body -// that gets written. -func (r *ResponseRecorder) Write(buf []byte) (int, error) { - n, err := r.ResponseWriterWrapper.Write(buf) - if err == nil { - r.size += n - } - return n, err -} - -// Size returns the size of the recorded response body. -func (r *ResponseRecorder) Size() int { - return r.size -} - -// Status returns the recorded response status code. -func (r *ResponseRecorder) Status() int { - return r.status -} - -// ResponseBuffer is a type that conditionally buffers the -// response in memory. It implements http.ResponseWriter so -// that it can stream the response if it is not buffering. -// Whether it buffers is decided by a func passed into the -// constructor, NewResponseBuffer. -// -// This type implements http.ResponseWriter, so you can pass -// this to the Next() middleware in the chain and record its -// response. However, since the entire response body will be -// buffered in memory, only use this when explicitly configured -// and required for some specific reason. For example, the -// text/template package only parses templates out of []byte -// and not io.Reader, so the templates directive uses this -// type to obtain the entire template text, but only on certain -// requests that match the right Content-Type, etc. -// -// ResponseBuffer also implements io.ReaderFrom for performance -// reasons. The standard lib's http.response type (unexported) -// uses io.Copy to write the body. io.Copy makes an allocation -// if the destination does not have a ReadFrom method (or if -// the source does not have a WriteTo method, but that's -// irrelevant here). Our ReadFrom is smart: if buffering, it -// calls the buffer's ReadFrom, which makes no allocs because -// it is already a buffer! If we're streaming the response -// instead, ReadFrom uses io.CopyBuffer with a pooled buffer -// that is managed within this package. -type ResponseBuffer struct { - *ResponseWriterWrapper - Buffer *bytes.Buffer - header http.Header - status int - shouldBuffer func(status int, header http.Header) bool - stream bool - rw http.ResponseWriter - wroteHeader bool -} - -// NewResponseBuffer returns a new ResponseBuffer that will -// use buf to store the full body of the response if shouldBuffer -// returns true. If shouldBuffer returns false, then the response -// body will be streamed directly to rw. -// -// shouldBuffer will be passed the status code and header fields of -// the response. With that information, the function should decide -// whether to buffer the response in memory. For example: the templates -// directive uses this to determine whether the response is the -// right Content-Type (according to user config) for a template. -// -// For performance, the buf you pass in should probably be obtained -// from a sync.Pool in order to reuse allocated space. -func NewResponseBuffer(buf *bytes.Buffer, rw http.ResponseWriter, - shouldBuffer func(status int, header http.Header) bool) *ResponseBuffer { - rb := &ResponseBuffer{ - Buffer: buf, - header: make(http.Header), - status: http.StatusOK, // default status code - shouldBuffer: shouldBuffer, - rw: rw, - } - rb.ResponseWriterWrapper = &ResponseWriterWrapper{ResponseWriter: rw} - return rb -} - -// Header returns the response header map. -func (rb *ResponseBuffer) Header() http.Header { - return rb.header -} - -// WriteHeader calls shouldBuffer to decide whether the -// upcoming body should be buffered, and then writes -// the header to the response. -func (rb *ResponseBuffer) WriteHeader(status int) { - if rb.wroteHeader { - return - } - rb.wroteHeader = true - - rb.status = status - rb.stream = !rb.shouldBuffer(status, rb.header) - if rb.stream { - rb.CopyHeader() - rb.ResponseWriterWrapper.WriteHeader(status) - } -} - -// Write writes buf to rb.Buffer if buffering, otherwise -// to the ResponseWriter directly if streaming. -func (rb *ResponseBuffer) Write(buf []byte) (int, error) { - if !rb.wroteHeader { - rb.WriteHeader(http.StatusOK) - } - - if rb.stream { - return rb.ResponseWriterWrapper.Write(buf) - } - return rb.Buffer.Write(buf) -} - -// Buffered returns whether rb has decided to buffer the response. -func (rb *ResponseBuffer) Buffered() bool { - return !rb.stream -} - -// CopyHeader copies the buffered header in rb to the ResponseWriter, -// but it does not write the header out. -func (rb *ResponseBuffer) CopyHeader() { - for field, val := range rb.header { - rb.ResponseWriterWrapper.Header()[field] = val - } -} - -// ReadFrom avoids allocations when writing to the buffer (if buffering), -// and reduces allocations when writing to the ResponseWriter directly -// (if streaming). -// -// In local testing with the templates directive, req/sec were improved -// from ~8,200 to ~9,600 on templated files by ensuring that this type -// implements io.ReaderFrom. -func (rb *ResponseBuffer) ReadFrom(src io.Reader) (int64, error) { - if !rb.wroteHeader { - rb.WriteHeader(http.StatusOK) - } - - if rb.stream { - // first see if we can avoid any allocations at all - if wt, ok := src.(io.WriterTo); ok { - return wt.WriteTo(rb.ResponseWriterWrapper) - } - // if not, use a pooled copy buffer to reduce allocs - // (this improved req/sec from ~25,300 to ~27,000 on - // static files served directly with the fileserver, - // but results fluctuated a little on each run). - // a note of caution: - // https://go-review.googlesource.com/c/22134#message-ff351762308fe05f6b72a487d6842e3988916486 - buf := respBufPool.Get().([]byte) - n, err := io.CopyBuffer(rb.ResponseWriterWrapper, src, buf) - respBufPool.Put(buf) // deferring this slowed down benchmarks a smidgin, I think - return n, err - } - return rb.Buffer.ReadFrom(src) -} - -// StatusCodeWriter returns an http.ResponseWriter that always -// writes the status code stored in rb from when a response -// was buffered to it. -func (rb *ResponseBuffer) StatusCodeWriter(w http.ResponseWriter) http.ResponseWriter { - return forcedStatusCodeWriter{w, rb} -} - -// forcedStatusCodeWriter is used to force a status code when -// writing the header. It uses the status code saved on rb. -// This is useful if passing a http.ResponseWriter into -// http.ServeContent because ServeContent hard-codes 2xx status -// codes. If we buffered the response, we force that status code -// instead. -type forcedStatusCodeWriter struct { - http.ResponseWriter - rb *ResponseBuffer -} - -func (fscw forcedStatusCodeWriter) WriteHeader(int) { - fscw.ResponseWriter.WriteHeader(fscw.rb.status) -} - -// respBufPool is used for io.CopyBuffer when ResponseBuffer -// is configured to stream a response. -var respBufPool = &sync.Pool{ - New: func() interface{} { - return make([]byte, 32*1024) - }, -} - -// Interface guards -var ( - _ HTTPInterfaces = (*ResponseRecorder)(nil) - _ HTTPInterfaces = (*ResponseBuffer)(nil) - _ io.ReaderFrom = (*ResponseBuffer)(nil) -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/replacer.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/replacer.go deleted file mode 100644 index bf5240d99..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/replacer.go +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bytes" - "crypto/sha256" - "crypto/x509" - "encoding/pem" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "net/url" - "os" - "path" - "strconv" - "strings" - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddytls" -) - -// requestReplacer is a strings.Replacer which is used to -// encode literal \r and \n characters and keep everything -// on one line -var requestReplacer = strings.NewReplacer( - "\r", "\\r", - "\n", "\\n", -) - -var now = time.Now - -// Replacer is a type which can replace placeholder -// substrings in a string with actual values from a -// http.Request and ResponseRecorder. Always use -// NewReplacer to get one of these. Any placeholders -// made with Set() should overwrite existing values if -// the key is already used. -type Replacer interface { - Replace(string) string - Set(key, value string) -} - -// replacer implements Replacer. customReplacements -// is used to store custom replacements created with -// Set() until the time of replacement, at which point -// they will be used to overwrite other replacements -// if there is a name conflict. -type replacer struct { - customReplacements map[string]string - emptyValue string - responseRecorder *ResponseRecorder - request *http.Request - requestBody *limitWriter -} - -type limitWriter struct { - w bytes.Buffer - remain int -} - -func newLimitWriter(max int) *limitWriter { - return &limitWriter{ - w: bytes.Buffer{}, - remain: max, - } -} - -func (lw *limitWriter) Write(p []byte) (int, error) { - // skip if we are full - if lw.remain <= 0 { - return len(p), nil - } - if n := len(p); n > lw.remain { - p = p[:lw.remain] - } - n, err := lw.w.Write(p) - lw.remain -= n - return n, err -} - -func (lw *limitWriter) String() string { - return lw.w.String() -} - -// NewReplacer makes a new replacer based on r and rr which -// are used for request and response placeholders, respectively. -// Request placeholders are created immediately, whereas -// response placeholders are not created until Replace() -// is invoked. rr may be nil if it is not available. -// emptyValue should be the string that is used in place -// of empty string (can still be empty string). -func NewReplacer(r *http.Request, rr *ResponseRecorder, emptyValue string) Replacer { - repl := &replacer{ - request: r, - responseRecorder: rr, - emptyValue: emptyValue, - } - - // extract customReplacements from a request replacer when present. - if existing, ok := r.Context().Value(ReplacerCtxKey).(*replacer); ok { - repl.requestBody = existing.requestBody - repl.customReplacements = existing.customReplacements - } else { - // if there is no existing replacer, build one from scratch. - rb := newLimitWriter(MaxLogBodySize) - if r.Body != nil { - r.Body = struct { - io.Reader - io.Closer - }{io.TeeReader(r.Body, rb), io.Closer(r.Body)} - } - repl.requestBody = rb - repl.customReplacements = make(map[string]string) - } - - return repl -} - -func canLogRequest(r *http.Request) bool { - if r.Method == "POST" || r.Method == "PUT" { - for _, cType := range r.Header[headerContentType] { - // the cType could have charset and other info - if strings.Contains(cType, contentTypeJSON) || strings.Contains(cType, contentTypeXML) { - return true - } - } - } - return false -} - -// unescapeBraces finds escaped braces in s and returns -// a string with those braces unescaped. -func unescapeBraces(s string) string { - s = strings.Replace(s, "\\{", "{", -1) - s = strings.Replace(s, "\\}", "}", -1) - return s -} - -// Replace performs a replacement of values on s and returns -// the string with the replaced values. -func (r *replacer) Replace(s string) string { - // Do not attempt replacements if no placeholder is found. - if !strings.ContainsAny(s, "{}") { - return s - } - - result := "" -Placeholders: // process each placeholder in sequence - for { - var idxStart, idxEnd int - - idxOffset := 0 - for { // find first unescaped opening brace - searchSpace := s[idxOffset:] - idxStart = strings.Index(searchSpace, "{") - if idxStart == -1 { - // no more placeholders - break Placeholders - } - if idxStart == 0 || searchSpace[idxStart-1] != '\\' { - // preceding character is not an escape - idxStart += idxOffset - break - } - // the brace we found was escaped - // search the rest of the string next - idxOffset += idxStart + 1 - } - - idxOffset = 0 - for { // find first unescaped closing brace - searchSpace := s[idxStart+idxOffset:] - idxEnd = strings.Index(searchSpace, "}") - if idxEnd == -1 { - // unpaired placeholder - break Placeholders - } - if idxEnd == 0 || searchSpace[idxEnd-1] != '\\' { - // preceding character is not an escape - idxEnd += idxOffset + idxStart - break - } - // the brace we found was escaped - // search the rest of the string next - idxOffset += idxEnd + 1 - } - - // get a replacement for the unescaped placeholder - placeholder := unescapeBraces(s[idxStart : idxEnd+1]) - replacement := r.getSubstitution(placeholder) - - // append unescaped prefix + replacement - result += strings.TrimPrefix(unescapeBraces(s[:idxStart]), "\\") + replacement - - // strip out scanned parts - s = s[idxEnd+1:] - } - - // append unscanned parts - return result + unescapeBraces(s) -} - -func roundDuration(d time.Duration) time.Duration { - if d >= time.Millisecond { - return round(d, time.Millisecond) - } else if d >= time.Microsecond { - return round(d, time.Microsecond) - } - - return d -} - -// round rounds d to the nearest r -func round(d, r time.Duration) time.Duration { - if r <= 0 { - return d - } - neg := d < 0 - if neg { - d = -d - } - if m := d % r; m+m < r { - d = d - m - } else { - d = d + r - m - } - if neg { - return -d - } - return d -} - -// getPeerCert returns peer certificate -func (r *replacer) getPeerCert() *x509.Certificate { - if r.request.TLS != nil && len(r.request.TLS.PeerCertificates) > 0 { - return r.request.TLS.PeerCertificates[0] - } - - return nil -} - -// getSubstitution retrieves value from corresponding key -func (r *replacer) getSubstitution(key string) string { - // search custom replacements first - if value, ok := r.customReplacements[key]; ok { - return value - } - - // search request headers then - if key[1] == '>' { - want := key[2 : len(key)-1] - for key, values := range r.request.Header { - // Header placeholders (case-insensitive) - if strings.EqualFold(key, want) { - return strings.Join(values, ",") - } - } - } - // search response headers then - if r.responseRecorder != nil && key[1] == '<' { - want := key[2 : len(key)-1] - for key, values := range r.responseRecorder.Header() { - // Header placeholders (case-insensitive) - if strings.EqualFold(key, want) { - return strings.Join(values, ",") - } - } - } - // next check for cookies - if key[1] == '~' { - name := key[2 : len(key)-1] - if cookie, err := r.request.Cookie(name); err == nil { - return cookie.Value - } - } - // next check for query argument - if key[1] == '?' { - query := r.request.URL.Query() - name := key[2 : len(key)-1] - return query.Get(name) - } - - // search default replacements in the end - switch key { - case "{method}": - return r.request.Method - case "{scheme}": - if r.request.TLS != nil { - return "https" - } - return "http" - case "{hostname}": - name, err := os.Hostname() - if err != nil { - return r.emptyValue - } - return name - case "{host}": - return r.request.Host - case "{hostonly}": - host, _, err := net.SplitHostPort(r.request.Host) - if err != nil { - return r.request.Host - } - return host - case "{path}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return u.Path - case "{path_escaped}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return url.QueryEscape(u.Path) - case "{request_id}": - reqid, _ := r.request.Context().Value(RequestIDCtxKey).(string) - return reqid - case "{rewrite_path}": - return r.request.URL.Path - case "{rewrite_path_escaped}": - return url.QueryEscape(r.request.URL.Path) - case "{query}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return u.RawQuery - case "{query_escaped}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return url.QueryEscape(u.RawQuery) - case "{fragment}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return u.Fragment - case "{proto}": - return r.request.Proto - case "{remote}": - host, _, err := net.SplitHostPort(r.request.RemoteAddr) - if err != nil { - return r.request.RemoteAddr - } - return host - case "{port}": - _, port, err := net.SplitHostPort(r.request.RemoteAddr) - if err != nil { - return r.emptyValue - } - return port - case "{uri}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return u.RequestURI() - case "{uri_escaped}": - u, _ := r.request.Context().Value(OriginalURLCtxKey).(url.URL) - return url.QueryEscape(u.RequestURI()) - case "{rewrite_uri}": - return r.request.URL.RequestURI() - case "{rewrite_uri_escaped}": - return url.QueryEscape(r.request.URL.RequestURI()) - case "{when}": - return now().Format(timeFormat) - case "{when_iso_local}": - return now().Format(timeFormatISO) - case "{when_iso}": - return now().UTC().Format(timeFormatISOUTC) - case "{when_unix}": - return strconv.FormatInt(now().Unix(), 10) - case "{when_unix_ms}": - return strconv.FormatInt(nanoToMilliseconds(now().UnixNano()), 10) - case "{file}": - _, file := path.Split(r.request.URL.Path) - return file - case "{dir}": - dir, _ := path.Split(r.request.URL.Path) - return dir - case "{request}": - dump, err := httputil.DumpRequest(r.request, false) - if err != nil { - return r.emptyValue - } - return requestReplacer.Replace(string(dump)) - case "{request_body}": - if !canLogRequest(r.request) { - return r.emptyValue - } - _, err := ioutil.ReadAll(r.request.Body) - if err != nil { - if err == ErrMaxBytesExceeded { - return r.emptyValue - } - } - return requestReplacer.Replace(r.requestBody.String()) - case "{mitm}": - if val, ok := r.request.Context().Value(caddy.CtxKey("mitm")).(bool); ok { - if val { - return "likely" - } - return "unlikely" - } - return "unknown" - case "{status}": - if r.responseRecorder == nil { - return r.emptyValue - } - return strconv.Itoa(r.responseRecorder.status) - case "{size}": - if r.responseRecorder == nil { - return r.emptyValue - } - return strconv.Itoa(r.responseRecorder.size) - case "{latency}": - if r.responseRecorder == nil { - return r.emptyValue - } - return roundDuration(time.Since(r.responseRecorder.start)).String() - case "{latency_ms}": - if r.responseRecorder == nil { - return r.emptyValue - } - elapsedDuration := time.Since(r.responseRecorder.start) - return strconv.FormatInt(convertToMilliseconds(elapsedDuration), 10) - case "{tls_protocol}": - if r.request.TLS != nil { - if name, err := caddytls.GetSupportedProtocolName(r.request.TLS.Version); err == nil { - return name - } else { - return "tls" // this should never happen, but guard in case - } - } - return r.emptyValue // because not using a secure channel - case "{tls_cipher}": - if r.request.TLS != nil { - if name, err := caddytls.GetSupportedCipherName(r.request.TLS.CipherSuite); err == nil { - return name - } else { - return "UNKNOWN" // this should never happen, but guard in case - } - } - return r.emptyValue - case "{tls_client_escaped_cert}": - cert := r.getPeerCert() - if cert != nil { - pemBlock := pem.Block{ - Type: "CERTIFICATE", - Bytes: cert.Raw, - } - return url.QueryEscape(string(pem.EncodeToMemory(&pemBlock))) - } - return r.emptyValue - case "{tls_client_fingerprint}": - cert := r.getPeerCert() - if cert != nil { - return fmt.Sprintf("%x", sha256.Sum256(cert.Raw)) - } - return r.emptyValue - case "{tls_client_i_dn}": - cert := r.getPeerCert() - if cert != nil { - return cert.Issuer.String() - } - return r.emptyValue - case "{tls_client_raw_cert}": - cert := r.getPeerCert() - if cert != nil { - return string(cert.Raw) - } - return r.emptyValue - case "{tls_client_s_dn}": - cert := r.getPeerCert() - if cert != nil { - return cert.Subject.String() - } - return r.emptyValue - case "{tls_client_serial}": - cert := r.getPeerCert() - if cert != nil { - return fmt.Sprintf("%x", cert.SerialNumber) - } - return r.emptyValue - case "{tls_client_v_end}": - cert := r.getPeerCert() - if cert != nil { - return cert.NotAfter.In(time.UTC).Format("Jan 02 15:04:05 2006 MST") - } - return r.emptyValue - case "{tls_client_v_remain}": - cert := r.getPeerCert() - if cert != nil { - now := time.Now().In(time.UTC) - days := int64(cert.NotAfter.Sub(now).Seconds() / 86400) - return strconv.FormatInt(days, 10) - } - return r.emptyValue - case "{tls_client_v_start}": - cert := r.getPeerCert() - if cert != nil { - return cert.NotBefore.Format("Jan 02 15:04:05 2006 MST") - } - return r.emptyValue - case "{server_port}": - _, port, err := net.SplitHostPort(r.request.Host) - if err != nil { - if r.request.TLS != nil { - return "443" - } else { - return "80" - } - } - return port - default: - // {labelN} - if strings.HasPrefix(key, "{label") { - nStr := key[6 : len(key)-1] // get the integer N in "{labelN}" - n, err := strconv.Atoi(nStr) - if err != nil || n < 1 { - return r.emptyValue - } - labels := strings.Split(r.request.Host, ".") - if n > len(labels) { - return r.emptyValue - } - return labels[n-1] - } - } - - return r.emptyValue -} - -func nanoToMilliseconds(d int64) int64 { - return d / 1e6 -} - -// convertToMilliseconds returns the number of milliseconds in the given duration -func convertToMilliseconds(d time.Duration) int64 { - return nanoToMilliseconds(d.Nanoseconds()) -} - -// Set sets key to value in the r.customReplacements map. -func (r *replacer) Set(key, value string) { - r.customReplacements["{"+key+"}"] = value -} - -const ( - timeFormat = "02/Jan/2006:15:04:05 -0700" - timeFormatISO = "2006-01-02T15:04:05" // ISO 8601 with timezone to be assumed as local - timeFormatISOUTC = "2006-01-02T15:04:05Z" // ISO 8601 with timezone to be assumed as UTC - headerContentType = "Content-Type" - contentTypeJSON = "application/json" - contentTypeXML = "application/xml" - // MaxLogBodySize limits the size of logged request's body - MaxLogBodySize = 100 * 1024 -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/responsewriterwrapper.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/responsewriterwrapper.go deleted file mode 100644 index ad92d6d87..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/responsewriterwrapper.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bufio" - "net" - "net/http" -) - -// ResponseWriterWrapper wrappers underlying ResponseWriter -// and inherits its Hijacker/Pusher/CloseNotifier/Flusher as well. -type ResponseWriterWrapper struct { - http.ResponseWriter -} - -// Hijack implements http.Hijacker. It simply wraps the underlying -// ResponseWriter's Hijack method if there is one, or returns an error. -func (rww *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hj, ok := rww.ResponseWriter.(http.Hijacker); ok { - return hj.Hijack() - } - return nil, nil, NonHijackerError{Underlying: rww.ResponseWriter} -} - -// Flush implements http.Flusher. It simply wraps the underlying -// ResponseWriter's Flush method if there is one, or panics. -func (rww *ResponseWriterWrapper) Flush() { - if f, ok := rww.ResponseWriter.(http.Flusher); ok { - f.Flush() - } else { - panic(NonFlusherError{Underlying: rww.ResponseWriter}) - } -} - -// CloseNotify implements http.CloseNotifier. -// It just inherits the underlying ResponseWriter's CloseNotify method. -// It panics if the underlying ResponseWriter is not a CloseNotifier. -func (rww *ResponseWriterWrapper) CloseNotify() <-chan bool { - if cn, ok := rww.ResponseWriter.(http.CloseNotifier); ok { - return cn.CloseNotify() - } - panic(NonCloseNotifierError{Underlying: rww.ResponseWriter}) -} - -// Push implements http.Pusher. -// It just inherits the underlying ResponseWriter's Push method. -// It panics if the underlying ResponseWriter is not a Pusher. -func (rww *ResponseWriterWrapper) Push(target string, opts *http.PushOptions) error { - if pusher, hasPusher := rww.ResponseWriter.(http.Pusher); hasPusher { - return pusher.Push(target, opts) - } - - return NonPusherError{Underlying: rww.ResponseWriter} -} - -// HTTPInterfaces mix all the interfaces that middleware ResponseWriters need to support. -type HTTPInterfaces interface { - http.ResponseWriter - http.Pusher - http.Flusher - http.CloseNotifier - http.Hijacker -} - -// Interface guards -var _ HTTPInterfaces = (*ResponseWriterWrapper)(nil) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/roller.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/roller.go deleted file mode 100644 index 4f9142761..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/roller.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "errors" - "io" - "path/filepath" - "strconv" - - lumberjack "gopkg.in/natefinch/lumberjack.v2" -) - -// LogRoller implements a type that provides a rolling logger. -type LogRoller struct { - Disabled bool - Filename string - MaxSize int - MaxAge int - MaxBackups int - Compress bool - LocalTime bool -} - -// GetLogWriter returns an io.Writer that writes to a rolling logger. -// This should be called only from the main goroutine (like during -// server setup) because this method is not thread-safe; it is careful -// to create only one log writer per log file, even if the log file -// is shared by different sites or middlewares. This ensures that -// rolling is synchronized, since a process (or multiple processes) -// should not create more than one roller on the same file at the -// same time. See issue #1363. -func (l LogRoller) GetLogWriter() io.Writer { - absPath, err := filepath.Abs(l.Filename) - if err != nil { - absPath = l.Filename // oh well, hopefully they're consistent in how they specify the filename - } - lj, has := lumberjacks[absPath] - if !has { - lj = &lumberjack.Logger{ - Filename: l.Filename, - MaxSize: l.MaxSize, - MaxAge: l.MaxAge, - MaxBackups: l.MaxBackups, - Compress: l.Compress, - LocalTime: l.LocalTime, - } - lumberjacks[absPath] = lj - } - return lj -} - -// IsLogRollerSubdirective is true if the subdirective is for the log roller. -func IsLogRollerSubdirective(subdir string) bool { - return subdir == directiveRotateSize || - subdir == directiveRotateAge || - subdir == directiveRotateKeep || - subdir == directiveRotateCompress || - subdir == directiveRotateDisable -} - -var errInvalidRollParameter = errors.New("invalid roller parameter") - -// ParseRoller parses roller contents out of c. -func ParseRoller(l *LogRoller, what string, where ...string) error { - if l == nil { - l = DefaultLogRoller() - } - - // rotate_compress doesn't accept any parameters. - // others only accept one parameter - if ((what == directiveRotateCompress || what == directiveRotateDisable) && len(where) != 0) || - ((what != directiveRotateCompress && what != directiveRotateDisable) && len(where) != 1) { - return errInvalidRollParameter - } - - var ( - value int - err error - ) - if what != directiveRotateCompress && what != directiveRotateDisable { - value, err = strconv.Atoi(where[0]) - if err != nil { - return err - } - } - - switch what { - case directiveRotateDisable: - l.Disabled = true - case directiveRotateSize: - l.MaxSize = value - case directiveRotateAge: - l.MaxAge = value - case directiveRotateKeep: - l.MaxBackups = value - case directiveRotateCompress: - l.Compress = true - } - return nil -} - -// DefaultLogRoller will roll logs by default. -func DefaultLogRoller() *LogRoller { - return &LogRoller{ - MaxSize: defaultRotateSize, - MaxAge: defaultRotateAge, - MaxBackups: defaultRotateKeep, - Compress: false, - LocalTime: true, - } -} - -const ( - // defaultRotateSize is 100 MB. - defaultRotateSize = 100 - // defaultRotateAge is 14 days. - defaultRotateAge = 14 - // defaultRotateKeep is 10 files. - defaultRotateKeep = 10 - - directiveRotateDisable = "rotate_disable" - directiveRotateSize = "rotate_size" - directiveRotateAge = "rotate_age" - directiveRotateKeep = "rotate_keep" - directiveRotateCompress = "rotate_compress" -) - -// lumberjacks maps log filenames to the logger -// that is being used to keep them rolled/maintained. -var lumberjacks = make(map[string]io.Writer) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/server.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/server.go deleted file mode 100644 index 7940ac832..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/server.go +++ /dev/null @@ -1,625 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver implements an HTTP server on top of Caddy. -package httpserver - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "log" - "net" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "runtime" - "strings" - "time" - - "github.com/lucas-clemente/quic-go/h2quic" - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/staticfiles" - "github.com/mholt/caddy/caddytls" - "github.com/mholt/caddy/telemetry" -) - -// Server is the HTTP server implementation. -type Server struct { - Server *http.Server - quicServer *h2quic.Server - sites []*SiteConfig - connTimeout time.Duration // max time to wait for a connection before force stop - tlsGovChan chan struct{} // close to stop the TLS maintenance goroutine - vhosts *vhostTrie -} - -// ensure it satisfies the interface -var _ caddy.GracefulServer = new(Server) - -var defaultALPN = []string{"h2", "http/1.1"} - -// makeTLSConfig extracts TLS settings from each site config to -// build a tls.Config usable in Caddy HTTP servers. The returned -// config will be nil if TLS is disabled for these sites. -func makeTLSConfig(group []*SiteConfig) (*tls.Config, error) { - var tlsConfigs []*caddytls.Config - for i := range group { - if HTTP2 && len(group[i].TLS.ALPN) == 0 { - // if no application-level protocol was configured up to now, - // default to HTTP/2, then HTTP/1.1 if necessary - group[i].TLS.ALPN = defaultALPN - } - tlsConfigs = append(tlsConfigs, group[i].TLS) - } - return caddytls.MakeTLSConfig(tlsConfigs) -} - -func getFallbacks(sites []*SiteConfig) []string { - fallbacks := []string{} - for _, sc := range sites { - if sc.FallbackSite { - fallbacks = append(fallbacks, sc.Addr.Host) - } - } - return fallbacks -} - -// NewServer creates a new Server instance that will listen on addr -// and will serve the sites configured in group. -func NewServer(addr string, group []*SiteConfig) (*Server, error) { - s := &Server{ - Server: makeHTTPServerWithTimeouts(addr, group), - vhosts: newVHostTrie(), - sites: group, - connTimeout: GracefulTimeout, - } - s.vhosts.fallbackHosts = append(s.vhosts.fallbackHosts, getFallbacks(group)...) - s.Server = makeHTTPServerWithHeaderLimit(s.Server, group) - s.Server.Handler = s // this is weird, but whatever - - // extract TLS settings from each site config to build - // a tls.Config, which will not be nil if TLS is enabled - tlsConfig, err := makeTLSConfig(group) - if err != nil { - return nil, err - } - s.Server.TLSConfig = tlsConfig - - // if TLS is enabled, make sure we prepare the Server accordingly - if s.Server.TLSConfig != nil { - // enable QUIC if desired (requires HTTP/2) - if HTTP2 && QUIC { - s.quicServer = &h2quic.Server{Server: s.Server} - s.Server.Handler = s.wrapWithSvcHeaders(s.Server.Handler) - } - - // wrap the HTTP handler with a handler that does MITM detection - tlsh := &tlsHandler{next: s.Server.Handler} - s.Server.Handler = tlsh // this needs to be the "outer" handler when Serve() is called, for type assertion - - // when Serve() creates the TLS listener later, that listener should - // be adding a reference the ClientHello info to a map; this callback - // will be sure to clear out that entry when the connection closes. - s.Server.ConnState = func(c net.Conn, cs http.ConnState) { - // when a connection closes or is hijacked, delete its entry - // in the map, because we are done with it. - if tlsh.listener != nil { - if cs == http.StateHijacked || cs == http.StateClosed { - tlsh.listener.helloInfosMu.Lock() - delete(tlsh.listener.helloInfos, c.RemoteAddr().String()) - tlsh.listener.helloInfosMu.Unlock() - } - } - } - - // As of Go 1.7, if the Server's TLSConfig is not nil, HTTP/2 is enabled only - // if TLSConfig.NextProtos includes the string "h2" - if HTTP2 && len(s.Server.TLSConfig.NextProtos) == 0 { - // some experimenting shows that this NextProtos must have at least - // one value that overlaps with the NextProtos of any other tls.Config - // that is returned from GetConfigForClient; if there is no overlap, - // the connection will fail (as of Go 1.8, Feb. 2017). - s.Server.TLSConfig.NextProtos = defaultALPN - } - } - - // Compile custom middleware for every site (enables virtual hosting) - for _, site := range group { - stack := Handler(staticfiles.FileServer{Root: http.Dir(site.Root), Hide: site.HiddenFiles, IndexPages: site.IndexPages}) - for i := len(site.middleware) - 1; i >= 0; i-- { - stack = site.middleware[i](stack) - } - site.middlewareChain = stack - s.vhosts.Insert(site.Addr.VHost(), site) - } - - return s, nil -} - -// makeHTTPServerWithHeaderLimit apply minimum header limit within a group to given http.Server -func makeHTTPServerWithHeaderLimit(s *http.Server, group []*SiteConfig) *http.Server { - var min int64 - for _, cfg := range group { - limit := cfg.Limits.MaxRequestHeaderSize - if limit == 0 { - continue - } - - // not set yet - if min == 0 { - min = limit - } - - // find a better one - if limit < min { - min = limit - } - } - - if min > 0 { - s.MaxHeaderBytes = int(min) - } - return s -} - -// makeHTTPServerWithTimeouts makes an http.Server from the group of -// configs in a way that configures timeouts (or, if not set, it uses -// the default timeouts) by combining the configuration of each -// SiteConfig in the group. (Timeouts are important for mitigating -// slowloris attacks.) -func makeHTTPServerWithTimeouts(addr string, group []*SiteConfig) *http.Server { - // find the minimum duration configured for each timeout - var min Timeouts - for _, cfg := range group { - if cfg.Timeouts.ReadTimeoutSet && - (!min.ReadTimeoutSet || cfg.Timeouts.ReadTimeout < min.ReadTimeout) { - min.ReadTimeoutSet = true - min.ReadTimeout = cfg.Timeouts.ReadTimeout - } - if cfg.Timeouts.ReadHeaderTimeoutSet && - (!min.ReadHeaderTimeoutSet || cfg.Timeouts.ReadHeaderTimeout < min.ReadHeaderTimeout) { - min.ReadHeaderTimeoutSet = true - min.ReadHeaderTimeout = cfg.Timeouts.ReadHeaderTimeout - } - if cfg.Timeouts.WriteTimeoutSet && - (!min.WriteTimeoutSet || cfg.Timeouts.WriteTimeout < min.WriteTimeout) { - min.WriteTimeoutSet = true - min.WriteTimeout = cfg.Timeouts.WriteTimeout - } - if cfg.Timeouts.IdleTimeoutSet && - (!min.IdleTimeoutSet || cfg.Timeouts.IdleTimeout < min.IdleTimeout) { - min.IdleTimeoutSet = true - min.IdleTimeout = cfg.Timeouts.IdleTimeout - } - } - - // for the values that were not set, use defaults - if !min.ReadTimeoutSet { - min.ReadTimeout = defaultTimeouts.ReadTimeout - } - if !min.ReadHeaderTimeoutSet { - min.ReadHeaderTimeout = defaultTimeouts.ReadHeaderTimeout - } - if !min.WriteTimeoutSet { - min.WriteTimeout = defaultTimeouts.WriteTimeout - } - if !min.IdleTimeoutSet { - min.IdleTimeout = defaultTimeouts.IdleTimeout - } - - // set the final values on the server and return it - return &http.Server{ - Addr: addr, - ReadTimeout: min.ReadTimeout, - ReadHeaderTimeout: min.ReadHeaderTimeout, - WriteTimeout: min.WriteTimeout, - IdleTimeout: min.IdleTimeout, - } -} - -func (s *Server) wrapWithSvcHeaders(previousHandler http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - if err := s.quicServer.SetQuicHeaders(w.Header()); err != nil { - log.Println("[Error] failed to set proper headers for QUIC: ", err) - } - previousHandler.ServeHTTP(w, r) - } -} - -// Listen creates an active listener for s that can be -// used to serve requests. -func (s *Server) Listen() (net.Listener, error) { - if s.Server == nil { - return nil, fmt.Errorf("server field is nil") - } - - ln, err := net.Listen("tcp", s.Server.Addr) - if err != nil { - var succeeded bool - if runtime.GOOS == "windows" { - // Windows has been known to keep sockets open even after closing the listeners. - // Tests reveal this error case easily because they call Start() then Stop() - // in succession. TODO: Better way to handle this? And why limit this to Windows? - for i := 0; i < 20; i++ { - time.Sleep(100 * time.Millisecond) - ln, err = net.Listen("tcp", s.Server.Addr) - if err == nil { - succeeded = true - break - } - } - } - if !succeeded { - return nil, err - } - } - - if tcpLn, ok := ln.(*net.TCPListener); ok { - ln = tcpKeepAliveListener{TCPListener: tcpLn} - } - - cln := s.WrapListener(ln) - - // Very important to return a concrete caddy.Listener - // implementation for graceful restarts. - return cln.(caddy.Listener), nil -} - -// WrapListener wraps ln in the listener middlewares configured -// for this server. -func (s *Server) WrapListener(ln net.Listener) net.Listener { - if ln == nil { - return nil - } - cln := ln.(caddy.Listener) - for _, site := range s.sites { - for _, m := range site.listenerMiddleware { - cln = m(cln) - } - } - return cln -} - -// ListenPacket creates udp connection for QUIC if it is enabled, -func (s *Server) ListenPacket() (net.PacketConn, error) { - if QUIC { - udpAddr, err := net.ResolveUDPAddr("udp", s.Server.Addr) - if err != nil { - return nil, err - } - return net.ListenUDP("udp", udpAddr) - } - return nil, nil -} - -// Serve serves requests on ln. It blocks until ln is closed. -func (s *Server) Serve(ln net.Listener) error { - if s.Server.TLSConfig != nil { - // Create TLS listener - note that we do not replace s.listener - // with this TLS listener; tls.listener is unexported and does - // not implement the File() method we need for graceful restarts - // on POSIX systems. - // TODO: Is this ^ still relevant anymore? Maybe we can now that it's a net.Listener... - ln = newTLSListener(ln, s.Server.TLSConfig) - if handler, ok := s.Server.Handler.(*tlsHandler); ok { - handler.listener = ln.(*tlsHelloListener) - } - - // Rotate TLS session ticket keys - s.tlsGovChan = caddytls.RotateSessionTicketKeys(s.Server.TLSConfig) - } - - defer func() { - if s.quicServer != nil { - if err := s.quicServer.Close(); err != nil { - log.Println("[ERROR] failed to close QUIC server: ", err) - } - } - }() - - err := s.Server.Serve(ln) - if err != nil && err != http.ErrServerClosed { - return err - } - return nil -} - -// ServePacket serves QUIC requests on pc until it is closed. -func (s *Server) ServePacket(pc net.PacketConn) error { - if s.quicServer != nil { - err := s.quicServer.Serve(pc.(*net.UDPConn)) - return fmt.Errorf("serving QUIC connections: %v", err) - } - return nil -} - -// ServeHTTP is the entry point of all HTTP requests. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer func() { - // We absolutely need to be sure we stay alive up here, - // even though, in theory, the errors middleware does this. - if rec := recover(); rec != nil { - log.Printf("[PANIC] %v", rec) - DefaultErrorFunc(w, r, http.StatusInternalServerError) - } - }() - - // record the User-Agent string (with a cap on its length to mitigate attacks) - ua := r.Header.Get("User-Agent") - if len(ua) > 512 { - ua = ua[:512] - } - uaHash := telemetry.FastHash([]byte(ua)) // this is a normalized field - go telemetry.SetNested("http_user_agent", uaHash, ua) - go telemetry.AppendUnique("http_user_agent_count", uaHash) - go telemetry.Increment("http_request_count") - - // copy the original, unchanged URL into the context - // so it can be referenced by middlewares - urlCopy := *r.URL - if r.URL.User != nil { - userInfo := new(url.Userinfo) - *userInfo = *r.URL.User - urlCopy.User = userInfo - } - c := context.WithValue(r.Context(), OriginalURLCtxKey, urlCopy) - r = r.WithContext(c) - - // Setup a replacer for the request that keeps track of placeholder - // values across plugins. - replacer := NewReplacer(r, nil, "") - c = context.WithValue(r.Context(), ReplacerCtxKey, replacer) - r = r.WithContext(c) - - w.Header().Set("Server", caddy.AppName) - - status, _ := s.serveHTTP(w, r) - - // Fallback error response in case error handling wasn't chained in - if status >= 400 { - DefaultErrorFunc(w, r, status) - } -} - -func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // strip out the port because it's not used in virtual - // hosting; the port is irrelevant because each listener - // is on a different port. - hostname, _, err := net.SplitHostPort(r.Host) - if err != nil { - hostname = r.Host - } - - // look up the virtualhost; if no match, serve error - vhost, pathPrefix := s.vhosts.Match(hostname + r.URL.Path) - c := context.WithValue(r.Context(), caddy.CtxKey("path_prefix"), pathPrefix) - r = r.WithContext(c) - - if vhost == nil { - // check for ACME challenge even if vhost is nil; - // could be a new host coming online soon - choose any - // vhost's cert manager configuration, I guess - if len(s.sites) > 0 && s.sites[0].TLS.Manager.HandleHTTPChallenge(w, r) { - return 0, nil - } - - // otherwise, log the error and write a message to the client - remoteHost, _, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - remoteHost = r.RemoteAddr - } - WriteSiteNotFound(w, r) // don't add headers outside of this function (http.forwardproxy) - log.Printf("[INFO] %s - No such site at %s (Remote: %s, Referer: %s)", - hostname, s.Server.Addr, remoteHost, r.Header.Get("Referer")) - return 0, nil - } - - // we still check for ACME challenge if the vhost exists, - // because the HTTP challenge might be disabled by its config - if vhost.TLS.Manager.HandleHTTPChallenge(w, r) { - return 0, nil - } - - // trim the path portion of the site address from the beginning of - // the URL path, so a request to example.com/foo/blog on the site - // defined as example.com/foo appears as /blog instead of /foo/blog. - if pathPrefix != "/" { - r.URL = trimPathPrefix(r.URL, pathPrefix) - } - - // enforce strict host matching, which ensures that the SNI - // value (if any), matches the Host header; essential for - // sites that rely on TLS ClientAuth sharing a port with - // sites that do not - if mismatched, close the connection - if vhost.StrictHostMatching && r.TLS != nil && - strings.ToLower(r.TLS.ServerName) != strings.ToLower(hostname) { - r.Close = true - log.Printf("[ERROR] %s - strict host matching: SNI (%s) and HTTP Host (%s) values differ", - vhost.Addr, r.TLS.ServerName, hostname) - return http.StatusForbidden, nil - } - - return vhost.middlewareChain.ServeHTTP(w, r) -} - -func trimPathPrefix(u *url.URL, prefix string) *url.URL { - // We need to use URL.EscapedPath() when trimming the pathPrefix as - // URL.Path is ambiguous about / or %2f - see docs. See #1927 - trimmedPath := strings.TrimPrefix(u.EscapedPath(), prefix) - if !strings.HasPrefix(trimmedPath, "/") { - trimmedPath = "/" + trimmedPath - } - // After trimming path reconstruct uri string with Query before parsing - trimmedURI := trimmedPath - if u.RawQuery != "" || u.ForceQuery == true { - trimmedURI = trimmedPath + "?" + u.RawQuery - } - if u.Fragment != "" { - trimmedURI = trimmedURI + "#" + u.Fragment - } - trimmedURL, err := url.Parse(trimmedURI) - if err != nil { - log.Printf("[ERROR] Unable to parse trimmed URL %s: %v", trimmedURI, err) - return u - } - return trimmedURL -} - -// Address returns the address s was assigned to listen on. -func (s *Server) Address() string { - return s.Server.Addr -} - -// Stop stops s gracefully (or forcefully after timeout) and -// closes its listener. -func (s *Server) Stop() error { - ctx, cancel := context.WithTimeout(context.Background(), s.connTimeout) - defer cancel() - - err := s.Server.Shutdown(ctx) - if err != nil { - return err - } - - // signal any TLS governor goroutines to exit - if s.tlsGovChan != nil { - close(s.tlsGovChan) - } - - return nil -} - -// OnStartupComplete lists the sites served by this server -// and any relevant information, assuming caddy.Quiet == false. -func (s *Server) OnStartupComplete() { - if !caddy.Quiet { - firstSite := s.sites[0] - scheme := "HTTP" - if firstSite.TLS.Enabled { - scheme = "HTTPS" - } - - fmt.Println("") - fmt.Printf("Serving %s on port "+firstSite.Port()+" \n", scheme) - s.outputSiteInfo(false) - fmt.Println("") - } - - // Print out process log without header comment - s.outputSiteInfo(true) -} - -func (s *Server) outputSiteInfo(isProcessLog bool) { - for _, site := range s.sites { - output := site.Addr.String() - if caddy.IsLoopback(s.Address()) && !caddy.IsLoopback(site.Addr.Host) { - output += " (only accessible on this machine)" - } - if isProcessLog { - log.Printf("[INFO] Serving %s \n", output) - } else { - fmt.Println(output) - } - } -} - -// defaultTimeouts stores the default timeout values to use -// if left unset by user configuration. NOTE: Most default -// timeouts are disabled (see issues #1464 and #1733). -var defaultTimeouts = Timeouts{IdleTimeout: 5 * time.Minute} - -// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted -// connections. It's used by ListenAndServe and ListenAndServeTLS so -// dead TCP connections (e.g. closing laptop mid-download) eventually -// go away. -// -// Borrowed from the Go standard library. -type tcpKeepAliveListener struct { - *net.TCPListener -} - -// Accept accepts the connection with a keep-alive enabled. -func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { - tc, err := ln.AcceptTCP() - if err != nil { - return - } - if err = tc.SetKeepAlive(true); err != nil { - return - } - if err = tc.SetKeepAlivePeriod(3 * time.Minute); err != nil { - return - } - return tc, nil -} - -// File implements caddy.Listener; it returns the underlying file of the listener. -func (ln tcpKeepAliveListener) File() (*os.File, error) { - return ln.TCPListener.File() -} - -// ErrMaxBytesExceeded is the error returned by MaxBytesReader -// when the request body exceeds the limit imposed -var ErrMaxBytesExceeded = errors.New("http: request body too large") - -// DefaultErrorFunc responds to an HTTP request with a simple description -// of the specified HTTP status code. -func DefaultErrorFunc(w http.ResponseWriter, r *http.Request, status int) { - WriteTextResponse(w, status, fmt.Sprintf("%d %s\n", status, http.StatusText(status))) -} - -const httpStatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - -// WriteSiteNotFound writes appropriate error code to w, signaling that -// requested host is not served by Caddy on a given port. -func WriteSiteNotFound(w http.ResponseWriter, r *http.Request) { - status := http.StatusNotFound - if r.ProtoMajor >= 2 { - // TODO: use http.StatusMisdirectedRequest when it gets defined - status = httpStatusMisdirectedRequest - } - WriteTextResponse(w, status, fmt.Sprintf("%d Site %s is not served on this interface\n", status, r.Host)) -} - -// WriteTextResponse writes body with code status to w. The body will -// be interpreted as plain text. -func WriteTextResponse(w http.ResponseWriter, status int, body string) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - w.Header().Set("X-Content-Type-Options", "nosniff") - w.WriteHeader(status) - if _, err := w.Write([]byte(body)); err != nil { - log.Println("[Error] failed to write body: ", err) - } -} - -// SafePath joins siteRoot and reqPath and converts it to a path that can -// be used to access a path on the local disk. It ensures the path does -// not traverse outside of the site root. -// -// If opening a file, use http.Dir instead. -func SafePath(siteRoot, reqPath string) string { - reqPath = filepath.ToSlash(reqPath) - reqPath = strings.Replace(reqPath, "\x00", "", -1) // NOTE: Go 1.9 checks for null bytes in the syscall package - if siteRoot == "" { - siteRoot = "." - } - return filepath.Join(siteRoot, filepath.FromSlash(path.Clean("/"+reqPath))) -} - -// OriginalURLCtxKey is the key for accessing the original, incoming URL on an HTTP request. -const OriginalURLCtxKey = caddy.CtxKey("original_url") diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/siteconfig.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/siteconfig.go deleted file mode 100644 index 069a4a106..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/siteconfig.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "time" - - "github.com/mholt/caddy/caddytls" -) - -// SiteConfig contains information about a site -// (also known as a virtual host). -type SiteConfig struct { - // The address of the site - Addr Address - - // The list of viable index page names of the site - IndexPages []string - - // The hostname to bind listener to; - // defaults to Addr.Host - ListenHost string - - // TLS configuration - TLS *caddytls.Config - - // If true, the Host header in the HTTP request must - // match the SNI value in the TLS handshake (if any). - // This should be enabled whenever a site relies on - // TLS client authentication, for example; or any time - // you want to enforce that THIS site's TLS config - // is used and not the TLS config of any other site - // on the same listener. TODO: Check how relevant this - // is with TLS 1.3. - StrictHostMatching bool - - // Uncompiled middleware stack - middleware []Middleware - - // Compiled middleware stack - middlewareChain Handler - - // listener middleware stack - listenerMiddleware []ListenerMiddleware - - // Directory from which to serve files - Root string - - // A list of files to hide (for example, the - // source Caddyfile). TODO: Enforcing this - // should be centralized, for example, a - // standardized way of loading files from disk - // for a request. - HiddenFiles []string - - // Max request's header/body size - Limits Limits - - // The path to the Caddyfile used to generate this site config - originCaddyfile string - - // These timeout values are used, in conjunction with other - // site configs on the same server instance, to set the - // respective timeout values on the http.Server that - // is created. Sensible values will mitigate slowloris - // attacks and overcome faulty networks, while still - // preserving functionality needed for proxying, - // websockets, etc. - Timeouts Timeouts - - // If true, any requests not matching other site definitions - // may be served by this site. - FallbackSite bool -} - -// Timeouts specify various timeouts for a server to use. -// If the associated bool field is true, then the duration -// value should be treated literally (i.e. a zero-value -// duration would mean "no timeout"). If false, the duration -// was left unset, so a zero-value duration would mean to -// use a default value (even if default is non-zero). -type Timeouts struct { - ReadTimeout time.Duration - ReadTimeoutSet bool - ReadHeaderTimeout time.Duration - ReadHeaderTimeoutSet bool - WriteTimeout time.Duration - WriteTimeoutSet bool - IdleTimeout time.Duration - IdleTimeoutSet bool -} - -// Limits specify size limit of request's header and body. -type Limits struct { - MaxRequestHeaderSize int64 - MaxRequestBodySizes []PathLimit -} - -// PathLimit is a mapping from a site's path to its corresponding -// maximum request body size (in bytes) -type PathLimit struct { - Path string - Limit int64 -} - -// AddMiddleware adds a middleware to a site's middleware stack. -func (s *SiteConfig) AddMiddleware(m Middleware) { - s.middleware = append(s.middleware, m) -} - -// AddListenerMiddleware adds a listener middleware to a site's listenerMiddleware stack. -func (s *SiteConfig) AddListenerMiddleware(l ListenerMiddleware) { - s.listenerMiddleware = append(s.listenerMiddleware, l) -} - -// TLSConfig returns s.TLS. -func (s SiteConfig) TLSConfig() *caddytls.Config { - return s.TLS -} - -// Host returns s.Addr.Host. -func (s SiteConfig) Host() string { - return s.Addr.Host -} - -// Port returns s.Addr.Port. -func (s SiteConfig) Port() string { - return s.Addr.Port -} - -// Middleware returns s.middleware (useful for tests). -func (s SiteConfig) Middleware() []Middleware { - return s.middleware -} - -// ListenerMiddleware returns s.listenerMiddleware -func (s SiteConfig) ListenerMiddleware() []ListenerMiddleware { - return s.listenerMiddleware -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/tplcontext.go b/vendor/github.com/mholt/caddy/caddyhttp/httpserver/tplcontext.go deleted file mode 100644 index ccad27a1b..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/httpserver/tplcontext.go +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 httpserver - -import ( - "bytes" - "crypto/rand" - "fmt" - "io/ioutil" - "log" - mathrand "math/rand" - "net" - "net/http" - "net/url" - "path" - "strings" - "sync" - "text/template" - "time" - - "os" - - "github.com/mholt/caddy/caddytls" - "github.com/russross/blackfriday" -) - -// This file contains the context and functions available for -// use in the templates. - -// Context is the context with which Caddy templates are executed. -type Context struct { - Root http.FileSystem - Req *http.Request - URL *url.URL - Args []interface{} // defined by arguments to .Include - - // just used for adding preload links for server push - responseHeader http.Header -} - -// NewContextWithHeader creates a context with given response header. -// -// To plugin developer: -// The returned context's exported fileds remain empty, -// you should then initialize them if you want. -func NewContextWithHeader(rh http.Header) Context { - return Context{ - responseHeader: rh, - } -} - -// Include returns the contents of filename relative to the site root. -func (c Context) Include(filename string, args ...interface{}) (string, error) { - c.Args = args - return ContextInclude(filename, c, c.Root) -} - -// Now returns the current timestamp in the specified format. -func (c Context) Now(format string) string { - return time.Now().Format(format) -} - -// NowDate returns the current date/time that can be used -// in other time functions. -func (c Context) NowDate() time.Time { - return time.Now() -} - -// Cookie gets the value of a cookie with name name. -func (c Context) Cookie(name string) string { - cookies := c.Req.Cookies() - for _, cookie := range cookies { - if cookie.Name == name { - return cookie.Value - } - } - return "" -} - -// Header gets the value of a request header with field name. -func (c Context) Header(name string) string { - return c.Req.Header.Get(name) -} - -// Hostname gets the (remote) hostname of the client making the request. -func (c Context) Hostname() string { - ip := c.IP() - - hostnameList, err := net.LookupAddr(ip) - if err != nil || len(hostnameList) == 0 { - return c.Req.RemoteAddr - } - - return hostnameList[0] -} - -// Env gets a map of the environment variables. -func (c Context) Env() map[string]string { - osEnv := os.Environ() - envVars := make(map[string]string, len(osEnv)) - for _, env := range osEnv { - data := strings.SplitN(env, "=", 2) - if len(data) == 2 && len(data[0]) > 0 { - envVars[data[0]] = data[1] - } - } - return envVars -} - -// IP gets the (remote) IP address of the client making the request. -func (c Context) IP() string { - ip, _, err := net.SplitHostPort(c.Req.RemoteAddr) - if err != nil { - return c.Req.RemoteAddr - } - return ip -} - -// To mock the net.InterfaceAddrs from the test. -var networkInterfacesFn = net.InterfaceAddrs - -// ServerIP gets the (local) IP address of the server. -// TODO: The bind directive should be honored in this method (see PR #1474). -func (c Context) ServerIP() string { - addrs, err := networkInterfacesFn() - if err != nil { - return "" - } - - for _, address := range addrs { - // Validate the address and check if it's not a loopback - if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { - if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil { - return ipnet.IP.String() - } - } - } - - return "" -} - -// URI returns the raw, unprocessed request URI (including query -// string and hash) obtained directly from the Request-Line of -// the HTTP request. -func (c Context) URI() string { - return c.Req.RequestURI -} - -// Host returns the hostname portion of the Host header -// from the HTTP request. -func (c Context) Host() (string, error) { - host, _, err := net.SplitHostPort(c.Req.Host) - if err != nil { - if !strings.Contains(c.Req.Host, ":") { - // common with sites served on the default port 80 - return c.Req.Host, nil - } - return "", err - } - return host, nil -} - -// Port returns the port portion of the Host header if specified. -func (c Context) Port() (string, error) { - _, port, err := net.SplitHostPort(c.Req.Host) - if err != nil { - if !strings.Contains(c.Req.Host, ":") { - // common with sites served on the default port 80 - return HTTPPort, nil - } - return "", err - } - return port, nil -} - -// Method returns the method (GET, POST, etc.) of the request. -func (c Context) Method() string { - return c.Req.Method -} - -// PathMatches returns true if the path portion of the request -// URL matches pattern. -func (c Context) PathMatches(pattern string) bool { - return Path(c.Req.URL.Path).Matches(pattern) -} - -// Truncate truncates the input string to the given length. -// If length is negative, it returns that many characters -// starting from the end of the string. If the absolute value -// of length is greater than len(input), the whole input is -// returned. -func (c Context) Truncate(input string, length int) string { - if length < 0 && len(input)+length > 0 { - return input[len(input)+length:] - } - if length >= 0 && len(input) > length { - return input[:length] - } - return input -} - -// StripHTML returns s without HTML tags. It is fairly naive -// but works with most valid HTML inputs. -func (c Context) StripHTML(s string) string { - var buf bytes.Buffer - var inTag, inQuotes bool - var tagStart int - for i, ch := range s { - if inTag { - if ch == '>' && !inQuotes { - inTag = false - } else if ch == '<' && !inQuotes { - // false start - buf.WriteString(s[tagStart:i]) - tagStart = i - } else if ch == '"' { - inQuotes = !inQuotes - } - continue - } - if ch == '<' { - inTag = true - tagStart = i - continue - } - buf.WriteRune(ch) - } - if inTag { - // false start - buf.WriteString(s[tagStart:]) - } - return buf.String() -} - -// Ext returns the suffix beginning at the final dot in the final -// slash-separated element of the pathStr (or in other words, the -// file extension). -func (c Context) Ext(pathStr string) string { - return path.Ext(pathStr) -} - -// StripExt returns the input string without the extension, -// which is the suffix starting with the final '.' character -// but not before the final path separator ('/') character. -// If there is no extension, the whole input is returned. -func (c Context) StripExt(path string) string { - for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- { - if path[i] == '.' { - return path[:i] - } - } - return path -} - -// Replace replaces instances of find in input with replacement. -func (c Context) Replace(input, find, replacement string) string { - return strings.Replace(input, find, replacement, -1) -} - -// Markdown returns the HTML contents of the markdown contained in filename -// (relative to the site root). -func (c Context) Markdown(filename string) (string, error) { - body, err := c.Include(filename) - if err != nil { - return "", err - } - renderer := blackfriday.HtmlRenderer(0, "", "") - extns := 0 - extns |= blackfriday.EXTENSION_TABLES - extns |= blackfriday.EXTENSION_FENCED_CODE - extns |= blackfriday.EXTENSION_STRIKETHROUGH - extns |= blackfriday.EXTENSION_DEFINITION_LISTS - markdown := blackfriday.Markdown([]byte(body), renderer, extns) - - return string(markdown), nil -} - -// ContextInclude opens filename using fs and executes a template with the context ctx. -// This does the same thing that Context.Include() does, but with the ability to provide -// your own context so that the included files can have access to additional fields your -// type may provide. You can embed Context in your type, then override its Include method -// to call this function with ctx being the instance of your type, and fs being Context.Root. -func ContextInclude(filename string, ctx interface{}, fs http.FileSystem) (string, error) { - file, err := fs.Open(filename) - if err != nil { - return "", err - } - defer file.Close() - - body, err := ioutil.ReadAll(file) - if err != nil { - return "", err - } - - tpl, err := template.New(filename).Funcs(TemplateFuncs).Parse(string(body)) - if err != nil { - return "", err - } - - buf := includeBufs.Get().(*bytes.Buffer) - buf.Reset() - defer includeBufs.Put(buf) - err = tpl.Execute(buf, ctx) - if err != nil { - return "", err - } - - return buf.String(), nil -} - -// ToLower will convert the given string to lower case. -func (c Context) ToLower(s string) string { - return strings.ToLower(s) -} - -// ToUpper will convert the given string to upper case. -func (c Context) ToUpper(s string) string { - return strings.ToUpper(s) -} - -// Split is a pass-through to strings.Split. It will split the first argument at each instance of the separator and return a slice of strings. -func (c Context) Split(s string, sep string) []string { - return strings.Split(s, sep) -} - -// Join is a pass-through to strings.Join. It will join the first argument slice with the separator in the second argument and return the result. -func (c Context) Join(a []string, sep string) string { - return strings.Join(a, sep) -} - -// Slice will convert the given arguments into a slice. -func (c Context) Slice(elems ...interface{}) []interface{} { - return elems -} - -// Map will convert the arguments into a map. It expects alternating string keys and values. This is useful for building more complicated data structures -// if you are using subtemplates or things like that. -func (c Context) Map(values ...interface{}) (map[string]interface{}, error) { - if len(values)%2 != 0 { - return nil, fmt.Errorf("Map expects an even number of arguments") - } - dict := make(map[string]interface{}, len(values)/2) - for i := 0; i < len(values); i += 2 { - key, ok := values[i].(string) - if !ok { - return nil, fmt.Errorf("Map keys must be strings") - } - dict[key] = values[i+1] - } - return dict, nil -} - -// Files reads and returns a slice of names from the given directory -// relative to the root of Context c. -func (c Context) Files(name string) ([]string, error) { - dir, err := c.Root.Open(path.Clean(name)) - if err != nil { - return nil, err - } - defer dir.Close() - - stat, err := dir.Stat() - if err != nil { - return nil, err - } - if !stat.IsDir() { - return nil, fmt.Errorf("%v is not a directory", name) - } - - dirInfo, err := dir.Readdir(0) - if err != nil { - return nil, err - } - - names := make([]string, len(dirInfo)) - for i, fileInfo := range dirInfo { - names[i] = fileInfo.Name() - } - - return names, nil -} - -// IsMITM returns true if it seems likely that the TLS connection -// is being intercepted. -func (c Context) IsMITM() bool { - if val, ok := c.Req.Context().Value(MitmCtxKey).(bool); ok { - return val - } - return false -} - -// RandomString generates a random string of random length given -// length bounds. Thanks to http://stackoverflow.com/a/35615565/1048862 -// for the clever technique that is fairly fast, secure, and maintains -// proper distributions over the dictionary. -func (c Context) RandomString(minLen, maxLen int) string { - const ( - letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - letterIdxBits = 6 // 6 bits to represent 64 possibilities (indexes) - letterIdxMask = 1< 0 { - ch := string(remainingPath[0]) - next, ok := t.edges[ch] - if !ok { - break - } - if next.site != nil { - longestMatch = next - } - t = next - remainingPath = remainingPath[1:] - } - return longestMatch -} - -// splitHostPath separates host from path in key. -func (t *vhostTrie) splitHostPath(key string) (host, path string) { - parts := strings.SplitN(key, "/", 2) - host, path = strings.ToLower(parts[0]), "/" - if len(parts) > 1 { - path += parts[1] - } - // strip out the port (if present) from the host, since - // each port has its own socket, and each socket has its - // own listener, and each listener has its own server - // instance, and each server instance has its own vhosts. - // removing the port is a simple way to standardize so - // when requests come in, we can be sure to get a match. - hostname, _, err := net.SplitHostPort(host) - if err == nil { - host = hostname - } - return -} - -// String returns a list of all the entries in t; assumes that -// t is a root node. -func (t *vhostTrie) String() string { - var s string - for host, edge := range t.edges { - s += edge.str(host) - } - return s -} - -func (t *vhostTrie) str(prefix string) string { - var s string - for key, edge := range t.edges { - if edge.site != nil { - s += prefix + key + "\n" - } - s += edge.str(prefix + key) - } - return s -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/index/index.go b/vendor/github.com/mholt/caddy/caddyhttp/index/index.go deleted file mode 100644 index fefc0ff73..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/index/index.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 index - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("index", caddy.Plugin{ - ServerType: "http", - Action: setupIndex, - }) -} - -func setupIndex(c *caddy.Controller) error { - var index []string - - cfg := httpserver.GetConfig(c) - - for c.Next() { - args := c.RemainingArgs() - - if len(args) == 0 { - return c.Errf("Expected at least one index") - } - - for _, in := range args { - index = append(index, in) - } - - cfg.IndexPages = index - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/internal.go b/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/internal.go deleted file mode 100644 index 3bd47956d..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/internal.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 internalsrv provides a simple middleware that (a) prevents access -// to internal locations and (b) allows to return files from internal location -// by setting a special header, e.g. in a proxy response. -// -// The package is named internalsrv so as not to conflict with Go tooling -// convention which treats folders called "internal" differently. -package internalsrv - -import ( - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Internal middleware protects internal locations from external requests - -// but allows access from the inside by using a special HTTP header. -type Internal struct { - Next httpserver.Handler - Paths []string -} - -const ( - redirectHeader string = "X-Accel-Redirect" - contentLengthHeader string = "Content-Length" - contentEncodingHeader string = "Content-Encoding" - maxRedirectCount int = 10 -) - -func isInternalRedirect(w http.ResponseWriter) bool { - return w.Header().Get(redirectHeader) != "" -} - -// ServeHTTP implements the httpserver.Handler interface. -func (i Internal) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // Internal location requested? -> Not found. - for _, prefix := range i.Paths { - if httpserver.Path(r.URL.Path).Matches(prefix) { - return http.StatusNotFound, nil - } - } - - // Use internal response writer to ignore responses that will be - // redirected to internal locations - iw := internalResponseWriter{ResponseWriterWrapper: &httpserver.ResponseWriterWrapper{ResponseWriter: w}} - status, err := i.Next.ServeHTTP(iw, r) - - for c := 0; c < maxRedirectCount && isInternalRedirect(iw); c++ { - // Redirect - adapt request URL path and send it again - // "down the chain" - r.URL.Path = iw.Header().Get(redirectHeader) - iw.ClearHeader() - status, err = i.Next.ServeHTTP(iw, r) - } - - if isInternalRedirect(iw) { - // Too many redirect cycles - iw.ClearHeader() - return http.StatusInternalServerError, nil - } - - return status, err -} - -// internalResponseWriter wraps the underlying http.ResponseWriter and ignores -// calls to Write and WriteHeader if the response should be redirected to an -// internal location. -type internalResponseWriter struct { - *httpserver.ResponseWriterWrapper -} - -// ClearHeader removes script headers that would interfere with follow up -// redirect requests. -func (w internalResponseWriter) ClearHeader() { - w.Header().Del(redirectHeader) - w.Header().Del(contentLengthHeader) - w.Header().Del(contentEncodingHeader) -} - -// WriteHeader ignores the call if the response should be redirected to an -// internal location. -func (w internalResponseWriter) WriteHeader(code int) { - if !isInternalRedirect(w) { - w.ResponseWriterWrapper.WriteHeader(code) - } -} - -// Write ignores the call if the response should be redirected to an internal -// location. -func (w internalResponseWriter) Write(b []byte) (int, error) { - if isInternalRedirect(w) { - return 0, nil - } - return w.ResponseWriterWrapper.Write(b) -} - -// Interface guards -var _ httpserver.HTTPInterfaces = internalResponseWriter{} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/setup.go deleted file mode 100644 index f70147ac8..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/internalsrv/setup.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 internalsrv - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("internal", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// Internal configures a new Internal middleware instance. -func setup(c *caddy.Controller) error { - paths, err := internalParse(c) - if err != nil { - return err - } - - // Append Internal paths to Caddy config HiddenFiles to ensure - // files do not appear in Browse - config := httpserver.GetConfig(c) - config.HiddenFiles = append(config.HiddenFiles, paths...) - - config.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Internal{Next: next, Paths: paths} - }) - - return nil -} - -func internalParse(c *caddy.Controller) ([]string, error) { - var paths []string - - for c.Next() { - if c.NextArg() { - paths = append(paths, c.Val()) - } - if c.NextArg() { - return nil, c.ArgErr() - } - } - - return paths, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/limits/handler.go b/vendor/github.com/mholt/caddy/caddyhttp/limits/handler.go deleted file mode 100644 index ded2fabe3..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/limits/handler.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 limits - -import ( - "io" - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Limit is a middleware to control request body size -type Limit struct { - Next httpserver.Handler - BodyLimits []httpserver.PathLimit -} - -func (l Limit) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if r.Body == nil { - return l.Next.ServeHTTP(w, r) - } - - // apply the path-based request body size limit. - for _, bl := range l.BodyLimits { - if httpserver.Path(r.URL.Path).Matches(bl.Path) { - r.Body = MaxBytesReader(w, r.Body, bl.Limit) - break - } - } - - return l.Next.ServeHTTP(w, r) -} - -// MaxBytesReader and its associated methods are borrowed from the -// Go Standard library (comments intact). The only difference is that -// it returns a ErrMaxBytesExceeded error instead of a generic error message -// when the request body has exceeded the requested limit -func MaxBytesReader(w http.ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser { - return &maxBytesReader{w: w, r: r, n: n} -} - -type maxBytesReader struct { - w http.ResponseWriter - r io.ReadCloser // underlying reader - n int64 // max bytes remaining - err error // sticky error -} - -func (l *maxBytesReader) Read(p []byte) (n int, err error) { - if l.err != nil { - return 0, l.err - } - if len(p) == 0 { - return 0, nil - } - // If they asked for a 32KB byte read but only 5 bytes are - // remaining, no need to read 32KB. 6 bytes will answer the - // question of the whether we hit the limit or go past it. - if int64(len(p)) > l.n+1 { - p = p[:l.n+1] - } - n, err = l.r.Read(p) - - if int64(n) <= l.n { - l.n -= int64(n) - l.err = err - return n, err - } - - n = int(l.n) - l.n = 0 - - // The server code and client code both use - // maxBytesReader. This "requestTooLarge" check is - // only used by the server code. To prevent binaries - // which only using the HTTP Client code (such as - // cmd/go) from also linking in the HTTP server, don't - // use a static type assertion to the server - // "*response" type. Check this interface instead: - type requestTooLarger interface { - requestTooLarge() - } - if res, ok := l.w.(requestTooLarger); ok { - res.requestTooLarge() - } - l.err = httpserver.ErrMaxBytesExceeded - return n, l.err -} - -func (l *maxBytesReader) Close() error { - return l.r.Close() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/limits/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/limits/setup.go deleted file mode 100644 index e9971263a..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/limits/setup.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 limits - -import ( - "errors" - "sort" - "strconv" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -const ( - serverType = "http" - pluginName = "limits" -) - -func init() { - caddy.RegisterPlugin(pluginName, caddy.Plugin{ - ServerType: serverType, - Action: setupLimits, - }) -} - -// pathLimitUnparsed is a PathLimit before it's parsed -type pathLimitUnparsed struct { - Path string - Limit string -} - -func setupLimits(c *caddy.Controller) error { - bls, err := parseLimits(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Limit{Next: next, BodyLimits: bls} - }) - return nil -} - -func parseLimits(c *caddy.Controller) ([]httpserver.PathLimit, error) { - config := httpserver.GetConfig(c) - - if !c.Next() { - return nil, c.ArgErr() - } - - args := c.RemainingArgs() - argList := []pathLimitUnparsed{} - headerLimit := "" - - switch len(args) { - case 0: - // Format: limits { - // header - // body - // body - // ... - // } - for c.NextBlock() { - kind := c.Val() - pathOrLimit := c.RemainingArgs() - switch kind { - case "header": - if len(pathOrLimit) != 1 { - return nil, c.ArgErr() - } - headerLimit = pathOrLimit[0] - case "body": - if len(pathOrLimit) == 1 { - argList = append(argList, pathLimitUnparsed{ - Path: "/", - Limit: pathOrLimit[0], - }) - break - } - - if len(pathOrLimit) == 2 { - argList = append(argList, pathLimitUnparsed{ - Path: pathOrLimit[0], - Limit: pathOrLimit[1], - }) - break - } - - fallthrough - default: - return nil, c.ArgErr() - } - } - case 1: - // Format: limits - headerLimit = args[0] - argList = []pathLimitUnparsed{{ - Path: "/", - Limit: args[0], - }} - default: - return nil, c.ArgErr() - } - - if headerLimit != "" { - size := parseSize(headerLimit) - if size < 1 { // also disallow size = 0 - return nil, c.ArgErr() - } - config.Limits.MaxRequestHeaderSize = size - } - - if len(argList) > 0 { - pathLimit, err := parseArguments(argList) - if err != nil { - return nil, c.ArgErr() - } - SortPathLimits(pathLimit) - config.Limits.MaxRequestBodySizes = pathLimit - } - - return config.Limits.MaxRequestBodySizes, nil -} - -func parseArguments(args []pathLimitUnparsed) ([]httpserver.PathLimit, error) { - pathLimit := []httpserver.PathLimit{} - - for _, pair := range args { - size := parseSize(pair.Limit) - if size < 1 { // also disallow size = 0 - return pathLimit, errors.New("Parse failed") - } - pathLimit = addPathLimit(pathLimit, pair.Path, size) - } - return pathLimit, nil -} - -var validUnits = []struct { - symbol string - multiplier int64 -}{ - {"KB", 1024}, - {"MB", 1024 * 1024}, - {"GB", 1024 * 1024 * 1024}, - {"B", 1}, - {"", 1}, // defaulting to "B" -} - -// parseSize parses the given string as size limit -// Size are positive numbers followed by a unit (case insensitive) -// Allowed units: "B" (bytes), "KB" (kilo), "MB" (mega), "GB" (giga) -// If the unit is omitted, "b" is assumed -// Returns the parsed size in bytes, or -1 if cannot parse -func parseSize(sizeStr string) int64 { - sizeStr = strings.ToUpper(sizeStr) - - for _, unit := range validUnits { - if strings.HasSuffix(sizeStr, unit.symbol) { - size, err := strconv.ParseInt(sizeStr[0:len(sizeStr)-len(unit.symbol)], 10, 64) - if err != nil { - return -1 - } - return size * unit.multiplier - } - } - - // Unreachable code - return -1 -} - -// addPathLimit appends the path-to-request body limit mapping to pathLimit -// Slashes are checked and added to path if necessary. Duplicates are ignored. -func addPathLimit(pathLimit []httpserver.PathLimit, path string, limit int64) []httpserver.PathLimit { - // Enforces preceding slash - if path[0] != '/' { - path = "/" + path - } - - // Use the last value if there are duplicates - for i, p := range pathLimit { - if p.Path == path { - pathLimit[i].Limit = limit - return pathLimit - } - } - - return append(pathLimit, httpserver.PathLimit{Path: path, Limit: limit}) -} - -// SortPathLimits sort pathLimits by their paths length, longest first -func SortPathLimits(pathLimits []httpserver.PathLimit) { - sorter := &pathLimitSorter{ - pathLimits: pathLimits, - by: LengthDescending, - } - sort.Sort(sorter) -} - -// structs and methods implementing the sorting interfaces for PathLimit -type pathLimitSorter struct { - pathLimits []httpserver.PathLimit - by func(p1, p2 *httpserver.PathLimit) bool -} - -func (s *pathLimitSorter) Len() int { - return len(s.pathLimits) -} - -func (s *pathLimitSorter) Swap(i, j int) { - s.pathLimits[i], s.pathLimits[j] = s.pathLimits[j], s.pathLimits[i] -} - -func (s *pathLimitSorter) Less(i, j int) bool { - return s.by(&s.pathLimits[i], &s.pathLimits[j]) -} - -// LengthDescending is the comparator for SortPathLimits -func LengthDescending(p1, p2 *httpserver.PathLimit) bool { - return len(p1.Path) > len(p2.Path) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/log/log.go b/vendor/github.com/mholt/caddy/caddyhttp/log/log.go deleted file mode 100644 index 251f091ca..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/log/log.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 log implements request (access) logging middleware. -package log - -import ( - "fmt" - "net" - "net/http" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("log", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// Logger is a basic request logging middleware. -type Logger struct { - Next httpserver.Handler - Rules []*Rule - ErrorFunc func(http.ResponseWriter, *http.Request, int) // failover error handler -} - -func (l Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - for _, rule := range l.Rules { - if httpserver.Path(r.URL.Path).Matches(rule.PathScope) { - // Record the response - responseRecorder := httpserver.NewResponseRecorder(w) - - // Attach the Replacer we'll use so that other middlewares can - // set their own placeholders if they want to. - rep := httpserver.NewReplacer(r, responseRecorder, CommonLogEmptyValue) - responseRecorder.Replacer = rep - - // Bon voyage, request! - status, err := l.Next.ServeHTTP(responseRecorder, r) - - if status >= 400 { - // There was an error up the chain, but no response has been written yet. - // The error must be handled here so the log entry will record the response size. - if l.ErrorFunc != nil { - l.ErrorFunc(responseRecorder, r, status) - } else { - // Default failover error handler - responseRecorder.WriteHeader(status) - fmt.Fprintf(responseRecorder, "%d %s", status, http.StatusText(status)) - } - status = 0 - } - - // Write log entries - for _, e := range rule.Entries { - // Check if there is an exception to prevent log being written - if !e.Log.ShouldLog(r.URL.Path) { - continue - } - - // Mask IP Address - if e.Log.IPMaskExists { - hostip, _, err := net.SplitHostPort(r.RemoteAddr) - if err == nil { - maskedIP := e.Log.MaskIP(hostip) - // Overwrite log value with Masked version - rep.Set("remote", maskedIP) - } - } - e.Log.Println(rep.Replace(e.Format)) - - } - - return status, err - } - } - return l.Next.ServeHTTP(w, r) -} - -// Entry represents a log entry under a path scope -type Entry struct { - Format string - Log *httpserver.Logger -} - -// Rule configures the logging middleware. -type Rule struct { - PathScope string - Entries []*Entry -} - -const ( - // DefaultLogFilename is the default log filename. - DefaultLogFilename = "access.log" - // CommonLogFormat is the common log format. - CommonLogFormat = `{remote} ` + CommonLogEmptyValue + ` {user} [{when}] "{method} {uri} {proto}" {status} {size}` - // CommonLogEmptyValue is the common empty log value. - CommonLogEmptyValue = "-" - // CombinedLogFormat is the combined log format. - CombinedLogFormat = CommonLogFormat + ` "{>Referer}" "{>User-Agent}"` - // DefaultLogFormat is the default log format. - DefaultLogFormat = CommonLogFormat -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/log/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/log/setup.go deleted file mode 100644 index 0c889a9f7..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/log/setup.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 log - -import ( - "net" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// setup sets up the logging middleware. -func setup(c *caddy.Controller) error { - rules, err := logParse(c) - if err != nil { - return err - } - - for _, rule := range rules { - for _, entry := range rule.Entries { - entry.Log.Attach(c) - } - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Logger{Next: next, Rules: rules, ErrorFunc: httpserver.DefaultErrorFunc} - }) - - return nil -} - -func logParse(c *caddy.Controller) ([]*Rule, error) { - var rules []*Rule - var logExceptions []string - for c.Next() { - args := c.RemainingArgs() - - ip4Mask := net.IPMask(net.ParseIP(DefaultIP4Mask).To4()) - ip6Mask := net.IPMask(net.ParseIP(DefaultIP6Mask)) - ipMaskExists := false - - var logRoller *httpserver.LogRoller - logRoller = httpserver.DefaultLogRoller() - - for c.NextBlock() { - what := c.Val() - where := c.RemainingArgs() - - if what == "ipmask" { - - if len(where) == 0 { - return nil, c.ArgErr() - } - - if where[0] != "" { - ip4MaskStr := where[0] - ipv4 := net.ParseIP(ip4MaskStr).To4() - - if ipv4 == nil { - return nil, c.Err("IPv4 Mask not valid IP Mask Format") - } else { - ip4Mask = net.IPMask(ipv4) - ipMaskExists = true - } - } - - if len(where) > 1 { - - ip6MaskStr := where[1] - ipv6 := net.ParseIP(ip6MaskStr) - - if ipv6 == nil { - return nil, c.Err("IPv6 Mask not valid IP Mask Format") - } else { - ip6Mask = net.IPMask(ipv6) - ipMaskExists = true - } - - } - - } else if what == "except" { - - for i := 0; i < len(where); i++ { - logExceptions = append(logExceptions, where[i]) - } - - } else if httpserver.IsLogRollerSubdirective(what) { - - if err := httpserver.ParseRoller(logRoller, what, where...); err != nil { - return nil, err - } - - } else { - return nil, c.ArgErr() - } - - } - - path := "/" - format := DefaultLogFormat - output := DefaultLogFilename - - switch len(args) { - case 0: - // nothing to change - case 1: - // Only an output file specified - output = args[0] - case 2, 3: - // Path scope, output file, and maybe a format specified - path = args[0] - output = args[1] - if len(args) > 2 { - format = strings.Replace(args[2], "{common}", CommonLogFormat, -1) - format = strings.Replace(format, "{combined}", CombinedLogFormat, -1) - } - default: - // Maximum number of args in log directive is 3. - return nil, c.ArgErr() - } - - rules = appendEntry(rules, path, &Entry{ - Log: &httpserver.Logger{ - Output: output, - Roller: logRoller, - V4ipMask: ip4Mask, - V6ipMask: ip6Mask, - IPMaskExists: ipMaskExists, - Exceptions: logExceptions, - }, - Format: format, - }) - } - - return rules, nil -} - -func appendEntry(rules []*Rule, pathScope string, entry *Entry) []*Rule { - for _, rule := range rules { - if rule.PathScope == pathScope { - rule.Entries = append(rule.Entries, entry) - return rules - } - } - - rules = append(rules, &Rule{ - PathScope: pathScope, - Entries: []*Entry{entry}, - }) - - return rules -} - -const ( - // IP Masks that have no effect on IP Address - DefaultIP4Mask = "255.255.255.255" - - DefaultIP6Mask = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go deleted file mode 100644 index 1bbb36b49..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/markdown.go +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 markdown is middleware to render markdown files as HTML -// on-the-fly. -package markdown - -import ( - "log" - "net/http" - "os" - "path" - "strconv" - "strings" - "text/template" - "time" - - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/russross/blackfriday" -) - -// Markdown implements a layer of middleware that serves -// markdown as HTML. -type Markdown struct { - // Server root - Root string - - // Jail the requests to site root with a mock file system - FileSys http.FileSystem - - // Next HTTP handler in the chain - Next httpserver.Handler - - // The list of markdown configurations - Configs []*Config -} - -// Config stores markdown middleware configurations. -type Config struct { - // Markdown renderer - Renderer blackfriday.Renderer - - // Base path to match - PathScope string - - // List of extensions to consider as markdown files - Extensions map[string]struct{} - - // List of style sheets to load for each markdown file - Styles []string - - // List of JavaScript files to load for each markdown file - Scripts []string - - // The list of index files to try - IndexFiles []string - - // Template(s) to render with - Template *template.Template - - // a pair of template's name and its underlying file information - TemplateFiles map[string]*cachedFileInfo -} - -type cachedFileInfo struct { - path string - fi os.FileInfo -} - -// ServeHTTP implements the http.Handler interface. -func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - var cfg *Config - for _, c := range md.Configs { - if httpserver.Path(r.URL.Path).Matches(c.PathScope) { // not negated - cfg = c - break // or goto - } - } - if cfg == nil { - return md.Next.ServeHTTP(w, r) // exit early - } - - // We only deal with HEAD/GET - switch r.Method { - case http.MethodGet, http.MethodHead: - default: - return http.StatusMethodNotAllowed, nil - } - - var dirents []os.FileInfo - var lastModTime time.Time - fpath := r.URL.Path - if idx, ok := httpserver.IndexFile(md.FileSys, fpath, cfg.IndexFiles); ok { - // We're serving a directory index file, which may be a markdown - // file with a template. Let's grab a list of files this directory - // URL points to, and pass that in to any possible template invocations, - // so that templates can customize the look and feel of a directory. - fdp, err := md.FileSys.Open(fpath) - switch { - case err == nil: // nop - case os.IsPermission(err): - return http.StatusForbidden, err - case os.IsExist(err): - return http.StatusNotFound, nil - default: // did we run out of FD? - return http.StatusInternalServerError, err - } - defer fdp.Close() - - // Grab a possible set of directory entries. Note, we do not check - // for errors here (unreadable directory, for example). It may - // still be useful to have a directory template file, without the - // directory contents being present. Note, the directory's last - // modification is also present here (entry "."). - dirents, _ = fdp.Readdir(-1) - for _, d := range dirents { - lastModTime = latest(lastModTime, d.ModTime()) - } - - // Set path to found index file - fpath = idx - } - - // If not supported extension, pass on it - if _, ok := cfg.Extensions[path.Ext(fpath)]; !ok { - return md.Next.ServeHTTP(w, r) - } - - // At this point we have a supported extension/markdown - f, err := md.FileSys.Open(fpath) - switch { - case err == nil: // nop - case os.IsPermission(err): - return http.StatusForbidden, err - case os.IsNotExist(err): - return http.StatusNotFound, nil - default: // did we run out of FD? - return http.StatusInternalServerError, err - } - defer f.Close() - - fs, err := f.Stat() - if err != nil { - return http.StatusGone, nil - } - lastModTime = latest(lastModTime, fs.ModTime()) - - ctx := httpserver.NewContextWithHeader(w.Header()) - ctx.Root = md.FileSys - ctx.Req = r - ctx.URL = r.URL - html, err := cfg.Markdown(title(fpath), f, dirents, ctx) - if err != nil { - return http.StatusInternalServerError, err - } - - w.Header().Set("Content-Type", "text/html; charset=utf-8") - w.Header().Set("Content-Length", strconv.Itoa(len(html))) - httpserver.SetLastModifiedHeader(w, lastModTime) - if r.Method == http.MethodGet { - if _, err := w.Write(html); err != nil { - log.Println("[ERROR] failed to write html response: ", err) - } - } - return http.StatusOK, nil -} - -// latest returns the latest time.Time -func latest(t ...time.Time) time.Time { - var last time.Time - - for _, tt := range t { - if tt.After(last) { - last = tt - } - } - - return last -} - -// title gives a backup generated title for a page -func title(p string) string { - return strings.TrimSuffix(path.Base(p), path.Ext(p)) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata.go deleted file mode 100644 index 22826bf15..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 metadata - -import ( - "bufio" - "bytes" - "time" -) - -var ( - // Date format YYYY-MM-DD HH:MM:SS or YYYY-MM-DD - timeLayout = []string{ - `2006-01-02 15:04:05-0700`, - `2006-01-02 15:04:05`, - `2006-01-02`, - } -) - -// Metadata stores a page's metadata -type Metadata struct { - // Page title - Title string - - // Page template - Template string - - // Publish date - Date time.Time - - // Variables to be used with Template - Variables map[string]interface{} -} - -// NewMetadata returns a new Metadata struct, loaded with the given map -func NewMetadata(parsedMap map[string]interface{}) Metadata { - md := Metadata{ - Variables: make(map[string]interface{}), - } - md.load(parsedMap) - - return md -} - -// load loads parsed values in parsedMap into Metadata -func (m *Metadata) load(parsedMap map[string]interface{}) { - - // Pull top level things out - if title, ok := parsedMap["title"]; ok { - m.Title, _ = title.(string) - } - if template, ok := parsedMap["template"]; ok { - m.Template, _ = template.(string) - } - if date, ok := parsedMap["date"].(string); ok { - for _, layout := range timeLayout { - if t, err := time.Parse(layout, date); err == nil { - m.Date = t - break - } - } - } - - m.Variables = parsedMap -} - -// Parser is a an interface that must be satisfied by each parser -type Parser interface { - // Initialize a parser - Init(b *bytes.Buffer) bool - - // Type of metadata - Type() string - - // Parsed metadata. - Metadata() Metadata - - // Raw markdown. - Markdown() []byte -} - -// GetParser returns a parser for the given data -func GetParser(buf []byte) Parser { - for _, p := range parsers() { - b := bytes.NewBuffer(buf) - if p.Init(b) { - return p - } - } - - return nil -} - -// parsers returns all available parsers -func parsers() []Parser { - return []Parser{ - &TOMLParser{}, - &YAMLParser{}, - &JSONParser{}, - - // This one must be last - &NoneParser{}, - } -} - -// Split out prefixed/suffixed metadata with given delimiter -func splitBuffer(b *bytes.Buffer, delim string) (*bytes.Buffer, *bytes.Buffer) { - scanner := bufio.NewScanner(b) - - // Read and check first line - if !scanner.Scan() { - return nil, nil - } - if string(bytes.TrimSpace(scanner.Bytes())) != delim { - return nil, nil - } - - // Accumulate metadata, until delimiter - meta := bytes.NewBuffer(nil) - for scanner.Scan() { - if string(bytes.TrimSpace(scanner.Bytes())) == delim { - break - } - if _, err := meta.Write(scanner.Bytes()); err != nil { - return nil, nil - } - if _, err := meta.WriteRune('\n'); err != nil { - return nil, nil - } - } - // Make sure we saw closing delimiter - if string(bytes.TrimSpace(scanner.Bytes())) != delim { - return nil, nil - } - - // The rest is markdown - markdown := new(bytes.Buffer) - for scanner.Scan() { - if _, err := markdown.Write(scanner.Bytes()); err != nil { - return nil, nil - } - if _, err := markdown.WriteRune('\n'); err != nil { - return nil, nil - } - } - - return meta, markdown -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_json.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_json.go deleted file mode 100644 index 285e70e1a..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_json.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 metadata - -import ( - "bytes" - "encoding/json" -) - -// JSONParser is the MetadataParser for JSON -type JSONParser struct { - metadata Metadata - markdown *bytes.Buffer -} - -// Type returns the kind of metadata parser implemented by this struct. -func (j *JSONParser) Type() string { - return "JSON" -} - -// Init prepares the metadata metadata/markdown file and parses it -func (j *JSONParser) Init(b *bytes.Buffer) bool { - m := make(map[string]interface{}) - - err := json.Unmarshal(b.Bytes(), &m) - if err != nil { - var offset int - - jerr, ok := err.(*json.SyntaxError) - if !ok { - return false - } - - offset = int(jerr.Offset) - - m = make(map[string]interface{}) - err = json.Unmarshal(b.Next(offset-1), &m) - if err != nil { - return false - } - } - - j.metadata = NewMetadata(m) - j.markdown = bytes.NewBuffer(b.Bytes()) - - return true -} - -// Metadata returns parsed metadata. It should be called -// only after a call to Parse returns without error. -func (j *JSONParser) Metadata() Metadata { - return j.metadata -} - -// Markdown returns the markdown text. It should be called only after a call to Parse returns without error. -func (j *JSONParser) Markdown() []byte { - return j.markdown.Bytes() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_none.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_none.go deleted file mode 100644 index e25e15dd0..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_none.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 metadata - -import ( - "bytes" -) - -// NoneParser is the parser for plaintext markdown with no metadata. -type NoneParser struct { - metadata Metadata - markdown *bytes.Buffer -} - -// Type returns the kind of parser this struct is. -func (n *NoneParser) Type() string { - return "None" -} - -// Init preparses and parses the metadata and markdown file -func (n *NoneParser) Init(b *bytes.Buffer) bool { - m := make(map[string]interface{}) - n.metadata = NewMetadata(m) - n.markdown = bytes.NewBuffer(b.Bytes()) - - return true -} - -// Parse the metadata -func (n *NoneParser) Parse(b []byte) ([]byte, error) { - return nil, nil -} - -// Metadata returns parsed metadata. It should be called -// only after a call to Parse returns without error. -func (n *NoneParser) Metadata() Metadata { - return n.metadata -} - -// Markdown returns parsed markdown. It should be called -// only after a call to Parse returns without error. -func (n *NoneParser) Markdown() []byte { - return n.markdown.Bytes() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_toml.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_toml.go deleted file mode 100644 index 03298e653..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_toml.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 metadata - -import ( - "bytes" - - "github.com/naoina/toml" -) - -// TOMLParser is the Parser for TOML -type TOMLParser struct { - metadata Metadata - markdown *bytes.Buffer -} - -// Type returns the kind of parser this struct is. -func (t *TOMLParser) Type() string { - return "TOML" -} - -// Init prepares and parses the metadata and markdown file itself -func (t *TOMLParser) Init(b *bytes.Buffer) bool { - meta, data := splitBuffer(b, "+++") - if meta == nil || data == nil { - return false - } - t.markdown = data - - m := make(map[string]interface{}) - if err := toml.Unmarshal(meta.Bytes(), &m); err != nil { - return false - } - t.metadata = NewMetadata(m) - - return true -} - -// Metadata returns parsed metadata. It should be called -// only after a call to Parse returns without error. -func (t *TOMLParser) Metadata() Metadata { - return t.metadata -} - -// Markdown returns parser markdown. It should be called only after a call to Parse returns without error. -func (t *TOMLParser) Markdown() []byte { - return t.markdown.Bytes() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_yaml.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_yaml.go deleted file mode 100644 index ddd90b600..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/metadata/metadata_yaml.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 metadata - -import ( - "bytes" - - "gopkg.in/yaml.v2" -) - -// YAMLParser is the Parser for YAML -type YAMLParser struct { - metadata Metadata - markdown *bytes.Buffer -} - -// Type returns the kind of metadata parser. -func (y *YAMLParser) Type() string { - return "YAML" -} - -// Init prepares the metadata parser for parsing. -func (y *YAMLParser) Init(b *bytes.Buffer) bool { - meta, data := splitBuffer(b, "---") - if meta == nil || data == nil { - return false - } - y.markdown = data - - m := make(map[string]interface{}) - if err := yaml.Unmarshal(meta.Bytes(), &m); err != nil { - return false - } - y.metadata = NewMetadata(m) - - return true -} - -// Metadata returns parsed metadata. It should be called -// only after a call to Parse returns without error. -func (y *YAMLParser) Metadata() Metadata { - return y.metadata -} - -// Markdown renders the text as a byte array -func (y *YAMLParser) Markdown() []byte { - return y.markdown.Bytes() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/process.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/process.go deleted file mode 100644 index e0594d991..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/process.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 markdown - -import ( - "io" - "io/ioutil" - "os" - - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddyhttp/markdown/metadata" - "github.com/mholt/caddy/caddyhttp/markdown/summary" - "github.com/russross/blackfriday" -) - -// FileInfo represents a file in a particular server context. It wraps the os.FileInfo struct. -type FileInfo struct { - os.FileInfo - ctx httpserver.Context -} - -var recognizedMetaTags = []string{ - "author", - "copyright", - "description", - "subject", -} - -// Summarize returns an abbreviated string representation of the markdown stored in this file. -// wordcount is the number of words returned in the summary. -func (f FileInfo) Summarize(wordcount int) (string, error) { - fp, err := f.ctx.Root.Open(f.Name()) - if err != nil { - return "", err - } - defer fp.Close() - - buf, err := ioutil.ReadAll(fp) - if err != nil { - return "", err - } - - return string(summary.Markdown(buf, wordcount)), nil -} - -// Markdown processes the contents of a page in b. It parses the metadata -// (if any) and uses the template (if found). -func (c *Config) Markdown(title string, r io.Reader, dirents []os.FileInfo, ctx httpserver.Context) ([]byte, error) { - body, err := ioutil.ReadAll(r) - if err != nil { - return nil, err - } - - parser := metadata.GetParser(body) - markdown := parser.Markdown() - mdata := parser.Metadata() - - // process markdown - extns := 0 - extns |= blackfriday.EXTENSION_TABLES - extns |= blackfriday.EXTENSION_FENCED_CODE - extns |= blackfriday.EXTENSION_STRIKETHROUGH - extns |= blackfriday.EXTENSION_DEFINITION_LISTS - html := blackfriday.Markdown(markdown, c.Renderer, extns) - - // set it as body for template - mdata.Variables["body"] = string(html) - - // fixup title - mdata.Variables["title"] = mdata.Title - if mdata.Variables["title"] == "" { - mdata.Variables["title"] = title - } - - // move available and valid front matters to the meta values - meta := make(map[string]string) - for _, val := range recognizedMetaTags { - if mVal, ok := mdata.Variables[val]; ok { - meta[val] = mVal.(string) - } - } - - // massage possible files - files := []FileInfo{} - for _, ent := range dirents { - file := FileInfo{ - FileInfo: ent, - ctx: ctx, - } - files = append(files, file) - } - - return execTemplate(c, mdata, meta, files, ctx) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/setup.go deleted file mode 100644 index 45ca71330..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/setup.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 markdown - -import ( - "net/http" - "path/filepath" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/russross/blackfriday" -) - -func init() { - caddy.RegisterPlugin("markdown", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Markdown middleware instance. -func setup(c *caddy.Controller) error { - mdconfigs, err := markdownParse(c) - if err != nil { - return err - } - - cfg := httpserver.GetConfig(c) - - md := Markdown{ - Root: cfg.Root, - FileSys: http.Dir(cfg.Root), - Configs: mdconfigs, - } - - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - md.Next = next - return md - }) - - return nil -} - -func markdownParse(c *caddy.Controller) ([]*Config, error) { - var mdconfigs []*Config - - for c.Next() { - md := &Config{ - Renderer: blackfriday.HtmlRenderer(0, "", ""), - Extensions: make(map[string]struct{}), - Template: GetDefaultTemplate(), - IndexFiles: []string{}, - TemplateFiles: make(map[string]*cachedFileInfo), - } - - // Get the path scope - args := c.RemainingArgs() - switch len(args) { - case 0: - md.PathScope = "/" - case 1: - md.PathScope = args[0] - default: - return mdconfigs, c.ArgErr() - } - - // Load any other configuration parameters - for c.NextBlock() { - if err := loadParams(c, md); err != nil { - return mdconfigs, err - } - } - - // If no extensions were specified, assume some defaults - if len(md.Extensions) == 0 { - md.Extensions[".md"] = struct{}{} - md.Extensions[".markdown"] = struct{}{} - md.Extensions[".mdown"] = struct{}{} - } - - // Make a list of index files to match extensions - for ext := range md.Extensions { - md.IndexFiles = append(md.IndexFiles, "index"+ext) - } - mdconfigs = append(mdconfigs, md) - } - - return mdconfigs, nil -} - -func loadParams(c *caddy.Controller, mdc *Config) error { - cfg := httpserver.GetConfig(c) - - switch c.Val() { - case "ext": - for _, ext := range c.RemainingArgs() { - mdc.Extensions[ext] = struct{}{} - } - return nil - case "css": - if !c.NextArg() { - return c.ArgErr() - } - mdc.Styles = append(mdc.Styles, c.Val()) - return nil - case "js": - if !c.NextArg() { - return c.ArgErr() - } - mdc.Scripts = append(mdc.Scripts, c.Val()) - return nil - case "template": - tArgs := c.RemainingArgs() - switch len(tArgs) { - default: - return c.ArgErr() - case 1: - fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[0])) - - if err := SetTemplate(mdc.Template, "", fpath); err != nil { - return c.Errf("default template parse error: %v", err) - } - - mdc.TemplateFiles[""] = &cachedFileInfo{ - path: fpath, - } - return nil - case 2: - fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[1])) - - if err := SetTemplate(mdc.Template, tArgs[0], fpath); err != nil { - return c.Errf("template parse error: %v", err) - } - - mdc.TemplateFiles[tArgs[0]] = &cachedFileInfo{ - path: fpath, - } - return nil - } - case "templatedir": - if !c.NextArg() { - return c.ArgErr() - } - - pattern := c.Val() - _, err := mdc.Template.ParseGlob(pattern) - if err != nil { - return c.Errf("template load error: %v", err) - } - if c.NextArg() { - return c.ArgErr() - } - - paths, err := filepath.Glob(pattern) - if err != nil { - return c.Errf("glob %q failed: %v", pattern, err) - } - for _, path := range paths { - mdc.TemplateFiles[filepath.Base(path)] = &cachedFileInfo{ - path: path, - } - } - return nil - default: - return c.Err("Expected valid markdown configuration property") - } -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/render.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/render.go deleted file mode 100644 index 95e375105..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/render.go +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 summary - -import ( - "bytes" - - "github.com/russross/blackfriday" -) - -// Ensure we implement the Blackfriday Markdown Renderer interface -var _ blackfriday.Renderer = (*renderer)(nil) - -// renderer renders Markdown to plain-text meant for listings and excerpts, -// and implements the blackfriday.Renderer interface. -// -// Many of the methods are stubs with no output to prevent output of HTML markup. -type renderer struct{} - -// Blocklevel callbacks - -// BlockCode is the code tag callback. -func (r renderer) BlockCode(out *bytes.Buffer, text []byte, land string) {} - -// BlockQuote is the quote tag callback. -func (r renderer) BlockQuote(out *bytes.Buffer, text []byte) {} - -// BlockHtml is the HTML tag callback. -func (r renderer) BlockHtml(out *bytes.Buffer, text []byte) {} - -// Header is the header tag callback. -func (r renderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {} - -// HRule is the horizontal rule tag callback. -func (r renderer) HRule(out *bytes.Buffer) {} - -// List is the list tag callback. -func (r renderer) List(out *bytes.Buffer, text func() bool, flags int) { - // TODO: This is not desired (we'd rather not write lists as part of summary), - // but see this issue: https://github.com/russross/blackfriday/issues/189 - marker := out.Len() - if !text() { - out.Truncate(marker) - } - out.Write([]byte{' '}) -} - -// ListItem is the list item tag callback. -func (r renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {} - -// Paragraph is the paragraph tag callback. This renders simple paragraph text -// into plain text, such that summaries can be easily generated. -func (r renderer) Paragraph(out *bytes.Buffer, text func() bool) { - marker := out.Len() - if !text() { - out.Truncate(marker) - } - out.Write([]byte{' '}) -} - -// Table is the table tag callback. -func (r renderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {} - -// TableRow is the table row tag callback. -func (r renderer) TableRow(out *bytes.Buffer, text []byte) {} - -// TableHeaderCell is the table header cell tag callback. -func (r renderer) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {} - -// TableCell is the table cell tag callback. -func (r renderer) TableCell(out *bytes.Buffer, text []byte, flags int) {} - -// Footnotes is the foot notes tag callback. -func (r renderer) Footnotes(out *bytes.Buffer, text func() bool) {} - -// FootnoteItem is the footnote item tag callback. -func (r renderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {} - -// TitleBlock is the title tag callback. -func (r renderer) TitleBlock(out *bytes.Buffer, text []byte) {} - -// Spanlevel callbacks - -// AutoLink is the autolink tag callback. -func (r renderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {} - -// CodeSpan is the code span tag callback. Outputs a simple Markdown version -// of the code span. -func (r renderer) CodeSpan(out *bytes.Buffer, text []byte) { - out.Write([]byte("`")) - out.Write(text) - out.Write([]byte("`")) -} - -// DoubleEmphasis is the double emphasis tag callback. Outputs a simple -// plain-text version of the input. -func (r renderer) DoubleEmphasis(out *bytes.Buffer, text []byte) { - out.Write(text) -} - -// Emphasis is the emphasis tag callback. Outputs a simple plain-text -// version of the input. -func (r renderer) Emphasis(out *bytes.Buffer, text []byte) { - out.Write(text) -} - -// Image is the image tag callback. -func (r renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {} - -// LineBreak is the line break tag callback. -func (r renderer) LineBreak(out *bytes.Buffer) {} - -// Link is the link tag callback. Outputs a simple plain-text version -// of the input. -func (r renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { - out.Write(content) -} - -// RawHtmlTag is the raw HTML tag callback. -func (r renderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {} - -// TripleEmphasis is the triple emphasis tag callback. Outputs a simple plain-text -// version of the input. -func (r renderer) TripleEmphasis(out *bytes.Buffer, text []byte) { - out.Write(text) -} - -// StrikeThrough is the strikethrough tag callback. -func (r renderer) StrikeThrough(out *bytes.Buffer, text []byte) {} - -// FootnoteRef is the footnote ref tag callback. -func (r renderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {} - -// Lowlevel callbacks - -// Entity callback. Outputs a simple plain-text version of the input. -func (r renderer) Entity(out *bytes.Buffer, entity []byte) { - out.Write(entity) -} - -// NormalText callback. Outputs a simple plain-text version of the input. -func (r renderer) NormalText(out *bytes.Buffer, text []byte) { - out.Write(text) -} - -// Header and footer - -// DocumentHeader callback. -func (r renderer) DocumentHeader(out *bytes.Buffer) {} - -// DocumentFooter callback. -func (r renderer) DocumentFooter(out *bytes.Buffer) {} - -// GetFlags returns zero. -func (r renderer) GetFlags() int { return 0 } diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary.go deleted file mode 100644 index 18d1db3b0..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/summary/summary.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 summary - -import ( - "bytes" - - "github.com/russross/blackfriday" -) - -// Markdown formats input using a plain-text renderer, and -// then returns up to the first `wordcount` words as a summary. -func Markdown(input []byte, wordcount int) []byte { - words := bytes.Fields(blackfriday.Markdown(input, renderer{}, 0)) - if wordcount > len(words) { - wordcount = len(words) - } - return bytes.Join(words[0:wordcount], []byte{' '}) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/markdown/template.go b/vendor/github.com/mholt/caddy/caddyhttp/markdown/template.go deleted file mode 100644 index ff8ad9b96..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/markdown/template.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 markdown - -import ( - "bytes" - "io/ioutil" - "os" - "sync" - "text/template" - - "github.com/mholt/caddy/caddyhttp/httpserver" - "github.com/mholt/caddy/caddyhttp/markdown/metadata" -) - -// Data represents a markdown document. -type Data struct { - httpserver.Context - Doc map[string]interface{} - Styles []string - Scripts []string - Meta map[string]string - Files []FileInfo -} - -// Include "overrides" the embedded httpserver.Context's Include() -// method so that included files have access to d's fields. -// Note: using {{template 'template-name' .}} instead might be better. -func (d Data) Include(filename string, args ...interface{}) (string, error) { - d.Args = args - return httpserver.ContextInclude(filename, d, d.Root) -} - -var templateUpdateMu sync.RWMutex - -// execTemplate executes a template given a requestPath, template, and metadata -func execTemplate(c *Config, mdata metadata.Metadata, meta map[string]string, files []FileInfo, ctx httpserver.Context) ([]byte, error) { - mdData := Data{ - Context: ctx, - Doc: mdata.Variables, - Styles: c.Styles, - Scripts: c.Scripts, - Meta: meta, - Files: files, - } - templateName := mdata.Template - - updateTemplate := func() error { - templateUpdateMu.Lock() - defer templateUpdateMu.Unlock() - - templateFile, ok := c.TemplateFiles[templateName] - if !ok { - return nil - } - - currentFileInfo, err := os.Lstat(templateFile.path) - if err != nil { - return err - } - - if !fileChanged(currentFileInfo, templateFile.fi) { - return nil - } - - // update template due to file changes - err = SetTemplate(c.Template, templateName, templateFile.path) - if err != nil { - return err - } - - templateFile.fi = currentFileInfo - return nil - } - - if err := updateTemplate(); err != nil { - return nil, err - } - - b := new(bytes.Buffer) - templateUpdateMu.RLock() - defer templateUpdateMu.RUnlock() - if err := c.Template.ExecuteTemplate(b, templateName, mdData); err != nil { - return nil, err - } - - return b.Bytes(), nil -} - -func fileChanged(new, old os.FileInfo) bool { - // never checked before - if old == nil { - return true - } - - if new.Size() != old.Size() || - new.Mode() != old.Mode() || - new.ModTime() != old.ModTime() { - return true - } - - return false -} - -// SetTemplate reads in the template with the filename provided. If the file does not exist or is not parsable, it will return an error. -func SetTemplate(t *template.Template, name, filename string) error { - - // Read template - buf, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - - // Update if exists - if tt := t.Lookup(name); tt != nil { - _, err = tt.Parse(string(buf)) - return err - } - - // Allocate new name if not - _, err = t.New(name).Parse(string(buf)) - return err -} - -// GetDefaultTemplate returns the default template. -func GetDefaultTemplate() *template.Template { - return template.Must(template.New("").Parse(defaultTemplate)) -} - -const ( - defaultTemplate = ` - - - {{.Doc.title}} - - {{range $key, $val := .Meta}} - - {{end}} - {{- range .Styles}} - - {{- end}} - {{- range .Scripts}} - - {{- end}} - - - {{.Doc.body}} - -` -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/mime/mime.go b/vendor/github.com/mholt/caddy/caddyhttp/mime/mime.go deleted file mode 100644 index 740a0ab05..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/mime/mime.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 mime - -import ( - "net/http" - "path" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Config represent a mime config. Map from extension to mime-type. -// Note, this should be safe with concurrent read access, as this is -// not modified concurrently. -type Config map[string]string - -// Mime sets Content-Type header of requests based on configurations. -type Mime struct { - Next httpserver.Handler - Configs Config -} - -// ServeHTTP implements the httpserver.Handler interface. -func (e Mime) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // Get a clean /-path, grab the extension - ext := path.Ext(path.Clean(r.URL.Path)) - - if contentType, ok := e.Configs[ext]; ok { - w.Header().Set("Content-Type", contentType) - } - - return e.Next.ServeHTTP(w, r) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/mime/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/mime/setup.go deleted file mode 100644 index 7c9aee5f1..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/mime/setup.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 mime - -import ( - "fmt" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("mime", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new mime middleware instance. -func setup(c *caddy.Controller) error { - configs, err := mimeParse(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Mime{Next: next, Configs: configs} - }) - - return nil -} - -func mimeParse(c *caddy.Controller) (Config, error) { - configs := Config{} - - for c.Next() { - // At least one extension is required - - args := c.RemainingArgs() - switch len(args) { - case 2: - if err := validateExt(configs, args[0]); err != nil { - return configs, err - } - configs[args[0]] = args[1] - case 1: - return configs, c.ArgErr() - case 0: - for c.NextBlock() { - ext := c.Val() - if err := validateExt(configs, ext); err != nil { - return configs, err - } - if !c.NextArg() { - return configs, c.ArgErr() - } - configs[ext] = c.Val() - } - } - - } - - return configs, nil -} - -// validateExt checks for valid file name extension. -func validateExt(configs Config, ext string) error { - if !strings.HasPrefix(ext, ".") { - return fmt.Errorf(`mime: invalid extension "%v" (must start with dot)`, ext) - } - if _, ok := configs[ext]; ok { - return fmt.Errorf(`mime: duplicate extension "%v" found`, ext) - } - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/pprof/pprof.go b/vendor/github.com/mholt/caddy/caddyhttp/pprof/pprof.go deleted file mode 100644 index 36938bae4..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/pprof/pprof.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 pprof - -import ( - "net/http" - pp "net/http/pprof" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// BasePath is the base path to match for all pprof requests. -const BasePath = "/debug/pprof" - -// Handler is a simple struct whose ServeHTTP will delegate pprof -// endpoints to their equivalent net/http/pprof handlers. -type Handler struct { - Next httpserver.Handler - Mux *http.ServeMux -} - -// ServeHTTP handles requests to BasePath with pprof, or passes -// all other requests up the chain. -func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if httpserver.Path(r.URL.Path).Matches(BasePath) { - h.Mux.ServeHTTP(w, r) - return 0, nil - } - return h.Next.ServeHTTP(w, r) -} - -// NewMux returns a new http.ServeMux that routes pprof requests. -// It pretty much copies what the std lib pprof does on init: -// https://golang.org/src/net/http/pprof/pprof.go#L67 -func NewMux() *http.ServeMux { - mux := http.NewServeMux() - mux.HandleFunc(BasePath+"/", func(w http.ResponseWriter, r *http.Request) { - // this endpoint, as implemented in the standard library, doesn't set - // its Content-Type header, so using this can confuse clients, especially - // if gzipping... - w.Header().Set("Content-Type", "text/html; charset=utf-8") - pp.Index(w, r) - }) - mux.HandleFunc(BasePath+"/cmdline", pp.Cmdline) - mux.HandleFunc(BasePath+"/profile", pp.Profile) - mux.HandleFunc(BasePath+"/symbol", pp.Symbol) - mux.HandleFunc(BasePath+"/trace", pp.Trace) - return mux -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/pprof/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/pprof/setup.go deleted file mode 100644 index c829020e7..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/pprof/setup.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 pprof - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("pprof", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup returns a new instance of a pprof handler. It accepts no arguments or options. -func setup(c *caddy.Controller) error { - found := false - - for c.Next() { - if found { - return c.Err("pprof can only be specified once") - } - if len(c.RemainingArgs()) != 0 { - return c.ArgErr() - } - if c.NextBlock() { - return c.ArgErr() - } - found = true - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return &Handler{Next: next, Mux: NewMux()} - }) - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/body.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/body.go deleted file mode 100644 index 45ab6a906..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/body.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 proxy - -import ( - "bytes" - "io" - "io/ioutil" -) - -type bufferedBody struct { - *bytes.Reader -} - -func (*bufferedBody) Close() error { - return nil -} - -// rewind allows bufferedBody to be read again. -func (b *bufferedBody) rewind() error { - if b == nil { - return nil - } - _, err := b.Seek(0, io.SeekStart) - return err -} - -// newBufferedBody returns *bufferedBody to use in place of src. Closes src -// and returns Read error on src. All content from src is buffered. -func newBufferedBody(src io.ReadCloser) (*bufferedBody, error) { - if src == nil { - return nil, nil - } - b, err := ioutil.ReadAll(src) - src.Close() - if err != nil { - return nil, err - } - return &bufferedBody{ - Reader: bytes.NewReader(b), - }, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/policy.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/policy.go deleted file mode 100644 index 0f8fab44f..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/policy.go +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 proxy - -import ( - "hash/fnv" - "log" - "math" - "math/rand" - "net" - "net/http" - "sync" -) - -// HostPool is a collection of UpstreamHosts. -type HostPool []*UpstreamHost - -// Policy decides how a host will be selected from a pool. -type Policy interface { - Select(pool HostPool, r *http.Request) *UpstreamHost -} - -func init() { - RegisterPolicy("random", func(arg string) Policy { return &Random{} }) - RegisterPolicy("least_conn", func(arg string) Policy { return &LeastConn{} }) - RegisterPolicy("round_robin", func(arg string) Policy { return &RoundRobin{} }) - RegisterPolicy("ip_hash", func(arg string) Policy { return &IPHash{} }) - RegisterPolicy("first", func(arg string) Policy { return &First{} }) - RegisterPolicy("uri_hash", func(arg string) Policy { return &URIHash{} }) - RegisterPolicy("header", func(arg string) Policy { return &Header{arg} }) -} - -// Random is a policy that selects up hosts from a pool at random. -type Random struct{} - -// Select selects an up host at random from the specified pool. -func (r *Random) Select(pool HostPool, request *http.Request) *UpstreamHost { - - // Because the number of available hosts isn't known - // up front, the host is selected via reservoir sampling - // https://en.wikipedia.org/wiki/Reservoir_sampling - var randHost *UpstreamHost - count := 0 - for _, host := range pool { - if !host.Available() { - continue - } - - // (n % 1 == 0) holds for all n, therefore randHost - // will always get assigned a value if there is - // at least 1 available host - count++ - if (rand.Int() % count) == 0 { - randHost = host - } - } - return randHost -} - -// LeastConn is a policy that selects the host with the least connections. -type LeastConn struct{} - -// Select selects the up host with the least number of connections in the -// pool. If more than one host has the same least number of connections, -// one of the hosts is chosen at random. -func (r *LeastConn) Select(pool HostPool, request *http.Request) *UpstreamHost { - var bestHost *UpstreamHost - count := 0 - leastConn := int64(math.MaxInt64) - for _, host := range pool { - if !host.Available() { - continue - } - - if host.Conns < leastConn { - leastConn = host.Conns - count = 0 - } - - // Among hosts with same least connections, perform a reservoir - // sample: https://en.wikipedia.org/wiki/Reservoir_sampling - if host.Conns == leastConn { - count++ - if (rand.Int() % count) == 0 { - bestHost = host - } - } - } - return bestHost -} - -// RoundRobin is a policy that selects hosts based on round-robin ordering. -type RoundRobin struct { - robin uint32 - mutex sync.Mutex -} - -// Select selects an up host from the pool using a round-robin ordering scheme. -func (r *RoundRobin) Select(pool HostPool, request *http.Request) *UpstreamHost { - poolLen := uint32(len(pool)) - r.mutex.Lock() - defer r.mutex.Unlock() - // Return next available host - for i := uint32(0); i < poolLen; i++ { - r.robin++ - host := pool[r.robin%poolLen] - if host.Available() { - return host - } - } - return nil -} - -// hostByHashing returns an available host from pool based on a hashable string -func hostByHashing(pool HostPool, s string) *UpstreamHost { - poolLen := uint32(len(pool)) - index := hash(s) % poolLen - for i := uint32(0); i < poolLen; i++ { - index += i - host := pool[index%poolLen] - if host.Available() { - return host - } - } - return nil -} - -// hash calculates a hash based on string s -func hash(s string) uint32 { - h := fnv.New32a() - if _, err := h.Write([]byte(s)); err != nil { - log.Println("[ERROR] failed to write bytes: ", err) - } - return h.Sum32() -} - -// IPHash is a policy that selects hosts based on hashing the request IP -type IPHash struct{} - -// Select selects an up host from the pool based on hashing the request IP -func (r *IPHash) Select(pool HostPool, request *http.Request) *UpstreamHost { - clientIP, _, err := net.SplitHostPort(request.RemoteAddr) - if err != nil { - clientIP = request.RemoteAddr - } - return hostByHashing(pool, clientIP) -} - -// URIHash is a policy that selects the host based on hashing the request URI -type URIHash struct{} - -// Select selects the host based on hashing the URI -func (r *URIHash) Select(pool HostPool, request *http.Request) *UpstreamHost { - return hostByHashing(pool, request.RequestURI) -} - -// First is a policy that selects the first available host -type First struct{} - -// Select selects the first available host from the pool -func (r *First) Select(pool HostPool, request *http.Request) *UpstreamHost { - for _, host := range pool { - if host.Available() { - return host - } - } - return nil -} - -// Header is a policy that selects based on a hash of the given header -type Header struct { - // The name of the request header, the value of which will determine - // how the request is routed - Name string -} - -var roundRobinPolicier RoundRobin - -// Select selects the host based on hashing the header value -func (r *Header) Select(pool HostPool, request *http.Request) *UpstreamHost { - if r.Name == "" { - return nil - } - val := request.Header.Get(r.Name) - if val == "" { - // fallback to RoundRobin policy in case no Header in request - return roundRobinPolicier.Select(pool, request) - } - return hostByHashing(pool, val) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/proxy.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/proxy.go deleted file mode 100644 index fe10ea614..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/proxy.go +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 proxy is middleware that proxies HTTP requests. -package proxy - -import ( - "context" - "errors" - "net" - "net/http" - "net/url" - "strings" - "sync/atomic" - "time" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Proxy represents a middleware instance that can proxy requests. -type Proxy struct { - Next httpserver.Handler - Upstreams []Upstream -} - -// Upstream manages a pool of proxy upstream hosts. -type Upstream interface { - // The path this upstream host should be routed on - From() string - - // Selects an upstream host to be routed to. It - // should return a suitable upstream host, or nil - // if no such hosts are available. - Select(*http.Request) *UpstreamHost - - // Checks if subpath is not an ignored path - AllowedPath(string) bool - - // Gets the duration of the headstart the first - // connection is given in the Go standard library's - // implementation of "Happy Eyeballs" when DualStack - // is enabled in net.Dialer. - GetFallbackDelay() time.Duration - - // Gets how long to try selecting upstream hosts - // in the case of cascading failures. - GetTryDuration() time.Duration - - // Gets how long to wait between selecting upstream - // hosts in the case of cascading failures. - GetTryInterval() time.Duration - - // Gets the number of upstream hosts. - GetHostCount() int - - // Gets how long to wait before timing out - // the request - GetTimeout() time.Duration - - // Stops the upstream from proxying requests to shutdown goroutines cleanly. - Stop() error -} - -// UpstreamHostDownFunc can be used to customize how Down behaves. -type UpstreamHostDownFunc func(*UpstreamHost) bool - -// UpstreamHost represents a single proxy upstream -type UpstreamHost struct { - // This field is read & written to concurrently, so all access must use - // atomic operations. - Conns int64 // must be first field to be 64-bit aligned on 32-bit systems - MaxConns int64 - Name string // hostname of this upstream host - UpstreamHeaders http.Header - DownstreamHeaders http.Header - FailTimeout time.Duration - CheckDown UpstreamHostDownFunc - WithoutPathPrefix string - ReverseProxy *ReverseProxy - Fails int32 - // This is an int32 so that we can use atomic operations to do concurrent - // reads & writes to this value. The default value of 0 indicates that it - // is healthy and any non-zero value indicates unhealthy. - Unhealthy int32 - HealthCheckResult atomic.Value - UpstreamHeaderReplacements headerReplacements - DownstreamHeaderReplacements headerReplacements -} - -// Down checks whether the upstream host is down or not. -// Down will try to use uh.CheckDown first, and will fall -// back to some default criteria if necessary. -func (uh *UpstreamHost) Down() bool { - if uh.CheckDown == nil { - // Default settings - return atomic.LoadInt32(&uh.Unhealthy) != 0 || atomic.LoadInt32(&uh.Fails) > 0 - } - return uh.CheckDown(uh) -} - -// Full checks whether the upstream host has reached its maximum connections -func (uh *UpstreamHost) Full() bool { - return uh.MaxConns > 0 && atomic.LoadInt64(&uh.Conns) >= uh.MaxConns -} - -// Available checks whether the upstream host is available for proxying to -func (uh *UpstreamHost) Available() bool { - return !uh.Down() && !uh.Full() -} - -// ServeHTTP satisfies the httpserver.Handler interface. -func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // start by selecting most specific matching upstream config - upstream := p.match(r) - if upstream == nil { - return p.Next.ServeHTTP(w, r) - } - - // this replacer is used to fill in header field values - replacer := httpserver.NewReplacer(r, nil, "") - - // outreq is the request that makes a roundtrip to the backend - outreq, cancel := createUpstreamRequest(w, r) - defer cancel() - - // If we have more than one upstream host defined and if retrying is enabled - // by setting try_duration to a non-zero value, caddy will try to - // retry the request at a different host if the first one failed. - // - // This requires us to possibly rewind and replay the request body though, - // which in turn requires us to buffer the request body first. - // - // An unbuffered request is usually preferrable, because it reduces latency - // as well as memory usage. Furthermore it enables different kinds of - // HTTP streaming applications like gRPC for instance. - requiresBuffering := upstream.GetHostCount() > 1 && upstream.GetTryDuration() != 0 - - if requiresBuffering { - body, err := newBufferedBody(outreq.Body) - if err != nil { - return http.StatusBadRequest, errors.New("failed to read downstream request body") - } - if body != nil { - outreq.Body = body - } - } - - // The keepRetrying function will return true if we should - // loop and try to select another host, or false if we - // should break and stop retrying. - start := time.Now() - keepRetrying := func(backendErr error) bool { - // if downstream has canceled the request, break - if backendErr == context.Canceled { - return false - } - // if we've tried long enough, break - if time.Since(start) >= upstream.GetTryDuration() { - return false - } - // otherwise, wait and try the next available host - time.Sleep(upstream.GetTryInterval()) - return true - } - - var backendErr error - for { - // since Select() should give us "up" hosts, keep retrying - // hosts until timeout (or until we get a nil host). - host := upstream.Select(r) - if host == nil { - if backendErr == nil { - backendErr = errors.New("no hosts available upstream") - } - if !keepRetrying(backendErr) { - break - } - continue - } - if rr, ok := w.(*httpserver.ResponseRecorder); ok && rr.Replacer != nil { - rr.Replacer.Set("upstream", host.Name) - } - - proxy := host.ReverseProxy - - // a backend's name may contain more than just the host, - // so we parse it as a URL to try to isolate the host. - if nameURL, err := url.Parse(host.Name); err == nil { - outreq.Host = nameURL.Host - if proxy == nil { - proxy = NewSingleHostReverseProxy(nameURL, - host.WithoutPathPrefix, - http.DefaultMaxIdleConnsPerHost, - upstream.GetTimeout(), - upstream.GetFallbackDelay(), - ) - } - - // use upstream credentials by default - if outreq.Header.Get("Authorization") == "" && nameURL.User != nil { - pwd, _ := nameURL.User.Password() - outreq.SetBasicAuth(nameURL.User.Username(), pwd) - } - } else { - outreq.Host = host.Name - } - if proxy == nil { - return http.StatusInternalServerError, errors.New("proxy for host '" + host.Name + "' is nil") - } - - // set headers for request going upstream - if host.UpstreamHeaders != nil { - // modify headers for request that will be sent to the upstream host - mutateHeadersByRules(outreq.Header, host.UpstreamHeaders, replacer, host.UpstreamHeaderReplacements) - if hostHeaders, ok := outreq.Header["Host"]; ok && len(hostHeaders) > 0 { - outreq.Host = hostHeaders[len(hostHeaders)-1] - } - } - - // prepare a function that will update response - // headers coming back downstream - var downHeaderUpdateFn respUpdateFn - if host.DownstreamHeaders != nil { - downHeaderUpdateFn = createRespHeaderUpdateFn(host.DownstreamHeaders, replacer, host.DownstreamHeaderReplacements) - } - - // Before we retry the request we have to make sure - // that the body is rewound to it's beginning. - if bb, ok := outreq.Body.(*bufferedBody); ok { - if err := bb.rewind(); err != nil { - return http.StatusInternalServerError, errors.New("unable to rewind downstream request body") - } - } - - // tell the proxy to serve the request - // - // NOTE: - // The call to proxy.ServeHTTP can theoretically panic. - // To prevent host.Conns from getting out-of-sync we thus have to - // make sure that it's _always_ correctly decremented afterwards. - func() { - atomic.AddInt64(&host.Conns, 1) - defer atomic.AddInt64(&host.Conns, -1) - backendErr = proxy.ServeHTTP(w, outreq, downHeaderUpdateFn) - }() - - // if no errors, we're done here - if backendErr == nil { - return 0, nil - } - - if backendErr == httpserver.ErrMaxBytesExceeded { - return http.StatusRequestEntityTooLarge, backendErr - } - - if backendErr == context.Canceled { - return CustomStatusContextCancelled, backendErr - } - - // failover; remember this failure for some time if - // request failure counting is enabled - timeout := host.FailTimeout - if timeout > 0 { - atomic.AddInt32(&host.Fails, 1) - go func(host *UpstreamHost, timeout time.Duration) { - time.Sleep(timeout) - atomic.AddInt32(&host.Fails, -1) - }(host, timeout) - } - - // if we've tried long enough, break - if !keepRetrying(backendErr) { - break - } - } - - return http.StatusBadGateway, backendErr -} - -// match finds the best match for a proxy config based on r. -func (p Proxy) match(r *http.Request) Upstream { - var u Upstream - var longestMatch int - for _, upstream := range p.Upstreams { - basePath := upstream.From() - if !httpserver.Path(r.URL.Path).Matches(basePath) || !upstream.AllowedPath(r.URL.Path) { - continue - } - if len(basePath) > longestMatch { - longestMatch = len(basePath) - u = upstream - } - } - return u -} - -// createUpstreamRequest shallow-copies r into a new request -// that can be sent upstream. -// -// Derived from reverseproxy.go in the standard Go httputil package. -func createUpstreamRequest(rw http.ResponseWriter, r *http.Request) (*http.Request, context.CancelFunc) { - // Original incoming server request may be canceled by the - // user or by std lib(e.g. too many idle connections). - ctx, cancel := context.WithCancel(r.Context()) - if cn, ok := rw.(http.CloseNotifier); ok { - notifyChan := cn.CloseNotify() - go func() { - select { - case <-notifyChan: - cancel() - case <-ctx.Done(): - } - }() - } - - outreq := r.WithContext(ctx) // includes shallow copies of maps, but okay - - // We should set body to nil explicitly if request body is empty. - // For server requests the Request Body is always non-nil. - if r.ContentLength == 0 { - outreq.Body = nil - } - - // We are modifying the same underlying map from req (shallow - // copied above) so we only copy it if necessary. - copiedHeaders := false - - // Remove hop-by-hop headers listed in the "Connection" header. - // See RFC 2616, section 14.10. - if c := outreq.Header.Get("Connection"); c != "" { - for _, f := range strings.Split(c, ",") { - if f = strings.TrimSpace(f); f != "" { - if !copiedHeaders { - outreq.Header = make(http.Header) - copyHeader(outreq.Header, r.Header) - copiedHeaders = true - } - outreq.Header.Del(f) - } - } - } - - // Remove hop-by-hop headers to the backend. Especially - // important is "Connection" because we want a persistent - // connection, regardless of what the client sent to us. - for _, h := range hopHeaders { - if outreq.Header.Get(h) != "" { - if !copiedHeaders { - outreq.Header = make(http.Header) - copyHeader(outreq.Header, r.Header) - copiedHeaders = true - } - outreq.Header.Del(h) - } - } - - if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { - // If we aren't the first proxy, retain prior - // X-Forwarded-For information as a comma+space - // separated list and fold multiple headers into one. - if prior, ok := outreq.Header["X-Forwarded-For"]; ok { - clientIP = strings.Join(prior, ", ") + ", " + clientIP - } - outreq.Header.Set("X-Forwarded-For", clientIP) - } - - return outreq, cancel -} - -func createRespHeaderUpdateFn(rules http.Header, replacer httpserver.Replacer, replacements headerReplacements) respUpdateFn { - return func(resp *http.Response) { - mutateHeadersByRules(resp.Header, rules, replacer, replacements) - } -} - -func mutateHeadersByRules(headers, rules http.Header, repl httpserver.Replacer, replacements headerReplacements) { - for ruleField, ruleValues := range rules { - if strings.HasPrefix(ruleField, "+") { - for _, ruleValue := range ruleValues { - replacement := repl.Replace(ruleValue) - if len(replacement) > 0 { - headers.Add(strings.TrimPrefix(ruleField, "+"), replacement) - } - } - } else if strings.HasPrefix(ruleField, "-") { - headers.Del(strings.TrimPrefix(ruleField, "-")) - } else if len(ruleValues) > 0 { - replacement := repl.Replace(ruleValues[len(ruleValues)-1]) - if len(replacement) > 0 { - headers.Set(ruleField, replacement) - } - } - } - - for ruleField, ruleValues := range replacements { - for _, ruleValue := range ruleValues { - // Replace variables in replacement string - replacement := repl.Replace(ruleValue.to) - original := headers.Get(ruleField) - if len(replacement) > 0 && len(original) > 0 { - // Replace matches in original string with replacement string - replaced := ruleValue.regexp.ReplaceAllString(original, replacement) - headers.Set(ruleField, replaced) - } - } - } -} - -const CustomStatusContextCancelled = 499 diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/reverseproxy.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/reverseproxy.go deleted file mode 100644 index c20be0c1f..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/reverseproxy.go +++ /dev/null @@ -1,776 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file is adapted from code in the net/http/httputil -// package of the Go standard library, which is by the -// Go Authors, and bears this copyright and license info: -// -// 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 has been modified from the standard lib to -// meet the needs of the application. - -package proxy - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "io" - "log" - "net" - "net/http" - "net/url" - "strings" - "sync" - "time" - - "golang.org/x/net/http2" - - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/h2quic" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -var ( - defaultDialer = &net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - DualStack: true, - } - - bufferPool = sync.Pool{New: createBuffer} - - defaultCryptoHandshakeTimeout = 10 * time.Second -) - -func createBuffer() interface{} { - return make([]byte, 0, 32*1024) -} - -func pooledIoCopy(dst io.Writer, src io.Reader) { - buf := bufferPool.Get().([]byte) - defer bufferPool.Put(buf) - - // CopyBuffer only uses buf up to its length and panics if it's 0. - // Due to that we extend buf's length to its capacity here and - // ensure it's always non-zero. - bufCap := cap(buf) - if _, err := io.CopyBuffer(dst, src, buf[0:bufCap:bufCap]); err != nil { - log.Println("[ERROR] failed to copy buffer: ", err) - } -} - -// onExitFlushLoop is a callback set by tests to detect the state of the -// flushLoop() goroutine. -var onExitFlushLoop func() - -// ReverseProxy is an HTTP Handler that takes an incoming request and -// sends it to another server, proxying the response back to the -// client. -type ReverseProxy struct { - // Director must be a function which modifies - // the request into a new request to be sent - // using Transport. Its response is then copied - // back to the original client unmodified. - Director func(*http.Request) - - // The transport used to perform proxy requests. - Transport http.RoundTripper - - // FlushInterval specifies the flush interval - // to flush to the client while copying the - // response body. - // If zero, no periodic flushing is done. - FlushInterval time.Duration - - // dialer is used when values from the - // defaultDialer need to be overridden per Proxy - dialer *net.Dialer - - srvResolver srvResolver -} - -// Though the relevant directive prefix is just "unix:", url.Parse -// will - assuming the regular URL scheme - add additional slashes -// as if "unix" was a request protocol. -// What we need is just the path, so if "unix:/var/run/www.socket" -// was the proxy directive, the parsed hostName would be -// "unix:///var/run/www.socket", hence the ambiguous trimming. -func socketDial(hostName string, timeout time.Duration) func(network, addr string) (conn net.Conn, err error) { - return func(network, addr string) (conn net.Conn, err error) { - return net.DialTimeout("unix", hostName[len("unix://"):], timeout) - } -} - -func (rp *ReverseProxy) srvDialerFunc(locator string, timeout time.Duration) func(network, addr string) (conn net.Conn, err error) { - service := locator - if strings.HasPrefix(locator, "srv://") { - service = locator[6:] - } else if strings.HasPrefix(locator, "srv+https://") { - service = locator[12:] - } - - return func(network, addr string) (conn net.Conn, err error) { - _, addrs, err := rp.srvResolver.LookupSRV(context.Background(), "", "", service) - if err != nil { - return nil, err - } - return net.DialTimeout("tcp", fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port), timeout) - } -} - -func singleJoiningSlash(a, b string) string { - aSlash := strings.HasSuffix(a, "/") - bSlash := strings.HasPrefix(b, "/") - switch { - case aSlash && bSlash: - return a + b[1:] - case !aSlash && !bSlash && b != "": - return a + "/" + b - } - return a + b -} - -// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites -// URLs to the scheme, host, and base path provided in target. If the -// target's path is "/base" and the incoming request was for "/dir", -// the target request will be for /base/dir. -// Without logic: target's path is "/", incoming is "/api/messages", -// without is "/api", then the target request will be for /messages. -func NewSingleHostReverseProxy(target *url.URL, without string, keepalive int, timeout, fallbackDelay time.Duration) *ReverseProxy { - targetQuery := target.RawQuery - director := func(req *http.Request) { - if target.Scheme == "unix" { - // to make Dial work with unix URL, - // scheme and host have to be faked - req.URL.Scheme = "http" - req.URL.Host = "socket" - } else if target.Scheme == "srv" { - req.URL.Scheme = "http" - req.URL.Host = target.Host - } else if target.Scheme == "srv+https" { - req.URL.Scheme = "https" - req.URL.Host = target.Host - } else { - req.URL.Scheme = target.Scheme - req.URL.Host = target.Host - } - - // remove the `without` prefix - if without != "" { - req.URL.Path = strings.TrimPrefix(req.URL.Path, without) - if req.URL.Opaque != "" { - req.URL.Opaque = strings.TrimPrefix(req.URL.Opaque, without) - } - if req.URL.RawPath != "" { - req.URL.RawPath = strings.TrimPrefix(req.URL.RawPath, without) - } - } - - // prefer returns val if it isn't empty, otherwise def - prefer := func(val, def string) string { - if val != "" { - return val - } - return def - } - - // Make up the final URL by concatenating the request and target URL. - // - // If there is encoded part in request or target URL, - // the final URL should also be in encoded format. - // Here, we concatenate their encoded parts which are stored - // in URL.Opaque and URL.RawPath, if it is empty use - // URL.Path instead. - if req.URL.Opaque != "" || target.Opaque != "" { - req.URL.Opaque = singleJoiningSlash( - prefer(target.Opaque, target.Path), - prefer(req.URL.Opaque, req.URL.Path)) - } - if req.URL.RawPath != "" || target.RawPath != "" { - req.URL.RawPath = singleJoiningSlash( - prefer(target.RawPath, target.Path), - prefer(req.URL.RawPath, req.URL.Path)) - } - req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path) - - // Trims the path of the socket from the URL path. - // This is done because req.URL passed to your proxied service - // will have the full path of the socket file prefixed to it. - // Calling /test on a server that proxies requests to - // unix:/var/run/www.socket will thus set the requested path - // to /var/run/www.socket/test, rendering paths useless. - if target.Scheme == "unix" { - // See comment on socketDial for the trim - socketPrefix := target.String()[len("unix://"):] - req.URL.Path = strings.TrimPrefix(req.URL.Path, socketPrefix) - if req.URL.Opaque != "" { - req.URL.Opaque = strings.TrimPrefix(req.URL.Opaque, socketPrefix) - } - if req.URL.RawPath != "" { - req.URL.RawPath = strings.TrimPrefix(req.URL.RawPath, socketPrefix) - } - } - - if targetQuery == "" || req.URL.RawQuery == "" { - req.URL.RawQuery = targetQuery + req.URL.RawQuery - } else { - req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery - } - } - - dialer := *defaultDialer - if timeout != defaultDialer.Timeout { - dialer.Timeout = timeout - } - if fallbackDelay != defaultDialer.FallbackDelay { - dialer.FallbackDelay = fallbackDelay - } - - rp := &ReverseProxy{ - Director: director, - FlushInterval: 250 * time.Millisecond, // flushing good for streaming & server-sent events - srvResolver: net.DefaultResolver, - dialer: &dialer, - } - - if target.Scheme == "unix" { - rp.Transport = &http.Transport{ - Dial: socketDial(target.String(), timeout), - } - } else if target.Scheme == "quic" { - rp.Transport = &h2quic.RoundTripper{ - QuicConfig: &quic.Config{ - HandshakeTimeout: defaultCryptoHandshakeTimeout, - KeepAlive: true, - }, - } - } else if keepalive != http.DefaultMaxIdleConnsPerHost || strings.HasPrefix(target.Scheme, "srv") { - dialFunc := rp.dialer.Dial - if strings.HasPrefix(target.Scheme, "srv") { - dialFunc = rp.srvDialerFunc(target.String(), timeout) - } - - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: dialFunc, - TLSHandshakeTimeout: defaultCryptoHandshakeTimeout, - ExpectContinueTimeout: 1 * time.Second, - } - if keepalive == 0 { - transport.DisableKeepAlives = true - } else { - transport.MaxIdleConnsPerHost = keepalive - } - if httpserver.HTTP2 { - if err := http2.ConfigureTransport(transport); err != nil { - log.Println("[ERROR] failed to configure transport to use HTTP/2: ", err) - } - } - rp.Transport = transport - } else { - transport := &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: rp.dialer.Dial, - } - if httpserver.HTTP2 { - if err := http2.ConfigureTransport(transport); err != nil { - log.Println("[ERROR] failed to configure transport to use HTTP/2: ", err) - } - } - rp.Transport = transport - } - return rp -} - -// UseInsecureTransport is used to facilitate HTTPS proxying -// when it is OK for upstream to be using a bad certificate, -// since this transport skips verification. -func (rp *ReverseProxy) UseInsecureTransport() { - if transport, ok := rp.Transport.(*http.Transport); ok { - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{} - } - transport.TLSClientConfig.InsecureSkipVerify = true - // No http2.ConfigureTransport() here. - // For now this is only added in places where - // an http.Transport is actually created. - } else if transport, ok := rp.Transport.(*h2quic.RoundTripper); ok { - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{} - } - transport.TLSClientConfig.InsecureSkipVerify = true - } -} - -// UseOwnCertificate is used to facilitate HTTPS proxying -// with locally provided certificate. -func (rp *ReverseProxy) UseOwnCACertificates(CaCertPool *x509.CertPool) { - if transport, ok := rp.Transport.(*http.Transport); ok { - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{} - } - transport.TLSClientConfig.RootCAs = CaCertPool - // No http2.ConfigureTransport() here. - // For now this is only added in places where - // an http.Transport is actually created. - } else if transport, ok := rp.Transport.(*h2quic.RoundTripper); ok { - if transport.TLSClientConfig == nil { - transport.TLSClientConfig = &tls.Config{} - } - transport.TLSClientConfig.RootCAs = CaCertPool - } -} - -// ServeHTTP serves the proxied request to the upstream by performing a roundtrip. -// It is designed to handle websocket connection upgrades as well. -func (rp *ReverseProxy) ServeHTTP(rw http.ResponseWriter, outreq *http.Request, respUpdateFn respUpdateFn) error { - transport := rp.Transport - if requestIsWebsocket(outreq) { - transport = newConnHijackerTransport(transport) - } - - rp.Director(outreq) - - if outreq.URL.Scheme == "quic" { - outreq.URL.Scheme = "https" // Change scheme back to https for QUIC RoundTripper - } - - res, err := transport.RoundTrip(outreq) - if err != nil { - return err - } - - isWebsocket := res.StatusCode == http.StatusSwitchingProtocols && strings.EqualFold(res.Header.Get("Upgrade"), "websocket") - - // Remove hop-by-hop headers listed in the - // "Connection" header of the response. - if c := res.Header.Get("Connection"); c != "" { - for _, f := range strings.Split(c, ",") { - if f = strings.TrimSpace(f); f != "" { - res.Header.Del(f) - } - } - } - - for _, h := range hopHeaders { - res.Header.Del(h) - } - - if respUpdateFn != nil { - respUpdateFn(res) - } - - if isWebsocket { - defer res.Body.Close() - hj, ok := rw.(http.Hijacker) - if !ok { - panic(httpserver.NonHijackerError{Underlying: rw}) - } - - conn, brw, err := hj.Hijack() - if err != nil { - return err - } - defer conn.Close() - - var backendConn net.Conn - if hj, ok := transport.(*connHijackerTransport); ok { - backendConn = hj.Conn - if _, err := conn.Write(hj.Replay); err != nil { - return err - } - bufferPool.Put(hj.Replay) - } else { - backendConn, err = net.DialTimeout("tcp", outreq.URL.Host, rp.dialer.Timeout) - if err != nil { - return err - } - if err := outreq.Write(backendConn); err != nil { - log.Println("[ERROR] failed to write: ", err) - } - } - defer backendConn.Close() - - proxyDone := make(chan struct{}, 2) - - // Proxy backend -> frontend. - go func() { - pooledIoCopy(conn, backendConn) - proxyDone <- struct{}{} - }() - - // Proxy frontend -> backend. - // - // NOTE: Hijack() sometimes returns buffered up bytes in brw which - // would be lost if we didn't read them out manually below. - if brw != nil { - if n := brw.Reader.Buffered(); n > 0 { - rbuf, err := brw.Reader.Peek(n) - if err != nil { - return err - } - if _, err := backendConn.Write(rbuf); err != nil { - log.Println("[ERROR] failed to write data to connection: ", err) - } - } - } - go func() { - pooledIoCopy(backendConn, conn) - proxyDone <- struct{}{} - }() - - // If one side is done, we are done. - <-proxyDone - } else { - // NOTE: - // Closing the Body involves acquiring a mutex, which is a - // unnecessarily heavy operation, considering that this defer will - // pretty much never be executed with the Body still unclosed. - bodyOpen := true - closeBody := func() { - if bodyOpen { - _ = res.Body.Close() - bodyOpen = false - } - } - defer closeBody() - - // Copy all headers over. - // res.Header does not include the "Trailer" header, - // which means we will have to do that manually below. - copyHeader(rw.Header(), res.Header) - - // The "Trailer" header isn't included in res' Header map, which - // is why we have to build one ourselves from res.Trailer. - // - // But res.Trailer does not necessarily contain all trailer keys at this - // point yet. The HTTP spec allows one to send "unannounced trailers" - // after a request and certain systems like gRPC make use of that. - announcedTrailerKeyCount := len(res.Trailer) - if announcedTrailerKeyCount > 0 { - vv := make([]string, 0, announcedTrailerKeyCount) - for k := range res.Trailer { - vv = append(vv, k) - } - rw.Header()["Trailer"] = vv - } - - // Now copy over the status code as well as the response body. - rw.WriteHeader(res.StatusCode) - if announcedTrailerKeyCount > 0 { - // Force chunking if we saw a response trailer. - // This prevents net/http from calculating the length - // for short bodies and adding a Content-Length. - if fl, ok := rw.(http.Flusher); ok { - fl.Flush() - } - } - rp.copyResponse(rw, res.Body) - - // Now close the body to fully populate res.Trailer. - closeBody() - - // Since Go does not remove keys from res.Trailer we - // can safely do a length comparison to check whether - // we received further, unannounced trailers. - // - // Most of the time forceSetTrailers should be false. - forceSetTrailers := len(res.Trailer) != announcedTrailerKeyCount - shallowCopyTrailers(rw.Header(), res.Trailer, forceSetTrailers) - } - - return nil -} - -func (rp *ReverseProxy) copyResponse(dst io.Writer, src io.Reader) { - if rp.FlushInterval != 0 { - if wf, ok := dst.(writeFlusher); ok { - mlw := &maxLatencyWriter{ - dst: wf, - latency: rp.FlushInterval, - done: make(chan bool), - } - go mlw.flushLoop() - defer mlw.stop() - dst = mlw - } - } - pooledIoCopy(dst, src) -} - -// skip these headers if they already exist. -// see https://github.com/mholt/caddy/pull/1112#discussion_r80092582 -var skipHeaders = map[string]struct{}{ - "Content-Type": {}, - "Content-Disposition": {}, - "Accept-Ranges": {}, - "Set-Cookie": {}, - "Cache-Control": {}, - "Expires": {}, -} - -func copyHeader(dst, src http.Header) { - for k, vv := range src { - if _, ok := dst[k]; ok { - // skip some predefined headers - // see https://github.com/mholt/caddy/issues/1086 - if _, shouldSkip := skipHeaders[k]; shouldSkip { - continue - } - // otherwise, overwrite to avoid duplicated fields that can be - // problematic (see issue #1086) -- however, allow duplicate - // Server fields so we can see the reality of the proxying. - if k != "Server" { - dst.Del(k) - } - } - for _, v := range vv { - dst.Add(k, v) - } - } -} - -// shallowCopyTrailers copies all headers from srcTrailer to dstHeader. -// -// If forceSetTrailers is set to true, the http.TrailerPrefix will be added to -// all srcTrailer key names. Otherwise the Go stdlib will ignore all keys -// which weren't listed in the Trailer map before submitting the Response. -// -// WARNING: Only a shallow copy will be created! -func shallowCopyTrailers(dstHeader, srcTrailer http.Header, forceSetTrailers bool) { - for k, vv := range srcTrailer { - if forceSetTrailers { - k = http.TrailerPrefix + k - } - dstHeader[k] = vv - } -} - -// Hop-by-hop headers. These are removed when sent to the backend. -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html -var hopHeaders = []string{ - "Alt-Svc", - "Alternate-Protocol", - "Connection", - "Keep-Alive", - "Proxy-Authenticate", - "Proxy-Authorization", - "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google - "Te", // canonicalized version of "TE" - "Trailer", // not Trailers per URL above; http://www.rfc-editor.org/errata_search.php?eid=4522 - "Transfer-Encoding", - "Upgrade", -} - -type respUpdateFn func(resp *http.Response) - -type hijackedConn struct { - net.Conn - hj *connHijackerTransport -} - -func (c *hijackedConn) Read(b []byte) (n int, err error) { - n, err = c.Conn.Read(b) - c.hj.Replay = append(c.hj.Replay, b[:n]...) - return -} - -func (c *hijackedConn) Close() error { - return nil -} - -type connHijackerTransport struct { - *http.Transport - Conn net.Conn - Replay []byte -} - -func newConnHijackerTransport(base http.RoundTripper) *connHijackerTransport { - t := &http.Transport{ - MaxIdleConnsPerHost: -1, - } - if b, _ := base.(*http.Transport); b != nil { - tlsClientConfig := b.TLSClientConfig - if tlsClientConfig != nil && tlsClientConfig.NextProtos != nil { - tlsClientConfig = tlsClientConfig.Clone() - tlsClientConfig.NextProtos = nil - } - - t.Proxy = b.Proxy - t.TLSClientConfig = tlsClientConfig - t.TLSHandshakeTimeout = b.TLSHandshakeTimeout - t.Dial = b.Dial - t.DialTLS = b.DialTLS - } else { - t.Proxy = http.ProxyFromEnvironment - t.TLSHandshakeTimeout = 10 * time.Second - } - hj := &connHijackerTransport{t, nil, bufferPool.Get().([]byte)[:0]} - - dial := getTransportDial(t) - dialTLS := getTransportDialTLS(t) - t.Dial = func(network, addr string) (net.Conn, error) { - c, err := dial(network, addr) - hj.Conn = c - return &hijackedConn{c, hj}, err - } - t.DialTLS = func(network, addr string) (net.Conn, error) { - c, err := dialTLS(network, addr) - hj.Conn = c - return &hijackedConn{c, hj}, err - } - - return hj -} - -// getTransportDial always returns a plain Dialer -// and defaults to the existing t.Dial. -func getTransportDial(t *http.Transport) func(network, addr string) (net.Conn, error) { - if t.Dial != nil { - return t.Dial - } - return defaultDialer.Dial -} - -// getTransportDial always returns a TLS Dialer -// and defaults to the existing t.DialTLS. -func getTransportDialTLS(t *http.Transport) func(network, addr string) (net.Conn, error) { - if t.DialTLS != nil { - return t.DialTLS - } - - // newConnHijackerTransport will modify t.Dial after calling this method - // => Create a backup reference. - plainDial := getTransportDial(t) - - // The following DialTLS implementation stems from the Go stdlib and - // is identical to what happens if DialTLS is not provided. - // Source: https://github.com/golang/go/blob/230a376b5a67f0e9341e1fa47e670ff762213c83/src/net/http/transport.go#L1018-L1051 - return func(network, addr string) (net.Conn, error) { - plainConn, err := plainDial(network, addr) - if err != nil { - return nil, err - } - - tlsClientConfig := t.TLSClientConfig - if tlsClientConfig == nil { - tlsClientConfig = &tls.Config{} - } - if !tlsClientConfig.InsecureSkipVerify && tlsClientConfig.ServerName == "" { - tlsClientConfig.ServerName = stripPort(addr) - } - - tlsConn := tls.Client(plainConn, tlsClientConfig) - errc := make(chan error, 2) - var timer *time.Timer - if d := t.TLSHandshakeTimeout; d != 0 { - timer = time.AfterFunc(d, func() { - errc <- tlsHandshakeTimeoutError{} - }) - } - go func() { - err := tlsConn.Handshake() - if timer != nil { - timer.Stop() - } - errc <- err - }() - if err := <-errc; err != nil { - _ = plainConn.Close() - return nil, err - } - if !tlsClientConfig.InsecureSkipVerify { - hostname := tlsClientConfig.ServerName - if hostname == "" { - hostname = stripPort(addr) - } - if err := tlsConn.VerifyHostname(hostname); err != nil { - _ = plainConn.Close() - return nil, err - } - } - - return tlsConn, nil - } -} - -// stripPort returns address without its port if it has one and -// works with IP addresses as well as hostnames formatted as host:port. -// -// IPv6 addresses (excluding the port) must be enclosed in -// square brackets similar to the requirements of Go's stdlib. -func stripPort(address string) string { - // Keep in mind that the address might be a IPv6 address - // and thus contain a colon, but not have a port. - portIdx := strings.LastIndex(address, ":") - ipv6Idx := strings.LastIndex(address, "]") - if portIdx > ipv6Idx { - address = address[:portIdx] - } - return address -} - -type tlsHandshakeTimeoutError struct{} - -func (tlsHandshakeTimeoutError) Timeout() bool { return true } -func (tlsHandshakeTimeoutError) Temporary() bool { return true } -func (tlsHandshakeTimeoutError) Error() string { return "net/http: TLS handshake timeout" } - -func requestIsWebsocket(req *http.Request) bool { - return strings.EqualFold(req.Header.Get("Upgrade"), "websocket") && strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") -} - -type writeFlusher interface { - io.Writer - http.Flusher -} - -type maxLatencyWriter struct { - dst writeFlusher - latency time.Duration - - lk sync.Mutex // protects Write + Flush - done chan bool -} - -func (m *maxLatencyWriter) Write(p []byte) (int, error) { - m.lk.Lock() - defer m.lk.Unlock() - return m.dst.Write(p) -} - -func (m *maxLatencyWriter) flushLoop() { - t := time.NewTicker(m.latency) - defer t.Stop() - for { - select { - case <-m.done: - if onExitFlushLoop != nil { - onExitFlushLoop() - } - return - case <-t.C: - m.lk.Lock() - m.dst.Flush() - m.lk.Unlock() - } - } -} - -func (m *maxLatencyWriter) stop() { m.done <- true } diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/setup.go deleted file mode 100644 index 8db25b660..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/setup.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 proxy - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("proxy", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Proxy middleware instance. -func setup(c *caddy.Controller) error { - upstreams, err := NewStaticUpstreams(c.Dispenser, httpserver.GetConfig(c).Host()) - if err != nil { - return err - } - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Proxy{Next: next, Upstreams: upstreams} - }) - - // Register shutdown handlers. - for _, upstream := range upstreams { - c.OnShutdown(upstream.Stop) - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/proxy/upstream.go b/vendor/github.com/mholt/caddy/caddyhttp/proxy/upstream.go deleted file mode 100644 index 882a0aeb0..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/proxy/upstream.go +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 proxy - -import ( - "bytes" - "context" - "crypto/x509" - "fmt" - "io" - "io/ioutil" - "log" - "net" - "net/http" - "net/textproto" - "net/url" - "path" - "regexp" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "crypto/tls" - - "github.com/mholt/caddy/caddyfile" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -var ( - supportedPolicies = make(map[string]func(string) Policy) -) - -type staticUpstream struct { - from string - upstreamHeaders http.Header - downstreamHeaders http.Header - stop chan struct{} // Signals running goroutines to stop. - wg sync.WaitGroup // Used to wait for running goroutines to stop. - Hosts HostPool - Policy Policy - KeepAlive int - FallbackDelay time.Duration - Timeout time.Duration - FailTimeout time.Duration - TryDuration time.Duration - TryInterval time.Duration - MaxConns int64 - HealthCheck struct { - Client http.Client - Path string - Interval time.Duration - Timeout time.Duration - Host string - Port string - ContentString string - } - WithoutPathPrefix string - IgnoredSubPaths []string - insecureSkipVerify bool - MaxFails int32 - resolver srvResolver - CaCertPool *x509.CertPool - upstreamHeaderReplacements headerReplacements - downstreamHeaderReplacements headerReplacements -} - -type srvResolver interface { - LookupSRV(context.Context, string, string, string) (string, []*net.SRV, error) -} - -// headerReplacement stores a compiled regex matcher and a string replacer, for replacement rules -type headerReplacement struct { - regexp *regexp.Regexp - to string -} - -// headerReplacements stores a mapping of canonical MIME header to headerReplacement -// Implements a subset of http.Header functions, to allow convenient addition and deletion of rules -type headerReplacements map[string][]headerReplacement - -func (h headerReplacements) Add(key string, value headerReplacement) { - key = textproto.CanonicalMIMEHeaderKey(key) - h[key] = append(h[key], value) -} - -func (h headerReplacements) Del(key string) { - delete(h, textproto.CanonicalMIMEHeaderKey(key)) -} - -// NewStaticUpstreams parses the configuration input and sets up -// static upstreams for the proxy middleware. The host string parameter, -// if not empty, is used for setting the upstream Host header for the -// health checks if the upstream header config requires it. -func NewStaticUpstreams(c caddyfile.Dispenser, host string) ([]Upstream, error) { - var upstreams []Upstream - for c.Next() { - - upstream := &staticUpstream{ - from: "", - stop: make(chan struct{}), - upstreamHeaders: make(http.Header), - downstreamHeaders: make(http.Header), - Hosts: nil, - Policy: &Random{}, - MaxFails: 1, - TryInterval: 250 * time.Millisecond, - MaxConns: 0, - KeepAlive: http.DefaultMaxIdleConnsPerHost, - Timeout: 30 * time.Second, - resolver: net.DefaultResolver, - upstreamHeaderReplacements: make(headerReplacements), - downstreamHeaderReplacements: make(headerReplacements), - } - - if !c.Args(&upstream.from) { - return upstreams, c.ArgErr() - } - - var to []string - hasSrv := false - - for _, t := range c.RemainingArgs() { - if len(to) > 0 && hasSrv { - return upstreams, c.Err("only one upstream is supported when using SRV locator") - } - - if strings.HasPrefix(t, "srv://") || strings.HasPrefix(t, "srv+https://") { - if len(to) > 0 { - return upstreams, c.Err("service locator upstreams can not be mixed with host names") - } - - hasSrv = true - } - - parsed, err := parseUpstream(t) - if err != nil { - return upstreams, err - } - to = append(to, parsed...) - } - - for c.NextBlock() { - switch c.Val() { - case "upstream": - if !c.NextArg() { - return upstreams, c.ArgErr() - } - - if hasSrv { - return upstreams, c.Err("upstream directive is not supported when backend is service locator") - } - - parsed, err := parseUpstream(c.Val()) - if err != nil { - return upstreams, err - } - to = append(to, parsed...) - default: - if err := parseBlock(&c, upstream, hasSrv); err != nil { - return upstreams, err - } - } - } - - if len(to) == 0 { - return upstreams, c.ArgErr() - } - - upstream.Hosts = make([]*UpstreamHost, len(to)) - for i, host := range to { - uh, err := upstream.NewHost(host) - if err != nil { - return upstreams, err - } - upstream.Hosts[i] = uh - } - - if upstream.HealthCheck.Path != "" { - upstream.HealthCheck.Client = http.Client{ - Timeout: upstream.HealthCheck.Timeout, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: upstream.insecureSkipVerify}, - }, - } - - // set up health check upstream host if we have one - if host != "" { - hostHeader := upstream.upstreamHeaders.Get("Host") - if strings.Contains(hostHeader, "{host}") { - upstream.HealthCheck.Host = strings.Replace(hostHeader, "{host}", host, -1) - } - } - upstream.wg.Add(1) - go func() { - defer upstream.wg.Done() - upstream.HealthCheckWorker(upstream.stop) - }() - } - upstreams = append(upstreams, upstream) - } - return upstreams, nil -} - -func (u *staticUpstream) From() string { - return u.from -} - -func (u *staticUpstream) NewHost(host string) (*UpstreamHost, error) { - if !strings.HasPrefix(host, "http") && - !strings.HasPrefix(host, "unix:") && - !strings.HasPrefix(host, "quic:") && - !strings.HasPrefix(host, "srv://") && - !strings.HasPrefix(host, "srv+https://") { - host = "http://" + host - } - uh := &UpstreamHost{ - Name: host, - Conns: 0, - Fails: 0, - FailTimeout: u.FailTimeout, - Unhealthy: 0, - UpstreamHeaders: u.upstreamHeaders, - DownstreamHeaders: u.downstreamHeaders, - CheckDown: func(u *staticUpstream) UpstreamHostDownFunc { - return func(uh *UpstreamHost) bool { - if atomic.LoadInt32(&uh.Unhealthy) != 0 { - return true - } - if atomic.LoadInt32(&uh.Fails) >= u.MaxFails { - return true - } - return false - } - }(u), - WithoutPathPrefix: u.WithoutPathPrefix, - MaxConns: u.MaxConns, - HealthCheckResult: atomic.Value{}, - UpstreamHeaderReplacements: u.upstreamHeaderReplacements, - DownstreamHeaderReplacements: u.downstreamHeaderReplacements, - } - - baseURL, err := url.Parse(uh.Name) - if err != nil { - return nil, err - } - - uh.ReverseProxy = NewSingleHostReverseProxy(baseURL, uh.WithoutPathPrefix, u.KeepAlive, u.Timeout, u.FallbackDelay) - if u.insecureSkipVerify { - uh.ReverseProxy.UseInsecureTransport() - } - - if u.CaCertPool != nil { - uh.ReverseProxy.UseOwnCACertificates(u.CaCertPool) - } - - return uh, nil -} - -func parseUpstream(u string) ([]string, error) { - if strings.HasPrefix(u, "unix:") { - return []string{u}, nil - } - - isSrv := strings.HasPrefix(u, "srv://") || strings.HasPrefix(u, "srv+https://") - colonIdx := strings.LastIndex(u, ":") - protoIdx := strings.Index(u, "://") - - if colonIdx == -1 || colonIdx == protoIdx { - return []string{u}, nil - } - - if isSrv { - return nil, fmt.Errorf("service locator %s can not have port specified", u) - } - - us := u[:colonIdx] - ue := "" - portsEnd := len(u) - if nextSlash := strings.Index(u[colonIdx:], "/"); nextSlash != -1 { - portsEnd = colonIdx + nextSlash - ue = u[portsEnd:] - } - - ports := u[len(us)+1 : portsEnd] - separators := strings.Count(ports, "-") - - if separators == 0 { - return []string{u}, nil - } - - if separators > 1 { - return nil, fmt.Errorf("port range [%s] has %d separators", ports, separators) - } - - portsStr := strings.Split(ports, "-") - pIni, err := strconv.Atoi(portsStr[0]) - if err != nil { - return nil, err - } - - pEnd, err := strconv.Atoi(portsStr[1]) - if err != nil { - return nil, err - } - - if pEnd <= pIni { - return nil, fmt.Errorf("port range [%s] is invalid", ports) - } - - hosts := []string{} - for p := pIni; p <= pEnd; p++ { - hosts = append(hosts, fmt.Sprintf("%s:%d%s", us, p, ue)) - } - - return hosts, nil -} - -func parseBlock(c *caddyfile.Dispenser, u *staticUpstream, hasSrv bool) error { - var isUpstream bool - - switch c.Val() { - case "policy": - if !c.NextArg() { - return c.ArgErr() - } - policyCreateFunc, ok := supportedPolicies[c.Val()] - if !ok { - return c.ArgErr() - } - arg := "" - if c.NextArg() { - arg = c.Val() - } - u.Policy = policyCreateFunc(arg) - case "fallback_delay": - if !c.NextArg() { - return c.ArgErr() - } - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.FallbackDelay = dur - case "fail_timeout": - if !c.NextArg() { - return c.ArgErr() - } - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.FailTimeout = dur - case "max_fails": - if !c.NextArg() { - return c.ArgErr() - } - n, err := strconv.Atoi(c.Val()) - if err != nil { - return err - } - if n < 1 { - return c.Err("max_fails must be at least 1") - } - u.MaxFails = int32(n) - case "try_duration": - if !c.NextArg() { - return c.ArgErr() - } - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.TryDuration = dur - case "try_interval": - if !c.NextArg() { - return c.ArgErr() - } - interval, err := time.ParseDuration(c.Val()) - if err != nil { - return err - } - u.TryInterval = interval - case "max_conns": - if !c.NextArg() { - return c.ArgErr() - } - n, err := strconv.ParseInt(c.Val(), 10, 64) - if err != nil { - return err - } - u.MaxConns = n - case "health_check": - if !c.NextArg() { - return c.ArgErr() - } - u.HealthCheck.Path = c.Val() - - // Set defaults - if u.HealthCheck.Interval == 0 { - u.HealthCheck.Interval = 30 * time.Second - } - if u.HealthCheck.Timeout == 0 { - u.HealthCheck.Timeout = 60 * time.Second - } - case "health_check_interval": - var interval string - if !c.Args(&interval) { - return c.ArgErr() - } - dur, err := time.ParseDuration(interval) - if err != nil { - return err - } - u.HealthCheck.Interval = dur - case "health_check_timeout": - var interval string - if !c.Args(&interval) { - return c.ArgErr() - } - dur, err := time.ParseDuration(interval) - if err != nil { - return err - } - u.HealthCheck.Timeout = dur - case "health_check_port": - if !c.NextArg() { - return c.ArgErr() - } - - if hasSrv { - return c.Err("health_check_port directive is not allowed when upstream is SRV locator") - } - - port := c.Val() - n, err := strconv.Atoi(port) - if err != nil { - return err - } - - if n < 0 { - return c.Errf("invalid health_check_port '%s'", port) - } - u.HealthCheck.Port = port - case "health_check_contains": - if !c.NextArg() { - return c.ArgErr() - } - u.HealthCheck.ContentString = c.Val() - case "header_upstream": - isUpstream = true - fallthrough - case "header_downstream": - var header, value, replaced string - if c.Args(&header, &value, &replaced) { - // Don't allow - or + in replacements - if strings.HasPrefix(header, "-") || strings.HasPrefix(header, "+") { - return c.ArgErr() - } - r, err := regexp.Compile(value) - if err != nil { - return err - } - if isUpstream { - u.upstreamHeaderReplacements.Add(header, headerReplacement{r, replaced}) - } else { - u.downstreamHeaderReplacements.Add(header, headerReplacement{r, replaced}) - } - } else { - if len(value) == 0 { - // When removing a header, the value can be optional. - if !strings.HasPrefix(header, "-") { - return c.ArgErr() - } - } - if isUpstream { - u.upstreamHeaders.Add(header, value) - } else { - u.downstreamHeaders.Add(header, value) - } - } - case "transparent": - // Note: X-Forwarded-For header is always being appended for proxy connections - // See implementation of createUpstreamRequest in proxy.go - u.upstreamHeaders.Add("Host", "{host}") - u.upstreamHeaders.Add("X-Real-IP", "{remote}") - u.upstreamHeaders.Add("X-Forwarded-Proto", "{scheme}") - u.upstreamHeaders.Add("X-Forwarded-Port", "{server_port}") - case "websocket": - u.upstreamHeaders.Add("Connection", "{>Connection}") - u.upstreamHeaders.Add("Upgrade", "{>Upgrade}") - case "without": - if !c.NextArg() { - return c.ArgErr() - } - u.WithoutPathPrefix = c.Val() - case "except": - ignoredPaths := c.RemainingArgs() - if len(ignoredPaths) == 0 { - return c.ArgErr() - } - u.IgnoredSubPaths = ignoredPaths - case "insecure_skip_verify": - u.insecureSkipVerify = true - case "ca_certificates": - caCertificates := c.RemainingArgs() - if len(caCertificates) == 0 { - return c.ArgErr() - } - - pool := x509.NewCertPool() - caCertificatesAdded := make(map[string]struct{}) - for _, caFile := range caCertificates { - // don't add cert to pool more than once - if _, ok := caCertificatesAdded[caFile]; ok { - continue - } - caCertificatesAdded[caFile] = struct{}{} - - // any client with a certificate from this CA will be allowed to connect - caCrt, err := ioutil.ReadFile(caFile) - if err != nil { - return c.Err(err.Error()) - } - - // attempt to parse pem and append to cert pool - if ok := pool.AppendCertsFromPEM(caCrt); !ok { - return c.Errf("loading CA certificate '%s': no certificates were successfully parsed", caFile) - } - } - - u.CaCertPool = pool - case "keepalive": - if !c.NextArg() { - return c.ArgErr() - } - n, err := strconv.Atoi(c.Val()) - if err != nil { - return err - } - if n < 0 { - return c.ArgErr() - } - u.KeepAlive = n - case "timeout": - if !c.NextArg() { - return c.ArgErr() - } - dur, err := time.ParseDuration(c.Val()) - if err != nil { - return c.Errf("unable to parse timeout duration '%s'", c.Val()) - } - u.Timeout = dur - default: - return c.Errf("unknown property '%s'", c.Val()) - } - - // these settings are at odds with one another. insecure_skip_verify disables security features over HTTPS - // which is what we are trying to achieve with ca_certificates - if u.insecureSkipVerify && u.CaCertPool != nil { - return c.Errf("both insecure_skip_verify and ca_certificates cannot be set in the proxy directive") - } - - return nil -} - -func (u *staticUpstream) resolveHost(h string) ([]string, bool, error) { - names := []string{} - proto := "http" - if !strings.HasPrefix(h, "srv://") && !strings.HasPrefix(h, "srv+https://") { - return []string{h}, false, nil - } - - if strings.HasPrefix(h, "srv+https://") { - proto = "https" - } - - _, addrs, err := u.resolver.LookupSRV(context.Background(), "", "", h) - if err != nil { - return names, true, err - } - - for _, addr := range addrs { - names = append(names, fmt.Sprintf("%s://%s:%d", proto, addr.Target, addr.Port)) - } - - return names, true, nil -} - -func (u *staticUpstream) healthCheck() { - for _, host := range u.Hosts { - candidates, isSrv, err := u.resolveHost(host.Name) - if err != nil { - host.HealthCheckResult.Store(err.Error()) - atomic.StoreInt32(&host.Unhealthy, 1) - continue - } - - unhealthyCount := 0 - for _, addr := range candidates { - hostURL := addr - if !isSrv && u.HealthCheck.Port != "" { - hostURL = replacePort(hostURL, u.HealthCheck.Port) - } - hostURL += u.HealthCheck.Path - - unhealthy := func() bool { - // set up request, needed to be able to modify headers - // possible errors are bad HTTP methods or un-parsable urls - req, err := http.NewRequest("GET", hostURL, nil) - if err != nil { - return true - } - // set host for request going upstream - if u.HealthCheck.Host != "" { - req.Host = u.HealthCheck.Host - } - r, err := u.HealthCheck.Client.Do(req) - if err != nil { - return true - } - defer func() { - if _, err := io.Copy(ioutil.Discard, r.Body); err != nil { - log.Println("[ERROR] failed to copy: ", err) - } - _ = r.Body.Close() - }() - if r.StatusCode < 200 || r.StatusCode >= 400 { - return true - } - if u.HealthCheck.ContentString == "" { // don't check for content string - return false - } - // TODO ReadAll will be replaced if deemed necessary - // See https://github.com/mholt/caddy/pull/1691 - buf, err := ioutil.ReadAll(r.Body) - if err != nil { - return true - } - if bytes.Contains(buf, []byte(u.HealthCheck.ContentString)) { - return false - } - return true - }() - - if unhealthy { - unhealthyCount++ - } - } - - if unhealthyCount == len(candidates) { - atomic.StoreInt32(&host.Unhealthy, 1) - host.HealthCheckResult.Store("Failed") - } else { - atomic.StoreInt32(&host.Unhealthy, 0) - host.HealthCheckResult.Store("OK") - } - } -} - -func (u *staticUpstream) HealthCheckWorker(stop chan struct{}) { - ticker := time.NewTicker(u.HealthCheck.Interval) - u.healthCheck() - for { - select { - case <-ticker.C: - u.healthCheck() - case <-stop: - ticker.Stop() - return - } - } -} - -func (u *staticUpstream) Select(r *http.Request) *UpstreamHost { - pool := u.Hosts - if len(pool) == 1 { - if !pool[0].Available() { - return nil - } - return pool[0] - } - allUnavailable := true - for _, host := range pool { - if host.Available() { - allUnavailable = false - break - } - } - if allUnavailable { - return nil - } - if u.Policy == nil { - return (&Random{}).Select(pool, r) - } - return u.Policy.Select(pool, r) -} - -func (u *staticUpstream) AllowedPath(requestPath string) bool { - for _, ignoredSubPath := range u.IgnoredSubPaths { - p := path.Clean(requestPath) - e := path.Join(u.From(), ignoredSubPath) - // Re-add a trailing slashes if the original - // paths had one and the cleaned paths don't - if strings.HasSuffix(requestPath, "/") && !strings.HasSuffix(p, "/") { - p = p + "/" - } - if strings.HasSuffix(ignoredSubPath, "/") && !strings.HasSuffix(e, "/") { - e = e + "/" - } - if httpserver.Path(p).Matches(e) { - return false - } - } - return true -} - -// GetFallbackDelay returns u.FallbackDelay. -func (u *staticUpstream) GetFallbackDelay() time.Duration { - return u.FallbackDelay -} - -// GetTryDuration returns u.TryDuration. -func (u *staticUpstream) GetTryDuration() time.Duration { - return u.TryDuration -} - -// GetTryInterval returns u.TryInterval. -func (u *staticUpstream) GetTryInterval() time.Duration { - return u.TryInterval -} - -// GetTimeout returns u.Timeout. -func (u *staticUpstream) GetTimeout() time.Duration { - return u.Timeout -} - -func (u *staticUpstream) GetHostCount() int { - return len(u.Hosts) -} - -// Stop sends a signal to all goroutines started by this staticUpstream to exit -// and waits for them to finish before returning. -func (u *staticUpstream) Stop() error { - close(u.stop) - u.wg.Wait() - return nil -} - -// RegisterPolicy adds a custom policy to the proxy. -func RegisterPolicy(name string, policy func(string) Policy) { - supportedPolicies[name] = policy -} - -func replacePort(originalURL string, newPort string) string { - parsedURL, err := url.Parse(originalURL) - if err != nil { - return originalURL - } - - // handles 'localhost' and 'localhost:8080' - parsedHost, _, err := net.SplitHostPort(parsedURL.Host) - if err != nil { - parsedHost = parsedURL.Host - } - - parsedURL.Host = net.JoinHostPort(parsedHost, newPort) - return parsedURL.String() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/push/handler.go b/vendor/github.com/mholt/caddy/caddyhttp/push/handler.go deleted file mode 100644 index 4a676c272..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/push/handler.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 push - -import ( - "net/http" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func (h Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - pusher, hasPusher := w.(http.Pusher) - - // no push possible, carry on - if !hasPusher { - return h.Next.ServeHTTP(w, r) - } - - // check if this is a request for the pushed resource (avoid recursion) - if _, exists := r.Header[pushHeader]; exists { - return h.Next.ServeHTTP(w, r) - } - - headers := h.filterProxiedHeaders(r.Header) - - // push first -outer: - for _, rule := range h.Rules { - urlPath := r.URL.Path - matches := httpserver.Path(urlPath).Matches(rule.Path) - // Also check IndexPages when requesting a directory - if !matches { - indexFile, isIndexFile := httpserver.IndexFile(h.Root, urlPath, h.indexPages) - if isIndexFile { - matches = httpserver.Path(indexFile).Matches(rule.Path) - } - } - if matches { - for _, resource := range rule.Resources { - pushErr := pusher.Push(resource.Path, &http.PushOptions{ - Method: resource.Method, - Header: h.mergeHeaders(headers, resource.Header), - }) - if pushErr != nil { - // if we cannot push (either not supported or concurrent streams are full - break) - break outer - } - } - } - } - - // serve later - code, err := h.Next.ServeHTTP(w, r) - - // push resources returned in Link headers from upstream middlewares or proxied apps - if links, exists := w.Header()["Link"]; exists { - h.servePreloadLinks(pusher, headers, links) - } - - return code, err -} - -// servePreloadLinks parses Link headers from backend and pushes resources found in them. -// For accepted header formats check parseLinkHeader function. -// -// If resource has 'nopush' attribute then it will be omitted. -func (h Middleware) servePreloadLinks(pusher http.Pusher, headers http.Header, resources []string) { -outer: - for _, resource := range resources { - for _, resource := range parseLinkHeader(resource) { - if _, exists := resource.params["nopush"]; exists { - continue - } - - if h.isRemoteResource(resource.uri) { - continue - } - - err := pusher.Push(resource.uri, &http.PushOptions{ - Method: http.MethodGet, - Header: headers, - }) - - if err != nil { - break outer - } - } - } -} - -func (h Middleware) isRemoteResource(resource string) bool { - return strings.HasPrefix(resource, "//") || - strings.HasPrefix(resource, "http://") || - strings.HasPrefix(resource, "https://") -} - -func (h Middleware) mergeHeaders(l, r http.Header) http.Header { - out := http.Header{} - - for k, v := range l { - out[k] = v - } - - for k, vv := range r { - for _, v := range vv { - out.Add(k, v) - } - } - - return out -} - -func (h Middleware) filterProxiedHeaders(headers http.Header) http.Header { - filter := http.Header{} - - for _, header := range proxiedHeaders { - if val, ok := headers[header]; ok { - filter[header] = val - } - } - - return filter -} - -var proxiedHeaders = []string{ - "Accept-Encoding", - "Accept-Language", - "Cache-Control", - "Host", - "User-Agent", -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/push/link_parser.go b/vendor/github.com/mholt/caddy/caddyhttp/push/link_parser.go deleted file mode 100644 index de7d078e3..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/push/link_parser.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 push - -import ( - "strings" -) - -const ( - commaSeparator = "," - semicolonSeparator = ";" - equalSeparator = "=" -) - -type linkResource struct { - uri string - params map[string]string -} - -// parseLinkHeader is responsible for parsing Link header and returning list of found resources. -// -// Accepted formats are: -// Link:
; as=script -// Link: ; as=script,; as=style -// Link: ; -func parseLinkHeader(header string) []linkResource { - resources := []linkResource{} - - if header == "" { - return resources - } - - for _, link := range strings.Split(header, commaSeparator) { - l := linkResource{params: make(map[string]string)} - - li, ri := strings.Index(link, "<"), strings.Index(link, ">") - - if li == -1 || ri == -1 { - continue - } - - l.uri = strings.TrimSpace(link[li+1 : ri]) - - for _, param := range strings.Split(strings.TrimSpace(link[ri+1:]), semicolonSeparator) { - parts := strings.SplitN(strings.TrimSpace(param), equalSeparator, 2) - key := strings.TrimSpace(parts[0]) - - if key == "" { - continue - } - - if len(parts) == 1 { - l.params[key] = key - } - - if len(parts) == 2 { - l.params[key] = strings.TrimSpace(parts[1]) - } - } - - resources = append(resources, l) - } - - return resources -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/push/push.go b/vendor/github.com/mholt/caddy/caddyhttp/push/push.go deleted file mode 100644 index 5dc75c113..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/push/push.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 push - -import ( - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -type ( - // Rule describes conditions on which resources will be pushed - Rule struct { - Path string - Resources []Resource - } - - // Resource describes resource to be pushed - Resource struct { - Path string - Method string - Header http.Header - } - - // Middleware supports pushing resources to clients - Middleware struct { - Next httpserver.Handler - Rules []Rule - Root http.FileSystem - indexPages []string // will be injected from SiteConfig on setup - } - - ruleOp func([]Resource) -) diff --git a/vendor/github.com/mholt/caddy/caddyhttp/push/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/push/setup.go deleted file mode 100644 index a5037df3f..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/push/setup.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 push - -import ( - "errors" - "fmt" - "net/http" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("push", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -var errInvalidHeader = errors.New("header directive requires [name] [value]") - -var errHeaderStartsWithColon = errors.New("header cannot start with colon") -var errMethodNotSupported = errors.New("push supports only GET and HEAD methods") - -const pushHeader = "X-Push" - -var emptyRules = []Rule{} - -// setup configures a new Push middleware -func setup(c *caddy.Controller) error { - rules, err := parsePushRules(c) - - if err != nil { - return err - } - - cfg := httpserver.GetConfig(c) - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Middleware{Next: next, Rules: rules, Root: http.Dir(cfg.Root), indexPages: cfg.IndexPages} - }) - - return nil -} - -func parsePushRules(c *caddy.Controller) ([]Rule, error) { - var rules = make(map[string]*Rule) - - for c.NextLine() { - var rule *Rule - var resources []Resource - var ops []ruleOp - - parseBlock := func() error { - for c.NextBlock() { - val := c.Val() - - switch val { - case "method": - if !c.NextArg() { - return c.ArgErr() - } - - method := c.Val() - - if err := validateMethod(method); err != nil { - return errMethodNotSupported - } - - ops = append(ops, setMethodOp(method)) - - case "header": - args := c.RemainingArgs() - - if len(args) != 2 { - return errInvalidHeader - } - - if err := validateHeader(args[0]); err != nil { - return err - } - - ops = append(ops, setHeaderOp(args[0], args[1])) - default: - resources = append(resources, Resource{ - Path: val, - Method: http.MethodGet, - Header: http.Header{pushHeader: []string{}}, - }) - } - } - return nil - } - - args := c.RemainingArgs() - - if len(args) == 0 { - rule = new(Rule) - rule.Path = "/" - rules["/"] = rule - err := parseBlock() - if err != nil { - return emptyRules, err - } - } else { - path := args[0] - - if existingRule, ok := rules[path]; ok { - rule = existingRule - } else { - rule = new(Rule) - rule.Path = path - rules[rule.Path] = rule - } - - for i := 1; i < len(args); i++ { - resources = append(resources, Resource{ - Path: args[i], - Method: http.MethodGet, - Header: http.Header{pushHeader: []string{}}, - }) - } - - err := parseBlock() - if err != nil { - return emptyRules, err - } - } - - for _, op := range ops { - op(resources) - } - rule.Resources = append(rule.Resources, resources...) - } - - var returnRules []Rule - for _, rule := range rules { - returnRules = append(returnRules, *rule) - } - - return returnRules, nil -} - -func setHeaderOp(key, value string) func(resources []Resource) { - return func(resources []Resource) { - for index := range resources { - resources[index].Header.Set(key, value) - } - } -} - -func setMethodOp(method string) func(resources []Resource) { - return func(resources []Resource) { - for index := range resources { - resources[index].Method = method - } - } -} - -func validateHeader(header string) error { - if strings.HasPrefix(header, ":") { - return errHeaderStartsWithColon - } - - switch strings.ToLower(header) { - case "content-length", "content-encoding", "trailer", "te", "expect", "host": - return fmt.Errorf("push headers cannot include %s", header) - } - - return nil -} - -// rules based on https://go-review.googlesource.com/#/c/29439/4/http2/go18.go#94 -func validateMethod(method string) error { - if method != http.MethodGet && method != http.MethodHead { - return errMethodNotSupported - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/redirect/redirect.go b/vendor/github.com/mholt/caddy/caddyhttp/redirect/redirect.go deleted file mode 100644 index 9ae102fe7..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/redirect/redirect.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 redirect is middleware for redirecting certain requests -// to other locations. -package redirect - -import ( - "fmt" - "html" - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Redirect is middleware to respond with HTTP redirects -type Redirect struct { - Next httpserver.Handler - Rules []Rule -} - -// ServeHTTP implements the httpserver.Handler interface. -func (rd Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - for _, rule := range rd.Rules { - if (rule.FromPath == "/" || r.URL.Path == rule.FromPath) && schemeMatches(rule, r) && rule.Match(r) { - to := httpserver.NewReplacer(r, nil, "").Replace(rule.To) - if rule.Meta { - safeTo := html.EscapeString(to) - fmt.Fprintf(w, metaRedir, safeTo, safeTo) - } else { - http.Redirect(w, r, to, rule.Code) - } - return 0, nil - } - } - return rd.Next.ServeHTTP(w, r) -} - -func schemeMatches(rule Rule, req *http.Request) bool { - return (rule.FromScheme() == "https" && req.TLS != nil) || - (rule.FromScheme() != "https" && req.TLS == nil) -} - -// Rule describes an HTTP redirect rule. -type Rule struct { - FromScheme func() string - FromPath, To string - Code int - Meta bool - httpserver.RequestMatcher -} - -// Script tag comes first since that will better imitate a redirect in the browser's -// history, but the meta tag is a fallback for most non-JS clients. -const metaRedir = ` - - - - - - Redirecting... - -` diff --git a/vendor/github.com/mholt/caddy/caddyhttp/redirect/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/redirect/setup.go deleted file mode 100644 index 7813fe24a..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/redirect/setup.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 redirect - -import ( - "net/http" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("redir", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Redirect middleware instance. -func setup(c *caddy.Controller) error { - rules, err := redirParse(c) - if err != nil { - return err - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Redirect{Next: next, Rules: rules} - }) - - return nil -} - -func redirParse(c *caddy.Controller) ([]Rule, error) { - var redirects []Rule - - cfg := httpserver.GetConfig(c) - - initRule := func(rule *Rule, defaultCode string, args []string) error { - rule.FromScheme = func() string { - if cfg.TLS.Enabled { - return "https" - } - return "http" - } - - var ( - from = "/" - to string - code = defaultCode - ) - switch len(args) { - case 1: - // To specified (catch-all redirect) - // Not sure why user is doing this in a table, as it causes all other redirects to be ignored. - // As such, this feature remains undocumented. - to = args[0] - case 2: - // From and To specified - from = args[0] - to = args[1] - case 3: - // From, To, and Code specified - from = args[0] - to = args[1] - code = args[2] - default: - return c.ArgErr() - } - - rule.FromPath = from - rule.To = to - if code == "meta" { - rule.Meta = true - code = defaultCode - } - if codeNumber, ok := httpRedirs[code]; ok { - rule.Code = codeNumber - } else { - return c.Errf("Invalid redirect code '%v'", code) - } - - return nil - } - - // checkAndSaveRule checks the rule for validity (except the redir code) - // and saves it if it's valid, or returns an error. - checkAndSaveRule := func(rule Rule) error { - if rule.FromPath == rule.To { - return c.Err("'from' and 'to' values of redirect rule cannot be the same") - } - - // prevent obvious duplicates (rules with if statements exempt) - if ifm, ok := rule.RequestMatcher.(httpserver.IfMatcher); !ok || !ifm.Enabled { - for _, otherRule := range redirects { - if otherRule.FromPath == rule.FromPath { - return c.Errf("rule with duplicate 'from' value: %s -> %s", otherRule.FromPath, otherRule.To) - } - } - } - - redirects = append(redirects, rule) - return nil - } - - const initDefaultCode = "301" - - for c.Next() { - args := c.RemainingArgs() - matcher, err := httpserver.SetupIfMatcher(c) - if err != nil { - return nil, err - } - - var hadOptionalBlock bool - for c.NextBlock() { - if httpserver.IfMatcherKeyword(c) { - continue - } - - hadOptionalBlock = true - - rule := Rule{ - RequestMatcher: matcher, - } - - defaultCode := initDefaultCode - // Set initial redirect code - if len(args) == 1 { - defaultCode = args[0] - } - - // RemainingArgs only gets the values after the current token, but in our - // case we want to include the current token to get an accurate count. - insideArgs := append([]string{c.Val()}, c.RemainingArgs()...) - err := initRule(&rule, defaultCode, insideArgs) - if err != nil { - return redirects, err - } - - err = checkAndSaveRule(rule) - if err != nil { - return redirects, err - } - } - - if !hadOptionalBlock { - rule := Rule{ - RequestMatcher: matcher, - } - err := initRule(&rule, initDefaultCode, args) - if err != nil { - return redirects, err - } - - err = checkAndSaveRule(rule) - if err != nil { - return redirects, err - } - } - } - - return redirects, nil -} - -// httpRedirs is a list of supported HTTP redirect codes. -var httpRedirs = map[string]int{ - "300": http.StatusMultipleChoices, - "301": http.StatusMovedPermanently, - "302": http.StatusFound, // (NOT CORRECT for "Temporary Redirect", see 307) - "303": http.StatusSeeOther, - "304": http.StatusNotModified, - "305": http.StatusUseProxy, - "307": http.StatusTemporaryRedirect, - "308": http.StatusPermanentRedirect, // Permanent Redirect (RFC 7238) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/requestid/requestid.go b/vendor/github.com/mholt/caddy/caddyhttp/requestid/requestid.go deleted file mode 100644 index b03c449f6..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/requestid/requestid.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 requestid - -import ( - "context" - "log" - "net/http" - - "github.com/google/uuid" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Handler is a middleware handler -type Handler struct { - Next httpserver.Handler - HeaderName string // (optional) header from which to read an existing ID -} - -func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - var reqid uuid.UUID - - uuidFromHeader := r.Header.Get(h.HeaderName) - if h.HeaderName != "" && uuidFromHeader != "" { - // use the ID in the header field if it exists - var err error - reqid, err = uuid.Parse(uuidFromHeader) - if err != nil { - log.Printf("[NOTICE] Parsing request ID from %s header: %v", h.HeaderName, err) - reqid = uuid.New() - } - } else { - // otherwise, create a new one - reqid = uuid.New() - } - - // set the request ID on the context - c := context.WithValue(r.Context(), httpserver.RequestIDCtxKey, reqid.String()) - r = r.WithContext(c) - - return h.Next.ServeHTTP(w, r) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/requestid/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/requestid/setup.go deleted file mode 100644 index 689f99e33..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/requestid/setup.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 requestid - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("request_id", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -func setup(c *caddy.Controller) error { - var headerName string - - for c.Next() { - if c.NextArg() { - headerName = c.Val() - } - if c.NextArg() { - return c.ArgErr() - } - } - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Handler{Next: next, HeaderName: headerName} - }) - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite.go b/vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite.go deleted file mode 100644 index f7f56cd39..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 rewrite is middleware for rewriting requests internally to -// a different path. -package rewrite - -import ( - "fmt" - "net/http" - "net/url" - "path" - "path/filepath" - "regexp" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Result is the result of a rewrite -type Result int - -const ( - // RewriteIgnored is returned when rewrite is not done on request. - RewriteIgnored Result = iota - // RewriteDone is returned when rewrite is done on request. - RewriteDone -) - -// Rewrite is middleware to rewrite request locations internally before being handled. -type Rewrite struct { - Next httpserver.Handler - FileSys http.FileSystem - Rules []httpserver.HandlerConfig -} - -// ServeHTTP implements the httpserver.Handler interface. -func (rw Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if rule := httpserver.ConfigSelector(rw.Rules).Select(r); rule != nil { - rule.(Rule).Rewrite(rw.FileSys, r) - } - - return rw.Next.ServeHTTP(w, r) -} - -// Rule describes an internal location rewrite rule. -type Rule interface { - httpserver.HandlerConfig - // Rewrite rewrites the internal location of the current request. - Rewrite(http.FileSystem, *http.Request) Result -} - -// SimpleRule is a simple rewrite rule. -type SimpleRule struct { - Regexp *regexp.Regexp - To string - Negate bool -} - -// NewSimpleRule creates a new Simple Rule -func NewSimpleRule(from, to string, negate bool) (*SimpleRule, error) { - r, err := regexp.Compile(from) - if err != nil { - return nil, err - } - return &SimpleRule{ - Regexp: r, - To: to, - Negate: negate, - }, nil -} - -// BasePath satisfies httpserver.Config -func (s SimpleRule) BasePath() string { return "/" } - -// Match satisfies httpserver.Config -func (s *SimpleRule) Match(r *http.Request) bool { - matches := regexpMatches(s.Regexp, "/", r.URL.Path) - if s.Negate { - return len(matches) == 0 - } - return len(matches) > 0 -} - -// Rewrite rewrites the internal location of the current request. -func (s *SimpleRule) Rewrite(fs http.FileSystem, r *http.Request) Result { - - // attempt rewrite - return To(fs, r, s.To, newReplacer(r)) -} - -// ComplexRule is a rewrite rule based on a regular expression -type ComplexRule struct { - // Path base. Request to this path and subpaths will be rewritten - Base string - - // Path to rewrite to - To string - - // Extensions to filter by - Exts []string - - // Request matcher - httpserver.RequestMatcher - - Regexp *regexp.Regexp -} - -// NewComplexRule creates a new RegexpRule. It returns an error if regexp -// pattern (pattern) or extensions (ext) are invalid. -func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.RequestMatcher) (ComplexRule, error) { - // validate regexp if present - var r *regexp.Regexp - if pattern != "" { - var err error - r, err = regexp.Compile(pattern) - if err != nil { - return ComplexRule{}, err - } - } - - // validate extensions if present - for _, v := range ext { - if len(v) < 2 || (len(v) < 3 && v[0] == '!') { - // check if no extension is specified - if v != "/" && v != "!/" { - return ComplexRule{}, fmt.Errorf("invalid extension %v", v) - } - } - } - - // use both IfMatcher and PathMatcher - matcher = httpserver.MergeRequestMatchers( - // If condition matcher - matcher, - // Base path matcher - httpserver.PathMatcher(base), - ) - - return ComplexRule{ - Base: base, - To: to, - Exts: ext, - RequestMatcher: matcher, - Regexp: r, - }, nil -} - -// BasePath satisfies httpserver.Config -func (r ComplexRule) BasePath() string { return r.Base } - -// Match satisfies httpserver.Config. -// -// Though ComplexRule embeds a RequestMatcher, additional -// checks are needed which requires a custom implementation. -func (r ComplexRule) Match(req *http.Request) bool { - // validate RequestMatcher - // includes if and path - if !r.RequestMatcher.Match(req) { - return false - } - - // validate extensions - if !r.matchExt(req.URL.Path) { - return false - } - - // if regex is nil, ignore - if r.Regexp == nil { - return true - } - // otherwise validate regex - return regexpMatches(r.Regexp, r.Base, req.URL.Path) != nil -} - -// Rewrite rewrites the internal location of the current request. -func (r ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result) { - replacer := newReplacer(req) - - // validate regexp if present - if r.Regexp != nil { - matches := regexpMatches(r.Regexp, r.Base, req.URL.Path) - switch len(matches) { - case 0: - // no match - return - default: - // set regexp match variables {1}, {2} ... - - // url escaped values of ? and #. - q, f := url.QueryEscape("?"), url.QueryEscape("#") - - for i := 1; i < len(matches); i++ { - // Special case of unescaped # and ? by stdlib regexp. - // Reverse the unescape. - if strings.ContainsAny(matches[i], "?#") { - matches[i] = strings.NewReplacer("?", q, "#", f).Replace(matches[i]) - } - - replacer.Set(fmt.Sprint(i), matches[i]) - } - } - } - - // attempt rewrite - return To(fs, req, r.To, replacer) -} - -// matchExt matches rPath against registered file extensions. -// Returns true if a match is found and false otherwise. -func (r ComplexRule) matchExt(rPath string) bool { - f := filepath.Base(rPath) - ext := path.Ext(f) - if ext == "" { - ext = "/" - } - - mustUse := false - for _, v := range r.Exts { - use := true - if v[0] == '!' { - use = false - v = v[1:] - } - - if use { - mustUse = true - } - - if ext == v { - return use - } - } - - return !mustUse -} - -func regexpMatches(regexp *regexp.Regexp, base, rPath string) []string { - if regexp != nil { - // include trailing slash in regexp if present - start := len(base) - if strings.HasSuffix(base, "/") { - start-- - } - return regexp.FindStringSubmatch(rPath[start:]) - } - return nil -} - -func newReplacer(r *http.Request) httpserver.Replacer { - return httpserver.NewReplacer(r, nil, "") -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup.go deleted file mode 100644 index f73d76a70..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 rewrite - -import ( - "net/http" - "strings" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("rewrite", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Rewrite middleware instance. -func setup(c *caddy.Controller) error { - rewrites, err := rewriteParse(c) - if err != nil { - return err - } - - cfg := httpserver.GetConfig(c) - - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return Rewrite{ - Next: next, - FileSys: http.Dir(cfg.Root), - Rules: rewrites, - } - }) - - return nil -} - -func rewriteParse(c *caddy.Controller) ([]httpserver.HandlerConfig, error) { - var rules []httpserver.HandlerConfig - - for c.Next() { - var rule Rule - var err error - var base = "/" - var pattern, to string - var ext []string - var negate bool - - args := c.RemainingArgs() - - var matcher httpserver.RequestMatcher - - switch len(args) { - case 1: - base = args[0] - fallthrough - case 0: - // Integrate request matcher for 'if' conditions. - matcher, err = httpserver.SetupIfMatcher(c) - if err != nil { - return nil, err - } - - for c.NextBlock() { - if httpserver.IfMatcherKeyword(c) { - continue - } - switch c.Val() { - case "r", "regexp": - if !c.NextArg() { - return nil, c.ArgErr() - } - pattern = c.Val() - case "to": - args1 := c.RemainingArgs() - if len(args1) == 0 { - return nil, c.ArgErr() - } - to = strings.Join(args1, " ") - case "ext": - args1 := c.RemainingArgs() - if len(args1) == 0 { - return nil, c.ArgErr() - } - ext = args1 - default: - return nil, c.ArgErr() - } - } - // ensure to is specified - if to == "" { - return nil, c.ArgErr() - } - if rule, err = NewComplexRule(base, pattern, to, ext, matcher); err != nil { - return nil, err - } - rules = append(rules, rule) - - // the only unhandled case is 2 and above - default: - if args[0] == "not" { - negate = true - args = args[1:] - } - rule, err = NewSimpleRule(args[0], strings.Join(args[1:], " "), negate) - if err != nil { - return nil, err - } - rules = append(rules, rule) - } - - } - - return rules, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/to.go b/vendor/github.com/mholt/caddy/caddyhttp/rewrite/to.go deleted file mode 100644 index 1b0856f70..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/rewrite/to.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 rewrite - -import ( - "log" - "net/http" - "net/url" - "path" - "strings" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// To attempts rewrite. It attempts to rewrite to first valid path -// or the last path if none of the paths are valid. -func To(fs http.FileSystem, r *http.Request, to string, replacer httpserver.Replacer) Result { - tos := strings.Fields(to) - - // try each rewrite paths - t := "" - query := "" - for _, v := range tos { - t = replacer.Replace(v) - tparts := strings.SplitN(t, "?", 2) - t = path.Clean(tparts[0]) - - if len(tparts) > 1 { - query = tparts[1] - } - - // add trailing slash for directories, if present - if strings.HasSuffix(tparts[0], "/") && !strings.HasSuffix(t, "/") { - t += "/" - } - - // validate file - if validFile(fs, t) { - break - } - } - - // validate resulting path - u, err := url.Parse(t) - if err != nil { - // Let the user know we got here. Rewrite is expected but - // the resulting url is invalid. - log.Printf("[ERROR] rewrite: resulting path '%v' is invalid. error: %v", t, err) - return RewriteIgnored - } - - // perform rewrite - r.URL.Path = u.Path - if query != "" { - // overwrite query string if present - r.URL.RawQuery = query - } - if u.Fragment != "" { - // overwrite fragment if present - r.URL.Fragment = u.Fragment - } - - return RewriteDone -} - -// validFile checks if file exists on the filesystem. -// if file ends with `/`, it is validated as a directory. -func validFile(fs http.FileSystem, file string) bool { - if fs == nil { - return false - } - - f, err := fs.Open(file) - if err != nil { - return false - } - defer f.Close() - - stat, err := f.Stat() - if err != nil { - return false - } - - // directory - if strings.HasSuffix(file, "/") { - return stat.IsDir() - } - - // file - return !stat.IsDir() -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/root/root.go b/vendor/github.com/mholt/caddy/caddyhttp/root/root.go deleted file mode 100644 index 47df53db8..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/root/root.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 root - -import ( - "log" - "os" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("root", caddy.Plugin{ - ServerType: "http", - Action: setupRoot, - }) -} - -func setupRoot(c *caddy.Controller) error { - config := httpserver.GetConfig(c) - - for c.Next() { - if !c.NextArg() { - return c.ArgErr() - } - config.Root = c.Val() - if c.NextArg() { - // only one argument allowed - return c.ArgErr() - } - } - //first check that the path is not a symlink, os.Stat panics when this is true - info, _ := os.Lstat(config.Root) - if info != nil && info.Mode()&os.ModeSymlink == os.ModeSymlink { - //just print out info, delegate responsibility for symlink validity to - //underlying Go framework, no need to test / verify twice - log.Printf("[INFO] Root path is symlink: %s", config.Root) - } else { - // Check if root path exists - _, err := os.Stat(config.Root) - if err != nil { - if os.IsNotExist(err) { - // Allow this, because the folder might appear later. - // But make sure the user knows! - log.Printf("[WARNING] Root path does not exist: %s", config.Root) - } else { - return c.Errf("Unable to access root path '%s': %v", config.Root, err) - } - } - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/staticfiles/fileserver.go b/vendor/github.com/mholt/caddy/caddyhttp/staticfiles/fileserver.go deleted file mode 100644 index 0863ebe58..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/staticfiles/fileserver.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 staticfiles provides middleware for serving static files from disk. -// Its handler is the default HTTP handler for the HTTP server. -// -// TODO: Should this package be rolled into the httpserver package? -package staticfiles - -import ( - "math/rand" - "net/http" - "os" - "path" - "path/filepath" - "runtime" - "strconv" - "strings" - - "github.com/mholt/caddy" -) - -// FileServer implements a production-ready file server -// and is the 'default' handler for all requests to Caddy. -// It simply loads and serves the URI requested. FileServer -// is adapted from the one in net/http by the Go authors. -// Significant modifications have been made. -// -// Original license: -// -// Copyright 2009 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. -type FileServer struct { - Root http.FileSystem // jailed access to the file system - Hide []string // list of files for which to respond with "Not Found" - - // A list of pages that may be understood as the "index" files to directories. - // Injected from *SiteConfig. - IndexPages []string -} - -// ServeHTTP serves static files for r according to fs's configuration. -func (fs FileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if r.Method != "GET" && r.Method != "HEAD" { - return http.StatusMethodNotAllowed, nil - } - return fs.serveFile(w, r) -} - -// serveFile writes the specified file to the HTTP response. -// name is '/'-separated, not filepath.Separator. -func (fs FileServer) serveFile(w http.ResponseWriter, r *http.Request) (int, error) { - reqPath := r.URL.Path - - // Prevent absolute path access on Windows. - // TODO remove when stdlib http.Dir fixes this. - if runtime.GOOS == "windows" && len(reqPath) > 0 && filepath.IsAbs(reqPath[1:]) { - return http.StatusNotFound, nil - } - - // open the requested file - f, err := fs.Root.Open(reqPath) - if err != nil { - if os.IsNotExist(err) { - return http.StatusNotFound, nil - } else if os.IsPermission(err) { - return http.StatusForbidden, err - } - // otherwise, maybe the server is under load and ran out of file descriptors? - backoff := int(3 + rand.Int31()%3) // 3–5 seconds to prevent a stampede - w.Header().Set("Retry-After", strconv.Itoa(backoff)) - return http.StatusServiceUnavailable, err - } - defer f.Close() - - // get information about the file - d, err := f.Stat() - if err != nil { - if os.IsNotExist(err) { - return http.StatusNotFound, nil - } else if os.IsPermission(err) { - return http.StatusForbidden, err - } - // return a different status code than above to distinguish these cases - return http.StatusInternalServerError, err - } - - // redirect to canonical path (being careful to preserve other parts of URL and - // considering cases where a site is defined with a path prefix that gets stripped) - urlCopy := *r.URL - pathPrefix, _ := r.Context().Value(caddy.CtxKey("path_prefix")).(string) - if pathPrefix != "/" { - urlCopy.Path = pathPrefix + urlCopy.Path - } - if urlCopy.Path == "" { - urlCopy.Path = "/" - } - if d.IsDir() { - // ensure there is a trailing slash - if urlCopy.Path[len(urlCopy.Path)-1] != '/' { - for strings.HasPrefix(urlCopy.Path, "//") { - // prevent path-based open redirects - urlCopy.Path = strings.TrimPrefix(urlCopy.Path, "/") - } - urlCopy.Path += "/" - http.Redirect(w, r, urlCopy.String(), http.StatusMovedPermanently) - return http.StatusMovedPermanently, nil - } - } else { - // ensure no trailing slash - redir := false - if urlCopy.Path[len(urlCopy.Path)-1] == '/' { - urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-1] - redir = true - } - - // if an index file was explicitly requested, strip file name from the request - // ("/foo/index.html" -> "/foo/") - var requestPage = path.Base(urlCopy.Path) - for _, indexPage := range fs.IndexPages { - if requestPage == indexPage { - urlCopy.Path = urlCopy.Path[:len(urlCopy.Path)-len(indexPage)] - redir = true - break - } - } - - if redir { - for strings.HasPrefix(urlCopy.Path, "//") { - // prevent path-based open redirects - urlCopy.Path = strings.TrimPrefix(urlCopy.Path, "/") - } - http.Redirect(w, r, urlCopy.String(), http.StatusMovedPermanently) - return http.StatusMovedPermanently, nil - } - } - - // use contents of an index file, if present, for directory requests - if d.IsDir() { - for _, indexPage := range fs.IndexPages { - indexPath := path.Join(reqPath, indexPage) - indexFile, err := fs.Root.Open(indexPath) - if err != nil { - continue - } - - indexInfo, err := indexFile.Stat() - if err != nil { - indexFile.Close() - continue - } - - // this defer does not leak fds even though we are in a loop, - // because previous iterations of the loop must have had an - // err, so there's nothing to close from earlier iterations. - defer indexFile.Close() - - // close previously-opened file immediately to release fd - f.Close() - - // switch to using the index file, and we're done here - d = indexInfo - f = indexFile - reqPath = indexPath - break - } - } - - // return Not Found if we either did not find an index file (and thus are - // still a directory) or if this file is supposed to be hidden - if d.IsDir() || fs.IsHidden(d) { - return http.StatusNotFound, nil - } - - etag := calculateEtag(d) - - // look for compressed versions of the file on disk, if the client supports that encoding - for _, encoding := range staticEncodingPriority { - // see if the client accepts a compressed encoding we offer - acceptEncoding := strings.Split(r.Header.Get("Accept-Encoding"), ",") - accepted := false - for _, acc := range acceptEncoding { - if strings.TrimSpace(acc) == encoding { - accepted = true - break - } - } - - // if client doesn't support this encoding, don't even bother; try next one - if !accepted { - continue - } - - // see if the compressed version of this file exists - encodedFile, err := fs.Root.Open(reqPath + staticEncoding[encoding]) - if err != nil { - continue - } - - encodedFileInfo, err := encodedFile.Stat() - if err != nil { - encodedFile.Close() - continue - } - - // close the encoded file when we're done, and close the - // previously-opened file immediately to release the fd - defer encodedFile.Close() - f.Close() - - // the encoded file is now what we're serving - f = encodedFile - etag = calculateEtag(encodedFileInfo) - w.Header().Add("Vary", "Accept-Encoding") - w.Header().Set("Content-Encoding", encoding) - w.Header().Set("Content-Length", strconv.FormatInt(encodedFileInfo.Size(), 10)) - break - } - - // Set the ETag returned to the user-agent. Note that a conditional If-None-Match - // request is handled in http.ServeContent below, which checks against this ETag value. - w.Header().Set("ETag", etag) - - // Note: Errors generated by ServeContent are written immediately - // to the response. This usually only happens if seeking fails (rare). - // Its signature does not bubble the error up to us, so we cannot - // return it for any logging middleware to record. Oh well. - http.ServeContent(w, r, d.Name(), d.ModTime(), f) - - return http.StatusOK, nil -} - -// IsHidden checks if file with FileInfo d is on hide list. -func (fs FileServer) IsHidden(d os.FileInfo) bool { - for _, hiddenPath := range fs.Hide { - // TODO: Could these FileInfos be stored instead of their paths, to avoid opening them all the time? - if hFile, err := fs.Root.Open(hiddenPath); err == nil { - fs, _ := hFile.Stat() - hFile.Close() - if os.SameFile(d, fs) { - return true - } - } - } - return false -} - -// calculateEtag produces a strong etag by default, although, for -// efficiency reasons, it does not actually consume the contents -// of the file to make a hash of all the bytes. ¯\_(ツ)_/¯ -// Prefix the etag with "W/" to convert it into a weak etag. -// See: https://tools.ietf.org/html/rfc7232#section-2.3 -func calculateEtag(d os.FileInfo) string { - t := strconv.FormatInt(d.ModTime().Unix(), 36) - s := strconv.FormatInt(d.Size(), 36) - return `"` + t + s + `"` -} - -// DefaultIndexPages is a list of pages that may be understood as -// the "index" files to directories. -var DefaultIndexPages = []string{ - "index.html", - "index.htm", - "index.txt", - "default.html", - "default.htm", - "default.txt", -} - -// staticEncoding is a map of content-encoding to a file extension. -// If client accepts given encoding (via Accept-Encoding header) and compressed file with given extensions exists -// it will be served to the client instead of original one. -var staticEncoding = map[string]string{ - "gzip": ".gz", - "br": ".br", -} - -// staticEncodingPriority is a list of preferred static encodings (most efficient compression to least one). -var staticEncodingPriority = []string{ - "br", - "gzip", -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/status/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/status/setup.go deleted file mode 100644 index 5c2943142..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/status/setup.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 status - -import ( - "strconv" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// init registers Status plugin -func init() { - caddy.RegisterPlugin("status", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures new Status middleware instance. -func setup(c *caddy.Controller) error { - rules, err := statusParse(c) - if err != nil { - return err - } - - cfg := httpserver.GetConfig(c) - mid := func(next httpserver.Handler) httpserver.Handler { - return Status{Rules: rules, Next: next} - } - cfg.AddMiddleware(mid) - - return nil -} - -// statusParse parses status directive -func statusParse(c *caddy.Controller) ([]httpserver.HandlerConfig, error) { - var rules []httpserver.HandlerConfig - - for c.Next() { - hadBlock := false - args := c.RemainingArgs() - - switch len(args) { - case 1: - status, err := strconv.Atoi(args[0]) - if err != nil { - return rules, c.Errf("Expecting a numeric status code, got '%s'", args[0]) - } - - for c.NextBlock() { - hadBlock = true - basePath := c.Val() - - for _, cfg := range rules { - rule := cfg.(*Rule) - if rule.Base == basePath { - return rules, c.Errf("Duplicate path: '%s'", basePath) - } - } - - rule := NewRule(basePath, status) - rules = append(rules, rule) - - if c.NextArg() { - return rules, c.ArgErr() - } - } - - if !hadBlock { - return rules, c.ArgErr() - } - case 2: - status, err := strconv.Atoi(args[0]) - if err != nil { - return rules, c.Errf("Expecting a numeric status code, got '%s'", args[0]) - } - - basePath := args[1] - for _, cfg := range rules { - rule := cfg.(*Rule) - if rule.Base == basePath { - return rules, c.Errf("Duplicate path: '%s'", basePath) - } - } - - rule := NewRule(basePath, status) - rules = append(rules, rule) - default: - return rules, c.ArgErr() - } - } - - return rules, nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/status/status.go b/vendor/github.com/mholt/caddy/caddyhttp/status/status.go deleted file mode 100644 index a00366e6a..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/status/status.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 status is middleware for returning status code for requests -package status - -import ( - "net/http" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// Rule describes status rewriting rule -type Rule struct { - // Base path. Request to this path and sub-paths will be answered with StatusCode - Base string - - // Status code to return - StatusCode int - - // Request matcher - httpserver.RequestMatcher -} - -// NewRule creates new Rule. -func NewRule(basePath string, status int) *Rule { - return &Rule{ - Base: basePath, - StatusCode: status, - RequestMatcher: httpserver.PathMatcher(basePath), - } -} - -// BasePath implements httpserver.HandlerConfig interface -func (rule *Rule) BasePath() string { - return rule.Base -} - -// Status is a middleware to return status code for request -type Status struct { - Rules []httpserver.HandlerConfig - Next httpserver.Handler -} - -// ServeHTTP implements the httpserver.Handler interface -func (status Status) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - if cfg := httpserver.ConfigSelector(status.Rules).Select(r); cfg != nil { - rule := cfg.(*Rule) - - if rule.StatusCode < 400 { - // There's no ability to return response body -- - // write the response status code in header and signal - // to other handlers that response is already handled - w.WriteHeader(rule.StatusCode) - return 0, nil - } - - return rule.StatusCode, nil - } - - return status.Next.ServeHTTP(w, r) -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/templates/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/templates/setup.go deleted file mode 100644 index e9fe105ff..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/templates/setup.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 templates - -import ( - "bytes" - "net/http" - "sync" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("templates", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new Templates middleware instance. -func setup(c *caddy.Controller) error { - rules, err := templatesParse(c) - if err != nil { - return err - } - - cfg := httpserver.GetConfig(c) - - tmpls := Templates{ - Rules: rules, - Root: cfg.Root, - FileSys: http.Dir(cfg.Root), - BufPool: &sync.Pool{ - New: func() interface{} { - return new(bytes.Buffer) - }, - }, - } - - cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - tmpls.Next = next - return tmpls - }) - - return nil -} - -func templatesParse(c *caddy.Controller) ([]Rule, error) { - var rules []Rule - - for c.Next() { - var rule Rule - - rule.Path = defaultTemplatePath - rule.Extensions = defaultTemplateExtensions - - args := c.RemainingArgs() - - switch len(args) { - case 0: - // Optional block - for c.NextBlock() { - switch c.Val() { - case "path": - args := c.RemainingArgs() - if len(args) != 1 { - return nil, c.ArgErr() - } - rule.Path = args[0] - - case "ext": - args := c.RemainingArgs() - if len(args) == 0 { - return nil, c.ArgErr() - } - rule.Extensions = args - - case "between": - args := c.RemainingArgs() - if len(args) != 2 { - return nil, c.ArgErr() - } - rule.Delims[0] = args[0] - rule.Delims[1] = args[1] - } - } - default: - // First argument would be the path - rule.Path = args[0] - - // Any remaining arguments are extensions - rule.Extensions = args[1:] - if len(rule.Extensions) == 0 { - rule.Extensions = defaultTemplateExtensions - } - } - - for _, ext := range rule.Extensions { - rule.IndexFiles = append(rule.IndexFiles, "index"+ext) - } - - rules = append(rules, rule) - } - return rules, nil -} - -const defaultTemplatePath = "/" - -var defaultTemplateExtensions = []string{".html", ".htm", ".tmpl", ".tpl", ".txt"} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/templates/templates.go b/vendor/github.com/mholt/caddy/caddyhttp/templates/templates.go deleted file mode 100644 index 9a1ae4ba1..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/templates/templates.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 templates implements template execution for files to be -// dynamically rendered for the client. -package templates - -import ( - "bytes" - "mime" - "net/http" - "path" - "path/filepath" - "strconv" - "strings" - "sync" - "text/template" - "time" - - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -// ServeHTTP implements the httpserver.Handler interface. -func (t Templates) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - // iterate rules, to find first one that matches the request path - for _, rule := range t.Rules { - if !httpserver.Path(r.URL.Path).Matches(rule.Path) { - continue - } - - fpath := r.URL.Path - - // get a buffer from the pool and make a response recorder - buf := t.BufPool.Get().(*bytes.Buffer) - buf.Reset() - defer t.BufPool.Put(buf) - - // only buffer the response when we want to execute a template - shouldBuf := func(status int, header http.Header) bool { - // see if this request matches a template extension - reqExt := path.Ext(fpath) - for _, ext := range rule.Extensions { - if reqExt == "" { - // request has no extension, so check response Content-Type - ct := mime.TypeByExtension(ext) - if ct != "" && strings.Contains(header.Get("Content-Type"), ct) { - return true - } - } else if reqExt == ext { - return true - } - } - return false - } - - // prepare a buffer to hold the response, if applicable - rb := httpserver.NewResponseBuffer(buf, w, shouldBuf) - - // pass request up the chain to let another middleware provide us the template - code, err := t.Next.ServeHTTP(rb, r) - if !rb.Buffered() || code >= 300 || err != nil { - return code, err - } - - // create a new template - templateName := filepath.Base(fpath) - tpl := template.New(templateName) - - // set delimiters - if rule.Delims != [2]string{} { - tpl.Delims(rule.Delims[0], rule.Delims[1]) - } - - // add custom functions - tpl.Funcs(httpserver.TemplateFuncs) - - // parse the template - parsedTpl, err := tpl.Parse(rb.Buffer.String()) - if err != nil { - return http.StatusInternalServerError, err - } - - // create execution context for the template template - ctx := httpserver.NewContextWithHeader(w.Header()) - ctx.Root = t.FileSys - ctx.Req = r - ctx.URL = r.URL - - // execute the template - buf.Reset() - err = parsedTpl.Execute(buf, ctx) - if err != nil { - return http.StatusInternalServerError, err - } - - // copy the buffered header into the real ResponseWriter - rb.CopyHeader() - - // set the actual content length now that the template was executed - w.Header().Set("Content-Length", strconv.Itoa(buf.Len())) - - // delete the headers related to cache - w.Header().Del("ETag") - w.Header().Del("Last-Modified") - - // get the modification time in preparation for http.ServeContent - modTime, _ := time.Parse(http.TimeFormat, w.Header().Get("Last-Modified")) - - // at last, write the rendered template to the response; make sure to use - // use the proper status code, since ServeContent hard-codes 2xx codes... - http.ServeContent(rb.StatusCodeWriter(w), r, templateName, modTime, bytes.NewReader(buf.Bytes())) - - return 0, nil - } - - return t.Next.ServeHTTP(w, r) -} - -// Templates is middleware to render templated files as the HTTP response. -type Templates struct { - Next httpserver.Handler - Rules []Rule - Root string - FileSys http.FileSystem - BufPool *sync.Pool // docs: "A Pool must not be copied after first use." -} - -// Rule represents a template rule. A template will only execute -// with this rule if the request path matches the Path specified -// and requests a resource with one of the extensions specified. -type Rule struct { - Path string - Extensions []string - IndexFiles []string - Delims [2]string -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/timeouts/timeouts.go b/vendor/github.com/mholt/caddy/caddyhttp/timeouts/timeouts.go deleted file mode 100644 index 749c03b02..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/timeouts/timeouts.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 timeouts - -import ( - "time" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("timeouts", caddy.Plugin{ - ServerType: "http", - Action: setupTimeouts, - }) -} - -func setupTimeouts(c *caddy.Controller) error { - config := httpserver.GetConfig(c) - - for c.Next() { - var hasOptionalBlock bool - for c.NextBlock() { - hasOptionalBlock = true - - // ensure the kind of timeout is recognized - kind := c.Val() - if kind != "read" && kind != "header" && kind != "write" && kind != "idle" { - return c.Errf("unknown timeout '%s': must be read, header, write, or idle", kind) - } - - // parse the timeout duration - if !c.NextArg() { - return c.ArgErr() - } - if c.NextArg() { - // only one value permitted - return c.ArgErr() - } - var dur time.Duration - if c.Val() != "none" { - var err error - dur, err = time.ParseDuration(c.Val()) - if err != nil { - return c.Errf("%v", err) - } - if dur < 0 { - return c.Err("non-negative duration required for timeout value") - } - } - - // set this timeout's duration - switch kind { - case "read": - config.Timeouts.ReadTimeout = dur - config.Timeouts.ReadTimeoutSet = true - case "header": - config.Timeouts.ReadHeaderTimeout = dur - config.Timeouts.ReadHeaderTimeoutSet = true - case "write": - config.Timeouts.WriteTimeout = dur - config.Timeouts.WriteTimeoutSet = true - case "idle": - config.Timeouts.IdleTimeout = dur - config.Timeouts.IdleTimeoutSet = true - } - } - if !hasOptionalBlock { - // set all timeouts to the same value - - if !c.NextArg() { - return c.ArgErr() - } - if c.NextArg() { - // only one value permitted - return c.ArgErr() - } - val := c.Val() - - config.Timeouts.ReadTimeoutSet = true - config.Timeouts.ReadHeaderTimeoutSet = true - config.Timeouts.WriteTimeoutSet = true - config.Timeouts.IdleTimeoutSet = true - - if val == "none" { - config.Timeouts.ReadTimeout = 0 - config.Timeouts.ReadHeaderTimeout = 0 - config.Timeouts.WriteTimeout = 0 - config.Timeouts.IdleTimeout = 0 - } else { - dur, err := time.ParseDuration(val) - if err != nil { - return c.Errf("unknown timeout duration: %v", err) - } - if dur < 0 { - return c.Err("non-negative duration required for timeout value") - } - config.Timeouts.ReadTimeout = dur - config.Timeouts.ReadHeaderTimeout = dur - config.Timeouts.WriteTimeout = dur - config.Timeouts.IdleTimeout = dur - } - } - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/websocket/setup.go b/vendor/github.com/mholt/caddy/caddyhttp/websocket/setup.go deleted file mode 100644 index 822ccd155..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/websocket/setup.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 websocket - -import ( - "github.com/mholt/caddy" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -func init() { - caddy.RegisterPlugin("websocket", caddy.Plugin{ - ServerType: "http", - Action: setup, - }) -} - -// setup configures a new WebSocket middleware instance. -func setup(c *caddy.Controller) error { - websocks, err := webSocketParse(c) - if err != nil { - return err - } - - GatewayInterface = caddy.AppName + "-CGI/1.1" - ServerSoftware = caddy.AppName + "/" + caddy.AppVersion - - httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { - return WebSocket{Next: next, Sockets: websocks} - }) - - return nil -} - -func webSocketParse(c *caddy.Controller) ([]Config, error) { - var websocks []Config - var respawn bool - - optionalBlock := func() (hadBlock bool, err error) { - for c.NextBlock() { - hadBlock = true - if c.Val() == "respawn" { - respawn = true - } else { - return true, c.Err("Expected websocket configuration parameter in block") - } - } - return - } - - for c.Next() { - var val, path, command string - - // Path or command; not sure which yet - if !c.NextArg() { - return nil, c.ArgErr() - } - val = c.Val() - - // Extra configuration may be in a block - hadBlock, err := optionalBlock() - if err != nil { - return nil, err - } - - if !hadBlock { - // The next argument on this line will be the command or an open curly brace - if c.NextArg() { - path = val - command = c.Val() - } else { - path = "/" - command = val - } - - // Okay, check again for optional block - _, err = optionalBlock() - if err != nil { - return nil, err - } - } - - // Split command into the actual command and its arguments - cmd, args, err := caddy.SplitCommandAndArgs(command) - if err != nil { - return nil, err - } - - websocks = append(websocks, Config{ - Path: path, - Command: cmd, - Arguments: args, - Respawn: respawn, // TODO: This isn't used currently - }) - } - - return websocks, nil - -} diff --git a/vendor/github.com/mholt/caddy/caddyhttp/websocket/websocket.go b/vendor/github.com/mholt/caddy/caddyhttp/websocket/websocket.go deleted file mode 100644 index a1f659bff..000000000 --- a/vendor/github.com/mholt/caddy/caddyhttp/websocket/websocket.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 websocket implements a WebSocket server by executing -// a command and piping its input and output through the WebSocket -// connection. -package websocket - -import ( - "bufio" - "bytes" - "io" - "log" - "net" - "net/http" - "os" - "os/exec" - "strings" - "time" - - "github.com/gorilla/websocket" - "github.com/mholt/caddy/caddyhttp/httpserver" -) - -const ( - // Time allowed to write a message to the peer. - writeWait = 10 * time.Second - - // Time allowed to read the next pong message from the peer. - pongWait = 60 * time.Second - - // Send pings to peer with this period. Must be less than pongWait. - pingPeriod = (pongWait * 9) / 10 - - // Maximum message size allowed from peer. - maxMessageSize = 1024 * 1024 * 10 // 10 MB default. -) - -var ( - // GatewayInterface is the dialect of CGI being used by the server - // to communicate with the script. See CGI spec, 4.1.4 - GatewayInterface string - - // ServerSoftware is the name and version of the information server - // software making the CGI request. See CGI spec, 4.1.17 - ServerSoftware string -) - -type ( - // WebSocket is a type that holds configuration for the - // websocket middleware generally, like a list of all the - // websocket endpoints. - WebSocket struct { - // Next is the next HTTP handler in the chain for when the path doesn't match - Next httpserver.Handler - - // Sockets holds all the web socket endpoint configurations - Sockets []Config - } - - // Config holds the configuration for a single websocket - // endpoint which may serve multiple websocket connections. - Config struct { - Path string - Command string - Arguments []string - Respawn bool // TODO: Not used, but parser supports it until we decide on it - } -) - -// ServeHTTP converts the HTTP request to a WebSocket connection and serves it up. -func (ws WebSocket) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { - for _, sockConfig := range ws.Sockets { - if httpserver.Path(r.URL.Path).Matches(sockConfig.Path) { - return serveWS(w, r, &sockConfig) - } - } - - // Didn't match a websocket path, so pass-through - return ws.Next.ServeHTTP(w, r) -} - -// serveWS is used for setting and upgrading the HTTP connection to a websocket connection. -// It also spawns the child process that is associated with matched HTTP path/url. -func serveWS(w http.ResponseWriter, r *http.Request, config *Config) (int, error) { - upgrader := websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - CheckOrigin: func(r *http.Request) bool { return true }, - } - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - // the connection has been "handled" -- WriteHeader was called with Upgrade, - // so don't return an error status code; just return an error - return 0, err - } - defer conn.Close() - - cmd := exec.Command(config.Command, config.Arguments...) - - stdout, err := cmd.StdoutPipe() - if err != nil { - return http.StatusBadGateway, err - } - defer stdout.Close() - - stdin, err := cmd.StdinPipe() - if err != nil { - return http.StatusBadGateway, err - } - defer stdin.Close() - - metavars, err := buildEnv(cmd.Path, r) - if err != nil { - return http.StatusBadGateway, err - } - - cmd.Env = metavars - - if err := cmd.Start(); err != nil { - return http.StatusBadGateway, err - } - - done := make(chan struct{}) - go pumpStdout(conn, stdout, done) - pumpStdin(conn, stdin) - - _ = stdin.Close() // close stdin to end the process - - if err := cmd.Process.Signal(os.Interrupt); err != nil { // signal an interrupt to kill the process - return http.StatusInternalServerError, err - } - - select { - case <-done: - case <-time.After(time.Second): - // terminate with extreme prejudice. - if err := cmd.Process.Signal(os.Kill); err != nil { - return http.StatusInternalServerError, err - } - <-done - } - - // not sure what we want to do here. - // status for an "exited" process is greater - // than 0, but isn't really an error per se. - // just going to ignore it for now. - if err := cmd.Wait(); err != nil { - log.Println("[ERROR] failed to release resources: ", err) - } - - return 0, nil -} - -// buildEnv creates the meta-variables for the child process according -// to the CGI 1.1 specification: http://tools.ietf.org/html/rfc3875#section-4.1 -// cmdPath should be the path of the command being run. -// The returned string slice can be set to the command's Env property. -func buildEnv(cmdPath string, r *http.Request) (metavars []string, err error) { - if !strings.Contains(r.RemoteAddr, ":") { - r.RemoteAddr += ":" - } - remoteHost, remotePort, err := net.SplitHostPort(r.RemoteAddr) - if err != nil { - return - } - - if !strings.Contains(r.Host, ":") { - r.Host += ":" - } - serverHost, serverPort, err := net.SplitHostPort(r.Host) - if err != nil { - return - } - - metavars = []string{ - `AUTH_TYPE=`, // Not used - `CONTENT_LENGTH=`, // Not used - `CONTENT_TYPE=`, // Not used - `GATEWAY_INTERFACE=` + GatewayInterface, - `PATH_INFO=`, // TODO - `PATH_TRANSLATED=`, // TODO - `QUERY_STRING=` + r.URL.RawQuery, - `REMOTE_ADDR=` + remoteHost, - `REMOTE_HOST=` + remoteHost, // Host lookups are slow - don't do them - `REMOTE_IDENT=`, // Not used - `REMOTE_PORT=` + remotePort, - `REMOTE_USER=`, // Not used, - `REQUEST_METHOD=` + r.Method, - `REQUEST_URI=` + r.RequestURI, - `SCRIPT_NAME=` + cmdPath, // path of the program being executed - `SERVER_NAME=` + serverHost, - `SERVER_PORT=` + serverPort, - `SERVER_PROTOCOL=` + r.Proto, - `SERVER_SOFTWARE=` + ServerSoftware, - } - - // Add each HTTP header to the environment as well - for header, values := range r.Header { - value := strings.Join(values, ", ") - header = strings.ToUpper(header) - header = strings.Replace(header, "-", "_", -1) - value = strings.Replace(value, "\n", " ", -1) - metavars = append(metavars, "HTTP_"+header+"="+value) - } - - return -} - -// pumpStdin handles reading data from the websocket connection and writing -// it to stdin of the process. -func pumpStdin(conn *websocket.Conn, stdin io.WriteCloser) { - // Setup our connection's websocket ping/pong handlers from our const values. - defer conn.Close() - conn.SetReadLimit(maxMessageSize) - if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil { - log.Println("[ERROR] failed to set read deadline: ", err) - } - conn.SetPongHandler(func(string) error { - if err := conn.SetReadDeadline(time.Now().Add(pongWait)); err != nil { - log.Println("[ERROR] failed to set read deadline: ", err) - } - return nil - }) - for { - _, message, err := conn.ReadMessage() - if err != nil { - break - } - message = append(message, '\n') - if _, err := stdin.Write(message); err != nil { - break - } - } -} - -// pumpStdout handles reading data from stdout of the process and writing -// it to websocket connection. -func pumpStdout(conn *websocket.Conn, stdout io.Reader, done chan struct{}) { - go pinger(conn, done) - defer func() { - _ = conn.Close() - close(done) // make sure to close the pinger when we are done. - }() - - s := bufio.NewScanner(stdout) - for s.Scan() { - if err := conn.SetWriteDeadline(time.Now().Add(writeWait)); err != nil { - log.Println("[ERROR] failed to set write deadline: ", err) - } - if err := conn.WriteMessage(websocket.TextMessage, bytes.TrimSpace(s.Bytes())); err != nil { - break - } - } - if s.Err() != nil { - err := conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, s.Err().Error()), time.Time{}) - if err != nil { - log.Println("[ERROR] WriteControl failed: ", err) - } - } -} - -// pinger simulates the websocket to keep it alive with ping messages. -func pinger(conn *websocket.Conn, done chan struct{}) { - ticker := time.NewTicker(pingPeriod) - defer ticker.Stop() - - for { // blocking loop with select to wait for stimulation. - select { - case <-ticker.C: - if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil { - err := conn.WriteControl(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseGoingAway, err.Error()), time.Time{}) - if err != nil { - log.Println("[ERROR] WriteControl failed: ", err) - } - return - } - case <-done: - return // clean up this routine. - } - } -} diff --git a/vendor/github.com/mholt/caddy/caddytls/config.go b/vendor/github.com/mholt/caddy/caddytls/config.go deleted file mode 100644 index 9fca894f5..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/config.go +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddytls - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "os" - "sync/atomic" - "time" - - "github.com/go-acme/lego/challenge/tlsalpn01" - - "github.com/go-acme/lego/certcrypto" - "github.com/klauspost/cpuid" - "github.com/mholt/caddy" - "github.com/mholt/certmagic" -) - -// Config describes how TLS should be configured and used. -type Config struct { - // The hostname or class of hostnames this config is - // designated for; can contain wildcard characters - // according to RFC 6125 §6.4.3 - this field MUST - // be set in order for things to work as expected, - // must be normalized, and if an IP address, must - // be normalized - Hostname string - - // Whether TLS is enabled - Enabled bool - - // Minimum and maximum protocol versions to allow - ProtocolMinVersion uint16 - ProtocolMaxVersion uint16 - - // The list of cipher suites; first should be - // TLS_FALLBACK_SCSV to prevent degrade attacks - Ciphers []uint16 - - // Whether to prefer server cipher suites - PreferServerCipherSuites bool - - // The list of preferred curves - CurvePreferences []tls.CurveID - - // Client authentication policy - ClientAuth tls.ClientAuthType - - // List of client CA certificates to allow, if - // client authentication is enabled - ClientCerts []string - - // Manual means user provides own certs and keys - Manual bool - - // Managed means this config should be managed - // by the CertMagic Config (Manager field) - Managed bool - - // Manager is how certificates are managed - Manager *certmagic.Config - - // SelfSigned means that this hostname is - // served with a self-signed certificate - // that we generated in memory for convenience - SelfSigned bool - - // The email address to use when creating or - // using an ACME account (fun fact: if this - // is set to "off" then this config will not - // qualify for managed TLS) - ACMEEmail string - - // The list of protocols to choose from for Application Layer - // Protocol Negotiation (ALPN). - ALPN []string - - // The final tls.Config created with - // buildStandardTLSConfig() - tlsConfig *tls.Config -} - -// NewConfig returns a new Config with a pointer to the instance's -// certificate cache. You will usually need to set other fields on -// the returned Config for successful practical use. -func NewConfig(inst *caddy.Instance) (*Config, error) { - inst.StorageMu.RLock() - certCache, ok := inst.Storage[CertCacheInstStorageKey].(*certmagic.Cache) - inst.StorageMu.RUnlock() - if !ok || certCache == nil { - // set up the clustering plugin, if there is one (and there should always - // be one since this tls plugin requires it) -- this should be done exactly - // once, but we can't do it during init while plugins are still registering, - // so do it as soon as we run a setup) - if atomic.CompareAndSwapInt32(&clusterPluginSetup, 0, 1) { - clusterPluginName := os.Getenv("CADDY_CLUSTERING") - if clusterPluginName == "" { - clusterPluginName = "file" // name of default storage plugin - } - clusterFn, ok := clusterProviders[clusterPluginName] - if ok { - storage, err := clusterFn() - if err != nil { - return nil, fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err) - } - certmagic.Default.Storage = storage - } else { - return nil, fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName) - } - } - certCache = certmagic.NewCache(certmagic.CacheOptions{ - GetConfigForCert: func(cert certmagic.Certificate) (certmagic.Config, error) { - inst.StorageMu.Lock() - cfgMap, ok := inst.Storage[configMapKey].(map[string]*Config) - inst.StorageMu.Unlock() - if ok { - for hostname, cfg := range cfgMap { - if cfg.Manager != nil && hostname == cert.Names[0] { - return *cfg.Manager, nil - } - } - } - // returning Default not strictly necessary, since Default is used as template - // anyway; but this makes it clear that that's what we fall back to - return certmagic.Default, nil - }, - }) - storageCleaningTicker := time.NewTicker(12 * time.Hour) - go func() { - for range storageCleaningTicker.C { - certmagic.CleanStorage(certmagic.Default.Storage, certmagic.CleanStorageOptions{ - OCSPStaples: true, - }) - } - }() - inst.OnShutdown = append(inst.OnShutdown, func() error { - certCache.Stop() - storageCleaningTicker.Stop() - return nil - }) - - inst.StorageMu.Lock() - inst.Storage[CertCacheInstStorageKey] = certCache - inst.StorageMu.Unlock() - } - return &Config{ - Manager: certmagic.New(certCache, certmagic.Config{}), - }, nil -} - -// buildStandardTLSConfig converts cfg (*caddytls.Config) to a *tls.Config -// and stores it in cfg so it can be used in servers. If TLS is disabled, -// no tls.Config is created. -func (c *Config) buildStandardTLSConfig() error { - if !c.Enabled { - return nil - } - - config := new(tls.Config) - - ciphersAdded := make(map[uint16]struct{}) - curvesAdded := make(map[tls.CurveID]struct{}) - - // add cipher suites - for _, ciph := range c.Ciphers { - if _, ok := ciphersAdded[ciph]; !ok { - ciphersAdded[ciph] = struct{}{} - config.CipherSuites = append(config.CipherSuites, ciph) - } - } - - config.PreferServerCipherSuites = c.PreferServerCipherSuites - - // add curve preferences - for _, curv := range c.CurvePreferences { - if _, ok := curvesAdded[curv]; !ok { - curvesAdded[curv] = struct{}{} - config.CurvePreferences = append(config.CurvePreferences, curv) - } - } - - // ensure ALPN includes the ACME TLS-ALPN protocol - var alpnFound bool - for _, a := range c.ALPN { - if a == tlsalpn01.ACMETLS1Protocol { - alpnFound = true - break - } - } - if !alpnFound { - c.ALPN = append(c.ALPN, tlsalpn01.ACMETLS1Protocol) - } - - config.MinVersion = c.ProtocolMinVersion - config.MaxVersion = c.ProtocolMaxVersion - config.ClientAuth = c.ClientAuth - config.NextProtos = c.ALPN - config.GetCertificate = c.Manager.GetCertificate - - // set up client authentication if enabled - if config.ClientAuth != tls.NoClientCert { - pool := x509.NewCertPool() - clientCertsAdded := make(map[string]struct{}) - - for _, caFile := range c.ClientCerts { - // don't add cert to pool more than once - if _, ok := clientCertsAdded[caFile]; ok { - continue - } - clientCertsAdded[caFile] = struct{}{} - - // Any client with a certificate from this CA will be allowed to connect - caCrt, err := ioutil.ReadFile(caFile) - if err != nil { - return err - } - - if !pool.AppendCertsFromPEM(caCrt) { - return fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile) - } - } - - config.ClientCAs = pool - } - - // default cipher suites - if len(config.CipherSuites) == 0 { - config.CipherSuites = getPreferredDefaultCiphers() - } - - // for security, ensure TLS_FALLBACK_SCSV is always included first - if len(config.CipherSuites) == 0 || config.CipherSuites[0] != tls.TLS_FALLBACK_SCSV { - config.CipherSuites = append([]uint16{tls.TLS_FALLBACK_SCSV}, config.CipherSuites...) - } - - // store the resulting new tls.Config - c.tlsConfig = config - - return nil -} - -// MakeTLSConfig makes a tls.Config from configs. The returned -// tls.Config is programmed to load the matching caddytls.Config -// based on the hostname in SNI, but that's all. This is used -// to create a single TLS configuration for a listener (a group -// of sites). -func MakeTLSConfig(configs []*Config) (*tls.Config, error) { - if len(configs) == 0 { - return nil, nil - } - - configMap := make(configGroup) - - for i, cfg := range configs { - if cfg == nil { - // avoid nil pointer dereference below this loop - configs[i] = new(Config) - continue - } - - // can't serve TLS and non-TLS on same port - if i > 0 && cfg.Enabled != configs[i-1].Enabled { - thisConfProto, lastConfProto := "not TLS", "not TLS" - if cfg.Enabled { - thisConfProto = "TLS" - } - if configs[i-1].Enabled { - lastConfProto = "TLS" - } - return nil, fmt.Errorf("cannot multiplex %s (%s) and %s (%s) on same listener", - configs[i-1].Hostname, lastConfProto, cfg.Hostname, thisConfProto) - } - - // convert this caddytls.Config into a tls.Config - if err := cfg.buildStandardTLSConfig(); err != nil { - return nil, err - } - - // if an existing config with this hostname was already - // configured, then they must be identical (or at least - // compatible), otherwise that is a configuration error - if otherConfig, ok := configMap[cfg.Hostname]; ok { - if err := assertConfigsCompatible(cfg, otherConfig); err != nil { - return nil, fmt.Errorf("incompatible TLS configurations for the same SNI "+ - "name (%s) on the same listener: %v", - cfg.Hostname, err) - } - } - - // key this config by its hostname (overwrites - // configs with the same hostname pattern; should - // be OK since we already asserted they are roughly - // the same); during TLS handshakes, configs are - // loaded based on the hostname pattern according - // to client's ServerName (SNI) value - if cfg.Hostname == "0.0.0.0" || cfg.Hostname == "::" { - configMap[""] = cfg - } else { - configMap[cfg.Hostname] = cfg - } - } - - // Is TLS disabled? By now, we know that all - // configs agree whether it is or not, so we - // can just look at the first one. If so, - // we're done here. - if len(configs) == 0 || !configs[0].Enabled { - return nil, nil - } - - return &tls.Config{ - // A tls.Config must have Certificates or GetCertificate - // set, in order to be accepted by tls.Listen and quic.Listen. - // TODO: remove this once the standard library allows a tls.Config with - // only GetConfigForClient set. https://github.com/mholt/caddy/pull/2404 - GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) { - return nil, fmt.Errorf("all certificates configured via GetConfigForClient") - }, - GetConfigForClient: configMap.GetConfigForClient, - }, nil -} - -// assertConfigsCompatible returns an error if the two Configs -// do not have the same (or roughly compatible) configurations. -// If one of the tlsConfig pointers on either Config is nil, -// an error will be returned. If both are nil, no error. -func assertConfigsCompatible(cfg1, cfg2 *Config) error { - c1, c2 := cfg1.tlsConfig, cfg2.tlsConfig - - if (c1 == nil && c2 != nil) || (c1 != nil && c2 == nil) { - return fmt.Errorf("one config is not made") - } - if c1 == nil && c2 == nil { - return nil - } - - if len(c1.CipherSuites) != len(c2.CipherSuites) { - return fmt.Errorf("different number of allowed cipher suites") - } - for i, ciph := range c1.CipherSuites { - if c2.CipherSuites[i] != ciph { - return fmt.Errorf("different cipher suites or different order") - } - } - - if len(c1.CurvePreferences) != len(c2.CurvePreferences) { - return fmt.Errorf("different number of allowed cipher suites") - } - for i, curve := range c1.CurvePreferences { - if c2.CurvePreferences[i] != curve { - return fmt.Errorf("different curve preferences or different order") - } - } - - if len(c1.NextProtos) != len(c2.NextProtos) { - return fmt.Errorf("different number of ALPN (NextProtos) values") - } - for i, proto := range c1.NextProtos { - if c2.NextProtos[i] != proto { - return fmt.Errorf("different ALPN (NextProtos) values or different order") - } - } - - if c1.PreferServerCipherSuites != c2.PreferServerCipherSuites { - return fmt.Errorf("one prefers server cipher suites, the other does not") - } - if c1.MinVersion != c2.MinVersion { - return fmt.Errorf("minimum TLS version mismatch") - } - if c1.MaxVersion != c2.MaxVersion { - return fmt.Errorf("maximum TLS version mismatch") - } - if c1.ClientAuth != c2.ClientAuth { - return fmt.Errorf("client authentication policy mismatch") - } - if c1.ClientAuth != tls.NoClientCert && c2.ClientAuth != tls.NoClientCert && c1.ClientCAs != c2.ClientCAs { - // Two hosts defined on the same listener are not compatible if they - // have ClientAuth enabled, because there's no guarantee beyond the - // hostname which config will be used (because SNI only has server name). - // To prevent clients from bypassing authentication, require that - // ClientAuth be configured in an unambiguous manner. - return fmt.Errorf("multiple hosts requiring client authentication ambiguously configured") - } - - return nil -} - -// ConfigGetter gets a Config keyed by key. -type ConfigGetter func(c *caddy.Controller) *Config - -var configGetters = make(map[string]ConfigGetter) - -// RegisterConfigGetter registers fn as the way to get a -// Config for server type serverType. -func RegisterConfigGetter(serverType string, fn ConfigGetter) { - configGetters[serverType] = fn -} - -// SetDefaultTLSParams sets the default TLS cipher suites, protocol versions, -// and server preferences of a server.Config if they were not previously set -// (it does not overwrite; only fills in missing values). -func SetDefaultTLSParams(config *Config) { - // If no ciphers provided, use default list - if len(config.Ciphers) == 0 { - config.Ciphers = getPreferredDefaultCiphers() - } - - // Not a cipher suite, but still important for mitigating protocol downgrade attacks - // (prepend since having it at end breaks http2 due to non-h2-approved suites before it) - config.Ciphers = append([]uint16{tls.TLS_FALLBACK_SCSV}, config.Ciphers...) - - // If no curves provided, use default list - if len(config.CurvePreferences) == 0 { - config.CurvePreferences = defaultCurves - } - - // Set default protocol min and max versions - must balance compatibility and security - if config.ProtocolMinVersion == 0 { - config.ProtocolMinVersion = tls.VersionTLS12 - } - if config.ProtocolMaxVersion == 0 { - config.ProtocolMaxVersion = tls.VersionTLS13 - } - - // Prefer server cipher suites - config.PreferServerCipherSuites = true -} - -// Map of supported key types -var supportedKeyTypes = map[string]certcrypto.KeyType{ - "P384": certcrypto.EC384, - "P256": certcrypto.EC256, - "RSA4096": certcrypto.RSA4096, - "RSA2048": certcrypto.RSA2048, -} - -// SupportedProtocols is a map of supported protocols. -// HTTP/2 only supports TLS 1.2 and higher. -// If updating this map, also update tlsProtocolStringToMap in caddyhttp/fastcgi/fastcgi.go -var SupportedProtocols = map[string]uint16{ - "tls1.0": tls.VersionTLS10, - "tls1.1": tls.VersionTLS11, - "tls1.2": tls.VersionTLS12, - "tls1.3": tls.VersionTLS13, -} - -// GetSupportedProtocolName returns the protocol name -func GetSupportedProtocolName(protocol uint16) (string, error) { - for k, v := range SupportedProtocols { - if v == protocol { - return k, nil - } - } - - return "", fmt.Errorf("name: unsupported protocol") -} - -// SupportedCiphersMap has supported ciphers, used only for parsing config. -// -// Note that, at time of writing, HTTP/2 blacklists 276 cipher suites, -// including all but four of the suites below (the four GCM suites). -// See https://http2.github.io/http2-spec/#BadCipherSuites -// -// TLS_FALLBACK_SCSV is not in this list because we manually ensure -// it is always added (even though it is not technically a cipher suite). -// -// This map, like any map, is NOT ORDERED. Do not range over this map. -var SupportedCiphersMap = map[string]uint16{ - "ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "ECDHE-ECDSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - "ECDHE-RSA-WITH-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - "ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, -} - -// GetSupportedCipherName returns the cipher name -func GetSupportedCipherName(cipher uint16) (string, error) { - for k, v := range SupportedCiphersMap { - if v == cipher { - return k, nil - } - } - - return "", fmt.Errorf("name: unsupported cipher") -} - -// List of all the ciphers we want to use by default -var defaultCiphers = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, -} - -// List of ciphers we should prefer if native AESNI support is missing -var defaultCiphersNonAESNI = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, -} - -// getPreferredDefaultCiphers returns an appropriate cipher suite to use, depending on -// the hardware support available for AES-NI. -// -// See https://github.com/mholt/caddy/issues/1674 -func getPreferredDefaultCiphers() []uint16 { - if cpuid.CPU.AesNi() { - return defaultCiphers - } - - // Return a cipher suite that prefers ChaCha20 - return defaultCiphersNonAESNI -} - -// Map of supported curves -// https://golang.org/pkg/crypto/tls/#CurveID -var supportedCurvesMap = map[string]tls.CurveID{ - "X25519": tls.X25519, - "P256": tls.CurveP256, - "P384": tls.CurveP384, - "P521": tls.CurveP521, -} - -// List of all the curves we want to use by default. -// -// This list should only include curves which are fast by design (e.g. X25519) -// and those for which an optimized assembly implementation exists (e.g. P256). -// The latter ones can be found here: https://github.com/golang/go/tree/master/src/crypto/elliptic -var defaultCurves = []tls.CurveID{ - tls.X25519, - tls.CurveP256, -} - -var clusterPluginSetup int32 // access atomically - -// CertCacheInstStorageKey is the name of the key for -// accessing the certificate storage on the *caddy.Instance. -const CertCacheInstStorageKey = "tls_cert_cache" diff --git a/vendor/github.com/mholt/caddy/caddytls/crypto.go b/vendor/github.com/mholt/caddy/caddytls/crypto.go deleted file mode 100644 index ff2e9f912..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/crypto.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddytls - -import ( - "crypto/rand" - "crypto/tls" - "io" - "sync" - "time" -) - -// RotateSessionTicketKeys rotates the TLS session ticket keys -// on cfg every TicketRotateInterval. It spawns a new goroutine so -// this function does NOT block. It returns a channel you should -// close when you are ready to stop the key rotation, like when the -// server using cfg is no longer running. -// -// TODO: See about moving this into CertMagic and using its Storage -func RotateSessionTicketKeys(cfg *tls.Config) chan struct{} { - ch := make(chan struct{}) - ticker := time.NewTicker(TicketRotateInterval) - go runTLSTicketKeyRotation(cfg, ticker, ch) - return ch -} - -// Functions that may be swapped out for testing -var ( - runTLSTicketKeyRotation = standaloneTLSTicketKeyRotation - setSessionTicketKeysTestHook = func(keys [][32]byte) [][32]byte { return keys } - setSessionTicketKeysTestHookMu sync.Mutex -) - -// standaloneTLSTicketKeyRotation governs over the array of TLS ticket keys used to de/crypt TLS tickets. -// It periodically sets a new ticket key as the first one, used to encrypt (and decrypt), -// pushing any old ticket keys to the back, where they are considered for decryption only. -// -// Lack of entropy for the very first ticket key results in the feature being disabled (as does Go), -// later lack of entropy temporarily disables ticket key rotation. -// Old ticket keys are still phased out, though. -// -// Stops the ticker when returning. -func standaloneTLSTicketKeyRotation(c *tls.Config, ticker *time.Ticker, exitChan chan struct{}) { - defer ticker.Stop() - - // The entire page should be marked as sticky, but Go cannot do that - // without resorting to syscall#Mlock. And, we don't have madvise (for NODUMP), too. ☹ - keys := make([][32]byte, 1, NumTickets) - - rng := c.Rand - if rng == nil { - rng = rand.Reader - } - if _, err := io.ReadFull(rng, keys[0][:]); err != nil { - c.SessionTicketsDisabled = true // bail if we don't have the entropy for the first one - return - } - setSessionTicketKeysTestHookMu.Lock() - setSessionTicketKeysHook := setSessionTicketKeysTestHook - setSessionTicketKeysTestHookMu.Unlock() - c.SetSessionTicketKeys(setSessionTicketKeysHook(keys)) - - for { - select { - case _, isOpen := <-exitChan: - if !isOpen { - return - } - case <-ticker.C: - rng = c.Rand // could've changed since the start - if rng == nil { - rng = rand.Reader - } - var newTicketKey [32]byte - _, err := io.ReadFull(rng, newTicketKey[:]) - - if len(keys) < NumTickets { - keys = append(keys, keys[0]) // manipulates the internal length - } - for idx := len(keys) - 1; idx >= 1; idx-- { - keys[idx] = keys[idx-1] // yes, this makes copies - } - - if err == nil { - keys[0] = newTicketKey - } - // pushes the last key out, doesn't matter that we don't have a new one - c.SetSessionTicketKeys(setSessionTicketKeysHook(keys)) - } - } -} - -const ( - // NumTickets is how many tickets to hold and consider - // to decrypt TLS sessions. - NumTickets = 4 - - // TicketRotateInterval is how often to generate - // new ticket for TLS PFS encryption - TicketRotateInterval = 10 * time.Hour -) diff --git a/vendor/github.com/mholt/caddy/caddytls/handshake.go b/vendor/github.com/mholt/caddy/caddytls/handshake.go deleted file mode 100644 index 0cd8a15ad..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/handshake.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddytls - -import ( - "crypto/tls" - "fmt" - "log" - "net" - "strings" - - "github.com/mholt/caddy/telemetry" - "github.com/mholt/certmagic" -) - -// configGroup is a type that keys configs by their hostname -// (hostnames can have wildcard characters; use the getConfig -// method to get a config by matching its hostname). -type configGroup map[string]*Config - -// getConfig gets the config by the first key match for hello. -// In other words, "sub.foo.bar" will get the config for "*.foo.bar" -// if that is the closest match. If no match is found, the first -// (random) config will be loaded, which will defer any TLS alerts -// to the certificate validation (this may or may not be ideal; -// let's talk about it if this becomes problematic). -// -// This function follows nearly the same logic to lookup -// a hostname as the getCertificate function uses. -func (cg configGroup) getConfig(hello *tls.ClientHelloInfo) *Config { - name := certmagic.NormalizedName(hello.ServerName) - if name == "" { - name = certmagic.NormalizedName(certmagic.Default.DefaultServerName) - } - - // if SNI is empty, prefer matching IP address (it is - // more specific than a "catch-all" configuration) - if name == "" && hello.Conn != nil { - addr := hello.Conn.LocalAddr().String() - ip, _, err := net.SplitHostPort(addr) - if err == nil { - addr = ip - } - if config, ok := cg[addr]; ok { - return config - } - } - - // otherwise, try an exact match - if config, ok := cg[name]; ok { - return config - } - - // then try replacing labels in the name with - // wildcards until we get a match - labels := strings.Split(name, ".") - for i := range labels { - labels[i] = "*" - candidate := strings.Join(labels, ".") - if config, ok := cg[candidate]; ok { - return config - } - } - - // try a config that matches all names - this - // is needed to match configs defined without - // a specific host, like ":443", when SNI is - // a non-empty value - if config, ok := cg[""]; ok { - return config - } - - // failover with a random config: this is necessary - // because we might be needing to solve a TLS-ALPN - // ACME challenge for a name that we don't have a - // TLS configuration for; any config will do for - // this purpose - for _, config := range cg { - return config - } - - log.Printf("[ERROR] No TLS configuration available for ClientHello with ServerName: %s", hello.ServerName) - - return nil -} - -// GetConfigForClient gets a TLS configuration satisfying clientHello. -// In getting the configuration, it abides the rules and settings -// defined in the Config that matches clientHello.ServerName. If no -// tls.Config is set on the matching Config, a nil value is returned. -// -// This method is safe for use as a tls.Config.GetConfigForClient callback. -func (cg configGroup) GetConfigForClient(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { - config := cg.getConfig(clientHello) - if config != nil { - return config.tlsConfig, nil - } - return nil, nil -} - -// ClientHelloInfo is our own version of the standard lib's -// tls.ClientHelloInfo. As of May 2018, any fields populated -// by the Go standard library are not guaranteed to have their -// values in the original order as on the wire. -type ClientHelloInfo struct { - Version uint16 `json:"version,omitempty"` - CipherSuites []uint16 `json:"cipher_suites,omitempty"` - Extensions []uint16 `json:"extensions,omitempty"` - CompressionMethods []byte `json:"compression,omitempty"` - Curves []tls.CurveID `json:"curves,omitempty"` - Points []uint8 `json:"points,omitempty"` - - // Whether a couple of fields are unknown; if not, the key will encode - // differently to reflect that, as opposed to being known empty values. - // (some fields may be unknown depending on what package is being used; - // i.e. the Go standard lib doesn't expose some things) - // (very important to NOT encode these to JSON) - ExtensionsUnknown bool `json:"-"` - CompressionMethodsUnknown bool `json:"-"` -} - -// Key returns a standardized string form of the data in info, -// useful for identifying duplicates. -func (info ClientHelloInfo) Key() string { - extensions, compressionMethods := "?", "?" - if !info.ExtensionsUnknown { - extensions = fmt.Sprintf("%x", info.Extensions) - } - if !info.CompressionMethodsUnknown { - compressionMethods = fmt.Sprintf("%x", info.CompressionMethods) - } - return telemetry.FastHash([]byte(fmt.Sprintf("%x-%x-%s-%s-%x-%x", - info.Version, info.CipherSuites, extensions, - compressionMethods, info.Curves, info.Points))) -} - -// ClientHelloTelemetry determines whether to report -// TLS ClientHellos to telemetry. Disable if doing -// it from a different package. -var ClientHelloTelemetry = true diff --git a/vendor/github.com/mholt/caddy/caddytls/selfsigned.go b/vendor/github.com/mholt/caddy/caddytls/selfsigned.go deleted file mode 100644 index 639edc0c3..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/selfsigned.go +++ /dev/null @@ -1,103 +0,0 @@ -package caddytls - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - "math/big" - "net" - "strings" - "time" - - "github.com/go-acme/lego/certcrypto" -) - -// newSelfSignedCertificate returns a new self-signed certificate. -func newSelfSignedCertificate(ssconfig selfSignedConfig) (tls.Certificate, error) { - // start by generating private key - var privKey interface{} - var err error - switch ssconfig.KeyType { - case "", certcrypto.EC256: - privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case certcrypto.EC384: - privKey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case certcrypto.RSA2048: - privKey, err = rsa.GenerateKey(rand.Reader, 2048) - case certcrypto.RSA4096: - privKey, err = rsa.GenerateKey(rand.Reader, 4096) - case certcrypto.RSA8192: - privKey, err = rsa.GenerateKey(rand.Reader, 8192) - default: - return tls.Certificate{}, fmt.Errorf("cannot generate private key; unknown key type %v", ssconfig.KeyType) - } - if err != nil { - return tls.Certificate{}, fmt.Errorf("failed to generate private key: %v", err) - } - - // create certificate structure with proper values - notBefore := time.Now() - notAfter := ssconfig.Expire - if notAfter.IsZero() || notAfter.Before(notBefore) { - notAfter = notBefore.Add(24 * time.Hour * 7) - } - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return tls.Certificate{}, fmt.Errorf("failed to generate serial number: %v", err) - } - cert := &x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{Organization: []string{"Caddy Self-Signed"}}, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - } - if len(ssconfig.SAN) == 0 { - ssconfig.SAN = []string{""} - } - for _, san := range ssconfig.SAN { - if ip := net.ParseIP(san); ip != nil { - cert.IPAddresses = append(cert.IPAddresses, ip) - } else { - cert.DNSNames = append(cert.DNSNames, strings.ToLower(san)) - } - } - - // generate the associated public key - publicKey := func(privKey interface{}) interface{} { - switch k := privKey.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - default: - return fmt.Errorf("unknown key type") - } - } - derBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, publicKey(privKey), privKey) - if err != nil { - return tls.Certificate{}, fmt.Errorf("could not create certificate: %v", err) - } - - chain := [][]byte{derBytes} - - return tls.Certificate{ - Certificate: chain, - PrivateKey: privKey, - Leaf: cert, - }, nil -} - -// selfSignedConfig configures a self-signed certificate. -type selfSignedConfig struct { - SAN []string - KeyType certcrypto.KeyType - Expire time.Time -} diff --git a/vendor/github.com/mholt/caddy/caddytls/setup.go b/vendor/github.com/mholt/caddy/caddytls/setup.go deleted file mode 100644 index 3f0546904..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/setup.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddytls - -import ( - "bytes" - "crypto/tls" - "encoding/pem" - "fmt" - "io/ioutil" - "log" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - "sync/atomic" - - "github.com/mholt/caddy" - "github.com/mholt/caddy/telemetry" - "github.com/mholt/certmagic" -) - -func init() { - // opt-in TLS 1.3 for Go1.12 - // TODO: remove this line when Go1.13 is released. - if err := os.Setenv("GODEBUG", os.Getenv("GODEBUG")+",tls13=1"); err != nil { - log.Println("[ERROR] failed to set environment variable: ", err) - } - - caddy.RegisterPlugin("tls", caddy.Plugin{Action: setupTLS}) - - // ensure the default Storage implementation is plugged in - RegisterClusterPlugin("file", constructDefaultClusterPlugin) -} - -// setupTLS sets up the TLS configuration and installs certificates that -// are specified by the user in the config file. All the automatic HTTPS -// stuff comes later outside of this function. -func setupTLS(c *caddy.Controller) error { - // set up the clustering plugin, if there is one (and there should always - // be one since this tls plugin requires it) -- this should be done exactly - // once, but we can't do it during init while plugins are still registering, - // so do it as soon as we run a setup) - if atomic.CompareAndSwapInt32(&clusterPluginSetup, 0, 1) { - clusterPluginName := os.Getenv("CADDY_CLUSTERING") - if clusterPluginName == "" { - clusterPluginName = "file" // name of default storage plugin - } - clusterFn, ok := clusterProviders[clusterPluginName] - if ok { - storage, err := clusterFn() - if err != nil { - return fmt.Errorf("constructing cluster plugin %s: %v", clusterPluginName, err) - } - certmagic.Default.Storage = storage - } else { - return fmt.Errorf("unrecognized cluster plugin (was it included in the Caddy build?): %s", clusterPluginName) - } - } - - configGetter, ok := configGetters[c.ServerType()] - if !ok { - return fmt.Errorf("no caddytls.ConfigGetter for %s server type; must call RegisterConfigGetter", c.ServerType()) - } - config := configGetter(c) - if config == nil { - return fmt.Errorf("no caddytls.Config to set up for %s", c.Key) - } - - config.Enabled = true - - // we use certmagic events to collect metrics for telemetry - config.Manager.OnEvent = func(event string, data interface{}) { - switch event { - case "tls_handshake_started": - clientHello := data.(*tls.ClientHelloInfo) - if ClientHelloTelemetry && len(clientHello.SupportedVersions) > 0 { - // If no other plugin (such as the HTTP server type) is implementing ClientHello telemetry, we do it. - // NOTE: The values in the Go standard lib's ClientHelloInfo aren't guaranteed to be in order. - info := ClientHelloInfo{ - Version: clientHello.SupportedVersions[0], // report the highest - CipherSuites: clientHello.CipherSuites, - ExtensionsUnknown: true, // no extension info... :( - CompressionMethodsUnknown: true, // no compression methods... :( - Curves: clientHello.SupportedCurves, - Points: clientHello.SupportedPoints, - // We also have, but do not yet use: SignatureSchemes, ServerName, and SupportedProtos (ALPN) - // because the standard lib parses some extensions, but our MITM detector generally doesn't. - } - go telemetry.SetNested("tls_client_hello", info.Key(), info) - } - - case "tls_handshake_completed": - // TODO: This is a "best guess" for now - at this point, we only gave a - // certificate to the client; we need something listener-level to be sure - go telemetry.Increment("tls_handshake_count") - - case "acme_cert_obtained": - go telemetry.Increment("tls_acme_certs_obtained") - - case "acme_cert_renewed": - name := data.(string) - caddy.EmitEvent(caddy.CertRenewEvent, name) - go telemetry.Increment("tls_acme_certs_renewed") - - case "acme_cert_revoked": - telemetry.Increment("acme_certs_revoked") - - case "cached_managed_cert": - telemetry.Increment("tls_managed_cert_count") - - case "cached_unmanaged_cert": - telemetry.Increment("tls_unmanaged_cert_count") - } - } - - for c.Next() { - var certificateFile, keyFile, loadDir, maxCerts, askURL string - var onDemand bool - - args := c.RemainingArgs() - switch len(args) { - case 1: - // even if the email is one of the special values below, - // it is still necessary for future analysis that we store - // that value in the ACMEEmail field. - config.ACMEEmail = args[0] - - switch args[0] { - // user can force-disable managed TLS this way - case "off": - config.Enabled = false - return nil - // user might want a temporary, in-memory, self-signed cert - case "self_signed": - config.SelfSigned = true - default: - config.Manager.Email = args[0] - } - case 2: - certificateFile = args[0] - keyFile = args[1] - config.Manual = true - } - - // Optional block with extra parameters - var hadBlock bool - for c.NextBlock() { - hadBlock = true - switch c.Val() { - case "ca": - arg := c.RemainingArgs() - if len(arg) != 1 { - return c.ArgErr() - } - config.Manager.CA = arg[0] - case "key_type": - arg := c.RemainingArgs() - value, ok := supportedKeyTypes[strings.ToUpper(arg[0])] - if !ok { - return c.Errf("Wrong key type name or key type not supported: '%s'", c.Val()) - } - config.Manager.KeyType = value - case "protocols": - args := c.RemainingArgs() - if len(args) == 1 { - value, ok := SupportedProtocols[strings.ToLower(args[0])] - if !ok { - return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[0]) - } - config.ProtocolMinVersion, config.ProtocolMaxVersion = value, value - } else { - value, ok := SupportedProtocols[strings.ToLower(args[0])] - if !ok { - return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[0]) - } - config.ProtocolMinVersion = value - value, ok = SupportedProtocols[strings.ToLower(args[1])] - if !ok { - return c.Errf("Wrong protocol name or protocol not supported: '%s'", args[1]) - } - config.ProtocolMaxVersion = value - if config.ProtocolMinVersion > config.ProtocolMaxVersion { - return c.Errf("Minimum protocol version cannot be higher than maximum (reverse the order)") - } - } - case "ciphers": - for c.NextArg() { - value, ok := SupportedCiphersMap[strings.ToUpper(c.Val())] - if !ok { - return c.Errf("Wrong cipher name or cipher not supported: '%s'", c.Val()) - } - config.Ciphers = append(config.Ciphers, value) - } - case "curves": - for c.NextArg() { - value, ok := supportedCurvesMap[strings.ToUpper(c.Val())] - if !ok { - return c.Errf("Wrong curve name or curve not supported: '%s'", c.Val()) - } - config.CurvePreferences = append(config.CurvePreferences, value) - } - case "clients": - clientCertList := c.RemainingArgs() - if len(clientCertList) == 0 { - return c.ArgErr() - } - - listStart, mustProvideCA := 1, true - switch clientCertList[0] { - case "request": - config.ClientAuth = tls.RequestClientCert - mustProvideCA = false - case "require": - config.ClientAuth = tls.RequireAnyClientCert - mustProvideCA = false - case "verify_if_given": - config.ClientAuth = tls.VerifyClientCertIfGiven - default: - config.ClientAuth = tls.RequireAndVerifyClientCert - listStart = 0 - } - if mustProvideCA && len(clientCertList) <= listStart { - return c.ArgErr() - } - - config.ClientCerts = clientCertList[listStart:] - case "load": - c.Args(&loadDir) - config.Manual = true - case "max_certs": - c.Args(&maxCerts) - onDemand = true - case "ask": - c.Args(&askURL) - onDemand = true - case "dns": - args := c.RemainingArgs() - if len(args) != 1 { - return c.ArgErr() - } - // TODO: we can get rid of DNS provider plugins with this one line - // of code; however, currently (Dec. 2018) this adds about 20 MB - // of bloat to the Caddy binary, doubling its size to ~40 MB...! - // dnsProv, err := dns.NewDNSChallengeProviderByName(args[0]) - // if err != nil { - // return c.Errf("Configuring DNS provider '%s': %v", args[0], err) - // } - dnsProvName := args[0] - dnsProvConstructor, ok := dnsProviders[dnsProvName] - if !ok { - return c.Errf("Unknown DNS provider by name '%s'", dnsProvName) - } - dnsProv, err := dnsProvConstructor() - if err != nil { - return c.Errf("Setting up DNS provider '%s': %v", dnsProvName, err) - } - config.Manager.DNSProvider = dnsProv - case "alpn": - args := c.RemainingArgs() - if len(args) == 0 { - return c.ArgErr() - } - for _, arg := range args { - config.ALPN = append(config.ALPN, arg) - } - case "must_staple": - config.Manager.MustStaple = true - case "wildcard": - if !certmagic.HostQualifies(config.Hostname) { - return c.Errf("Hostname '%s' does not qualify for managed TLS, so cannot manage wildcard certificate for it", config.Hostname) - } - if strings.Contains(config.Hostname, "*") { - return c.Errf("Cannot convert domain name '%s' to a valid wildcard: already has a wildcard label", config.Hostname) - } - parts := strings.Split(config.Hostname, ".") - if len(parts) < 3 { - return c.Errf("Cannot convert domain name '%s' to a valid wildcard: too few labels", config.Hostname) - } - parts[0] = "*" - config.Hostname = strings.Join(parts, ".") - default: - return c.Errf("Unknown subdirective '%s'", c.Val()) - } - } - - // tls requires at least one argument if a block is not opened - if len(args) == 0 && !hadBlock { - return c.ArgErr() - } - - // configure on-demand TLS, if enabled - if onDemand { - config.Manager.OnDemand = new(certmagic.OnDemandConfig) - if maxCerts != "" { - maxCertsNum, err := strconv.Atoi(maxCerts) - if err != nil || maxCertsNum < 1 { - return c.Err("max_certs must be a positive integer") - } - config.Manager.OnDemand.MaxObtain = int32(maxCertsNum) - } - if askURL != "" { - parsedURL, err := url.Parse(askURL) - if err != nil { - return c.Err("ask must be a valid url") - } - if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { - return c.Err("ask URL must use http or https") - } - config.Manager.OnDemand.AskURL = parsedURL - } - } - - // don't try to load certificates unless we're supposed to - if !config.Enabled || !config.Manual { - continue - } - - // load a single certificate and key, if specified - if certificateFile != "" && keyFile != "" { - err := config.Manager.CacheUnmanagedCertificatePEMFile(certificateFile, keyFile) - if err != nil { - return c.Errf("Unable to load certificate and key files for '%s': %v", c.Key, err) - } - log.Printf("[INFO] Successfully loaded TLS assets from %s and %s", certificateFile, keyFile) - } - - // load a directory of certificates, if specified - if loadDir != "" { - err := loadCertsInDir(config, c, loadDir) - if err != nil { - return err - } - } - } - - SetDefaultTLSParams(config) - - // generate self-signed cert if needed - if config.SelfSigned { - ssCert, err := newSelfSignedCertificate(selfSignedConfig{ - SAN: []string{config.Hostname}, - KeyType: config.Manager.KeyType, - }) - if err != nil { - return fmt.Errorf("self-signed certificate generation: %v", err) - } - err = config.Manager.CacheUnmanagedTLSCertificate(ssCert) - if err != nil { - return fmt.Errorf("self-signed: %v", err) - } - telemetry.Increment("tls_self_signed_count") - } - - // store this as a custom config - cfgMap, ok := c.Get(configMapKey).(map[string]*Config) - if !ok || cfgMap == nil { - cfgMap = make(map[string]*Config) - } - cfgMap[config.Hostname] = config - c.Set(configMapKey, cfgMap) - - return nil -} - -// loadCertsInDir loads all the certificates/keys in dir, as long as -// the file ends with .pem. This method of loading certificates is -// modeled after haproxy, which expects the certificate and key to -// be bundled into the same file: -// https://cbonte.github.io/haproxy-dconv/configuration-1.5.html#5.1-crt -// -// This function may write to the log as it walks the directory tree. -func loadCertsInDir(cfg *Config, c *caddy.Controller, dir string) error { - return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { - if err != nil { - log.Printf("[WARNING] Unable to traverse into %s; skipping", path) - return nil - } - if info.IsDir() { - return nil - } - if strings.HasSuffix(strings.ToLower(info.Name()), ".pem") { - certBuilder, keyBuilder := new(bytes.Buffer), new(bytes.Buffer) - var foundKey bool // use only the first key in the file - - bundle, err := ioutil.ReadFile(path) - if err != nil { - return err - } - - for { - // Decode next block so we can see what type it is - var derBlock *pem.Block - derBlock, bundle = pem.Decode(bundle) - if derBlock == nil { - break - } - - if derBlock.Type == "CERTIFICATE" { - // Re-encode certificate as PEM, appending to certificate chain - if err := pem.Encode(certBuilder, derBlock); err != nil { - log.Println("[ERROR] failed to write PEM encoding: ", err) - } - } else if derBlock.Type == "EC PARAMETERS" { - // EC keys generated from openssl can be composed of two blocks: - // parameters and key (parameter block should come first) - if !foundKey { - // Encode parameters - if err := pem.Encode(keyBuilder, derBlock); err != nil { - log.Println("[ERROR] failed to write PEM encoding: ", err) - } - - // Key must immediately follow - derBlock, bundle = pem.Decode(bundle) - if derBlock == nil || derBlock.Type != "EC PRIVATE KEY" { - return c.Errf("%s: expected elliptic private key to immediately follow EC parameters", path) - } - if err := pem.Encode(keyBuilder, derBlock); err != nil { - log.Println("[ERROR] failed to write PEM encoding: ", err) - } - foundKey = true - } - } else if derBlock.Type == "PRIVATE KEY" || strings.HasSuffix(derBlock.Type, " PRIVATE KEY") { - // RSA key - if !foundKey { - if err := pem.Encode(keyBuilder, derBlock); err != nil { - log.Println("[ERROR] failed to write PEM encoding: ", err) - } - foundKey = true - } - } else { - return c.Errf("%s: unrecognized PEM block type: %s", path, derBlock.Type) - } - } - - certPEMBytes, keyPEMBytes := certBuilder.Bytes(), keyBuilder.Bytes() - if len(certPEMBytes) == 0 { - return c.Errf("%s: failed to parse PEM data", path) - } - if len(keyPEMBytes) == 0 { - return c.Errf("%s: no private key block found", path) - } - - err = cfg.Manager.CacheUnmanagedCertificatePEMBytes(certPEMBytes, keyPEMBytes) - if err != nil { - return c.Errf("%s: failed to load cert and key for '%s': %v", path, c.Key, err) - } - log.Printf("[INFO] Successfully loaded TLS assets from %s", path) - } - return nil - }) -} - -func constructDefaultClusterPlugin() (certmagic.Storage, error) { - return &certmagic.FileStorage{Path: caddy.AssetsPath()}, nil -} - -const configMapKey = "tls_custom_configs" diff --git a/vendor/github.com/mholt/caddy/caddytls/tls.go b/vendor/github.com/mholt/caddy/caddytls/tls.go deleted file mode 100644 index e419f6f4e..000000000 --- a/vendor/github.com/mholt/caddy/caddytls/tls.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddytls facilitates the management of TLS assets and integrates -// Let's Encrypt functionality into Caddy with first-class support for -// creating and renewing certificates automatically. It also implements -// the tls directive. It's mostly powered by the CertMagic package. -// -// This package is meant to be used by Caddy server types. To use the -// tls directive, a server type must import this package and call -// RegisterConfigGetter(). The server type must make and keep track of -// the caddytls.Config structs that this package produces. It must also -// add tls to its list of directives. When it comes time to make the -// server instances, the server type can call MakeTLSConfig() to convert -// a []caddytls.Config to a single tls.Config for use in tls.NewListener(). -// It is also recommended to call RotateSessionTicketKeys() when -// starting a new listener. -package caddytls - -import ( - "github.com/go-acme/lego/challenge" - "github.com/mholt/caddy" - "github.com/mholt/certmagic" -) - -// ConfigHolder is any type that has a Config; it presumably is -// connected to a hostname and port on which it is serving. -type ConfigHolder interface { - TLSConfig() *Config - Host() string - Port() string -} - -// QualifiesForManagedTLS returns true if c qualifies for -// for managed TLS (but not on-demand TLS specifically). -// It does NOT check to see if a cert and key already exist -// for the config. If the return value is true, you should -// be OK to set c.TLSConfig().Managed to true; then you should -// check that value in the future instead, because the process -// of setting up the config may make it look like it doesn't -// qualify even though it originally did. -func QualifiesForManagedTLS(c ConfigHolder) bool { - if c == nil { - return false - } - tlsConfig := c.TLSConfig() - if tlsConfig == nil || tlsConfig.Manager == nil { - return false - } - onDemand := tlsConfig.Manager.OnDemand != nil - - return (!tlsConfig.Manual || onDemand) && // user might provide own cert and key - - // if self-signed, we've already generated one to use - !tlsConfig.SelfSigned && - - // user can force-disable managed TLS - c.Port() != "80" && - tlsConfig.ACMEEmail != "off" && - - // we get can't certs for some kinds of hostnames, but - // on-demand TLS allows empty hostnames at startup - (certmagic.HostQualifies(c.Host()) || onDemand) -} - -// Revoke revokes the certificate fro host via the ACME protocol. -// It assumes the certificate was obtained from certmagic.CA. -func Revoke(domainName string) error { - return certmagic.NewDefault().RevokeCert(domainName, true) -} - -// KnownACMECAs is a list of ACME directory endpoints of -// known, public, and trusted ACME-compatible certificate -// authorities. -var KnownACMECAs = []string{ - "https://acme-v02.api.letsencrypt.org/directory", -} - -// ChallengeProvider defines an own type that should be used in Caddy plugins -// over challenge.Provider. Using challenge.Provider causes version mismatches -// with vendored dependencies (see https://github.com/mattfarina/golang-broken-vendor) -// -// challenge.Provider is an interface that allows the implementation of custom -// challenge providers. For more details, see: -// https://godoc.org/github.com/go-acme/lego/acme#ChallengeProvider -type ChallengeProvider challenge.Provider - -// DNSProviderConstructor is a function that takes credentials and -// returns a type that can solve the ACME DNS challenges. -type DNSProviderConstructor func(credentials ...string) (ChallengeProvider, error) - -// dnsProviders is the list of DNS providers that have been plugged in. -var dnsProviders = make(map[string]DNSProviderConstructor) - -// RegisterDNSProvider registers provider by name for solving the ACME DNS challenge. -func RegisterDNSProvider(name string, provider DNSProviderConstructor) { - dnsProviders[name] = provider - caddy.RegisterPlugin("tls.dns."+name, caddy.Plugin{}) -} - -// ClusterPluginConstructor is a function type that is used to -// instantiate a new implementation of both certmagic.Storage -// and certmagic.Locker, which are required for successful -// use in cluster environments. -type ClusterPluginConstructor func() (certmagic.Storage, error) - -// clusterProviders is the list of storage providers -var clusterProviders = make(map[string]ClusterPluginConstructor) - -// RegisterClusterPlugin registers provider by name for facilitating -// cluster-wide operations like storage and synchronization. -func RegisterClusterPlugin(name string, provider ClusterPluginConstructor) { - clusterProviders[name] = provider - caddy.RegisterPlugin("tls.cluster."+name, caddy.Plugin{}) -} diff --git a/vendor/github.com/mholt/caddy/commands.go b/vendor/github.com/mholt/caddy/commands.go deleted file mode 100644 index 74796e1df..000000000 --- a/vendor/github.com/mholt/caddy/commands.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "errors" - "runtime" - "unicode" - - "github.com/flynn/go-shlex" -) - -var runtimeGoos = runtime.GOOS - -// SplitCommandAndArgs takes a command string and parses it shell-style into the -// command and its separate arguments. -func SplitCommandAndArgs(command string) (cmd string, args []string, err error) { - var parts []string - - if runtimeGoos == "windows" { - parts = parseWindowsCommand(command) // parse it Windows-style - } else { - parts, err = parseUnixCommand(command) // parse it Unix-style - if err != nil { - err = errors.New("error parsing command: " + err.Error()) - return - } - } - - if len(parts) == 0 { - err = errors.New("no command contained in '" + command + "'") - return - } - - cmd = parts[0] - if len(parts) > 1 { - args = parts[1:] - } - - return -} - -// parseUnixCommand parses a unix style command line and returns the -// command and its arguments or an error -func parseUnixCommand(cmd string) ([]string, error) { - return shlex.Split(cmd) -} - -// parseWindowsCommand parses windows command lines and -// returns the command and the arguments as an array. It -// should be able to parse commonly used command lines. -// Only basic syntax is supported: -// - spaces in double quotes are not token delimiters -// - double quotes are escaped by either backspace or another double quote -// - except for the above case backspaces are path separators (not special) -// -// Many sources point out that escaping quotes using backslash can be unsafe. -// Use two double quotes when possible. (Source: http://stackoverflow.com/a/31413730/2616179 ) -// -// This function has to be used on Windows instead -// of the shlex package because this function treats backslash -// characters properly. -func parseWindowsCommand(cmd string) []string { - const backslash = '\\' - const quote = '"' - - var parts []string - var part string - var inQuotes bool - var lastRune rune - - for i, ch := range cmd { - - if i != 0 { - lastRune = rune(cmd[i-1]) - } - - if ch == backslash { - // put it in the part - for now we don't know if it's an - // escaping char or path separator - part += string(ch) - continue - } - - if ch == quote { - if lastRune == backslash { - // remove the backslash from the part and add the escaped quote instead - part = part[:len(part)-1] - part += string(ch) - continue - } - - if lastRune == quote { - // revert the last change of the inQuotes state - // it was an escaping quote - inQuotes = !inQuotes - part += string(ch) - continue - } - - // normal escaping quotes - inQuotes = !inQuotes - continue - - } - - if unicode.IsSpace(ch) && !inQuotes && len(part) > 0 { - parts = append(parts, part) - part = "" - continue - } - - part += string(ch) - } - - if len(part) > 0 { - parts = append(parts, part) - } - - return parts -} diff --git a/vendor/github.com/mholt/caddy/controller.go b/vendor/github.com/mholt/caddy/controller.go deleted file mode 100644 index af40a23a4..000000000 --- a/vendor/github.com/mholt/caddy/controller.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "strings" - - "github.com/mholt/caddy/caddyfile" -) - -// Controller is given to the setup function of directives which -// gives them access to be able to read tokens with which to -// configure themselves. It also stores state for the setup -// functions, can get the current context, and can be used to -// identify a particular server block using the Key field. -type Controller struct { - caddyfile.Dispenser - - // The instance in which the setup is occurring - instance *Instance - - // Key is the key from the top of the server block, usually - // an address, hostname, or identifier of some sort. - Key string - - // OncePerServerBlock is a function that executes f - // exactly once per server block, no matter how many - // hosts are associated with it. If it is the first - // time, the function f is executed immediately - // (not deferred) and may return an error which is - // returned by OncePerServerBlock. - OncePerServerBlock func(f func() error) error - - // ServerBlockIndex is the 0-based index of the - // server block as it appeared in the input. - ServerBlockIndex int - - // ServerBlockKeyIndex is the 0-based index of this - // key as it appeared in the input at the head of the - // server block. - ServerBlockKeyIndex int - - // ServerBlockKeys is a list of keys that are - // associated with this server block. All these - // keys, consequently, share the same tokens. - ServerBlockKeys []string - - // ServerBlockStorage is used by a directive's - // setup function to persist state between all - // the keys on a server block. - ServerBlockStorage interface{} -} - -// ServerType gets the name of the server type that is being set up. -func (c *Controller) ServerType() string { - return c.instance.serverType -} - -// OnFirstStartup adds fn to the list of callback functions to execute -// when the server is about to be started NOT as part of a restart. -func (c *Controller) OnFirstStartup(fn func() error) { - c.instance.OnFirstStartup = append(c.instance.OnFirstStartup, fn) -} - -// OnStartup adds fn to the list of callback functions to execute -// when the server is about to be started (including restarts). -func (c *Controller) OnStartup(fn func() error) { - c.instance.OnStartup = append(c.instance.OnStartup, fn) -} - -// OnRestart adds fn to the list of callback functions to execute -// when the server is about to be restarted. -func (c *Controller) OnRestart(fn func() error) { - c.instance.OnRestart = append(c.instance.OnRestart, fn) -} - -// OnRestartFailed adds fn to the list of callback functions to execute -// if the server failed to restart. -func (c *Controller) OnRestartFailed(fn func() error) { - c.instance.OnRestartFailed = append(c.instance.OnRestartFailed, fn) -} - -// OnShutdown adds fn to the list of callback functions to execute -// when the server is about to be shut down (including restarts). -func (c *Controller) OnShutdown(fn func() error) { - c.instance.OnShutdown = append(c.instance.OnShutdown, fn) -} - -// OnFinalShutdown adds fn to the list of callback functions to execute -// when the server is about to be shut down NOT as part of a restart. -func (c *Controller) OnFinalShutdown(fn func() error) { - c.instance.OnFinalShutdown = append(c.instance.OnFinalShutdown, fn) -} - -// Context gets the context associated with the instance associated with c. -func (c *Controller) Context() Context { - return c.instance.context -} - -// Get safely gets a value from the Instance's storage. -func (c *Controller) Get(key interface{}) interface{} { - c.instance.StorageMu.RLock() - defer c.instance.StorageMu.RUnlock() - return c.instance.Storage[key] -} - -// Set safely sets a value on the Instance's storage. -func (c *Controller) Set(key, val interface{}) { - c.instance.StorageMu.Lock() - c.instance.Storage[key] = val - c.instance.StorageMu.Unlock() -} - -// NewTestController creates a new Controller for -// the server type and input specified. The filename -// is "Testfile". If the server type is not empty and -// is plugged in, a context will be created so that -// the results of setup functions can be checked for -// correctness. -// -// Used only for testing, but exported so plugins can -// use this for convenience. -func NewTestController(serverType, input string) *Controller { - testInst := &Instance{serverType: serverType, Storage: make(map[interface{}]interface{})} - if stype, err := getServerType(serverType); err == nil { - testInst.context = stype.NewContext(testInst) - } - return &Controller{ - instance: testInst, - Dispenser: caddyfile.NewDispenser("Testfile", strings.NewReader(input)), - OncePerServerBlock: func(f func() error) error { return f() }, - } -} diff --git a/vendor/github.com/mholt/caddy/go.mod b/vendor/github.com/mholt/caddy/go.mod deleted file mode 100644 index 96d209aae..000000000 --- a/vendor/github.com/mholt/caddy/go.mod +++ /dev/null @@ -1,25 +0,0 @@ -module github.com/mholt/caddy - -go 1.12 - -require ( - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/dustin/go-humanize v1.0.0 - github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 - github.com/go-acme/lego v2.5.0+incompatible - github.com/google/uuid v1.1.1 - github.com/gorilla/websocket v1.4.0 - github.com/hashicorp/go-syslog v1.0.0 - github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a - github.com/klauspost/cpuid v1.2.0 - github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 // indirect - github.com/lucas-clemente/quic-go v0.10.2 - github.com/mholt/certmagic v0.5.0 - github.com/naoina/go-stringutil v0.1.0 // indirect - github.com/naoina/toml v0.1.1 - github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4 - golang.org/x/net v0.0.0-20190328230028-74de082e2cca - gopkg.in/mcuadros/go-syslog.v2 v2.2.1 - gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/yaml.v2 v2.2.2 -) diff --git a/vendor/github.com/mholt/caddy/go.sum b/vendor/github.com/mholt/caddy/go.sum deleted file mode 100644 index 8ba8dd93c..000000000 --- a/vendor/github.com/mholt/caddy/go.sum +++ /dev/null @@ -1,100 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115 h1:fUjoj2bT6dG8LoEe+uNsKk8J+sLkDbQkJnB6Z1F02Bc= -github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU= -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9 h1:a1zrFsLFac2xoM6zG1u72DWJwZG3ayttYLfmLbxVETk= -github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47 h1:UnszMmmmm5vLwWzDjTFVIkfhvWF1NdrmChl8L2NUDCw= -github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a h1:BcF8coBl0QFVhe8vAMMlD+CV8EISiu9MGKLoj6ZEyJA= -github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f h1:sSeNEkJrs+0F9TUau0CgWTTNEwF23HST3Eq0A+QIx+A= -github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04= -github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk= -github.com/lucas-clemente/quic-go v0.10.2 h1:iQtTSZVbd44k94Lu0U16lLBIG3lrnjDvQongjPd4B/s= -github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao= -github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced h1:zqEC1GJZFbGZA0tRyNZqRjep92K5fujFtFsu5ZW7Aug= -github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= -github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk= -github.com/mholt/certmagic v0.5.0 h1:lYXxsLUFya/I3BgDCrfuwcMQOB+4auzI8CCzpK41tjc= -github.com/mholt/certmagic v0.5.0/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY= -github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= -github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= -github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= -github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4 h1:S9YlS71UNJIyS61OqGAmLXv3w5zclSidN+qwr80XxKs= -github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190328230028-74de082e2cca h1:hyA6yiAgbUwuWqtscNvWAI7U1CtlaD1KilQ6iudt1aI= -golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e h1:ZytStCyV048ZqDsWHiYDdoI2Vd4msMcrDECFxS+tL9c= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1 h1:60g8zx1BijSVSgLTzLCW9UC4/+i1Ih9jJ1DR5Tgp9vE= -gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/mholt/caddy/onevent/hook/config.go b/vendor/github.com/mholt/caddy/onevent/hook/config.go deleted file mode 100644 index 5055e7eb4..000000000 --- a/vendor/github.com/mholt/caddy/onevent/hook/config.go +++ /dev/null @@ -1,20 +0,0 @@ -package hook - -import ( - "github.com/mholt/caddy" -) - -// Config describes how Hook should be configured and used. -type Config struct { - ID string - Event caddy.EventName - Command string - Args []string -} - -// SupportedEvents is a map of supported events. -var SupportedEvents = map[string]caddy.EventName{ - "startup": caddy.InstanceStartupEvent, - "shutdown": caddy.ShutdownEvent, - "certrenew": caddy.CertRenewEvent, -} diff --git a/vendor/github.com/mholt/caddy/onevent/hook/hook.go b/vendor/github.com/mholt/caddy/onevent/hook/hook.go deleted file mode 100644 index 9d2c4d0b5..000000000 --- a/vendor/github.com/mholt/caddy/onevent/hook/hook.go +++ /dev/null @@ -1,41 +0,0 @@ -package hook - -import ( - "log" - "os" - "os/exec" - "strings" - - "github.com/mholt/caddy" -) - -// Hook executes a command. -func (cfg *Config) Hook(event caddy.EventName, info interface{}) error { - if event != cfg.Event { - return nil - } - - nonblock := false - if len(cfg.Args) >= 1 && cfg.Args[len(cfg.Args)-1] == "&" { - // Run command in background; non-blocking - nonblock = true - cfg.Args = cfg.Args[:len(cfg.Args)-1] - } - - // Execute command. - cmd := exec.Command(cfg.Command, cfg.Args...) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if nonblock { - log.Printf("[INFO] Nonblocking Command \"%s %s\" with ID %s", cfg.Command, strings.Join(cfg.Args, " "), cfg.ID) - return cmd.Start() - } - log.Printf("[INFO] Blocking Command \"%s %s\" with ID %s", cfg.Command, strings.Join(cfg.Args, " "), cfg.ID) - err := cmd.Run() - if err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/mholt/caddy/onevent/on.go b/vendor/github.com/mholt/caddy/onevent/on.go deleted file mode 100644 index 473cba55d..000000000 --- a/vendor/github.com/mholt/caddy/onevent/on.go +++ /dev/null @@ -1,71 +0,0 @@ -package onevent - -import ( - "strings" - - "github.com/google/uuid" - "github.com/mholt/caddy" - "github.com/mholt/caddy/onevent/hook" -) - -func init() { - // Register Directive. - caddy.RegisterPlugin("on", caddy.Plugin{Action: setup}) -} - -func setup(c *caddy.Controller) error { - config, err := onParse(c) - if err != nil { - return err - } - - // Register Event Hooks. - err = c.OncePerServerBlock(func() error { - for _, cfg := range config { - caddy.RegisterEventHook("on-"+cfg.ID, cfg.Hook) - } - return nil - }) - if err != nil { - return err - } - - return nil -} - -func onParse(c *caddy.Controller) ([]*hook.Config, error) { - var config []*hook.Config - - for c.Next() { - cfg := new(hook.Config) - - if !c.NextArg() { - return config, c.ArgErr() - } - - // Configure Event. - event, ok := hook.SupportedEvents[strings.ToLower(c.Val())] - if !ok { - return config, c.Errf("Wrong event name or event not supported: '%s'", c.Val()) - } - cfg.Event = event - - // Assign an unique ID. - cfg.ID = uuid.New().String() - - args := c.RemainingArgs() - - // Extract command and arguments. - command, args, err := caddy.SplitCommandAndArgs(strings.Join(args, " ")) - if err != nil { - return config, c.Err(err.Error()) - } - - cfg.Command = command - cfg.Args = args - - config = append(config, cfg) - } - - return config, nil -} diff --git a/vendor/github.com/mholt/caddy/plugins.go b/vendor/github.com/mholt/caddy/plugins.go deleted file mode 100644 index cd4497fa9..000000000 --- a/vendor/github.com/mholt/caddy/plugins.go +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "fmt" - "log" - "net" - "sort" - "sync" - - "github.com/mholt/caddy/caddyfile" -) - -// These are all the registered plugins. -var ( - // serverTypes is a map of registered server types. - serverTypes = make(map[string]ServerType) - - // plugins is a map of server type to map of plugin name to - // Plugin. These are the "general" plugins that may or may - // not be associated with a specific server type. If it's - // applicable to multiple server types or the server type is - // irrelevant, the key is empty string (""). But all plugins - // must have a name. - plugins = make(map[string]map[string]Plugin) - - // eventHooks is a map of hook name to Hook. All hooks plugins - // must have a name. - eventHooks = &sync.Map{} - - // parsingCallbacks maps server type to map of directive - // to list of callback functions. These aren't really - // plugins on their own, but are often registered from - // plugins. - parsingCallbacks = make(map[string]map[string][]ParsingCallback) - - // caddyfileLoaders is the list of all Caddyfile loaders - // in registration order. - caddyfileLoaders []caddyfileLoader -) - -// DescribePlugins returns a string describing the registered plugins. -func DescribePlugins() string { - pl := ListPlugins() - - str := "Server types:\n" - for _, name := range pl["server_types"] { - str += " " + name + "\n" - } - - str += "\nCaddyfile loaders:\n" - for _, name := range pl["caddyfile_loaders"] { - str += " " + name + "\n" - } - - if len(pl["event_hooks"]) > 0 { - str += "\nEvent hook plugins:\n" - for _, name := range pl["event_hooks"] { - str += " hook." + name + "\n" - } - } - - if len(pl["clustering"]) > 0 { - str += "\nClustering plugins:\n" - for _, name := range pl["clustering"] { - str += " " + name + "\n" - } - } - - str += "\nOther plugins:\n" - for _, name := range pl["others"] { - str += " " + name + "\n" - } - - return str -} - -// ListPlugins makes a list of the registered plugins, -// keyed by plugin type. -func ListPlugins() map[string][]string { - p := make(map[string][]string) - - // server type plugins - for name := range serverTypes { - p["server_types"] = append(p["server_types"], name) - } - - // caddyfile loaders in registration order - for _, loader := range caddyfileLoaders { - p["caddyfile_loaders"] = append(p["caddyfile_loaders"], loader.name) - } - if defaultCaddyfileLoader.name != "" { - p["caddyfile_loaders"] = append(p["caddyfile_loaders"], defaultCaddyfileLoader.name) - } - - // List the event hook plugins - eventHooks.Range(func(k, _ interface{}) bool { - p["event_hooks"] = append(p["event_hooks"], k.(string)) - return true - }) - - // alphabetize the rest of the plugins - var others []string - for stype, stypePlugins := range plugins { - for name := range stypePlugins { - var s string - if stype != "" { - s = stype + "." - } - s += name - others = append(others, s) - } - } - - sort.Strings(others) - for _, name := range others { - p["others"] = append(p["others"], name) - } - - return p -} - -// ValidDirectives returns the list of all directives that are -// recognized for the server type serverType. However, not all -// directives may be installed. This makes it possible to give -// more helpful error messages, like "did you mean ..." or -// "maybe you need to plug in ...". -func ValidDirectives(serverType string) []string { - stype, err := getServerType(serverType) - if err != nil { - return nil - } - return stype.Directives() -} - -// ServerListener pairs a server to its listener and/or packetconn. -type ServerListener struct { - server Server - listener net.Listener - packet net.PacketConn -} - -// LocalAddr returns the local network address of the packetconn. It returns -// nil when it is not set. -func (s ServerListener) LocalAddr() net.Addr { - if s.packet == nil { - return nil - } - return s.packet.LocalAddr() -} - -// Addr returns the listener's network address. It returns nil when it is -// not set. -func (s ServerListener) Addr() net.Addr { - if s.listener == nil { - return nil - } - return s.listener.Addr() -} - -// Context is a type which carries a server type through -// the load and setup phase; it maintains the state -// between loading the Caddyfile, then executing its -// directives, then making the servers for Caddy to -// manage. Typically, such state involves configuration -// structs, etc. -type Context interface { - // Called after the Caddyfile is parsed into server - // blocks but before the directives are executed, - // this method gives you an opportunity to inspect - // the server blocks and prepare for the execution - // of directives. Return the server blocks (which - // you may modify, if desired) and an error, if any. - // The first argument is the name or path to the - // configuration file (Caddyfile). - // - // This function can be a no-op and simply return its - // input if there is nothing to do here. - InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) - - // This is what Caddy calls to make server instances. - // By this time, all directives have been executed and, - // presumably, the context has enough state to produce - // server instances for Caddy to start. - MakeServers() ([]Server, error) -} - -// RegisterServerType registers a server type srv by its -// name, typeName. -func RegisterServerType(typeName string, srv ServerType) { - if _, ok := serverTypes[typeName]; ok { - panic("server type already registered") - } - serverTypes[typeName] = srv -} - -// ServerType contains information about a server type. -type ServerType struct { - // Function that returns the list of directives, in - // execution order, that are valid for this server - // type. Directives should be one word if possible - // and lower-cased. - Directives func() []string - - // DefaultInput returns a default config input if none - // is otherwise loaded. This is optional, but highly - // recommended, otherwise a blank Caddyfile will be - // used. - DefaultInput func() Input - - // The function that produces a new server type context. - // This will be called when a new Caddyfile is being - // loaded, parsed, and executed independently of any - // startup phases before this one. It's a way to keep - // each set of server instances separate and to reduce - // the amount of global state you need. - NewContext func(inst *Instance) Context -} - -// Plugin is a type which holds information about a plugin. -type Plugin struct { - // ServerType is the type of server this plugin is for. - // Can be empty if not applicable, or if the plugin - // can associate with any server type. - ServerType string - - // Action is the plugin's setup function, if associated - // with a directive in the Caddyfile. - Action SetupFunc -} - -// RegisterPlugin plugs in plugin. All plugins should register -// themselves, even if they do not perform an action associated -// with a directive. It is important for the process to know -// which plugins are available. -// -// The plugin MUST have a name: lower case and one word. -// If this plugin has an action, it must be the name of -// the directive that invokes it. A name is always required -// and must be unique for the server type. -func RegisterPlugin(name string, plugin Plugin) { - if name == "" { - panic("plugin must have a name") - } - if _, ok := plugins[plugin.ServerType]; !ok { - plugins[plugin.ServerType] = make(map[string]Plugin) - } - if _, dup := plugins[plugin.ServerType][name]; dup { - panic("plugin named " + name + " already registered for server type " + plugin.ServerType) - } - plugins[plugin.ServerType][name] = plugin -} - -// EventName represents the name of an event used with event hooks. -type EventName string - -// Define names for the various events -const ( - StartupEvent EventName = "startup" - ShutdownEvent = "shutdown" - CertRenewEvent = "certrenew" - InstanceStartupEvent = "instancestartup" - InstanceRestartEvent = "instancerestart" -) - -// EventHook is a type which holds information about a startup hook plugin. -type EventHook func(eventType EventName, eventInfo interface{}) error - -// RegisterEventHook plugs in hook. All the hooks should register themselves -// and they must have a name. -func RegisterEventHook(name string, hook EventHook) { - if name == "" { - panic("event hook must have a name") - } - _, dup := eventHooks.LoadOrStore(name, hook) - if dup { - panic("hook named " + name + " already registered") - } -} - -// EmitEvent executes the different hooks passing the EventType as an -// argument. This is a blocking function. Hook developers should -// use 'go' keyword if they don't want to block Caddy. -func EmitEvent(event EventName, info interface{}) { - eventHooks.Range(func(k, v interface{}) bool { - err := v.(EventHook)(event, info) - if err != nil { - log.Printf("error on '%s' hook: %v", k.(string), err) - } - return true - }) -} - -// cloneEventHooks return a clone of the event hooks *sync.Map -func cloneEventHooks() *sync.Map { - c := &sync.Map{} - eventHooks.Range(func(k, v interface{}) bool { - c.Store(k, v) - return true - }) - return c -} - -// purgeEventHooks purges all event hooks from the map -func purgeEventHooks() { - eventHooks.Range(func(k, _ interface{}) bool { - eventHooks.Delete(k) - return true - }) -} - -// restoreEventHooks restores eventHooks with a provided *sync.Map -func restoreEventHooks(m *sync.Map) { - // Purge old event hooks - purgeEventHooks() - - // Restore event hooks - m.Range(func(k, v interface{}) bool { - eventHooks.Store(k, v) - return true - }) -} - -// ParsingCallback is a function that is called after -// a directive's setup functions have been executed -// for all the server blocks. -type ParsingCallback func(Context) error - -// RegisterParsingCallback registers callback to be called after -// executing the directive afterDir for server type serverType. -func RegisterParsingCallback(serverType, afterDir string, callback ParsingCallback) { - if _, ok := parsingCallbacks[serverType]; !ok { - parsingCallbacks[serverType] = make(map[string][]ParsingCallback) - } - parsingCallbacks[serverType][afterDir] = append(parsingCallbacks[serverType][afterDir], callback) -} - -// SetupFunc is used to set up a plugin, or in other words, -// execute a directive. It will be called once per key for -// each server block it appears in. -type SetupFunc func(c *Controller) error - -// DirectiveAction gets the action for directive dir of -// server type serverType. -func DirectiveAction(serverType, dir string) (SetupFunc, error) { - if stypePlugins, ok := plugins[serverType]; ok { - if plugin, ok := stypePlugins[dir]; ok { - return plugin.Action, nil - } - } - if genericPlugins, ok := plugins[""]; ok { - if plugin, ok := genericPlugins[dir]; ok { - return plugin.Action, nil - } - } - return nil, fmt.Errorf("no action found for directive '%s' with server type '%s' (missing a plugin?)", - dir, serverType) -} - -// Loader is a type that can load a Caddyfile. -// It is passed the name of the server type. -// It returns an error only if something went -// wrong, not simply if there is no Caddyfile -// for this loader to load. -// -// A Loader should only load the Caddyfile if -// a certain condition or requirement is met, -// as returning a non-nil Input value along with -// another Loader will result in an error. -// In other words, loading the Caddyfile must -// be deliberate & deterministic, not haphazard. -// -// The exception is the default Caddyfile loader, -// which will be called only if no other Caddyfile -// loaders return a non-nil Input. The default -// loader may always return an Input value. -type Loader interface { - Load(serverType string) (Input, error) -} - -// LoaderFunc is a convenience type similar to http.HandlerFunc -// that allows you to use a plain function as a Load() method. -type LoaderFunc func(serverType string) (Input, error) - -// Load loads a Caddyfile. -func (lf LoaderFunc) Load(serverType string) (Input, error) { - return lf(serverType) -} - -// RegisterCaddyfileLoader registers loader named name. -func RegisterCaddyfileLoader(name string, loader Loader) { - caddyfileLoaders = append(caddyfileLoaders, caddyfileLoader{name: name, loader: loader}) -} - -// SetDefaultCaddyfileLoader registers loader by name -// as the default Caddyfile loader if no others produce -// a Caddyfile. If another Caddyfile loader has already -// been set as the default, this replaces it. -// -// Do not call RegisterCaddyfileLoader on the same -// loader; that would be redundant. -func SetDefaultCaddyfileLoader(name string, loader Loader) { - defaultCaddyfileLoader = caddyfileLoader{name: name, loader: loader} -} - -// loadCaddyfileInput iterates the registered Caddyfile loaders -// and, if needed, calls the default loader, to load a Caddyfile. -// It is an error if any of the loaders return an error or if -// more than one loader returns a Caddyfile. -func loadCaddyfileInput(serverType string) (Input, error) { - var loadedBy string - var caddyfileToUse Input - for _, l := range caddyfileLoaders { - cdyfile, err := l.loader.Load(serverType) - if err != nil { - return nil, fmt.Errorf("loading Caddyfile via %s: %v", l.name, err) - } - if cdyfile != nil { - if caddyfileToUse != nil { - return nil, fmt.Errorf("Caddyfile loaded multiple times; first by %s, then by %s", loadedBy, l.name) - } - loaderUsed = l - caddyfileToUse = cdyfile - loadedBy = l.name - } - } - if caddyfileToUse == nil && defaultCaddyfileLoader.loader != nil { - cdyfile, err := defaultCaddyfileLoader.loader.Load(serverType) - if err != nil { - return nil, err - } - if cdyfile != nil { - loaderUsed = defaultCaddyfileLoader - caddyfileToUse = cdyfile - } - } - return caddyfileToUse, nil -} - -// OnProcessExit is a list of functions to run when the process -// exits -- they are ONLY for cleanup and should not block, -// return errors, or do anything fancy. They will be run with -// every signal, even if "shutdown callbacks" are not executed. -// This variable must only be modified in the main goroutine -// from init() functions. -var OnProcessExit []func() - -// caddyfileLoader pairs the name of a loader to the loader. -type caddyfileLoader struct { - name string - loader Loader -} - -var ( - defaultCaddyfileLoader caddyfileLoader // the default loader if all else fail - loaderUsed caddyfileLoader // the loader that was used (relevant for reloads) -) diff --git a/vendor/github.com/mholt/caddy/rlimit_nonposix.go b/vendor/github.com/mholt/caddy/rlimit_nonposix.go deleted file mode 100644 index 05ca942d3..000000000 --- a/vendor/github.com/mholt/caddy/rlimit_nonposix.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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. - -// +build windows plan9 nacl js - -package caddy - -// checkFdlimit issues a warning if the OS limit for -// max file descriptors is below a recommended minimum. -func checkFdlimit() { -} diff --git a/vendor/github.com/mholt/caddy/rlimit_posix.go b/vendor/github.com/mholt/caddy/rlimit_posix.go deleted file mode 100644 index 9e85bf661..000000000 --- a/vendor/github.com/mholt/caddy/rlimit_posix.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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. - -// +build !windows,!plan9,!nacl,!js - -package caddy - -import ( - "fmt" - "syscall" -) - -// checkFdlimit issues a warning if the OS limit for -// max file descriptors is below a recommended minimum. -func checkFdlimit() { - const min = 8192 - - // Warn if ulimit is too low for production sites - rlimit := &syscall.Rlimit{} - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimit) - if err == nil && rlimit.Cur < min { - fmt.Printf("WARNING: File descriptor limit %d is too low for production servers. "+ - "At least %d is recommended. Fix with `ulimit -n %d`.\n", rlimit.Cur, min, min) - } - -} diff --git a/vendor/github.com/mholt/caddy/sigtrap.go b/vendor/github.com/mholt/caddy/sigtrap.go deleted file mode 100644 index dbb01b7ec..000000000 --- a/vendor/github.com/mholt/caddy/sigtrap.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "log" - "os" - "os/signal" - "sync" - - "github.com/mholt/caddy/telemetry" -) - -// TrapSignals create signal handlers for all applicable signals for this -// system. If your Go program uses signals, this is a rather invasive -// function; best to implement them yourself in that case. Signals are not -// required for the caddy package to function properly, but this is a -// convenient way to allow the user to control this part of your program. -func TrapSignals() { - trapSignalsCrossPlatform() - trapSignalsPosix() -} - -// trapSignalsCrossPlatform captures SIGINT, which triggers forceful -// shutdown that executes shutdown callbacks first. A second interrupt -// signal will exit the process immediately. -func trapSignalsCrossPlatform() { - go func() { - shutdown := make(chan os.Signal, 1) - signal.Notify(shutdown, os.Interrupt) - - for i := 0; true; i++ { - <-shutdown - - if i > 0 { - log.Println("[INFO] SIGINT: Force quit") - for _, f := range OnProcessExit { - f() // important cleanup actions only - } - os.Exit(2) - } - - log.Println("[INFO] SIGINT: Shutting down") - - telemetry.AppendUnique("sigtrap", "SIGINT") - go telemetry.StopEmitting() // not guaranteed to finish in time; that's OK (just don't block!) - - // important cleanup actions before shutdown callbacks - for _, f := range OnProcessExit { - f() - } - - go func() { - os.Exit(executeShutdownCallbacks("SIGINT")) - }() - } - }() -} - -// executeShutdownCallbacks executes the shutdown callbacks as initiated -// by signame. It logs any errors and returns the recommended exit status. -// This function is idempotent; subsequent invocations always return 0. -func executeShutdownCallbacks(signame string) (exitCode int) { - shutdownCallbacksOnce.Do(func() { - // execute third-party shutdown hooks - EmitEvent(ShutdownEvent, signame) - - errs := allShutdownCallbacks() - if len(errs) > 0 { - for _, err := range errs { - log.Printf("[ERROR] %s shutdown: %v", signame, err) - } - exitCode = 4 - } - }) - return -} - -// allShutdownCallbacks executes all the shutdown callbacks -// for all the instances, and returns all the errors generated -// during their execution. An error executing one shutdown -// callback does not stop execution of others. Only one shutdown -// callback is executed at a time. -func allShutdownCallbacks() []error { - var errs []error - instancesMu.Lock() - for _, inst := range instances { - errs = append(errs, inst.ShutdownCallbacks()...) - } - instancesMu.Unlock() - return errs -} - -// shutdownCallbacksOnce ensures that shutdown callbacks -// for all instances are only executed once. -var shutdownCallbacksOnce sync.Once diff --git a/vendor/github.com/mholt/caddy/sigtrap_nonposix.go b/vendor/github.com/mholt/caddy/sigtrap_nonposix.go deleted file mode 100644 index 517e3a2e4..000000000 --- a/vendor/github.com/mholt/caddy/sigtrap_nonposix.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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. - -// +build windows plan9 nacl js - -package caddy - -func trapSignalsPosix() {} diff --git a/vendor/github.com/mholt/caddy/sigtrap_posix.go b/vendor/github.com/mholt/caddy/sigtrap_posix.go deleted file mode 100644 index fb9703611..000000000 --- a/vendor/github.com/mholt/caddy/sigtrap_posix.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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. - -// +build !windows,!plan9,!nacl,!js - -package caddy - -import ( - "log" - "os" - "os/signal" - "syscall" - - "github.com/mholt/caddy/telemetry" -) - -// trapSignalsPosix captures POSIX-only signals. -func trapSignalsPosix() { - go func() { - sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2) - - for sig := range sigchan { - switch sig { - case syscall.SIGQUIT: - log.Println("[INFO] SIGQUIT: Quitting process immediately") - for _, f := range OnProcessExit { - f() // only perform important cleanup actions - } - os.Exit(0) - - case syscall.SIGTERM: - log.Println("[INFO] SIGTERM: Shutting down servers then terminating") - exitCode := executeShutdownCallbacks("SIGTERM") - for _, f := range OnProcessExit { - f() // only perform important cleanup actions - } - err := Stop() - if err != nil { - log.Printf("[ERROR] SIGTERM stop: %v", err) - exitCode = 3 - } - - telemetry.AppendUnique("sigtrap", "SIGTERM") - go telemetry.StopEmitting() // won't finish in time, but that's OK - just don't block - - os.Exit(exitCode) - - case syscall.SIGUSR1: - log.Println("[INFO] SIGUSR1: Reloading") - go telemetry.AppendUnique("sigtrap", "SIGUSR1") - - // Start with the existing Caddyfile - caddyfileToUse, inst, err := getCurrentCaddyfile() - if err != nil { - log.Printf("[ERROR] SIGUSR1: %v", err) - continue - } - if loaderUsed.loader == nil { - // This also should never happen - log.Println("[ERROR] SIGUSR1: no Caddyfile loader with which to reload Caddyfile") - continue - } - - // Load the updated Caddyfile - newCaddyfile, err := loaderUsed.loader.Load(inst.serverType) - if err != nil { - log.Printf("[ERROR] SIGUSR1: loading updated Caddyfile: %v", err) - continue - } - if newCaddyfile != nil { - caddyfileToUse = newCaddyfile - } - - // Backup old event hooks - oldEventHooks := cloneEventHooks() - - // Purge the old event hooks - purgeEventHooks() - - // Kick off the restart; our work is done - EmitEvent(InstanceRestartEvent, nil) - _, err = inst.Restart(caddyfileToUse) - if err != nil { - restoreEventHooks(oldEventHooks) - - log.Printf("[ERROR] SIGUSR1: %v", err) - } - - case syscall.SIGUSR2: - log.Println("[INFO] SIGUSR2: Upgrading") - go telemetry.AppendUnique("sigtrap", "SIGUSR2") - if err := Upgrade(); err != nil { - log.Printf("[ERROR] SIGUSR2: upgrading: %v", err) - } - - case syscall.SIGHUP: - // ignore; this signal is sometimes sent outside of the user's control - go telemetry.AppendUnique("sigtrap", "SIGHUP") - } - } - }() -} diff --git a/vendor/github.com/mholt/caddy/telemetry/collection.go b/vendor/github.com/mholt/caddy/telemetry/collection.go deleted file mode 100644 index 8b05d17d1..000000000 --- a/vendor/github.com/mholt/caddy/telemetry/collection.go +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 telemetry - -import ( - "fmt" - "hash/fnv" - "log" - "strings" - - "github.com/google/uuid" -) - -// Init initializes this package so that it may -// be used. Do not call this function more than -// once. Init panics if it is called more than -// once or if the UUID value is empty. Once this -// function is called, the rest of the package -// may safely be used. If this function is not -// called, the collector functions may still be -// invoked, but they will be no-ops. -// -// Any metrics keys that are passed in the second -// argument will be permanently disabled for the -// lifetime of the process. -func Init(instanceID uuid.UUID, disabledMetricsKeys []string) { - if enabled { - panic("already initialized") - } - if str := instanceID.String(); str == "" || - str == "00000000-0000-0000-0000-000000000000" { - panic("empty UUID") - } - instanceUUID = instanceID - disabledMetricsMu.Lock() - for _, key := range disabledMetricsKeys { - disabledMetrics[strings.TrimSpace(key)] = false - } - disabledMetricsMu.Unlock() - enabled = true -} - -// StartEmitting sends the current payload and begins the -// transmission cycle for updates. This is the first -// update sent, and future ones will be sent until -// StopEmitting is called. -// -// This function is non-blocking (it spawns a new goroutine). -// -// This function panics if it was called more than once. -// It is a no-op if this package was not initialized. -func StartEmitting() { - if !enabled { - return - } - updateTimerMu.Lock() - if updateTimer != nil { - updateTimerMu.Unlock() - panic("updates already started") - } - updateTimerMu.Unlock() - updateMu.Lock() - if updating { - updateMu.Unlock() - panic("update already in progress") - } - updateMu.Unlock() - go logEmit(false) -} - -// StopEmitting sends the current payload and terminates -// the update cycle. No more updates will be sent. -// -// It is a no-op if the package was never initialized -// or if emitting was never started. -// -// NOTE: This function is blocking. Run in a goroutine if -// you want to guarantee no blocking at critical times -// like exiting the program. -func StopEmitting() { - if !enabled { - return - } - updateTimerMu.Lock() - if updateTimer == nil { - updateTimerMu.Unlock() - return - } - updateTimerMu.Unlock() - logEmit(true) // likely too early; may take minutes to return -} - -// Reset empties the current payload buffer. -func Reset() { - resetBuffer() -} - -// Set puts a value in the buffer to be included -// in the next emission. It overwrites any -// previous value. -// -// This function is safe for multiple goroutines, -// and it is recommended to call this using the -// go keyword after the call to SendHello so it -// doesn't block crucial code. -func Set(key string, val interface{}) { - if !enabled || isDisabled(key) { - return - } - bufferMu.Lock() - if _, ok := buffer[key]; !ok { - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - bufferItemCount++ - } - buffer[key] = val - bufferMu.Unlock() -} - -// SetNested puts a value in the buffer to be included -// in the next emission, nested under the top-level key -// as subkey. It overwrites any previous value. -// -// This function is safe for multiple goroutines, -// and it is recommended to call this using the -// go keyword after the call to SendHello so it -// doesn't block crucial code. -func SetNested(key, subkey string, val interface{}) { - if !enabled || isDisabled(key) { - return - } - bufferMu.Lock() - if topLevel, ok1 := buffer[key]; ok1 { - topLevelMap, ok2 := topLevel.(map[string]interface{}) - if !ok2 { - bufferMu.Unlock() - log.Printf("[PANIC] Telemetry: key %s is already used for non-nested-map value", key) - return - } - if _, ok3 := topLevelMap[subkey]; !ok3 { - // don't exceed max buffer size - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - bufferItemCount++ - } - topLevelMap[subkey] = val - } else { - // don't exceed max buffer size - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - bufferItemCount++ - buffer[key] = map[string]interface{}{subkey: val} - } - bufferMu.Unlock() -} - -// Append appends value to a list named key. -// If key is new, a new list will be created. -// If key maps to a type that is not a list, -// a panic is logged, and this is a no-op. -func Append(key string, value interface{}) { - if !enabled || isDisabled(key) { - return - } - bufferMu.Lock() - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - // TODO: Test this... - bufVal, inBuffer := buffer[key] - sliceVal, sliceOk := bufVal.([]interface{}) - if inBuffer && !sliceOk { - bufferMu.Unlock() - log.Printf("[PANIC] Telemetry: key %s already used for non-slice value", key) - return - } - if sliceVal == nil { - buffer[key] = []interface{}{value} - } else if sliceOk { - buffer[key] = append(sliceVal, value) - } - bufferItemCount++ - bufferMu.Unlock() -} - -// AppendUnique adds value to a set named key. -// Set items are unordered. Values in the set -// are unique, but how many times they are -// appended is counted. The value must be -// hashable. -// -// If key is new, a new set will be created for -// values with that key. If key maps to a type -// that is not a counting set, a panic is logged, -// and this is a no-op. -func AppendUnique(key string, value interface{}) { - if !enabled || isDisabled(key) { - return - } - bufferMu.Lock() - bufVal, inBuffer := buffer[key] - setVal, setOk := bufVal.(countingSet) - if inBuffer && !setOk { - bufferMu.Unlock() - log.Printf("[PANIC] Telemetry: key %s already used for non-counting-set value", key) - return - } - if setVal == nil { - // ensure the buffer is not too full, then add new unique value - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - buffer[key] = countingSet{value: 1} - bufferItemCount++ - } else if setOk { - // unique value already exists, so just increment counter - setVal[value]++ - } - bufferMu.Unlock() -} - -// Add adds amount to a value named key. -// If it does not exist, it is created with -// a value of 1. If key maps to a type that -// is not an integer, a panic is logged, -// and this is a no-op. -func Add(key string, amount int) { - atomicAdd(key, amount) -} - -// Increment is a shortcut for Add(key, 1) -func Increment(key string) { - atomicAdd(key, 1) -} - -// atomicAdd adds amount (negative to subtract) -// to key. -func atomicAdd(key string, amount int) { - if !enabled || isDisabled(key) { - return - } - bufferMu.Lock() - bufVal, inBuffer := buffer[key] - intVal, intOk := bufVal.(int) - if inBuffer && !intOk { - bufferMu.Unlock() - log.Printf("[PANIC] Telemetry: key %s already used for non-integer value", key) - return - } - if !inBuffer { - if bufferItemCount >= maxBufferItems { - bufferMu.Unlock() - return - } - bufferItemCount++ - } - buffer[key] = intVal + amount - bufferMu.Unlock() -} - -// FastHash hashes input using a 32-bit hashing algorithm -// that is fast, and returns the hash as a hex-encoded string. -// Do not use this for cryptographic purposes. -func FastHash(input []byte) string { - h := fnv.New32a() - if _, err := h.Write(input); err != nil { - log.Println("[ERROR] failed to write bytes: ", err) - } - - return fmt.Sprintf("%x", h.Sum32()) -} - -// isDisabled returns whether key is -// a disabled metric key. ALL collection -// functions should call this and not -// save the value if this returns true. -func isDisabled(key string) bool { - // for keys that are augmented with data, such as - // "tls_client_hello_ua:", just - // check the prefix "tls_client_hello_ua" - checkKey := key - if idx := strings.Index(key, ":"); idx > -1 { - checkKey = key[:idx] - } - - disabledMetricsMu.RLock() - _, ok := disabledMetrics[checkKey] - disabledMetricsMu.RUnlock() - return ok -} diff --git a/vendor/github.com/mholt/caddy/telemetry/telemetry.go b/vendor/github.com/mholt/caddy/telemetry/telemetry.go deleted file mode 100644 index 2aab30b88..000000000 --- a/vendor/github.com/mholt/caddy/telemetry/telemetry.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 telemetry implements the client for server-side telemetry -// of the network. Functions in this package are synchronous and blocking -// unless otherwise specified. For convenience, most functions here do -// not return errors, but errors are logged to the standard logger. -// -// To use this package, first call Init(). You can then call any of the -// collection/aggregation functions. Call StartEmitting() when you are -// ready to begin sending telemetry updates. -// -// When collecting metrics (functions like Set, AppendUnique, or Increment), -// it may be desirable and even recommended to invoke them in a new -// goroutine in case there is lock contention; they are thread-safe (unless -// noted), and you may not want them to block the main thread of execution. -// However, sometimes blocking may be necessary too; for example, adding -// startup metrics to the buffer before the call to StartEmitting(). -// -// This package is designed to be as fast and space-efficient as reasonably -// possible, so that it does not disrupt the flow of execution. -package telemetry - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "math/rand" - "net/http" - "runtime" - "strconv" - "strings" - "sync" - "time" - - "github.com/google/uuid" -) - -// logEmit calls emit and then logs the error, if any. -// See docs for emit. -func logEmit(final bool) { - err := emit(final) - if err != nil { - log.Printf("[ERROR] Sending telemetry: %v", err) - } -} - -// emit sends an update to the telemetry server. -// Set final to true if this is the last call to emit. -// If final is true, no future updates will be scheduled. -// Otherwise, the next update will be scheduled. -func emit(final bool) error { - if !enabled { - return fmt.Errorf("telemetry not enabled") - } - - // some metrics are updated/set at time of emission - setEmitTimeMetrics() - - // ensure only one update happens at a time; - // skip update if previous one still in progress - updateMu.Lock() - if updating { - updateMu.Unlock() - log.Println("[NOTICE] Skipping this telemetry update because previous one is still working") - return nil - } - updating = true - updateMu.Unlock() - defer func() { - updateMu.Lock() - updating = false - updateMu.Unlock() - }() - - // terminate any pending update if this is the last one - if final { - stopUpdateTimer() - } - - payloadBytes, err := makePayloadAndResetBuffer() - if err != nil { - return err - } - - // this will hold the server's reply - var reply Response - - // transmit the payload - use a loop to retry in case of failure - for i := 0; i < 4; i++ { - if i > 0 && err != nil { - // don't hammer the server; first failure might have been - // a fluke, but back off more after that - log.Printf("[WARNING] Sending telemetry (attempt %d): %v - backing off and retrying", i, err) - time.Sleep(time.Duration((i+1)*(i+1)*(i+1)) * time.Second) - } - - // send it - var resp *http.Response - resp, err = httpClient.Post(endpoint+instanceUUID.String(), "application/json", bytes.NewReader(payloadBytes)) - if err != nil { - continue - } - - // check for any special-case response codes - if resp.StatusCode == http.StatusGone { - // the endpoint has been deprecated and is no longer servicing clients - err = fmt.Errorf("telemetry server replied with HTTP %d; upgrade required", resp.StatusCode) - if clen := resp.Header.Get("Content-Length"); clen != "0" && clen != "" { - bodyBytes, readErr := ioutil.ReadAll(resp.Body) - if readErr != nil { - log.Printf("[ERROR] Reading response body from server: %v", readErr) - } - err = fmt.Errorf("%v - %s", err, bodyBytes) - } - resp.Body.Close() - reply.Stop = true - break - } - if resp.StatusCode == http.StatusUnavailableForLegalReasons { - // the endpoint is unavailable, at least to this client, for legal reasons (!) - err = fmt.Errorf("telemetry server replied with HTTP %d %s: please consult the project website and developers for guidance", resp.StatusCode, resp.Status) - if clen := resp.Header.Get("Content-Length"); clen != "0" && clen != "" { - bodyBytes, readErr := ioutil.ReadAll(resp.Body) - if readErr != nil { - log.Printf("[ERROR] Reading response body from server: %v", readErr) - } - err = fmt.Errorf("%v - %s", err, bodyBytes) - } - resp.Body.Close() - reply.Stop = true - break - } - - // okay, ensure we can interpret the response - if ct := resp.Header.Get("Content-Type"); (resp.StatusCode < 300 || resp.StatusCode >= 400) && - !strings.Contains(ct, "json") { - err = fmt.Errorf("telemetry server replied with unknown content-type: '%s' and HTTP %s", ct, resp.Status) - resp.Body.Close() - continue - } - - // read the response body - err = json.NewDecoder(resp.Body).Decode(&reply) - resp.Body.Close() // close response body as soon as we're done with it - if err != nil { - continue - } - - // update the list of enabled/disabled keys, if any - for _, key := range reply.EnableKeys { - disabledMetricsMu.Lock() - // only re-enable this metric if it is temporarily disabled - if temp, ok := disabledMetrics[key]; ok && temp { - delete(disabledMetrics, key) - } - disabledMetricsMu.Unlock() - } - for _, key := range reply.DisableKeys { - disabledMetricsMu.Lock() - disabledMetrics[key] = true // all remotely-disabled keys are "temporarily" disabled - disabledMetricsMu.Unlock() - } - - // make sure we didn't send the update too soon; if so, - // just wait and try again -- this is a special case of - // error that we handle differently, as you can see - if resp.StatusCode == http.StatusTooManyRequests { - if reply.NextUpdate <= 0 { - raStr := resp.Header.Get("Retry-After") - if ra, err := strconv.Atoi(raStr); err == nil { - reply.NextUpdate = time.Duration(ra) * time.Second - } - } - if !final { - log.Printf("[NOTICE] Sending telemetry: we were too early; waiting %s before trying again", reply.NextUpdate) - time.Sleep(reply.NextUpdate) - continue - } - } else if resp.StatusCode >= 400 { - err = fmt.Errorf("telemetry server returned status code %d", resp.StatusCode) - continue - } - - break - } - if err == nil && !final { - // (remember, if there was an error, we return it - // below, so it WILL get logged if it's supposed to) - log.Println("[INFO] Sending telemetry: success") - } - - // even if there was an error after all retries, we should - // schedule the next update using our default update - // interval because the server might be healthy later - - // ensure we won't slam the telemetry server; add a little variance - if reply.NextUpdate < 1*time.Second { - reply.NextUpdate = defaultUpdateInterval + time.Duration(rand.Int63n(int64(1*time.Minute))) - } - - // schedule the next update (if this wasn't the last one and - // if the remote server didn't tell us to stop sending) - if !final && !reply.Stop { - updateTimerMu.Lock() - updateTimer = time.AfterFunc(reply.NextUpdate, func() { - logEmit(false) - }) - updateTimerMu.Unlock() - } - - return err -} - -func stopUpdateTimer() { - updateTimerMu.Lock() - updateTimer.Stop() - updateTimer = nil - updateTimerMu.Unlock() -} - -// setEmitTimeMetrics sets some metrics that should -// be recorded just before emitting. -func setEmitTimeMetrics() { - Set("goroutines", runtime.NumGoroutine()) - - var mem runtime.MemStats - runtime.ReadMemStats(&mem) - SetNested("memory", "heap_alloc", mem.HeapAlloc) - SetNested("memory", "sys", mem.Sys) -} - -// makePayloadAndResetBuffer prepares a payload -// by emptying the collection buffer. It returns -// the bytes of the payload to send to the server. -// Since the buffer is reset by this, if the -// resulting byte slice is lost, the payload is -// gone with it. -func makePayloadAndResetBuffer() ([]byte, error) { - bufCopy := resetBuffer() - - // encode payload in preparation for transmission - payload := Payload{ - InstanceID: instanceUUID.String(), - Timestamp: time.Now().UTC(), - Data: bufCopy, - } - return json.Marshal(payload) -} - -// resetBuffer makes a local pointer to the buffer, -// then resets the buffer by assigning to be a newly- -// made value to clear it out, then sets the buffer -// item count to 0. It returns the copied pointer to -// the original map so the old buffer value can be -// used locally. -func resetBuffer() map[string]interface{} { - bufferMu.Lock() - bufCopy := buffer - buffer = make(map[string]interface{}) - bufferItemCount = 0 - bufferMu.Unlock() - return bufCopy -} - -// Response contains the body of a response from the -// telemetry server. -type Response struct { - // NextUpdate is how long to wait before the next update. - NextUpdate time.Duration `json:"next_update"` - - // Stop instructs the telemetry server to stop sending - // telemetry. This would only be done under extenuating - // circumstances, but we are prepared for it nonetheless. - Stop bool `json:"stop,omitempty"` - - // Error will be populated with an error message, if any. - // This field should be empty if the status code is < 400. - Error string `json:"error,omitempty"` - - // DisableKeys will contain a list of keys/metrics that - // should NOT be sent until further notice. The client - // must NOT store these items in its buffer or send them - // to the telemetry server while they are disabled. If - // this list and EnableKeys have the same value (which is - // not supposed to happen), this field should dominate. - DisableKeys []string `json:"disable_keys,omitempty"` - - // EnableKeys will contain a list of keys/metrics that - // MAY be sent until further notice. - EnableKeys []string `json:"enable_keys,omitempty"` -} - -// Payload is the data that gets sent to the telemetry server. -type Payload struct { - // The universally unique ID of the instance - InstanceID string `json:"instance_id"` - - // The UTC timestamp of the transmission - Timestamp time.Time `json:"timestamp"` - - // The timestamp before which the next update is expected - // (NOT populated by client - the server fills this in - // before it stores the data) - ExpectNext time.Time `json:"expect_next,omitempty"` - - // The metrics - Data map[string]interface{} `json:"data,omitempty"` -} - -// Int returns the value of the data keyed by key -// if it is an integer; otherwise it returns 0. -func (p Payload) Int(key string) int { - val, _ := p.Data[key] - switch p.Data[key].(type) { - case int: - return val.(int) - case float64: // after JSON-decoding, int becomes float64... - return int(val.(float64)) - } - return 0 -} - -// countingSet implements a set that counts how many -// times a key is inserted. It marshals to JSON in a -// way such that keys are converted to values next -// to their associated counts. -type countingSet map[interface{}]int - -// MarshalJSON implements the json.Marshaler interface. -// It converts the set to an array so that the values -// are JSON object values instead of keys, since keys -// are difficult to query in databases. -func (s countingSet) MarshalJSON() ([]byte, error) { - type Item struct { - Value interface{} `json:"value"` - Count int `json:"count"` - } - var list []Item - - for k, v := range s { - list = append(list, Item{Value: k, Count: v}) - } - - return json.Marshal(list) -} - -var ( - // httpClient should be used for HTTP requests. It - // is configured with a timeout for reliability. - httpClient = http.Client{ - Transport: &http.Transport{ - TLSHandshakeTimeout: 30 * time.Second, - DisableKeepAlives: true, - }, - Timeout: 1 * time.Minute, - } - - // buffer holds the data that we are building up to send. - buffer = make(map[string]interface{}) - bufferItemCount = 0 - bufferMu sync.RWMutex // protects both the buffer and its count - - // updating is used to ensure only one - // update happens at a time. - updating bool - updateMu sync.Mutex - - // updateTimer fires off the next update. - // If no update is scheduled, this is nil. - updateTimer *time.Timer - updateTimerMu sync.Mutex - - // disabledMetrics is a set of metric keys - // that should NOT be saved to the buffer - // or sent to the telemetry server. The value - // indicates whether the entry is temporary. - // If the value is true, it may be removed if - // the metric is re-enabled remotely later. If - // the value is false, it is permanent - // (presumably because the user explicitly - // disabled it) and can only be re-enabled - // with user consent. - disabledMetrics = make(map[string]bool) - disabledMetricsMu sync.RWMutex - - // instanceUUID is the ID of the current instance. - // This MUST be set to emit telemetry. - // This MUST NOT be openly exposed to clients, for privacy. - instanceUUID uuid.UUID - - // enabled indicates whether the package has - // been initialized and can be actively used. - enabled bool - - // maxBufferItems is the maximum number of items we'll allow - // in the buffer before we start dropping new ones, in a - // rough (simple) attempt to keep memory use under control. - maxBufferItems = 100000 -) - -const ( - // endpoint is the base URL to remote telemetry server; - // the instance ID will be appended to it. - endpoint = "https://telemetry.caddyserver.com/v1/update/" - - // defaultUpdateInterval is how long to wait before emitting - // more telemetry data if all retires fail. This value is - // only used if the client receives a nonsensical value, or - // doesn't send one at all, or if a connection can't be made, - // likely indicating a problem with the server. Thus, this - // value should be a long duration to help alleviate extra - // load on the server. - defaultUpdateInterval = 1 * time.Hour -) diff --git a/vendor/github.com/mholt/caddy/upgrade.go b/vendor/github.com/mholt/caddy/upgrade.go deleted file mode 100644 index c35f9b1cc..000000000 --- a/vendor/github.com/mholt/caddy/upgrade.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2015 Light Code Labs, LLC -// -// 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 caddy - -import ( - "encoding/gob" - "fmt" - "io/ioutil" - "log" - "os" - "os/exec" - "sync" -) - -func init() { - // register CaddyfileInput with gob so it knows into - // which concrete type to decode an Input interface - gob.Register(CaddyfileInput{}) -} - -// IsUpgrade returns true if this process is part of an upgrade -// where a parent caddy process spawned this one to upgrade -// the binary. -func IsUpgrade() bool { - mu.Lock() - defer mu.Unlock() - return isUpgrade -} - -// Upgrade re-launches the process, preserving the listeners -// for a graceful upgrade. It does NOT load new configuration; -// it only starts the process anew with the current config. -// This makes it possible to perform zero-downtime binary upgrades. -// -// TODO: For more information when debugging, see: -// https://forum.golangbridge.org/t/bind-address-already-in-use-even-after-listener-closed/1510?u=matt -// https://github.com/mholt/shared-conn -func Upgrade() error { - log.Println("[INFO] Upgrading") - - // use existing Caddyfile; do not change configuration during upgrade - currentCaddyfile, _, err := getCurrentCaddyfile() - if err != nil { - return err - } - - if len(os.Args) == 0 { // this should never happen, but... - os.Args = []string{""} - } - - // tell the child that it's a restart - env := os.Environ() - if !IsUpgrade() { - env = append(env, "CADDY__UPGRADE=1") - } - - // prepare our payload to the child process - cdyfileGob := transferGob{ - ListenerFds: make(map[string]uintptr), - Caddyfile: currentCaddyfile, - } - - // prepare a pipe to the fork's stdin so it can get the Caddyfile - rpipe, wpipe, err := os.Pipe() - if err != nil { - return err - } - - // prepare a pipe that the child process will use to communicate - // its success with us by sending > 0 bytes - sigrpipe, sigwpipe, err := os.Pipe() - if err != nil { - return err - } - - // pass along relevant file descriptors to child process; ordering - // is very important since we rely on these being in certain positions. - extraFiles := []*os.File{sigwpipe} // fd 3 - - // add file descriptors of all the sockets - for i, j := 0, 0; ; i++ { - instancesMu.Lock() - if i >= len(instances) { - instancesMu.Unlock() - break - } - inst := instances[i] - instancesMu.Unlock() - - for _, s := range inst.servers { - gs, gracefulOk := s.server.(GracefulServer) - ln, lnOk := s.listener.(Listener) - pc, pcOk := s.packet.(PacketConn) - if gracefulOk { - if lnOk { - lnFile, _ := ln.File() - extraFiles = append(extraFiles, lnFile) - cdyfileGob.ListenerFds["tcp"+gs.Address()] = uintptr(4 + j) // 4 fds come before any of the listeners - j++ - } - if pcOk { - pcFile, _ := pc.File() - extraFiles = append(extraFiles, pcFile) - cdyfileGob.ListenerFds["udp"+gs.Address()] = uintptr(4 + j) // 4 fds come before any of the listeners - j++ - } - } - } - } - - // set up the command - cmd := exec.Command(os.Args[0], os.Args[1:]...) - cmd.Stdin = rpipe // fd 0 - cmd.Stdout = os.Stdout // fd 1 - cmd.Stderr = os.Stderr // fd 2 - cmd.ExtraFiles = extraFiles - cmd.Env = env - - // spawn the child process - err = cmd.Start() - if err != nil { - return err - } - - // immediately close our dup'ed fds and the write end of our signal pipe - for _, f := range extraFiles { - err = f.Close() - if err != nil { - return err - } - } - - // feed Caddyfile to the child - err = gob.NewEncoder(wpipe).Encode(cdyfileGob) - if err != nil { - return err - } - err = wpipe.Close() - if err != nil { - return err - } - - // determine whether child startup succeeded - answer, readErr := ioutil.ReadAll(sigrpipe) - if len(answer) == 0 { - cmdErr := cmd.Wait() // get exit status - errStr := fmt.Sprintf("child failed to initialize: %v", cmdErr) - if readErr != nil { - errStr += fmt.Sprintf(" - additionally, error communicating with child process: %v", readErr) - } - return fmt.Errorf(errStr) - } - - // looks like child is successful; we can exit gracefully. - log.Println("[INFO] Upgrade finished") - return Stop() -} - -// getCurrentCaddyfile gets the Caddyfile used by the -// current (first) Instance and returns both of them. -func getCurrentCaddyfile() (Input, *Instance, error) { - instancesMu.Lock() - if len(instances) == 0 { - instancesMu.Unlock() - return nil, nil, fmt.Errorf("no server instances are fully running") - } - inst := instances[0] - instancesMu.Unlock() - - currentCaddyfile := inst.caddyfileInput - if currentCaddyfile == nil { - // hmm, did spawning process forget to close stdin? Anyhow, this is unusual. - return nil, inst, fmt.Errorf("no Caddyfile to reload (was stdin left open?)") - } - return currentCaddyfile, inst, nil -} - -// signalSuccessToParent tells the parent our status using pipe at index 3. -// If this process is not a restart, this function does nothing. -// Calling this function once this process has successfully initialized -// is vital so that the parent process can unblock and kill itself. -// This function is idempotent; it executes at most once per process. -func signalSuccessToParent() { - signalParentOnce.Do(func() { - if IsUpgrade() { - ppipe := os.NewFile(3, "") // parent is reading from pipe at index 3 - _, err := ppipe.Write([]byte("success")) // we must send some bytes to the parent - if err != nil { - log.Printf("[ERROR] Communicating successful init to parent: %v", err) - } - ppipe.Close() - } - }) -} - -// signalParentOnce is used to make sure that the parent is only -// signaled once; doing so more than once breaks whatever socket is -// at fd 4 (TODO: the reason for this is still unclear - to reproduce, -// call Stop() and Start() in succession at least once after a -// restart, then try loading first host of Caddyfile in the browser -// - this was pre-v0.9; this code and godoc is borrowed from the -// implementation then, but I'm not sure if it's been fixed yet, as -// of v0.10.7). Do not use this directly; call signalSuccessToParent -// instead. -var signalParentOnce sync.Once - -// transferGob is used if this is a child process as part of -// a graceful upgrade; it is used to map listeners to their -// index in the list of inherited file descriptors. This -// variable is not safe for concurrent access. -var loadedGob transferGob - -// transferGob maps bind address to index of the file descriptor -// in the Files array passed to the child process. It also contains -// the Caddyfile contents and any other state needed by the new process. -// Used only during graceful upgrades. -type transferGob struct { - ListenerFds map[string]uintptr - Caddyfile Input -} diff --git a/vendor/github.com/mholt/certmagic/.gitignore b/vendor/github.com/mholt/certmagic/.gitignore deleted file mode 100644 index fbd281d14..000000000 --- a/vendor/github.com/mholt/certmagic/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_gitignore/ diff --git a/vendor/github.com/mholt/certmagic/.travis.yml b/vendor/github.com/mholt/certmagic/.travis.yml deleted file mode 100644 index 9dfaec42e..000000000 --- a/vendor/github.com/mholt/certmagic/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: go - -go: - - 1.x - - tip - -matrix: - allow_failures: - - go: tip - fast_finish: true - -install: - - go get -t ./... - - go get golang.org/x/lint/golint - - go get github.com/alecthomas/gometalinter - -script: - - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.15.0 - - golangci-lint run --disable-all -E vet -E gofmt -E misspell -E ineffassign -E goimports -E deadcode --tests - - go test -race ./... - -after_script: - - golint ./... diff --git a/vendor/github.com/mholt/certmagic/LICENSE.txt b/vendor/github.com/mholt/certmagic/LICENSE.txt deleted file mode 100644 index 8dada3eda..000000000 --- a/vendor/github.com/mholt/certmagic/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - 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/github.com/mholt/certmagic/README.md b/vendor/github.com/mholt/certmagic/README.md deleted file mode 100644 index 4d3b5f422..000000000 --- a/vendor/github.com/mholt/certmagic/README.md +++ /dev/null @@ -1,500 +0,0 @@ -

- CertMagic -

-

Easy and Powerful TLS Automation

-

The same library used by the Caddy Web Server

-

- - - - -

- - -Caddy's automagic TLS features, now for your own Go programs, in one powerful and easy-to-use library! - -CertMagic is the most mature, robust, and capable ACME client integration for Go. - -With CertMagic, you can add one line to your Go application to serve securely over TLS, without ever having to touch certificates. - -Instead of: - -```go -// plaintext HTTP, gross 🤢 -http.ListenAndServe(":80", mux) -``` - -Use CertMagic: - -```go -// encrypted HTTPS with HTTP->HTTPS redirects - yay! 🔒😠-certmagic.HTTPS([]string{"example.com"}, mux) -``` - -That line of code will serve your HTTP router `mux` over HTTPS, complete with HTTP->HTTPS redirects. It obtains and renews the TLS certificates. It staples OCSP responses for greater privacy and security. As long as your domain name points to your server, CertMagic will keep its connections secure. - -Compared to other ACME client libraries for Go, only CertMagic supports the full suite of ACME features, and no other library matches CertMagic's maturity and reliability. - - - - -CertMagic - Automatic HTTPS using Let's Encrypt -=============================================== - -**Sponsored by Relica - Cross-platform local and cloud file backup:** - -Relica - Cross-platform file backup to the cloud, local disks, or other computers - - -## Menu - -- [Features](#features) -- [Requirements](#requirements) -- [Installation](#installation) -- [Usage](#usage) - - [Package Overview](#package-overview) - - [Certificate authority](#certificate-authority) - - [The `Config` type](#the-config-type) - - [Defaults](#defaults) - - [Providing an email address](#providing-an-email-address) - - [Development and testing](#development-and-testing) - - [Examples](#examples) - - [Serving HTTP handlers with HTTPS](#serving-http-handlers-with-https) - - [Starting a TLS listener](#starting-a-tls-listener) - - [Getting a tls.Config](#getting-a-tlsconfig) - - [Advanced use](#advanced-use) - - [Wildcard Certificates](#wildcard-certificates) - - [Behind a load balancer (or in a cluster)](#behind-a-load-balancer-or-in-a-cluster) - - [The ACME Challenges](#the-acme-challenges) - - [HTTP Challenge](#http-challenge) - - [TLS-ALPN Challenge](#tls-alpn-challenge) - - [DNS Challenge](#dns-challenge) - - [On-Demand TLS](#on-demand-tls) - - [Storage](#storage) - - [Cache](#cache) -- [Contributing](#contributing) -- [Project History](#project-history) -- [Credits and License](#credits-and-license) - - -## Features - -- Fully automated certificate management including issuance and renewal -- One-liner, fully managed HTTPS servers -- Full control over almost every aspect of the system -- HTTP->HTTPS redirects (for HTTP applications) -- Solves all 3 ACME challenges: HTTP, TLS-ALPN, and DNS -- Over 50 DNS providers work out-of-the-box (powered by [lego](https://github.com/go-acme/lego)!) -- Pluggable storage implementations (default: file system) -- Wildcard certificates (requires DNS challenge) -- OCSP stapling for each qualifying certificate ([done right](https://gist.github.com/sleevi/5efe9ef98961ecfb4da8#gistcomment-2336055)) -- Distributed solving of all challenges (works behind load balancers) -- Supports "on-demand" issuance of certificates (during TLS handshakes!) - - Custom decision functions - - Hostname whitelist - - Ask an external URL - - Rate limiting -- Optional event hooks to observe internal behaviors -- Works with any certificate authority (CA) compliant with the ACME specification -- Certificate revocation (please, only if private key is compromised) -- Must-Staple (optional; not default) -- Cross-platform support! Mac, Windows, Linux, BSD, Android... -- Scales well to thousands of names/certificates per instance -- Use in conjunction with your own certificates - - -## Requirements - -1. Public DNS name(s) you control -2. Server reachable from public Internet - - Or use the DNS challenge to waive this requirement -3. Control over port 80 (HTTP) and/or 443 (HTTPS) - - Or they can be forwarded to other ports you control - - Or use the DNS challenge to waive this requirement - - (This is a requirement of the ACME protocol, not a library limitation) -4. Persistent storage - - Typically the local file system (default) - - Other integrations available/possible - -**_Before using this library, your domain names MUST be pointed (A/AAAA records) at your server (unless you use the DNS challenge)!_** - - -## Installation - -```bash -$ go get -u github.com/mholt/certmagic -``` - - -## Usage - -### Package Overview - -#### Certificate authority - -This library uses Let's Encrypt by default, but you can use any certificate authority that conforms to the ACME specification. Known/common CAs are provided as consts in the package, for example `LetsEncryptStagingCA` and `LetsEncryptProductionCA`. - -#### The `Config` type - -The `certmagic.Config` struct is how you can wield the power of this fully armed and operational battle station. However, an empty/uninitialized `Config` is _not_ a valid one! In time, you will learn to use the force of `certmagic.NewDefault()` as I have. - -#### Defaults - -The default `Config` value is called `certmagic.Default`. Change its fields to suit your needs, then call `certmagic.NewDefault()` when you need a valid `Config` value. In other words, `certmagic.Default` is a template and is not valid for use directly. - -You can set the default values easily, for example: `certmagic.Default.Email = ...`. - -The high-level functions in this package (`HTTPS()`, `Listen()`, and `Manage()`) use the default config exclusively. This is how most of you will interact with the package. This is suitable when all your certificates are managed the same way. However, if you need to manage certificates differently depending on their name, you will need to make your own cache and configs (keep reading). - - -#### Providing an email address - -Although not strictly required, this is highly recommended best practice. It allows you to receive expiration emails if your certificates are expiring for some reason, and also allows the CA's engineers to potentially get in touch with you if something is wrong. I recommend setting `certmagic.Default.Email` or always setting the `Email` field of a new `Config` struct. - - -### Development and Testing - -Note that Let's Encrypt imposes [strict rate limits](https://letsencrypt.org/docs/rate-limits/) at its production endpoint, so using it while developing your application may lock you out for a few days if you aren't careful! - -While developing your application and testing it, use [their staging endpoint](https://letsencrypt.org/docs/staging-environment/) which has much higher rate limits. Even then, don't hammer it: but it's much safer for when you're testing. When deploying, though, use their production CA because their staging CA doesn't issue trusted certificates. - -To use staging, set `certmagic.Default.CA = certmagic.LetsEncryptStagingCA` or set `CA` of every `Config` struct. - - - -### Examples - -There are many ways to use this library. We'll start with the highest-level (simplest) and work down (more control). - -All these high-level examples use `certmagic.Default` for the config and the default cache and storage for serving up certificates. - -First, we'll follow best practices and do the following: - -```go -// read and agree to your CA's legal documents -certmagic.Default.Agreed = true - -// provide an email address -certmagic.Default.Email = "you@yours.com" - -// use the staging endpoint while we're developing -certmagic.Default.CA = certmagic.LetsEncryptStagingCA -``` - -For fully-functional program examples, check out [this Twitter thread](https://twitter.com/mholt6/status/1073103805112147968) (or read it [unrolled into a single post](https://threadreaderapp.com/thread/1073103805112147968.html)). (Note that the package API has changed slightly since these posts.) - - -#### Serving HTTP handlers with HTTPS - -```go -err := certmagic.HTTPS([]string{"example.com", "www.example.com"}, mux) -if err != nil { - return err -} -``` - -This starts HTTP and HTTPS listeners and redirects HTTP to HTTPS! - -#### Starting a TLS listener - -```go -ln, err := certmagic.Listen([]string{"example.com"}) -if err != nil { - return err -} -``` - - -#### Getting a tls.Config - -```go -tlsConfig, err := certmagic.TLS([]string{"example.com"}) -if err != nil { - return err -} -``` - - -#### Advanced use - -For more control (particularly, if you need a different way of managing each certificate), you'll make and use a `Cache` and a `Config` like so: - -```go -cache := certmagic.NewCache(certmagic.CacheOptions{ - GetConfigForCert: func(cert certmagic.Certificate) (*certmagic.Config, error) { - // do whatever you need to do to get the right - // configuration for this certificate; keep in - // mind that this config value is used as a - // template, and will be completed with any - // defaults that are set in the Default config - return certmagic.Config{ - // ... - }), nil - }, - ... -}) - -magic := certmagic.New(cache, certmagic.Config{ - CA: certmagic.LetsEncryptStagingCA, - Email: "you@yours.com", - Agreed: true, - // plus any other customization you want -}) - -// this obtains certificates or renews them if necessary -err := magic.Manage([]string{"example.com", "sub.example.com"}) -if err != nil { - return err -} - -// to use its certificates and solve the TLS-ALPN challenge, -// you can get a TLS config to use in a TLS listener! -tlsConfig := magic.TLSConfig() - -//// OR //// - -// if you already have a TLS config you don't want to replace, -// we can simply set its GetCertificate field and append the -// TLS-ALPN challenge protocol to the NextProtos -myTLSConfig.GetCertificate = magic.GetCertificate -myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol} - -// the HTTP challenge has to be handled by your HTTP server; -// if you don't have one, you should have disabled it earlier -// when you made the certmagic.Config -httpMux = magic.HTTPChallengeHandler(httpMux) -``` - -Great! This example grants you much more flexibility for advanced programs. However, _the vast majority of you will only use the high-level functions described earlier_, especially since you can still customize them by setting the package-level `Default` config. - - -### Wildcard certificates - -At time of writing (December 2018), Let's Encrypt only issues wildcard certificates with the DNS challenge. You can easily enable the DNS challenge with CertMagic for numerous providers (see the relevant section in the docs). - - -### Behind a load balancer (or in a cluster) - -CertMagic runs effectively behind load balancers and/or in cluster/fleet environments. In other words, you can have 10 or 1,000 servers all serving the same domain names, all sharing certificates and OCSP staples. - -To do so, simply ensure that each instance is using the same Storage. That is the sole criteria for determining whether an instance is part of a cluster. - -The default Storage is implemented using the file system, so mounting the same shared folder is sufficient (see [Storage](#storage) for more on that)! If you need an alternate Storage implementation, feel free to use one, provided that all the instances use the _same_ one. :) - -See [Storage](#storage) and the associated [godoc](https://godoc.org/github.com/mholt/certmagic#Storage) for more information! - - -## The ACME Challenges - -This section describes how to solve the ACME challenges. Challenges are how you demonstrate to the certificate authority some control over your domain name, thus authorizing them to grant you a certificate for that name. [The great innovation of ACME](https://www.dotconferences.com/2016/10/matthew-holt-go-with-acme) is that verification by CAs can now be automated, rather than having to click links in emails (who ever thought that was a good idea??). - -If you're using the high-level convenience functions like `HTTPS()`, `Listen()`, or `TLS()`, the HTTP and/or TLS-ALPN challenges are solved for you because they also start listeners. However, if you're making a `Config` and you start your own server manually, you'll need to be sure the ACME challenges can be solved so certificates can be renewed. - -The HTTP and TLS-ALPN challenges are the defaults because they don't require configuration from you, but they require that your server is accessible from external IPs on low ports. If that is not possible in your situation, you can enable the DNS challenge, which will disable the HTTP and TLS-ALPN challenges and use the DNS challenge exclusively. - -Technically, only one challenge needs to be enabled for things to work, but using multiple is good for reliability in case a challenge is discontinued by the CA. This happened to the TLS-SNI challenge in early 2018—many popular ACME clients such as Traefik and Autocert broke, resulting in downtime for some sites, until new releases were made and patches deployed, because they used only one challenge; Caddy, however—this library's forerunner—was unaffected because it also used the HTTP challenge. If multiple challenges are enabled, they are chosen randomly to help prevent false reliance on a single challenge type. - - -### HTTP Challenge - -Per the ACME spec, the HTTP challenge requires port 80, or at least packet forwarding from port 80. It works by serving a specific HTTP response that only the genuine server would have to a normal HTTP request at a special endpoint. - -If you are running an HTTP server, solving this challenge is very easy: just wrap your handler in `HTTPChallengeHandler` _or_ call `SolveHTTPChallenge()` inside your own `ServeHTTP()` method. - -For example, if you're using the standard library: - -```go -mux := http.NewServeMux() -mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { - fmt.Fprintf(w, "Lookit my cool website over HTTPS!") -}) - -http.ListenAndServe(":80", magic.HTTPChallengeHandler(mux)) -``` - -If wrapping your handler is not a good solution, try this inside your `ServeHTTP()` instead: - -```go -magic := certmagic.NewDefault() - -func ServeHTTP(w http.ResponseWriter, req *http.Request) { - if magic.HandleHTTPChallenge(w, r) { - return // challenge handled; nothing else to do - } - ... -} -``` - -If you are not running an HTTP server, you should disable the HTTP challenge _or_ run an HTTP server whose sole job it is to solve the HTTP challenge. - - -### TLS-ALPN Challenge - -Per the ACME spec, the TLS-ALPN challenge requires port 443, or at least packet forwarding from port 443. It works by providing a special certificate using a standard TLS extension, Application Layer Protocol Negotiation (ALPN), having a special value. This is the most convenient challenge type because it usually requires no extra configuration and uses the standard TLS port which is where the certificates are used, also. - -This challenge is easy to solve: just use the provided `tls.Config` when you make your TLS listener: - -```go -// use this to configure a TLS listener -tlsConfig := magic.TLSConfig() -``` - -Or make two simple changes to an existing `tls.Config`: - -```go -myTLSConfig.GetCertificate = magic.GetCertificate -myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol} -``` - -Then just make sure your TLS listener is listening on port 443: - -```go -ln, err := tls.Listen("tcp", ":443", myTLSConfig) -``` - - -### DNS Challenge - -The DNS challenge is perhaps the most useful challenge because it allows you to obtain certificates without your server needing to be publicly accessible on the Internet, and it's the only challenge by which Let's Encrypt will issue wildcard certificates. - -This challenge works by setting a special record in the domain's zone. To do this automatically, your DNS provider needs to offer an API by which changes can be made to domain names, and the changes need to take effect immediately for best results. CertMagic supports [all of lego's DNS provider implementations](https://github.com/go-acme/lego/tree/master/providers/dns)! All of them clean up the temporary record after the challenge completes. - -To enable it, just set the `DNSProvider` field on a `certmagic.Config` struct, or set the default `certmagic.DNSProvider` variable. For example, if my domains' DNS was served by DNSimple and I set my DNSimple API credentials in environment variables: - -```go -import "github.com/go-acme/lego/providers/dns/dnsimple" - -provider, err := dnsimple.NewDNSProvider() -if err != nil { - return err -} - -certmagic.Default.DNSProvider = provider -``` - -Now the DNS challenge will be used by default, and I can obtain certificates for wildcard domains. See the [godoc documentation for the provider you're using](https://godoc.org/github.com/go-acme/lego/providers/dns#pkg-subdirectories) to learn how to configure it. Most can be configured by env variables or by passing in a config struct. If you pass a config struct instead of using env variables, you will probably need to set some other defaults (that's just how lego works, currently): - -```go -PropagationTimeout: dns01.DefaultPollingInterval, -PollingInterval: dns01.DefaultPollingInterval, -TTL: dns01.DefaultTTL, -``` - -Enabling the DNS challenge disables the other challenges for that `certmagic.Config` instance. - - -## On-Demand TLS - -Normally, certificates are obtained and renewed before a listener starts serving, and then those certificates are maintained throughout the lifetime of the program. In other words, the certificate names are static. But sometimes you don't know all the names ahead of time. This is where On-Demand TLS shines. - -Originally invented for use in Caddy (which was the first program to use such technology), On-Demand TLS makes it possible and easy to serve certificates for arbitrary names during the lifetime of the server. When a TLS handshake is received, CertMagic will read the Server Name Indication (SNI) value and either load and present that certificate in the ServerHello, or if one does not exist, it will obtain it from a CA right then-and-there. - -Of course, this has some obvious security implications. You don't want to DoS a CA or allow arbitrary clients to fill your storage with spammy TLS handshakes. That's why, in order to enable On-Demand issuance, you'll need to set some limits or some policy to allow getting a certificate. - -CertMagic provides several ways to enforce decision policies for On-Demand TLS, in descending order of priority: - -- A generic function that you write which will decide whether to allow the certificate request -- A name whitelist -- The ability to make an HTTP request to a URL for permission -- Rate limiting - -The simplest way to enable On-Demand issuance is to set the OnDemand field of a Config (or the default package-level value): - -```go -certmagic.Default.OnDemand = &certmagic.OnDemandConfig{MaxObtain: 5} -``` - -This allows only 5 certificates to be requested and is the simplest way to enable On-Demand TLS, but is the least recommended. It prevents abuse, but only in the least helpful way. - -The [godoc](https://godoc.org/github.com/mholt/certmagic#OnDemandConfig) describes how to use the other policies, all of which are much more recommended! :) - -If `OnDemand` is set and `Manage()` is called, then the names given to `Manage()` will be whitelisted rather than obtained right away. - - -## Storage - -CertMagic relies on storage to store certificates and other TLS assets (OCSP staple cache, coordinating locks, etc). Persistent storage is a requirement when using CertMagic: ephemeral storage will likely lead to rate limiting on the CA-side as CertMagic will always have to get new certificates. - -By default, CertMagic stores assets on the local file system in `$HOME/.local/share/certmagic` (and honors `$XDG_DATA_HOME` if set). CertMagic will create the directory if it does not exist. If writes are denied, things will not be happy, so make sure CertMagic can write to it! - -The notion of a "cluster" or "fleet" of instances that may be serving the same site and sharing certificates, etc, is tied to storage. Simply, any instances that use the same storage facilities are considered part of the cluster. So if you deploy 100 instances of CertMagic behind a load balancer, they are all part of the same cluster if they share the same storage configuration. Sharing storage could be mounting a shared folder, or implementing some other distributed storage system such as a database server or KV store. - -The easiest way to change the storage being used is to set `certmagic.DefaultStorage` to a value that satisfies the [Storage interface](https://godoc.org/github.com/mholt/certmagic#Storage). Keep in mind that a valid `Storage` must be able to implement some operations atomically in order to provide locking and synchronization. - -If you write a Storage implementation, please add it to the [project wiki](https://github.com/mholt/certmagic/wiki/Storage-Implementations) so people can find it! - - -## Cache - -All of the certificates in use are de-duplicated and cached in memory for optimal performance at handshake-time. This cache must be backed by persistent storage as described above. - -Most applications will not need to interact with certificate caches directly. Usually, the closest you will come is to set the package-wide `certmagic.DefaultStorage` variable (before attempting to create any Configs). However, if your use case requires using different storage facilities for different Configs (that's highly unlikely and NOT recommended! Even Caddy doesn't get that crazy), you will need to call `certmagic.NewCache()` and pass in the storage you want to use, then get new `Config` structs with `certmagic.NewWithCache()` and pass in the cache. - -Again, if you're needing to do this, you've probably over-complicated your application design. - - -## FAQ - -### Can I use some of my own certificates while using CertMagic? - -Yes, just call the relevant method on the `Config` to add your own certificate to the cache: - -- [`CacheUnmanagedCertificatePEMBytes()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedCertificatePEMBytes) -- [`CacheUnmanagedCertificatePEMFile()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedCertificatePEMFile) -- [`CacheUnmanagedTLSCertificate()`](https://godoc.org/github.com/mholt/certmagic#Config.CacheUnmanagedTLSCertificate) - -Keep in mind that unmanaged certificates are (obviously) not renewed for you, so you'll have to replace them when you do. However, OCSP stapling is performed even for unmanaged certificates that qualify. - - -### Does CertMagic obtain SAN certificates? - -Technically all certificates these days are SAN certificates because CommonName is deprecated. But if you're asking whether CertMagic issues and manages certificates with multiple SANs, the answer is no. But it does support serving them, if you provide your own. - - -### How can I listen on ports 80 and 443? Do I have to run as root? - -On Linux, you can use `setcap` to grant your binary the permission to bind low ports: - -```bash -$ sudo setcap cap_net_bind_service=+ep /path/to/your/binary -``` - -and then you will not need to run with root privileges. - - -## Contributing - -We welcome your contributions! Please see our **[contributing guidelines](https://github.com/mholt/certmagic/blob/master/.github/CONTRIBUTING.md)** for instructions. - - -## Project History - -CertMagic is the core of Caddy's advanced TLS automation code, extracted into a library. The underlying ACME client implementation is [lego](https://github.com/go-acme/lego), which was originally developed for use in Caddy even before Let's Encrypt entered public beta in 2015. - -In the years since then, Caddy's TLS automation techniques have been widely adopted, tried and tested in production, and served millions of sites and secured trillions of connections. - -Now, CertMagic is _the actual library used by Caddy_. It's incredibly powerful and feature-rich, but also easy to use for simple Go programs: one line of code can enable fully-automated HTTPS applications with HTTP->HTTPS redirects. - -Caddy is known for its robust HTTPS+ACME features. When ACME certificate authorities have had outages, in some cases Caddy was the only major client that didn't experience any downtime. Caddy can weather OCSP outages lasting days, or CA outages lasting weeks, without taking your sites offline. - -Caddy was also the first to sport "on-demand" issuance technology, which obtains certificates during the first TLS handshake for an allowed SNI name. - -Consequently, CertMagic brings all these (and more) features and capabilities right into your own Go programs. - -You can [watch a 2016 dotGo talk](https://www.dotconferences.com/2016/10/matthew-holt-go-with-acme) by the author of this library about using ACME to automate certificate management in Go programs: - -[![Matthew Holt speaking at dotGo 2016 about ACME in Go](https://user-images.githubusercontent.com/1128849/49921557-2d506780-fe6b-11e8-97bf-6053b6b4eb48.png)](https://www.dotconferences.com/2016/10/matthew-holt-go-with-acme) - - - -## Credits and License - -CertMagic is a project by [Matthew Holt](https://twitter.com/mholt6), who is the author; and various contributors, who are credited in the commit history of either CertMagic or Caddy. - -CertMagic is licensed under Apache 2.0, an open source license. For convenience, its main points are summarized as follows (but this is no replacement for the actual license text): - -- The author owns the copyright to this code -- Use, distribute, and modify the software freely -- Private and internal use is allowed -- License text and copyright notices must stay intact and be included with distributions -- Any and all changes to the code must be documented diff --git a/vendor/github.com/mholt/certmagic/appveyor.yml b/vendor/github.com/mholt/certmagic/appveyor.yml deleted file mode 100644 index 2ab77bec7..000000000 --- a/vendor/github.com/mholt/certmagic/appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "{build}" - -clone_folder: c:\gopath\src\github.com\mholt\certmagic - -environment: - GOPATH: c:\gopath - -stack: go 1.11 - -install: - - set PATH=%GOPATH%\bin;%PATH% - - set PATH=C:\msys64\mingw64\bin;%PATH% - - go get -t ./... - - go get golang.org/x/lint/golint - - go get github.com/alecthomas/gometalinter - -build: off - -before_test: - - go version - - go env - -test_script: - - go test -race ./... - -after_test: - - golint ./... - -deploy: off diff --git a/vendor/github.com/mholt/certmagic/cache.go b/vendor/github.com/mholt/certmagic/cache.go deleted file mode 100644 index 79641df68..000000000 --- a/vendor/github.com/mholt/certmagic/cache.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "fmt" - "sync" - "time" -) - -// Cache is a structure that stores certificates in memory. -// A Cache indexes certificates by name for quick access -// during TLS handshakes, and avoids duplicating certificates -// in memory. Generally, there should only be one per process. -// However, that is not a strict requirement; but using more -// than one is a code smell, and may indicate an -// over-engineered design. -// -// An empty cache is INVALID and must not be used. Be sure -// to call NewCache to get a valid value. -// -// These should be very long-lived values and must not be -// copied. Before all references leave scope to be garbage -// collected, ensure you call Stop() to stop maintenance on -// the certificates stored in this cache and release locks. -// -// Caches are not usually manipulated directly; create a -// Config value with a pointer to a Cache, and then use -// the Config to interact with the cache. Caches are -// agnostic of any particular storage or ACME config, -// since each certificate may be managed and stored -// differently. -type Cache struct { - // User configuration of the cache - options CacheOptions - - // The cache is keyed by certificate hash - cache map[string]Certificate - - // cacheIndex is a map of SAN to cache key (cert hash) - cacheIndex map[string][]string - - // Protects the cache and index maps - mu sync.RWMutex - - // Close this channel to cancel asset maintenance - stopChan chan struct{} - - // Used to signal when stopping is completed - doneChan chan struct{} -} - -// NewCache returns a new, valid Cache for efficiently -// accessing certificates in memory. It also begins a -// maintenance goroutine to tend to the certificates -// in the cache. Call Stop() when you are done with the -// cache so it can clean up locks and stuff. -// -// Most users of this package will not need to call this -// because a default certificate cache is created for you. -// Only advanced use cases require creating a new cache. -// -// This function panics if opts.GetConfigForCert is not -// set. The reason is that a cache absolutely needs to -// be able to get a Config with which to manage TLS -// assets, and it is not safe to assume that the Default -// config is always the correct one, since you have -// created the cache yourself. -// -// See the godoc for Cache to use it properly. When -// no longer needed, caches should be stopped with -// Stop() to clean up resources even if the process -// is being terminated, so that it can clean up -// any locks for other processes to unblock! -func NewCache(opts CacheOptions) *Cache { - // assume default options if necessary - if opts.OCSPCheckInterval <= 0 { - opts.OCSPCheckInterval = DefaultOCSPCheckInterval - } - if opts.RenewCheckInterval <= 0 { - opts.RenewCheckInterval = DefaultRenewCheckInterval - } - - // this must be set, because we cannot not - // safely assume that the Default Config - // is always the correct one to use - if opts.GetConfigForCert == nil { - panic("cache must be initialized with a GetConfigForCert callback") - } - - c := &Cache{ - options: opts, - cache: make(map[string]Certificate), - cacheIndex: make(map[string][]string), - stopChan: make(chan struct{}), - doneChan: make(chan struct{}), - } - - go c.maintainAssets() - - return c -} - -// Stop stops the maintenance goroutine for -// certificates in certCache. It blocks until -// stopping is complete. Once a cache is -// stopped, it cannot be reused. -func (certCache *Cache) Stop() { - close(certCache.stopChan) // signal to stop - <-certCache.doneChan // wait for stop to complete -} - -// CacheOptions is used to configure certificate caches. -// Once a cache has been created with certain options, -// those settings cannot be changed. -type CacheOptions struct { - // REQUIRED. A function that returns a configuration - // used for managing a certificate, or for accessing - // that certificate's asset storage (e.g. for - // OCSP staples, etc). The returned Config MUST - // be associated with the same Cache as the caller. - // - // The reason this is a callback function, dynamically - // returning a Config (instead of attaching a static - // pointer to a Config on each certificate) is because - // the config for how to manage a domain's certificate - // might change from maintenance to maintenance. The - // cache is so long-lived, we cannot assume that the - // host's situation will always be the same; e.g. the - // certificate might switch DNS providers, so the DNS - // challenge (if used) would need to be adjusted from - // the last time it was run ~8 weeks ago. - GetConfigForCert ConfigGetter - - // How often to check certificates for renewal; - // if unset, DefaultOCSPCheckInterval will be used. - OCSPCheckInterval time.Duration - - // How often to check certificates for renewal; - // if unset, DefaultRenewCheckInterval will be used. - RenewCheckInterval time.Duration -} - -// ConfigGetter is a function that returns a config that -// should be used when managing the given certificate -// or its assets. -type ConfigGetter func(Certificate) (Config, error) - -// cacheCertificate calls unsyncedCacheCertificate with a write lock. -// -// This function is safe for concurrent use. -func (certCache *Cache) cacheCertificate(cert Certificate) { - certCache.mu.Lock() - certCache.unsyncedCacheCertificate(cert) - certCache.mu.Unlock() -} - -// unsyncedCacheCertificate adds cert to the in-memory cache unless -// it already exists in the cache (according to cert.Hash). It -// updates the name index. -// -// This function is NOT safe for concurrent use. Callers MUST acquire -// a write lock on certCache.mu first. -func (certCache *Cache) unsyncedCacheCertificate(cert Certificate) { - // no-op if this certificate already exists in the cache - if _, ok := certCache.cache[cert.Hash]; ok { - return - } - - // store the certificate - certCache.cache[cert.Hash] = cert - - // update the index so we can access it by name - for _, name := range cert.Names { - certCache.cacheIndex[name] = append(certCache.cacheIndex[name], cert.Hash) - } -} - -// removeCertificate removes cert from the cache. -// -// This function is NOT safe for concurrent use; callers -// MUST first acquire a write lock on certCache.mu. -func (certCache *Cache) removeCertificate(cert Certificate) { - // delete all mentions of this cert from the name index - for _, name := range cert.Names { - keyList := certCache.cacheIndex[name] - for i, cacheKey := range keyList { - if cacheKey == cert.Hash { - keyList = append(keyList[:i], keyList[i+1:]...) - } - } - if len(keyList) == 0 { - delete(certCache.cacheIndex, name) - } else { - certCache.cacheIndex[name] = keyList - } - } - - // delete the actual cert from the cache - delete(certCache.cache, cert.Hash) -} - -// replaceCertificate atomically replaces oldCert with newCert in -// the cache. -// -// This method is safe for concurrent use. -func (certCache *Cache) replaceCertificate(oldCert, newCert Certificate) { - certCache.mu.Lock() - certCache.removeCertificate(oldCert) - certCache.unsyncedCacheCertificate(newCert) - certCache.mu.Unlock() -} - -func (certCache *Cache) getFirstMatchingCert(name string) (Certificate, bool) { - certCache.mu.RLock() - defer certCache.mu.RUnlock() - - allCertKeys := certCache.cacheIndex[name] - if len(allCertKeys) == 0 { - return Certificate{}, false - } - - cert, ok := certCache.cache[allCertKeys[0]] - return cert, ok -} - -// TODO: This seems unused (but could be useful if TLS -// handshakes serve up different certs for a single -// name depending on other properties such as key type) -func (certCache *Cache) getAllMatchingCerts(name string) []Certificate { - certCache.mu.RLock() - defer certCache.mu.RUnlock() - - allCertKeys := certCache.cacheIndex[name] - - certs := make([]Certificate, len(allCertKeys)) - for i := range allCertKeys { - certs[i] = certCache.cache[allCertKeys[i]] - } - - return certs -} - -func (certCache *Cache) getConfig(cert Certificate) (*Config, error) { - cfg, err := certCache.options.GetConfigForCert(cert) - if err != nil { - return nil, err - } - if cfg.certCache != nil && cfg.certCache != certCache { - return nil, fmt.Errorf("config returned for certificate %v is not nil and points to different cache; got %p, expected %p (this one)", - cert.Names, cfg.certCache, certCache) - } - return New(certCache, cfg), nil -} - -var ( - defaultCache *Cache - defaultCacheMu sync.Mutex -) diff --git a/vendor/github.com/mholt/certmagic/certificates.go b/vendor/github.com/mholt/certmagic/certificates.go deleted file mode 100644 index 5675a7c8c..000000000 --- a/vendor/github.com/mholt/certmagic/certificates.go +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "log" - "net" - "strings" - "time" - - "golang.org/x/crypto/ocsp" -) - -// Certificate is a tls.Certificate with associated metadata tacked on. -// Even if the metadata can be obtained by parsing the certificate, -// we are more efficient by extracting the metadata onto this struct. -type Certificate struct { - tls.Certificate - - // Names is the list of names this certificate is written for. - // The first is the CommonName (if any), the rest are SAN. - Names []string - - // NotAfter is when the certificate expires. - NotAfter time.Time - - // OCSP contains the certificate's parsed OCSP response. - OCSP *ocsp.Response - - // The hex-encoded hash of this cert's chain's bytes. - Hash string - - // Whether this certificate is under our management - managed bool -} - -// NeedsRenewal returns true if the certificate is -// expiring soon (according to cfg) or has expired. -func (cert Certificate) NeedsRenewal(cfg *Config) bool { - if cert.NotAfter.IsZero() { - return false - } - renewDurationBefore := DefaultRenewDurationBefore - if cfg.RenewDurationBefore > 0 { - renewDurationBefore = cfg.RenewDurationBefore - } - return time.Until(cert.NotAfter) < renewDurationBefore -} - -// CacheManagedCertificate loads the certificate for domain into the -// cache, from the TLS storage for managed certificates. It returns a -// copy of the Certificate that was put into the cache. -// -// This is a lower-level method; normally you'll call Manage() instead. -// -// This method is safe for concurrent use. -func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) { - cert, err := cfg.loadManagedCertificate(domain) - if err != nil { - return cert, err - } - cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_managed_cert", cert.Names) - } - return cert, nil -} - -// loadManagedCertificate loads the managed certificate for domain, -// but it does not add it to the cache. It just loads from storage. -func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) { - certRes, err := cfg.loadCertResource(domain) - if err != nil { - return Certificate{}, err - } - cert, err := makeCertificateWithOCSP(cfg.Storage, certRes.Certificate, certRes.PrivateKey) - if err != nil { - return cert, err - } - cert.managed = true - return cert, nil -} - -// CacheUnmanagedCertificatePEMFile loads a certificate for host using certFile -// and keyFile, which must be in PEM format. It stores the certificate in -// the in-memory cache. -// -// This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedCertificatePEMFile(certFile, keyFile string) error { - cert, err := makeCertificateFromDiskWithOCSP(cfg.Storage, certFile, keyFile) - if err != nil { - return err - } - cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } - return nil -} - -// CacheUnmanagedTLSCertificate adds tlsCert to the certificate cache. -// It staples OCSP if possible. -// -// This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate) error { - var cert Certificate - err := fillCertFromLeaf(&cert, tlsCert) - if err != nil { - return err - } - err = stapleOCSP(cfg.Storage, &cert, nil) - if err != nil { - log.Printf("[WARNING] Stapling OCSP: %v", err) - } - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } - cfg.certCache.cacheCertificate(cert) - return nil -} - -// CacheUnmanagedCertificatePEMBytes makes a certificate out of the PEM bytes -// of the certificate and key, then caches it in memory. -// -// This method is safe for concurrent use. -func (cfg *Config) CacheUnmanagedCertificatePEMBytes(certBytes, keyBytes []byte) error { - cert, err := makeCertificateWithOCSP(cfg.Storage, certBytes, keyBytes) - if err != nil { - return err - } - cfg.certCache.cacheCertificate(cert) - if cfg.OnEvent != nil { - cfg.OnEvent("cached_unmanaged_cert", cert.Names) - } - return nil -} - -// makeCertificateFromDiskWithOCSP makes a Certificate by loading the -// certificate and key files. It fills out all the fields in -// the certificate except for the Managed and OnDemand flags. -// (It is up to the caller to set those.) It staples OCSP. -func makeCertificateFromDiskWithOCSP(storage Storage, certFile, keyFile string) (Certificate, error) { - certPEMBlock, err := ioutil.ReadFile(certFile) - if err != nil { - return Certificate{}, err - } - keyPEMBlock, err := ioutil.ReadFile(keyFile) - if err != nil { - return Certificate{}, err - } - return makeCertificateWithOCSP(storage, certPEMBlock, keyPEMBlock) -} - -// makeCertificateWithOCSP is the same as makeCertificate except that it also -// staples OCSP to the certificate. -func makeCertificateWithOCSP(storage Storage, certPEMBlock, keyPEMBlock []byte) (Certificate, error) { - cert, err := makeCertificate(certPEMBlock, keyPEMBlock) - if err != nil { - return cert, err - } - err = stapleOCSP(storage, &cert, certPEMBlock) - if err != nil { - log.Printf("[WARNING] Stapling OCSP: %v", err) - } - return cert, nil -} - -// makeCertificate turns a certificate PEM bundle and a key PEM block into -// a Certificate with necessary metadata from parsing its bytes filled into -// its struct fields for convenience (except for the OnDemand and Managed -// flags; it is up to the caller to set those properties!). This function -// does NOT staple OCSP. -func makeCertificate(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { - var cert Certificate - - // Convert to a tls.Certificate - tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) - if err != nil { - return cert, err - } - - // Extract necessary metadata - err = fillCertFromLeaf(&cert, tlsCert) - if err != nil { - return cert, err - } - - return cert, nil -} - -// fillCertFromLeaf populates metadata fields on cert from tlsCert. -func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error { - if len(tlsCert.Certificate) == 0 { - return fmt.Errorf("certificate is empty") - } - cert.Certificate = tlsCert - - // the leaf cert should be the one for the site; it has what we need - leaf, err := x509.ParseCertificate(tlsCert.Certificate[0]) - if err != nil { - return err - } - - if leaf.Subject.CommonName != "" { // TODO: CommonName is deprecated - cert.Names = []string{strings.ToLower(leaf.Subject.CommonName)} - } - for _, name := range leaf.DNSNames { - if name != leaf.Subject.CommonName { // TODO: CommonName is deprecated - cert.Names = append(cert.Names, strings.ToLower(name)) - } - } - for _, ip := range leaf.IPAddresses { - if ipStr := ip.String(); ipStr != leaf.Subject.CommonName { // TODO: CommonName is deprecated - cert.Names = append(cert.Names, strings.ToLower(ipStr)) - } - } - for _, email := range leaf.EmailAddresses { - if email != leaf.Subject.CommonName { // TODO: CommonName is deprecated - cert.Names = append(cert.Names, strings.ToLower(email)) - } - } - if len(cert.Names) == 0 { - return fmt.Errorf("certificate has no names") - } - - // save the hash of this certificate (chain) and - // expiration date, for necessity and efficiency - cert.Hash = hashCertificateChain(cert.Certificate.Certificate) - cert.NotAfter = leaf.NotAfter - - return nil -} - -// managedCertInStorageExpiresSoon returns true if cert (being a -// managed certificate) is expiring within RenewDurationBefore. -// It returns false if there was an error checking the expiration -// of the certificate as found in storage, or if the certificate -// in storage is NOT expiring soon. A certificate that is expiring -// soon in our cache but is not expiring soon in storage probably -// means that another instance renewed the certificate in the -// meantime, and it would be a good idea to simply load the cert -// into our cache rather than repeating the renewal process again. -func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, error) { - certRes, err := cfg.loadCertResource(cert.Names[0]) - if err != nil { - return false, err - } - tlsCert, err := tls.X509KeyPair(certRes.Certificate, certRes.PrivateKey) - if err != nil { - return false, err - } - leaf, err := x509.ParseCertificate(tlsCert.Certificate[0]) - if err != nil { - return false, err - } - timeLeft := leaf.NotAfter.Sub(time.Now().UTC()) - return timeLeft < cfg.RenewDurationBefore, nil -} - -// reloadManagedCertificate reloads the certificate corresponding to the name(s) -// on oldCert into the cache, from storage. This also replaces the old certificate -// with the new one, so that all configurations that used the old cert now point -// to the new cert. -func (cfg *Config) reloadManagedCertificate(oldCert Certificate) error { - newCert, err := cfg.loadManagedCertificate(oldCert.Names[0]) - if err != nil { - return fmt.Errorf("loading managed certificate for %v from storage: %v", oldCert.Names, err) - } - cfg.certCache.replaceCertificate(oldCert, newCert) - return nil -} - -// HostQualifies returns true if the hostname alone -// appears eligible for automagic TLS. For example: -// localhost, empty hostname, and IP addresses are -// not eligible because we cannot obtain certificates -// for those names. Wildcard names are allowed, as long -// as they conform to CABF requirements (only one wildcard -// label, and it must be the left-most label). -func HostQualifies(hostname string) bool { - return hostname != "localhost" && // localhost is ineligible - - // hostname must not be empty - strings.TrimSpace(hostname) != "" && - - // only one wildcard label allowed, and it must be left-most - (!strings.Contains(hostname, "*") || - (strings.Count(hostname, "*") == 1 && - strings.HasPrefix(hostname, "*."))) && - - // must not start or end with a dot - !strings.HasPrefix(hostname, ".") && - !strings.HasSuffix(hostname, ".") && - - // cannot be an IP address, see - // https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt - net.ParseIP(hostname) == nil -} diff --git a/vendor/github.com/mholt/certmagic/certmagic.go b/vendor/github.com/mholt/certmagic/certmagic.go deleted file mode 100644 index 4739ad270..000000000 --- a/vendor/github.com/mholt/certmagic/certmagic.go +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic automates the obtaining and renewal of TLS certificates, -// including TLS & HTTPS best practices such as robust OCSP stapling, caching, -// HTTP->HTTPS redirects, and more. -// -// Its high-level API serves your HTTP handlers over HTTPS if you simply give -// the domain name(s) and the http.Handler; CertMagic will create and run -// the HTTPS server for you, fully managing certificates during the lifetime -// of the server. Similarly, it can be used to start TLS listeners or return -// a ready-to-use tls.Config -- whatever layer you need TLS for, CertMagic -// makes it easy. See the HTTPS, Listen, and TLS functions for that. -// -// If you need more control, create a Cache using NewCache() and then make -// a Config using New(). You can then call Manage() on the config. But if -// you use this lower-level API, you'll have to be sure to solve the HTTP -// and TLS-ALPN challenges yourself (unless you disabled them or use the -// DNS challenge) by using the provided Config.GetCertificate function -// in your tls.Config and/or Config.HTTPChallangeHandler in your HTTP -// handler. -// -// See the package's README for more instruction. -package certmagic - -import ( - "crypto/tls" - "fmt" - "log" - "net" - "net/http" - "net/url" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/go-acme/lego/certcrypto" -) - -// HTTPS serves mux for all domainNames using the HTTP -// and HTTPS ports, redirecting all HTTP requests to HTTPS. -// It uses the Default config. -// -// This high-level convenience function is opinionated and -// applies sane defaults for production use, including -// timeouts for HTTP requests and responses. To allow very -// long-lived connections, you should make your own -// http.Server values and use this package's Listen(), TLS(), -// or Config.TLSConfig() functions to customize to your needs. -// For example, servers which need to support large uploads or -// downloads with slow clients may need to use longer timeouts, -// thus this function is not suitable. -// -// Calling this function signifies your acceptance to -// the CA's Subscriber Agreement and/or Terms of Service. -func HTTPS(domainNames []string, mux http.Handler) error { - if mux == nil { - mux = http.DefaultServeMux - } - - Default.Agreed = true - cfg := NewDefault() - - err := cfg.Manage(domainNames) - if err != nil { - return err - } - - httpWg.Add(1) - defer httpWg.Done() - - // if we haven't made listeners yet, do so now, - // and clean them up when all servers are done - lnMu.Lock() - if httpLn == nil && httpsLn == nil { - httpLn, err = net.Listen("tcp", fmt.Sprintf(":%d", HTTPPort)) - if err != nil { - lnMu.Unlock() - return err - } - - httpsLn, err = tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) - if err != nil { - httpLn.Close() - httpLn = nil - lnMu.Unlock() - return err - } - - go func() { - httpWg.Wait() - lnMu.Lock() - httpLn.Close() - httpsLn.Close() - lnMu.Unlock() - }() - } - hln, hsln := httpLn, httpsLn - lnMu.Unlock() - - // create HTTP/S servers that are configured - // with sane default timeouts and appropriate - // handlers (the HTTP server solves the HTTP - // challenge and issues redirects to HTTPS, - // while the HTTPS server simply serves the - // user's handler) - httpServer := &http.Server{ - ReadHeaderTimeout: 5 * time.Second, - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - IdleTimeout: 5 * time.Second, - Handler: cfg.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler)), - } - httpsServer := &http.Server{ - ReadHeaderTimeout: 10 * time.Second, - ReadTimeout: 30 * time.Second, - WriteTimeout: 2 * time.Minute, - IdleTimeout: 5 * time.Minute, - Handler: mux, - } - - log.Printf("%v Serving HTTP->HTTPS on %s and %s", - domainNames, hln.Addr(), hsln.Addr()) - - go httpServer.Serve(hln) - return httpsServer.Serve(hsln) -} - -func httpRedirectHandler(w http.ResponseWriter, r *http.Request) { - toURL := "https://" - - // since we redirect to the standard HTTPS port, we - // do not need to include it in the redirect URL - requestHost, _, err := net.SplitHostPort(r.Host) - if err != nil { - requestHost = r.Host // host probably did not contain a port - } - - toURL += requestHost - toURL += r.URL.RequestURI() - - // get rid of this disgusting unencrypted HTTP connection 🤢 - w.Header().Set("Connection", "close") - - http.Redirect(w, r, toURL, http.StatusMovedPermanently) -} - -// TLS enables management of certificates for domainNames -// and returns a valid tls.Config. It uses the Default -// config. -// -// Because this is a convenience function that returns -// only a tls.Config, it does not assume HTTP is being -// served on the HTTP port, so the HTTP challenge is -// disabled (no HTTPChallengeHandler is necessary). The -// package variable Default is modified so that the -// HTTP challenge is disabled. -// -// Calling this function signifies your acceptance to -// the CA's Subscriber Agreement and/or Terms of Service. -func TLS(domainNames []string) (*tls.Config, error) { - Default.Agreed = true - Default.DisableHTTPChallenge = true - cfg := NewDefault() - return cfg.TLSConfig(), cfg.Manage(domainNames) -} - -// Listen manages certificates for domainName and returns a -// TLS listener. It uses the Default config. -// -// Because this convenience function returns only a TLS-enabled -// listener and does not presume HTTP is also being served, -// the HTTP challenge will be disabled. The package variable -// Default is modified so that the HTTP challenge is disabled. -// -// Calling this function signifies your acceptance to -// the CA's Subscriber Agreement and/or Terms of Service. -func Listen(domainNames []string) (net.Listener, error) { - Default.Agreed = true - Default.DisableHTTPChallenge = true - cfg := NewDefault() - err := cfg.Manage(domainNames) - if err != nil { - return nil, err - } - return tls.Listen("tcp", fmt.Sprintf(":%d", HTTPSPort), cfg.TLSConfig()) -} - -// Manage obtains certificates for domainNames and keeps them -// renewed using the Default config. -// -// This is a slightly lower-level function; you will need to -// wire up support for the ACME challenges yourself. You can -// obtain a Config to help you do that by calling NewDefault(). -// -// You will need to ensure that you use a TLS config that gets -// certificates from this Config and that the HTTP and TLS-ALPN -// challenges can be solved. The easiest way to do this is to -// use NewDefault().TLSConfig() as your TLS config and to wrap -// your HTTP handler with NewDefault().HTTPChallengeHandler(). -// If you don't have an HTTP server, you will need to disable -// the HTTP challenge. -// -// If you already have a TLS config you want to use, you can -// simply set its GetCertificate field to -// NewDefault().GetCertificate. -// -// Calling this function signifies your acceptance to -// the CA's Subscriber Agreement and/or Terms of Service. -func Manage(domainNames []string) error { - Default.Agreed = true - return NewDefault().Manage(domainNames) -} - -// OnDemandConfig contains some state relevant for providing -// on-demand TLS. Important note: If you are using the -// MaxObtain property to limit the maximum number of certs -// to be issued, the count of how many certs were issued -// will be reset if this struct gets garbage-collected. -type OnDemandConfig struct { - // If set, this function will be the absolute - // authority on whether the hostname (according - // to SNI) is allowed to try to get a cert. - DecisionFunc func(name string) error - - // If no DecisionFunc is set, this whitelist - // is the absolute authority as to whether - // a certificate should be allowed to be tried. - // Names are compared against SNI value. - HostWhitelist []string - - // If no DecisionFunc or HostWhitelist are set, - // then an HTTP request will be made to AskURL - // to determine if a certificate should be - // obtained. If the request fails or the response - // is anything other than 2xx status code, the - // issuance will be denied. - AskURL *url.URL - - // If no DecisionFunc, HostWhitelist, or AskURL - // are set, then only this many certificates may - // be obtained on-demand; this field is required - // if all others are empty, otherwise, all cert - // issuances will fail. - MaxObtain int32 - - // The number of certificates that have been issued on-demand - // by this config. It is only safe to modify this count atomically. - // If it reaches MaxObtain, on-demand issuances must fail. - // Note that this will necessarily be reset to 0 if the - // struct leaves scope and/or gets garbage-collected. - obtainedCount int32 -} - -// Allowed returns whether the issuance for name is allowed according to o. -func (o *OnDemandConfig) Allowed(name string) error { - // The decision function has absolute authority, if set - if o.DecisionFunc != nil { - return o.DecisionFunc(name) - } - - // Otherwise, the host whitelist has decision authority - if len(o.HostWhitelist) > 0 { - return o.checkWhitelistForObtainingNewCerts(name) - } - - // Otherwise, a URL is checked for permission to issue this cert - if o.AskURL != nil { - return o.checkURLForObtainingNewCerts(name) - } - - // Otherwise use the limit defined by the "max_certs" setting - return o.checkLimitsForObtainingNewCerts(name) -} - -func (o *OnDemandConfig) whitelistContains(name string) bool { - for _, n := range o.HostWhitelist { - if strings.ToLower(n) == strings.ToLower(name) { - return true - } - } - return false -} - -func (o *OnDemandConfig) checkWhitelistForObtainingNewCerts(name string) error { - if !o.whitelistContains(name) { - return fmt.Errorf("%s: name is not whitelisted", name) - } - return nil -} - -func (o *OnDemandConfig) checkURLForObtainingNewCerts(name string) error { - client := http.Client{ - Timeout: 10 * time.Second, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return fmt.Errorf("following http redirects is not allowed") - }, - } - - // Copy the URL from the config in order to modify it for this request - askURL := new(url.URL) - *askURL = *o.AskURL - - query := askURL.Query() - query.Set("domain", name) - askURL.RawQuery = query.Encode() - - resp, err := client.Get(askURL.String()) - if err != nil { - return fmt.Errorf("error checking %v to deterine if certificate for hostname '%s' should be allowed: %v", o.AskURL, name, err) - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return fmt.Errorf("certificate for hostname '%s' not allowed, non-2xx status code %d returned from %v", name, resp.StatusCode, o.AskURL) - } - - return nil -} - -// checkLimitsForObtainingNewCerts checks to see if name can be issued right -// now according the maximum count defined in the configuration. If a non-nil -// error is returned, do not issue a new certificate for name. -func (o *OnDemandConfig) checkLimitsForObtainingNewCerts(name string) error { - if o.MaxObtain == 0 { - return fmt.Errorf("%s: no certificates allowed to be issued on-demand", name) - } - - // User can set hard limit for number of certs for the process to issue - if o.MaxObtain > 0 && - atomic.LoadInt32(&o.obtainedCount) >= o.MaxObtain { - return fmt.Errorf("%s: maximum certificates issued (%d)", name, o.MaxObtain) - } - - // Make sure name hasn't failed a challenge recently - failedIssuanceMu.RLock() - when, ok := failedIssuance[name] - failedIssuanceMu.RUnlock() - if ok { - return fmt.Errorf("%s: throttled; refusing to issue cert since last attempt on %s failed", name, when.String()) - } - - // Make sure, if we've issued a few certificates already, that we haven't - // issued any recently - lastIssueTimeMu.Lock() - since := time.Since(lastIssueTime) - lastIssueTimeMu.Unlock() - if atomic.LoadInt32(&o.obtainedCount) >= 10 && since < 10*time.Minute { - return fmt.Errorf("%s: throttled; last certificate was obtained %v ago", name, since) - } - - // Good to go 👠- return nil -} - -// failedIssuance is a set of names that we recently failed to get a -// certificate for from the ACME CA. They are removed after some time. -// When a name is in this map, do not issue a certificate for it on-demand. -var failedIssuance = make(map[string]time.Time) -var failedIssuanceMu sync.RWMutex - -// lastIssueTime records when we last obtained a certificate successfully. -// If this value is recent, do not make any on-demand certificate requests. -var lastIssueTime time.Time -var lastIssueTimeMu sync.Mutex - -// isLoopback returns true if the hostname of addr looks -// explicitly like a common local hostname. addr must only -// be a host or a host:port combination. -func isLoopback(addr string) bool { - host, _, err := net.SplitHostPort(strings.ToLower(addr)) - if err != nil { - host = addr // happens if the addr is only a hostname - } - return host == "localhost" || - strings.Trim(host, "[]") == "::1" || - strings.HasPrefix(host, "127.") -} - -// isInternal returns true if the IP of addr -// belongs to a private network IP range. addr -// must only be an IP or an IP:port combination. -// Loopback addresses are considered false. -func isInternal(addr string) bool { - privateNetworks := []string{ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - "fc00::/7", - } - host, _, err := net.SplitHostPort(addr) - if err != nil { - host = addr // happens if the addr is just a hostname, missing port - // if we encounter an error, the brackets need to be stripped - // because SplitHostPort didn't do it for us - host = strings.Trim(host, "[]") - } - ip := net.ParseIP(host) - if ip == nil { - return false - } - for _, privateNetwork := range privateNetworks { - _, ipnet, _ := net.ParseCIDR(privateNetwork) - if ipnet.Contains(ip) { - return true - } - } - return false -} - -// Default contains the package defaults for the -// various Config fields. This is used as a template -// when creating your own Configs with New(), and it -// is also used as the Config by all the high-level -// functions in this package. -// -// The fields of this value will be used for Config -// fields which are unset. Feel free to modify these -// defaults, but do not use this Config by itself: it -// is only a template. Valid configurations can be -// obtained by calling New() (if you have your own -// certificate cache) or NewDefault() (if you only -// need a single config and want to use the default -// cache). This is the only Config which can access -// the default certificate cache. -var Default = Config{ - CA: LetsEncryptProductionCA, - RenewDurationBefore: DefaultRenewDurationBefore, - RenewDurationBeforeAtStartup: DefaultRenewDurationBeforeAtStartup, - KeyType: certcrypto.EC256, - Storage: defaultFileStorage, -} - -const ( - // HTTPChallengePort is the officially-designated port for - // the HTTP challenge according to the ACME spec. - HTTPChallengePort = 80 - - // TLSALPNChallengePort is the officially-designated port for - // the TLS-ALPN challenge according to the ACME spec. - TLSALPNChallengePort = 443 -) - -// Some well-known CA endpoints available to use. -const ( - LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory" - LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory" -) - -// Port variables must remain their defaults unless you -// forward packets from the defaults to whatever these -// are set to; otherwise ACME challenges will fail. -var ( - // HTTPPort is the port on which to serve HTTP - // and, by extension, the HTTP challenge (unless - // Default.AltHTTPPort is set). - HTTPPort = 80 - - // HTTPSPort is the port on which to serve HTTPS - // and, by extension, the TLS-ALPN challenge - // (unless Default.AltTLSALPNPort is set). - HTTPSPort = 443 -) - -// Variables for conveniently serving HTTPS. -var ( - httpLn, httpsLn net.Listener - lnMu sync.Mutex - httpWg sync.WaitGroup -) diff --git a/vendor/github.com/mholt/certmagic/client.go b/vendor/github.com/mholt/certmagic/client.go deleted file mode 100644 index b1e417a31..000000000 --- a/vendor/github.com/mholt/certmagic/client.go +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "fmt" - "log" - "net" - "net/url" - "strings" - "sync" - "time" - - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/http01" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/lego" - "github.com/go-acme/lego/registration" -) - -// acmeMu ensures that only one ACME challenge occurs at a time. -var acmeMu sync.Mutex - -// acmeClient is a wrapper over acme.Client with -// some custom state attached. It is used to obtain, -// renew, and revoke certificates with ACME. -type acmeClient struct { - config *Config - acmeClient *lego.Client -} - -// listenerAddressInUse returns true if a TCP connection -// can be made to addr within a short time interval. -func listenerAddressInUse(addr string) bool { - conn, err := net.DialTimeout("tcp", addr, 250*time.Millisecond) - if err == nil { - conn.Close() - } - return err == nil -} - -func (cfg *Config) newManager(interactive bool) (Manager, error) { - if cfg.NewManager != nil { - return cfg.NewManager(interactive) - } - return cfg.newACMEClient(interactive) -} - -func (cfg *Config) newACMEClient(interactive bool) (*acmeClient, error) { - // look up or create the user account - leUser, err := cfg.getUser(cfg.Email) - if err != nil { - return nil, err - } - - // ensure key type and timeout are set - keyType := cfg.KeyType - if keyType == "" { - keyType = Default.KeyType - } - certObtainTimeout := cfg.CertObtainTimeout - if certObtainTimeout == 0 { - certObtainTimeout = Default.CertObtainTimeout - } - - // ensure CA URL (directory endpoint) is set - caURL := Default.CA - if cfg.CA != "" { - caURL = cfg.CA - } - - // ensure endpoint is secure (assume HTTPS if scheme is missing) - if !strings.Contains(caURL, "://") { - caURL = "https://" + caURL - } - u, err := url.Parse(caURL) - if err != nil { - return nil, err - } - - if u.Scheme != "https" && !isLoopback(u.Host) && !isInternal(u.Host) { - return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) - } - - clientKey := caURL + leUser.Email + string(keyType) - - // if an underlying client with this configuration already exists, reuse it - // TODO: Could this be a global cache instead, perhaps? - cfg.acmeClientsMu.Lock() - client, ok := cfg.acmeClients[clientKey] - if !ok { - // the client facilitates our communication with the CA server - legoCfg := lego.NewConfig(&leUser) - legoCfg.CADirURL = caURL - legoCfg.UserAgent = buildUAString() - legoCfg.HTTPClient.Timeout = HTTPTimeout - legoCfg.Certificate = lego.CertificateConfig{ - KeyType: keyType, - Timeout: certObtainTimeout, - } - client, err = lego.NewClient(legoCfg) - if err != nil { - cfg.acmeClientsMu.Unlock() - return nil, err - } - cfg.acmeClients[clientKey] = client - } - cfg.acmeClientsMu.Unlock() - - // if not registered, the user must register an account - // with the CA and agree to terms - if leUser.Registration == nil { - if interactive { // can't prompt a user who isn't there - termsURL := client.GetToSURL() - if !cfg.Agreed && termsURL != "" { - cfg.Agreed = cfg.askUserAgreement(client.GetToSURL()) - } - if !cfg.Agreed && termsURL != "" { - return nil, fmt.Errorf("user must agree to CA terms") - } - } - - reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: cfg.Agreed}) - if err != nil { - return nil, fmt.Errorf("registration error: %v", err) - } - leUser.Registration = reg - - // persist the user to storage - err = cfg.saveUser(leUser) - if err != nil { - return nil, fmt.Errorf("could not save user: %v", err) - } - } - - c := &acmeClient{ - config: cfg, - acmeClient: client, - } - - if cfg.DNSProvider == nil { - // Use HTTP and TLS-ALPN challenges by default - - // figure out which ports we'll be serving the challenges on - useHTTPPort := HTTPChallengePort - useTLSALPNPort := TLSALPNChallengePort - if HTTPPort > 0 && HTTPPort != HTTPChallengePort { - useHTTPPort = HTTPPort - } - if HTTPSPort > 0 && HTTPSPort != TLSALPNChallengePort { - useTLSALPNPort = HTTPSPort - } - if cfg.AltHTTPPort > 0 { - useHTTPPort = cfg.AltHTTPPort - } - if cfg.AltTLSALPNPort > 0 { - useTLSALPNPort = cfg.AltTLSALPNPort - } - - // If this machine is already listening on the HTTP or TLS-ALPN port - // designated for the challenges, then we need to handle the challenges - // a little differently: for HTTP, we will answer the challenge request - // using our own HTTP handler (the HandleHTTPChallenge function - this - // works only because challenge info is written to storage associated - // with cfg when the challenge is initiated); for TLS-ALPN, we will add - // the challenge cert to our cert cache and serve it up during the - // handshake. As for the default solvers... we are careful to honor the - // listener bind preferences by using cfg.ListenHost. - var httpSolver, alpnSolver challenge.Provider - httpSolver = http01.NewProviderServer(cfg.ListenHost, fmt.Sprintf("%d", useHTTPPort)) - alpnSolver = tlsalpn01.NewProviderServer(cfg.ListenHost, fmt.Sprintf("%d", useTLSALPNPort)) - if listenerAddressInUse(net.JoinHostPort(cfg.ListenHost, fmt.Sprintf("%d", useHTTPPort))) { - httpSolver = nil - } - if listenerAddressInUse(net.JoinHostPort(cfg.ListenHost, fmt.Sprintf("%d", useTLSALPNPort))) { - alpnSolver = tlsALPNSolver{certCache: cfg.certCache} - } - - // because of our nifty Storage interface, we can distribute the HTTP and - // TLS-ALPN challenges across all instances that share the same storage - - // in fact, this is required now for successful solving of the HTTP challenge - // if the port is already in use, since we must write the challenge info - // to storage for the HTTPChallengeHandler to solve it successfully - c.acmeClient.Challenge.SetHTTP01Provider(distributedSolver{ - config: cfg, - providerServer: httpSolver, - }) - c.acmeClient.Challenge.SetTLSALPN01Provider(distributedSolver{ - config: cfg, - providerServer: alpnSolver, - }) - - // disable any challenges that should not be used - if cfg.DisableHTTPChallenge { - c.acmeClient.Challenge.Remove(challenge.HTTP01) - } - if cfg.DisableTLSALPNChallenge { - c.acmeClient.Challenge.Remove(challenge.TLSALPN01) - } - } else { - // Otherwise, use DNS challenge exclusively - c.acmeClient.Challenge.Remove(challenge.HTTP01) - c.acmeClient.Challenge.Remove(challenge.TLSALPN01) - c.acmeClient.Challenge.SetDNS01Provider(cfg.DNSProvider) - } - - return c, nil -} - -// lockKey returns a key for a lock that is specific to the operation -// named op being performed related to domainName and this config's CA. -func (cfg *Config) lockKey(op, domainName string) string { - return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.CA) -} - -// Obtain obtains a single certificate for name. It stores the certificate -// on the disk if successful. This function is safe for concurrent use. -// -// Our storage mechanism only supports one name per certificate, so this -// function (along with Renew and Revoke) only accepts one domain as input. -// It could be easily modified to support SAN certificates if our storage -// mechanism is upgraded later, but that will increase logical complexity -// in other areas. -// -// Callers who have access to a Config value should use the ObtainCert -// method on that instead of this lower-level method. -func (c *acmeClient) Obtain(name string) error { - // ensure idempotency of the obtain operation for this name - lockKey := c.config.lockKey("cert_acme", name) - err := c.config.Storage.Lock(lockKey) - if err != nil { - return err - } - defer func() { - if err := c.config.Storage.Unlock(lockKey); err != nil { - log.Printf("[ERROR][%s] Obtain: Unable to unlock '%s': %v", name, lockKey, err) - } - }() - - // check if obtain is still needed -- might have - // been obtained during lock - if c.config.storageHasCertResources(name) { - log.Printf("[INFO][%s] Obtain: Certificate already exists in storage", name) - return nil - } - - for attempts := 0; attempts < 2; attempts++ { - request := certificate.ObtainRequest{ - Domains: []string{name}, - Bundle: true, - MustStaple: c.config.MustStaple, - } - acmeMu.Lock() - certificate, err := c.acmeClient.Certificate.Obtain(request) - acmeMu.Unlock() - if err != nil { - return fmt.Errorf("[%s] failed to obtain certificate: %s", name, err) - } - - // double-check that we actually got a certificate, in case there's a bug upstream (see issue mholt/caddy#2121) - if certificate.Domain == "" || certificate.Certificate == nil { - return fmt.Errorf("returned certificate was empty; probably an unchecked error obtaining it") - } - - // Success - immediately save the certificate resource - err = c.config.saveCertResource(certificate) - if err != nil { - return fmt.Errorf("error saving assets for %v: %v", name, err) - } - - break - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_obtained", name) - } - - return nil -} - -// Renew renews the managed certificate for name. It puts the renewed -// certificate into storage (not the cache). This function is safe for -// concurrent use. -// -// Callers who have access to a Config value should use the RenewCert -// method on that instead of this lower-level method. -func (c *acmeClient) Renew(name string) error { - // ensure idempotency of the renew operation for this name - lockKey := c.config.lockKey("cert_acme", name) - err := c.config.Storage.Lock(lockKey) - if err != nil { - return err - } - defer func() { - if err := c.config.Storage.Unlock(lockKey); err != nil { - log.Printf("[ERROR][%s] Renew: Unable to unlock '%s': %v", name, lockKey, err) - } - }() - - // Prepare for renewal (load PEM cert, key, and meta) - certRes, err := c.config.loadCertResource(name) - if err != nil { - return err - } - - // Check if renew is still needed - might have been renewed while waiting for lock - if !c.config.managedCertNeedsRenewal(certRes) { - log.Printf("[INFO][%s] Renew: Certificate appears to have been renewed already", name) - return nil - } - - // Perform renewal and retry if necessary, but not too many times. - var newCertMeta *certificate.Resource - var success bool - for attempts := 0; attempts < 2; attempts++ { - acmeMu.Lock() - newCertMeta, err = c.acmeClient.Certificate.Renew(certRes, true, c.config.MustStaple) - acmeMu.Unlock() - if err == nil { - // double-check that we actually got a certificate; check a couple fields, just in case - if newCertMeta == nil || newCertMeta.Domain == "" || newCertMeta.Certificate == nil { - err = fmt.Errorf("returned certificate was empty; probably an unchecked error renewing it") - } else { - success = true - break - } - } - - // wait a little bit and try again - wait := 10 * time.Second - log.Printf("[ERROR] Renewing [%v]: %v; trying again in %s", name, err, wait) - time.Sleep(wait) - } - - if !success { - return fmt.Errorf("too many renewal attempts; last error: %v", err) - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_renewed", name) - } - - return c.config.saveCertResource(newCertMeta) -} - -// Revoke revokes the certificate for name and deletes -// it from storage. -func (c *acmeClient) Revoke(name string) error { - if !c.config.Storage.Exists(StorageKeys.SitePrivateKey(c.config.CA, name)) { - return fmt.Errorf("private key not found for %s", name) - } - - certRes, err := c.config.loadCertResource(name) - if err != nil { - return err - } - - err = c.acmeClient.Certificate.Revoke(certRes.Certificate) - if err != nil { - return err - } - - if c.config.OnEvent != nil { - c.config.OnEvent("acme_cert_revoked", name) - } - - err = c.config.Storage.Delete(StorageKeys.SiteCert(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err) - } - err = c.config.Storage.Delete(StorageKeys.SitePrivateKey(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err) - } - err = c.config.Storage.Delete(StorageKeys.SiteMeta(c.config.CA, name)) - if err != nil { - return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err) - } - - return nil -} - -func buildUAString() string { - ua := "CertMagic" - if UserAgent != "" { - ua += " " + UserAgent - } - return ua -} - -// Some default values passed down to the underlying lego client. -var ( - UserAgent string - HTTPTimeout = 30 * time.Second -) - -// Interface guard -var _ Manager = (*acmeClient)(nil) diff --git a/vendor/github.com/mholt/certmagic/config.go b/vendor/github.com/mholt/certmagic/config.go deleted file mode 100644 index 12ff698d7..000000000 --- a/vendor/github.com/mholt/certmagic/config.go +++ /dev/null @@ -1,469 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "crypto/tls" - "fmt" - "sync" - "time" - - "github.com/go-acme/lego/certcrypto" - "github.com/go-acme/lego/certificate" - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/tlsalpn01" - "github.com/go-acme/lego/lego" -) - -// Config configures a certificate manager instance. -// An empty Config is not valid: use New() to obtain -// a valid Config. -type Config struct { - // The endpoint of the directory for the ACME - // CA we are to use - CA string - - // The email address to use when creating or - // selecting an existing ACME server account - Email string - - // Set to true if agreed to the CA's - // subscriber agreement - Agreed bool - - // Disable all HTTP challenges - DisableHTTPChallenge bool - - // Disable all TLS-ALPN challenges - DisableTLSALPNChallenge bool - - // How long before expiration to renew certificates - RenewDurationBefore time.Duration - - // How long before expiration to require a renewed - // certificate when in interactive mode, like when - // the program is first starting up (see - // mholt/caddy#1680). A wider window between - // RenewDurationBefore and this value will suppress - // errors under duress (bad) but hopefully this duration - // will give it enough time for the blockage to be - // relieved. - RenewDurationBeforeAtStartup time.Duration - - // An optional event callback clients can set - // to subscribe to certain things happening - // internally by this config; invocations are - // synchronous, so make them return quickly! - OnEvent func(event string, data interface{}) - - // The host (ONLY the host, not port) to listen - // on if necessary to start a listener to solve - // an ACME challenge - ListenHost string - - // The alternate port to use for the ACME HTTP - // challenge; if non-empty, this port will be - // used instead of HTTPChallengePort to spin up - // a listener for the HTTP challenge - AltHTTPPort int - - // The alternate port to use for the ACME - // TLS-ALPN challenge; the system must forward - // TLSALPNChallengePort to this port for - // challenge to succeed - AltTLSALPNPort int - - // The DNS provider to use when solving the - // ACME DNS challenge - DNSProvider challenge.Provider - - // The type of key to use when generating - // certificates - KeyType certcrypto.KeyType - - // The maximum amount of time to allow for - // obtaining a certificate. If empty, the - // default from the underlying lego lib is - // used. If set, it must not be too low so - // as to cancel orders too early, running - // the risk of rate limiting. - CertObtainTimeout time.Duration - - // DefaultServerName specifies a server name - // to use when choosing a certificate if the - // ClientHello's ServerName field is empty - DefaultServerName string - - // The state needed to operate on-demand TLS - OnDemand *OnDemandConfig - - // Add the must staple TLS extension to the - // CSR generated by lego/acme - MustStaple bool - - // The storage to access when storing or - // loading TLS assets - Storage Storage - - // NewManager returns a new Manager. If nil, - // an ACME client will be created and used. - NewManager func(interactive bool) (Manager, error) - - // Pointer to the in-memory certificate cache - certCache *Cache - - // Map of client config key to ACME clients - // so they can be reused - // TODO: It might be better if these were globally cached, rather than per-config, which are ephemeral... but maybe evict them after a certain time, like 1 day or something - acmeClients map[string]*lego.Client - acmeClientsMu *sync.Mutex -} - -// NewDefault makes a valid config based on the package -// Default config. Most users will call this function -// instead of New() since most use cases require only a -// single config for any and all certificates. -// -// If your requirements are more advanced (for example, -// multiple configs depending on the certificate), then use -// New() instead. (You will need to make your own Cache -// first.) If you only need a single Config to manage your -// certs (even if that config changes, as long as it is the -// only one), customize the Default package variable before -// calling NewDefault(). -// -// All calls to NewDefault() will return configs that use the -// same, default certificate cache. All configs returned -// by NewDefault() are based on the values of the fields of -// Default at the time it is called. -func NewDefault() *Config { - defaultCacheMu.Lock() - if defaultCache == nil { - defaultCache = NewCache(CacheOptions{ - // the cache will likely need to renew certificates, - // so it will need to know how to do that, which - // depends on the certificate being managed and which - // can change during the lifetime of the cache; this - // callback makes it possible to get the latest and - // correct config with which to manage the cert, - // but if the user does not provide one, we can only - // assume that we are to use the default config - GetConfigForCert: func(Certificate) (Config, error) { - return Default, nil - }, - }) - } - certCache := defaultCache - defaultCacheMu.Unlock() - - return newWithCache(certCache, Default) -} - -// New makes a new, valid config based on cfg and -// uses the provided certificate cache. certCache -// MUST NOT be nil or this function will panic. -// -// Use this method when you have an advanced use case -// that requires a custom certificate cache and config -// that may differ from the Default. For example, if -// not all certificates are managed/renewed the same -// way, you need to make your own Cache value with a -// GetConfigForCert callback that returns the correct -// configuration for each certificate. However, for -// the vast majority of cases, there will be only a -// single Config, thus the default cache (which always -// uses the default Config) and default config will -// suffice, and you should use New() instead. -func New(certCache *Cache, cfg Config) *Config { - if certCache == nil { - panic("a certificate cache is required") - } - if certCache.options.GetConfigForCert == nil { - panic("cache must have GetConfigForCert set in its options") - } - return newWithCache(certCache, cfg) -} - -// newWithCache ensures that cfg is a valid config by populating -// zero-value fields from the Default Config. If certCache is -// nil, this function panics. -func newWithCache(certCache *Cache, cfg Config) *Config { - if certCache == nil { - panic("cannot make a valid config without a pointer to a certificate cache") - } - - // fill in default values - if cfg.CA == "" { - cfg.CA = Default.CA - } - if cfg.Email == "" { - cfg.Email = Default.Email - } - if cfg.OnDemand == nil { - cfg.OnDemand = Default.OnDemand - } - if !cfg.Agreed { - cfg.Agreed = Default.Agreed - } - if !cfg.DisableHTTPChallenge { - cfg.DisableHTTPChallenge = Default.DisableHTTPChallenge - } - if !cfg.DisableTLSALPNChallenge { - cfg.DisableTLSALPNChallenge = Default.DisableTLSALPNChallenge - } - if cfg.RenewDurationBefore == 0 { - cfg.RenewDurationBefore = Default.RenewDurationBefore - } - if cfg.RenewDurationBeforeAtStartup == 0 { - cfg.RenewDurationBeforeAtStartup = Default.RenewDurationBeforeAtStartup - } - if cfg.OnEvent == nil { - cfg.OnEvent = Default.OnEvent - } - if cfg.ListenHost == "" { - cfg.ListenHost = Default.ListenHost - } - if cfg.AltHTTPPort == 0 { - cfg.AltHTTPPort = Default.AltHTTPPort - } - if cfg.AltTLSALPNPort == 0 { - cfg.AltTLSALPNPort = Default.AltTLSALPNPort - } - if cfg.DNSProvider == nil { - cfg.DNSProvider = Default.DNSProvider - } - if cfg.KeyType == "" { - cfg.KeyType = Default.KeyType - } - if cfg.CertObtainTimeout == 0 { - cfg.CertObtainTimeout = Default.CertObtainTimeout - } - if cfg.DefaultServerName == "" { - cfg.DefaultServerName = Default.DefaultServerName - } - if cfg.OnDemand == nil { - cfg.OnDemand = Default.OnDemand - } - if !cfg.MustStaple { - cfg.MustStaple = Default.MustStaple - } - if cfg.Storage == nil { - cfg.Storage = Default.Storage - } - if cfg.NewManager == nil { - cfg.NewManager = Default.NewManager - } - - // absolutely don't allow a nil storage, - // because that would make almost anything - // a config can do pointless - if cfg.Storage == nil { - cfg.Storage = defaultFileStorage - } - - // ensure the unexported fields are valid - cfg.certCache = certCache - cfg.acmeClients = make(map[string]*lego.Client) - cfg.acmeClientsMu = new(sync.Mutex) - - return &cfg -} - -// Manage causes the certificates for domainNames to be managed -// according to cfg. If cfg is enabled for OnDemand, then this -// simply whitelists the domain names. Otherwise, the certificate(s) -// for each name are loaded from storage or obtained from the CA; -// and if loaded from storage, renewed if they are expiring or -// expired. It then caches the certificate in memory and is -// prepared to serve them up during TLS handshakes. -func (cfg *Config) Manage(domainNames []string) error { - for _, domainName := range domainNames { - if !HostQualifies(domainName) { - return fmt.Errorf("name does not qualify for automatic certificate management: %s", domainName) - } - - // if on-demand is configured, simply whitelist this name - if cfg.OnDemand != nil { - if !cfg.OnDemand.whitelistContains(domainName) { - cfg.OnDemand.HostWhitelist = append(cfg.OnDemand.HostWhitelist, domainName) - } - continue - } - - // try loading an existing certificate; if it doesn't - // exist yet, obtain one and try loading it again - cert, err := cfg.CacheManagedCertificate(domainName) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // if it doesn't exist, get it, then try loading it again - err := cfg.ObtainCert(domainName, false) - if err != nil { - return fmt.Errorf("%s: obtaining certificate: %v", domainName, err) - } - cert, err = cfg.CacheManagedCertificate(domainName) - if err != nil { - return fmt.Errorf("%s: caching certificate after obtaining it: %v", domainName, err) - } - continue - } - return fmt.Errorf("%s: caching certificate: %v", domainName, err) - } - - // for existing certificates, make sure it is renewed - if cert.NeedsRenewal(cfg) { - err := cfg.RenewCert(domainName, false) - if err != nil { - return fmt.Errorf("%s: renewing certificate: %v", domainName, err) - } - } - } - - return nil -} - -// ObtainCert obtains a certificate for name using cfg, as long -// as a certificate does not already exist in storage for that -// name. The name must qualify and cfg must be flagged as Managed. -// This function is a no-op if storage already has a certificate -// for name. -// -// It only obtains and stores certificates (and their keys), -// it does not load them into memory. If interactive is true, -// the user may be shown a prompt. -func (cfg *Config) ObtainCert(name string, interactive bool) error { - if cfg.storageHasCertResources(name) { - return nil - } - skip, err := cfg.preObtainOrRenewChecks(name, interactive) - if err != nil { - return err - } - if skip { - return nil - } - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Obtain(name) -} - -// RenewCert renews the certificate for name using cfg. It stows the -// renewed certificate and its assets in storage if successful. -func (cfg *Config) RenewCert(name string, interactive bool) error { - skip, err := cfg.preObtainOrRenewChecks(name, interactive) - if err != nil { - return err - } - if skip { - return nil - } - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Renew(name) -} - -// RevokeCert revokes the certificate for domain via ACME protocol. -func (cfg *Config) RevokeCert(domain string, interactive bool) error { - manager, err := cfg.newManager(interactive) - if err != nil { - return err - } - return manager.Revoke(domain) -} - -// TLSConfig is an opinionated method that returns a -// recommended, modern TLS configuration that can be -// used to configure TLS listeners, which also supports -// the TLS-ALPN challenge and serves up certificates -// managed by cfg. -// -// Unlike the package TLS() function, this method does -// not, by itself, enable certificate management for -// any domain names. -// -// Feel free to further customize the returned tls.Config, -// but do not mess with the GetCertificate or NextProtos -// fields unless you know what you're doing, as they're -// necessary to solve the TLS-ALPN challenge. -func (cfg *Config) TLSConfig() *tls.Config { - return &tls.Config{ - // these two fields necessary for TLS-ALPN challenge - GetCertificate: cfg.GetCertificate, - NextProtos: []string{"h2", "http/1.1", tlsalpn01.ACMETLS1Protocol}, - - // the rest recommended for modern TLS servers - MinVersion: tls.VersionTLS12, - CurvePreferences: []tls.CurveID{ - tls.X25519, - tls.CurveP256, - }, - CipherSuites: preferredDefaultCipherSuites(), - PreferServerCipherSuites: true, - } -} - -// preObtainOrRenewChecks perform a few simple checks before -// obtaining or renewing a certificate with ACME, and returns -// whether this name should be skipped (like if it's not -// managed TLS) as well as any error. It ensures that the -// config is Managed, that the name qualifies for a certificate, -// and that an email address is available. -func (cfg *Config) preObtainOrRenewChecks(name string, allowPrompts bool) (bool, error) { - if !HostQualifies(name) { - return true, nil - } - - err := cfg.getEmail(allowPrompts) - if err != nil { - return false, err - } - - return false, nil -} - -// storageHasCertResources returns true if the storage -// associated with cfg's certificate cache has all the -// resources related to the certificate for domain: the -// certificate, the private key, and the metadata. -func (cfg *Config) storageHasCertResources(domain string) bool { - certKey := StorageKeys.SiteCert(cfg.CA, domain) - keyKey := StorageKeys.SitePrivateKey(cfg.CA, domain) - metaKey := StorageKeys.SiteMeta(cfg.CA, domain) - return cfg.Storage.Exists(certKey) && - cfg.Storage.Exists(keyKey) && - cfg.Storage.Exists(metaKey) -} - -// managedCertNeedsRenewal returns true if certRes is -// expiring soon or already expired, or if the process -// of checking the expiration returned an error. -func (cfg *Config) managedCertNeedsRenewal(certRes certificate.Resource) bool { - cert, err := makeCertificate(certRes.Certificate, certRes.PrivateKey) - if err != nil { - return true - } - return cert.NeedsRenewal(cfg) -} - -// Manager is a type that can manage a certificate. -// They are usually very short-lived. -type Manager interface { - Obtain(name string) error - Renew(name string) error - Revoke(name string) error -} diff --git a/vendor/github.com/mholt/certmagic/crypto.go b/vendor/github.com/mholt/certmagic/crypto.go deleted file mode 100644 index c98878fd1..000000000 --- a/vendor/github.com/mholt/certmagic/crypto.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/sha256" - "crypto/tls" - "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - "hash/fnv" - - "github.com/go-acme/lego/certificate" - "github.com/klauspost/cpuid" -) - -// encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes. -func encodePrivateKey(key crypto.PrivateKey) ([]byte, error) { - var pemType string - var keyBytes []byte - switch key := key.(type) { - case *ecdsa.PrivateKey: - var err error - pemType = "EC" - keyBytes, err = x509.MarshalECPrivateKey(key) - if err != nil { - return nil, err - } - case *rsa.PrivateKey: - pemType = "RSA" - keyBytes = x509.MarshalPKCS1PrivateKey(key) - } - pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes} - return pem.EncodeToMemory(&pemKey), nil -} - -// decodePrivateKey loads a PEM-encoded ECC/RSA private key from an array of bytes. -func decodePrivateKey(keyPEMBytes []byte) (crypto.PrivateKey, error) { - keyBlock, _ := pem.Decode(keyPEMBytes) - switch keyBlock.Type { - case "RSA PRIVATE KEY": - return x509.ParsePKCS1PrivateKey(keyBlock.Bytes) - case "EC PRIVATE KEY": - return x509.ParseECPrivateKey(keyBlock.Bytes) - } - return nil, fmt.Errorf("unknown private key type") -} - -// parseCertsFromPEMBundle parses a certificate bundle from top to bottom and returns -// a slice of x509 certificates. This function will error if no certificates are found. -func parseCertsFromPEMBundle(bundle []byte) ([]*x509.Certificate, error) { - var certificates []*x509.Certificate - var certDERBlock *pem.Block - for { - certDERBlock, bundle = pem.Decode(bundle) - if certDERBlock == nil { - break - } - if certDERBlock.Type == "CERTIFICATE" { - cert, err := x509.ParseCertificate(certDERBlock.Bytes) - if err != nil { - return nil, err - } - certificates = append(certificates, cert) - } - } - if len(certificates) == 0 { - return nil, fmt.Errorf("no certificates found in bundle") - } - return certificates, nil -} - -// fastHash hashes input using a hashing algorithm that -// is fast, and returns the hash as a hex-encoded string. -// Do not use this for cryptographic purposes. -func fastHash(input []byte) string { - h := fnv.New32a() - h.Write(input) - return fmt.Sprintf("%x", h.Sum32()) -} - -// saveCertResource saves the certificate resource to disk. This -// includes the certificate file itself, the private key, and the -// metadata file. -func (cfg *Config) saveCertResource(cert *certificate.Resource) error { - metaBytes, err := json.MarshalIndent(&cert, "", "\t") - if err != nil { - return fmt.Errorf("encoding certificate metadata: %v", err) - } - - all := []keyValue{ - { - key: StorageKeys.SiteCert(cfg.CA, cert.Domain), - value: cert.Certificate, - }, - { - key: StorageKeys.SitePrivateKey(cfg.CA, cert.Domain), - value: cert.PrivateKey, - }, - { - key: StorageKeys.SiteMeta(cfg.CA, cert.Domain), - value: metaBytes, - }, - } - - return storeTx(cfg.Storage, all) -} - -func (cfg *Config) loadCertResource(domain string) (certificate.Resource, error) { - var certRes certificate.Resource - certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(cfg.CA, domain)) - if err != nil { - return certRes, err - } - keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(cfg.CA, domain)) - if err != nil { - return certRes, err - } - metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(cfg.CA, domain)) - if err != nil { - return certRes, err - } - err = json.Unmarshal(metaBytes, &certRes) - if err != nil { - return certRes, fmt.Errorf("decoding certificate metadata: %v", err) - } - certRes.Certificate = certBytes - certRes.PrivateKey = keyBytes - return certRes, nil -} - -// hashCertificateChain computes the unique hash of certChain, -// which is the chain of DER-encoded bytes. It returns the -// hex encoding of the hash. -func hashCertificateChain(certChain [][]byte) string { - h := sha256.New() - for _, certInChain := range certChain { - h.Write(certInChain) - } - return fmt.Sprintf("%x", h.Sum(nil)) -} - -// preferredDefaultCipherSuites returns an appropriate -// cipher suite to use depending on hardware support -// for AES-NI. -// -// See https://github.com/mholt/caddy/issues/1674 -func preferredDefaultCipherSuites() []uint16 { - if cpuid.CPU.AesNi() { - return defaultCiphersPreferAES - } - return defaultCiphersPreferChaCha -} - -var ( - defaultCiphersPreferAES = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - } - defaultCiphersPreferChaCha = []uint16{ - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - } -) diff --git a/vendor/github.com/mholt/certmagic/filestorage.go b/vendor/github.com/mholt/certmagic/filestorage.go deleted file mode 100644 index 0de8f746e..000000000 --- a/vendor/github.com/mholt/certmagic/filestorage.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path" - "path/filepath" - "runtime" - "time" -) - -// FileStorage facilitates forming file paths derived from a root -// directory. It is used to get file paths in a consistent, -// cross-platform way or persisting ACME assets on the file system. -type FileStorage struct { - Path string -} - -// Exists returns true if key exists in fs. -func (fs *FileStorage) Exists(key string) bool { - _, err := os.Stat(fs.Filename(key)) - return !os.IsNotExist(err) -} - -// Store saves value at key. -func (fs *FileStorage) Store(key string, value []byte) error { - filename := fs.Filename(key) - err := os.MkdirAll(filepath.Dir(filename), 0700) - if err != nil { - return err - } - return ioutil.WriteFile(filename, value, 0600) -} - -// Load retrieves the value at key. -func (fs *FileStorage) Load(key string) ([]byte, error) { - contents, err := ioutil.ReadFile(fs.Filename(key)) - if os.IsNotExist(err) { - return nil, ErrNotExist(err) - } - return contents, nil -} - -// Delete deletes the value at key. -func (fs *FileStorage) Delete(key string) error { - err := os.Remove(fs.Filename(key)) - if os.IsNotExist(err) { - return ErrNotExist(err) - } - return err -} - -// List returns all keys that match prefix. -func (fs *FileStorage) List(prefix string, recursive bool) ([]string, error) { - var keys []string - walkPrefix := fs.Filename(prefix) - - err := filepath.Walk(walkPrefix, func(fpath string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info == nil { - return fmt.Errorf("%s: file info is nil", fpath) - } - if fpath == walkPrefix { - return nil - } - - suffix, err := filepath.Rel(walkPrefix, fpath) - if err != nil { - return fmt.Errorf("%s: could not make path relative: %v", fpath, err) - } - keys = append(keys, path.Join(prefix, suffix)) - - if !recursive && info.IsDir() { - return filepath.SkipDir - } - return nil - }) - - return keys, err -} - -// Stat returns information about key. -func (fs *FileStorage) Stat(key string) (KeyInfo, error) { - fi, err := os.Stat(fs.Filename(key)) - if os.IsNotExist(err) { - return KeyInfo{}, ErrNotExist(err) - } - if err != nil { - return KeyInfo{}, err - } - return KeyInfo{ - Key: key, - Modified: fi.ModTime(), - Size: fi.Size(), - IsTerminal: !fi.IsDir(), - }, nil -} - -// Filename returns the key as a path on the file -// system prefixed by fs.Path. -func (fs *FileStorage) Filename(key string) string { - return filepath.Join(fs.Path, filepath.FromSlash(key)) -} - -// Lock obtains a lock named by the given key. It blocks -// until the lock can be obtained or an error is returned. -func (fs *FileStorage) Lock(key string) error { - start := time.Now() - filename := fs.lockFilename(key) - - for { - err := createLockfile(filename) - if err == nil { - // got the lock, yay - return nil - } - if !os.IsExist(err) { - // unexpected error - return fmt.Errorf("creating lock file: %v", err) - } - - // lock file already exists - - info, err := os.Stat(filename) - switch { - case os.IsNotExist(err): - // must have just been removed; try again to create it - continue - - case err != nil: - // unexpected error - return fmt.Errorf("accessing lock file: %v", err) - - case fileLockIsStale(info): - // lock file is stale - delete it and try again to create one - log.Printf("[INFO][%s] Lock for '%s' is stale; removing then retrying: %s", - fs, key, filename) - removeLockfile(filename) - continue - - case time.Since(start) > staleLockDuration*2: - // should never happen, hopefully - return fmt.Errorf("possible deadlock: %s passed trying to obtain lock for %s", - time.Since(start), key) - - default: - // lockfile exists and is not stale; - // just wait a moment and try again - time.Sleep(fileLockPollInterval) - } - } -} - -// Unlock releases the lock for name. -func (fs *FileStorage) Unlock(key string) error { - return removeLockfile(fs.lockFilename(key)) -} - -func (fs *FileStorage) String() string { - return "FileStorage:" + fs.Path -} - -func (fs *FileStorage) lockFilename(key string) string { - return filepath.Join(fs.lockDir(), StorageKeys.Safe(key)+".lock") -} - -func (fs *FileStorage) lockDir() string { - return filepath.Join(fs.Path, "locks") -} - -func fileLockIsStale(info os.FileInfo) bool { - if info == nil { - return true - } - return time.Since(info.ModTime()) > staleLockDuration -} - -// createLockfile atomically creates the lockfile -// identified by filename. A successfully created -// lockfile should be removed with removeLockfile. -func createLockfile(filename string) error { - err := atomicallyCreateFile(filename) - if err == nil { - // if the app crashes in removeLockfile(), there is a - // small chance the .unlock file is left behind; it's - // safe to simply remove it as it's a guard against - // double removal of the .lock file. - os.Remove(filename + ".unlock") - } - return err -} - -// removeLockfile atomically removes filename, -// which must be a lockfile created by createLockfile. -// See discussion in PR #7 for more background: -// https://github.com/mholt/certmagic/pull/7 -func removeLockfile(filename string) error { - unlockFilename := filename + ".unlock" - if err := atomicallyCreateFile(unlockFilename); err != nil { - if os.IsExist(err) { - // another process is handling the unlocking - return nil - } - return err - } - defer os.Remove(unlockFilename) - return os.Remove(filename) -} - -// atomicallyCreateFile atomically creates the file -// identified by filename if it doesn't already exist. -func atomicallyCreateFile(filename string) error { - // no need to check this, we only really care about the file creation error - os.MkdirAll(filepath.Dir(filename), 0700) - f, err := os.OpenFile(filename, os.O_CREATE|os.O_EXCL, 0644) - if err == nil { - f.Close() - } - return err -} - -// homeDir returns the best guess of the current user's home -// directory from environment variables. If unknown, "." (the -// current directory) is returned instead. -func homeDir() string { - home := os.Getenv("HOME") - if home == "" && runtime.GOOS == "windows" { - drive := os.Getenv("HOMEDRIVE") - path := os.Getenv("HOMEPATH") - home = drive + path - if drive == "" || path == "" { - home = os.Getenv("USERPROFILE") - } - } - if home == "" { - home = "." - } - return home -} - -func dataDir() string { - baseDir := filepath.Join(homeDir(), ".local", "share") - if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" { - baseDir = xdgData - } - return filepath.Join(baseDir, "certmagic") -} - -// staleLockDuration is the length of time -// before considering a lock to be stale. -const staleLockDuration = 2 * time.Hour - -// fileLockPollInterval is how frequently -// to check the existence of a lock file -const fileLockPollInterval = 1 * time.Second - -var _ Storage = (*FileStorage)(nil) diff --git a/vendor/github.com/mholt/certmagic/go.mod b/vendor/github.com/mholt/certmagic/go.mod deleted file mode 100644 index 75559b41a..000000000 --- a/vendor/github.com/mholt/certmagic/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/mholt/certmagic - -require ( - github.com/cenkalti/backoff v2.1.1+incompatible // indirect - github.com/go-acme/lego v2.5.0+incompatible - github.com/klauspost/cpuid v1.2.0 - github.com/miekg/dns v1.1.3 // indirect - github.com/stretchr/testify v1.3.0 // indirect - golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b - golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect - golang.org/x/sys v0.0.0-20190124100055-b90733256f2e // indirect - golang.org/x/text v0.3.0 // indirect - gopkg.in/square/go-jose.v2 v2.2.2 // indirect -) diff --git a/vendor/github.com/mholt/certmagic/go.sum b/vendor/github.com/mholt/certmagic/go.sum deleted file mode 100644 index 7093ad5b4..000000000 --- a/vendor/github.com/mholt/certmagic/go.sum +++ /dev/null @@ -1,29 +0,0 @@ -github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-acme/lego v2.3.1-0.20190318164254-3684cc738d37+incompatible h1:D8mQOFMowsqoVMibY3U+xeNmd83bdNPEjTScRiPgVoc= -github.com/go-acme/lego v2.3.1-0.20190318164254-3684cc738d37+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s= -github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/miekg/dns v1.1.3 h1:1g0r1IvskvgL8rR+AcHzUA+oFmGcQlaIm4IqakufeMM= -github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b h1:Elez2XeF2p9uyVj0yEUDqQ56NFcDtcBNkYP7yv8YbUE= -golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e h1:3GIlrlVLfkoipSReOMNAgApI0ajnalyLa/EZHHca/XI= -golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/vendor/github.com/mholt/certmagic/handshake.go b/vendor/github.com/mholt/certmagic/handshake.go deleted file mode 100644 index 51dd39fd1..000000000 --- a/vendor/github.com/mholt/certmagic/handshake.go +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "crypto/tls" - "encoding/json" - "fmt" - "log" - "net" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/go-acme/lego/challenge/tlsalpn01" -) - -// GetCertificate gets a certificate to satisfy clientHello. In getting -// the certificate, it abides the rules and settings defined in the -// Config that matches clientHello.ServerName. It first checks the in- -// memory cache, then, if the config enables "OnDemand", it accesses -// disk, then accesses the network if it must obtain a new certificate -// via ACME. -// -// This method is safe for use as a tls.Config.GetCertificate callback. -func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - if cfg.OnEvent != nil { - cfg.OnEvent("tls_handshake_started", clientHello) - } - - // special case: serve up the certificate for a TLS-ALPN ACME challenge - // (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05) - for _, proto := range clientHello.SupportedProtos { - if proto == tlsalpn01.ACMETLS1Protocol { - cfg.certCache.mu.RLock() - challengeCert, ok := cfg.certCache.cache[tlsALPNCertKeyName(clientHello.ServerName)] - cfg.certCache.mu.RUnlock() - if !ok { - // see if this challenge was started in a cluster; try distributed challenge solver - // (note that the tls.Config's ALPN settings must include the ACME TLS-ALPN challenge - // protocol string, otherwise a valid certificate will not solve the challenge; we - // should already have taken care of that when we made the tls.Config) - challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello) - if err != nil { - log.Printf("[ERROR][%s] TLS-ALPN: %v", clientHello.ServerName, err) - } - if ok { - return &challengeCert.Certificate, nil - } - - return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName) - } - return &challengeCert.Certificate, nil - } - } - - // get the certificate and serve it up - cert, err := cfg.getCertDuringHandshake(clientHello, true, true) - if err == nil && cfg.OnEvent != nil { - cfg.OnEvent("tls_handshake_completed", clientHello) - } - return &cert.Certificate, err -} - -// getCertificate gets a certificate that matches name from the in-memory -// cache, according to the lookup table associated with cfg. The lookup then -// points to a certificate in the Instance certificate cache. -// -// The name is expected to already be normalized (e.g. lowercased). -// -// If there is no exact match for name, it will be checked against names of -// the form '*.example.com' (wildcard certificates) according to RFC 6125. -// If a match is found, matched will be true. If no matches are found, matched -// will be false and a "default" certificate will be returned with defaulted -// set to true. If defaulted is false, then no certificates were available. -// -// The logic in this function is adapted from the Go standard library, -// which is by the Go Authors. -// -// This function is safe for concurrent use. -func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) { - name := NormalizedName(hello.ServerName) - - var ok bool - - if name == "" { - // if SNI is empty, prefer matching IP address - if hello.Conn != nil { - addr := hello.Conn.LocalAddr().String() - ip, _, err := net.SplitHostPort(addr) - if err == nil { - addr = ip - } - if cert, ok = cfg.certCache.getFirstMatchingCert(addr); ok { - matched = true - return - } - } - - // fall back to a "default" certificate, if specified - if cfg.DefaultServerName != "" { - normDefault := NormalizedName(cfg.DefaultServerName) - if cert, ok = cfg.certCache.getFirstMatchingCert(normDefault); ok { - defaulted = true - return - } - } - } else { - // if SNI is specified, try an exact match first - if cert, ok = cfg.certCache.getFirstMatchingCert(name); ok { - matched = true - return - } - - // try replacing labels in the name with - // wildcards until we get a match - labels := strings.Split(name, ".") - for i := range labels { - labels[i] = "*" - candidate := strings.Join(labels, ".") - if cert, ok = cfg.certCache.getFirstMatchingCert(candidate); ok { - matched = true - return - } - } - - // check the certCache directly to see if the SNI name is - // already the key of the certificate it wants; this implies - // that the SNI can contain the hash of a specific cert - // (chain) it wants and we will still be able to serve it up - // (this behavior, by the way, could be controversial as to - // whether it complies with RFC 6066 about SNI, but I think - // it does, soooo...) - // (this is how we solved the former ACME TLS-SNI challenge) - cfg.certCache.mu.RLock() - directCert, ok := cfg.certCache.cache[name] - cfg.certCache.mu.RUnlock() - if ok { - cert = directCert - matched = true - return - } - } - - // otherwise, we're bingo on ammo; see issues - // mholt/caddy#2035 and mholt/caddy#1303 (any - // change to certificate matching behavior must - // account for hosts defined where the hostname - // is empty or a catch-all, like ":443" or - // "0.0.0.0:443") - - return -} - -// getCertDuringHandshake will get a certificate for hello. It first tries -// the in-memory cache. If no certificate for hello is in the cache, the -// config most closely corresponding to hello will be loaded. If that config -// allows it (OnDemand==true) and if loadIfNecessary == true, it goes to disk -// to load it into the cache and serve it. If it's not on disk and if -// obtainIfNecessary == true, the certificate will be obtained from the CA, -// cached, and served. If obtainIfNecessary is true, then loadIfNecessary -// must also be set to true. An error will be returned if and only if no -// certificate is available. -// -// This function is safe for concurrent use. -func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNecessary, obtainIfNecessary bool) (Certificate, error) { - name := NormalizedName(hello.ServerName) - - // First check our in-memory cache to see if we've already loaded it - cert, matched, defaulted := cfg.getCertificate(hello) - if matched { - return cert, nil - } - - // If OnDemand is enabled, then we might be able to load or - // obtain a needed certificate - if cfg.OnDemand != nil && loadIfNecessary { - // Then check to see if we have one on disk - loadedCert, err := cfg.CacheManagedCertificate(name) - if err == nil { - loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert) - if err != nil { - log.Printf("[ERROR] Maintaining newly-loaded certificate for %s: %v", name, err) - } - return loadedCert, nil - } - if obtainIfNecessary { - // By this point, we need to ask the CA for a certificate - - // Make sure the certificate should be obtained based on config - err := cfg.checkIfCertShouldBeObtained(name) - if err != nil { - return Certificate{}, err - } - - // Name has to qualify for a certificate - if !HostQualifies(name) { - return cert, fmt.Errorf("hostname '%s' does not qualify for certificate", name) - } - - // Obtain certificate from the CA - return cfg.obtainOnDemandCertificate(hello) - } - } - - // Fall back to the default certificate if there is one - if defaulted { - return cert, nil - } - - return Certificate{}, fmt.Errorf("no certificate available for '%s'", name) -} - -// checkIfCertShouldBeObtained checks to see if an on-demand tls certificate -// should be obtained for a given domain based upon the config settings. If -// a non-nil error is returned, do not issue a new certificate for name. -func (cfg *Config) checkIfCertShouldBeObtained(name string) error { - if cfg.OnDemand == nil { - return fmt.Errorf("not configured for on-demand certificate issuance") - } - return cfg.OnDemand.Allowed(name) -} - -// obtainOnDemandCertificate obtains a certificate for hello. -// If another goroutine has already started obtaining a cert for -// hello, it will wait and use what the other goroutine obtained. -// -// This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certificate, error) { - name := NormalizedName(hello.ServerName) - - // We must protect this process from happening concurrently, so synchronize. - obtainCertWaitChansMu.Lock() - wait, ok := obtainCertWaitChans[name] - if ok { - // lucky us -- another goroutine is already obtaining the certificate. - // wait for it to finish obtaining the cert and then we'll use it. - obtainCertWaitChansMu.Unlock() - <-wait - return cfg.getCertDuringHandshake(hello, true, false) - } - - // looks like it's up to us to do all the work and obtain the cert. - // make a chan others can wait on if needed - wait = make(chan struct{}) - obtainCertWaitChans[name] = wait - obtainCertWaitChansMu.Unlock() - - // obtain the certificate - log.Printf("[INFO] Obtaining new certificate for %s", name) - err := cfg.ObtainCert(name, false) - - // immediately unblock anyone waiting for it; doing this in - // a defer would risk deadlock because of the recursive call - // to getCertDuringHandshake below when we return! - obtainCertWaitChansMu.Lock() - close(wait) - delete(obtainCertWaitChans, name) - obtainCertWaitChansMu.Unlock() - - if err != nil { - // Failed to solve challenge, so don't allow another on-demand - // issue for this name to be attempted for a little while. - failedIssuanceMu.Lock() - failedIssuance[name] = time.Now() - go func(name string) { - time.Sleep(5 * time.Minute) - failedIssuanceMu.Lock() - delete(failedIssuance, name) - failedIssuanceMu.Unlock() - }(name) - failedIssuanceMu.Unlock() - return Certificate{}, err - } - - // Success - update counters and stuff - atomic.AddInt32(&cfg.OnDemand.obtainedCount, 1) - lastIssueTimeMu.Lock() - lastIssueTime = time.Now() - lastIssueTimeMu.Unlock() - - // certificate is already on disk; now just start over to load it and serve it - return cfg.getCertDuringHandshake(hello, true, false) -} - -// handshakeMaintenance performs a check on cert for expiration and OCSP validity. -// -// This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certificate) (Certificate, error) { - // Check cert expiration - timeLeft := cert.NotAfter.Sub(time.Now().UTC()) - if timeLeft < cfg.RenewDurationBefore { - log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", cert.Names, timeLeft) - return cfg.renewDynamicCertificate(hello, cert) - } - - // Check OCSP staple validity - if cert.OCSP != nil { - refreshTime := cert.OCSP.ThisUpdate.Add(cert.OCSP.NextUpdate.Sub(cert.OCSP.ThisUpdate) / 2) - if time.Now().After(refreshTime) { - err := stapleOCSP(cfg.Storage, &cert, nil) - if err != nil { - // An error with OCSP stapling is not the end of the world, and in fact, is - // quite common considering not all certs have issuer URLs that support it. - log.Printf("[ERROR] Getting OCSP for %s: %v", hello.ServerName, err) - } - cfg.certCache.mu.Lock() - cfg.certCache.cache[cert.Hash] = cert - cfg.certCache.mu.Unlock() - } - } - - return cert, nil -} - -// renewDynamicCertificate renews the certificate for name using cfg. It returns the -// certificate to use and an error, if any. name should already be lower-cased before -// calling this function. name is the name obtained directly from the handshake's -// ClientHello. -// -// This function is safe for use by multiple concurrent goroutines. -func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) { - name := NormalizedName(hello.ServerName) - - obtainCertWaitChansMu.Lock() - wait, ok := obtainCertWaitChans[name] - if ok { - // lucky us -- another goroutine is already renewing the certificate. - // wait for it to finish, then we'll use the new one. - obtainCertWaitChansMu.Unlock() - <-wait - return cfg.getCertDuringHandshake(hello, true, false) - } - - // looks like it's up to us to do all the work and renew the cert - wait = make(chan struct{}) - obtainCertWaitChans[name] = wait - obtainCertWaitChansMu.Unlock() - - // renew and reload the certificate - log.Printf("[INFO] Renewing certificate for %s", name) - err := cfg.RenewCert(name, false) - if err == nil { - // even though the recursive nature of the dynamic cert loading - // would just call this function anyway, we do it here to - // make the replacement as atomic as possible. - newCert, err := cfg.CacheManagedCertificate(name) - if err != nil { - log.Printf("[ERROR] loading renewed certificate for %s: %v", name, err) - } else { - // replace the old certificate with the new one - cfg.certCache.replaceCertificate(currentCert, newCert) - } - } - - // immediately unblock anyone waiting for it; doing this in - // a defer would risk deadlock because of the recursive call - // to getCertDuringHandshake below when we return! - obtainCertWaitChansMu.Lock() - close(wait) - delete(obtainCertWaitChans, name) - obtainCertWaitChansMu.Unlock() - - if err != nil { - return Certificate{}, err - } - - return cfg.getCertDuringHandshake(hello, true, false) -} - -// tryDistributedChallengeSolver is to be called when the clientHello pertains to -// a TLS-ALPN challenge and a certificate is required to solve it. This method -// checks the distributed store of challenge info files and, if a matching ServerName -// is present, it makes a certificate to solve this challenge and returns it. -// A boolean true is returned if a valid certificate is returned. -func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) { - tokenKey := distributedSolver{config: cfg}.challengeTokensKey(clientHello.ServerName) - chalInfoBytes, err := cfg.Storage.Load(tokenKey) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - return Certificate{}, false, nil - } - return Certificate{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err) - } - - var chalInfo challengeInfo - err = json.Unmarshal(chalInfoBytes, &chalInfo) - if err != nil { - return Certificate{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err) - } - - cert, err := tlsalpn01.ChallengeCert(chalInfo.Domain, chalInfo.KeyAuth) - if err != nil { - return Certificate{}, false, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err) - } - if cert == nil { - return Certificate{}, false, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error") - } - - return Certificate{Certificate: *cert}, true, nil -} - -// NormalizedName returns a cleaned form of serverName that is -// used for consistency when referring to a SNI value. -func NormalizedName(serverName string) string { - return strings.ToLower(strings.TrimSpace(serverName)) -} - -// obtainCertWaitChans is used to coordinate obtaining certs for each hostname. -var obtainCertWaitChans = make(map[string]chan struct{}) -var obtainCertWaitChansMu sync.Mutex diff --git a/vendor/github.com/mholt/certmagic/httphandler.go b/vendor/github.com/mholt/certmagic/httphandler.go deleted file mode 100644 index b649c3031..000000000 --- a/vendor/github.com/mholt/certmagic/httphandler.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "encoding/json" - "log" - "net/http" - "strings" - - "github.com/go-acme/lego/challenge/http01" -) - -// HTTPChallengeHandler wraps h in a handler that can solve the ACME -// HTTP challenge. cfg is required, and it must have a certificate -// cache backed by a functional storage facility, since that is where -// the challenge state is stored between initiation and solution. -// -// If a request is not an ACME HTTP challenge, h willl be invoked. -func (cfg *Config) HTTPChallengeHandler(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if cfg.HandleHTTPChallenge(w, r) { - return - } - h.ServeHTTP(w, r) - }) -} - -// HandleHTTPChallenge uses cfg to solve challenge requests from an ACME -// server that were initiated by this instance or any other instance in -// this cluster (being, any instances using the same storage cfg does). -// -// If the HTTP challenge is disabled, this function is a no-op. -// -// If cfg is nil or if cfg does not have a certificate cache backed by -// usable storage, solving the HTTP challenge will fail. -// -// It returns true if it handled the request; if so, the response has -// already been written. If false is returned, this call was a no-op and -// the request has not been handled. -func (cfg *Config) HandleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool { - if cfg == nil { - return false - } - if cfg.DisableHTTPChallenge { - return false - } - if !LooksLikeHTTPChallenge(r) { - return false - } - return cfg.distributedHTTPChallengeSolver(w, r) -} - -// distributedHTTPChallengeSolver checks to see if this challenge -// request was initiated by this or another instance which uses the -// same storage as cfg does, and attempts to complete the challenge for -// it. It returns true if the request was handled; false otherwise. -func (cfg *Config) distributedHTTPChallengeSolver(w http.ResponseWriter, r *http.Request) bool { - if cfg == nil { - return false - } - - tokenKey := distributedSolver{config: cfg}.challengeTokensKey(r.Host) - chalInfoBytes, err := cfg.Storage.Load(tokenKey) - if err != nil { - if _, ok := err.(ErrNotExist); !ok { - log.Printf("[ERROR][%s] Opening distributed HTTP challenge token file: %v", r.Host, err) - } - return false - } - - var chalInfo challengeInfo - err = json.Unmarshal(chalInfoBytes, &chalInfo) - if err != nil { - log.Printf("[ERROR][%s] Decoding challenge token file %s (corrupted?): %v", r.Host, tokenKey, err) - return false - } - - return answerHTTPChallenge(w, r, chalInfo) -} - -// answerHTTPChallenge solves the challenge with chalInfo. -// Most of this code borrowed from xenolf/lego's built-in HTTP-01 -// challenge solver in March 2018. -func answerHTTPChallenge(w http.ResponseWriter, r *http.Request, chalInfo challengeInfo) bool { - challengeReqPath := http01.ChallengePath(chalInfo.Token) - if r.URL.Path == challengeReqPath && - strings.HasPrefix(r.Host, chalInfo.Domain) && - r.Method == "GET" { - w.Header().Add("Content-Type", "text/plain") - w.Write([]byte(chalInfo.KeyAuth)) - r.Close = true - log.Printf("[INFO][%s] Served key authentication (distributed)", chalInfo.Domain) - return true - } - return false -} - -// LooksLikeHTTPChallenge returns true if r looks like an ACME -// HTTP challenge request from an ACME server. -func LooksLikeHTTPChallenge(r *http.Request) bool { - return r.Method == "GET" && strings.HasPrefix(r.URL.Path, challengeBasePath) -} - -const challengeBasePath = "/.well-known/acme-challenge" diff --git a/vendor/github.com/mholt/certmagic/maintain.go b/vendor/github.com/mholt/certmagic/maintain.go deleted file mode 100644 index 75870adfc..000000000 --- a/vendor/github.com/mholt/certmagic/maintain.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "log" - "time" - - "golang.org/x/crypto/ocsp" -) - -// maintainAssets is a permanently-blocking function -// that loops indefinitely and, on a regular schedule, checks -// certificates for expiration and initiates a renewal of certs -// that are expiring soon. It also updates OCSP stapling. It -// should only be called once per cache. -func (certCache *Cache) maintainAssets() { - renewalTicker := time.NewTicker(certCache.options.RenewCheckInterval) - ocspTicker := time.NewTicker(certCache.options.OCSPCheckInterval) - - log.Printf("[INFO][cache:%p] Started certificate maintenance routine", certCache) - - for { - select { - case <-renewalTicker.C: - log.Printf("[INFO][cache:%p] Scanning for expiring certificates", certCache) - err := certCache.RenewManagedCertificates(false) - if err != nil { - log.Printf("[ERROR][cache:%p] Renewing managed certificates: %v", certCache, err) - } - log.Printf("[INFO][cache:%p] Done scanning certificates", certCache) - case <-ocspTicker.C: - log.Printf("[INFO][cache:%p] Scanning for stale OCSP staples", certCache) - certCache.updateOCSPStaples() - // certCache.deleteOldStapleFiles() - log.Printf("[INFO][cache:%p] Done checking OCSP staples", certCache) - case <-certCache.stopChan: - renewalTicker.Stop() - ocspTicker.Stop() - // TODO: stop any in-progress maintenance operations and clear locks we made - log.Printf("[INFO][cache:%p] Stopped certificate maintenance routine", certCache) - close(certCache.doneChan) - return - } - } -} - -// RenewManagedCertificates renews managed certificates, -// including ones loaded on-demand. Note that this is done -// automatically on a regular basis; normally you will not -// need to call this. -func (certCache *Cache) RenewManagedCertificates(interactive bool) error { - // configs will hold a map of certificate name to the config - // to use when managing that certificate - configs := make(map[string]*Config) - - // we use the queues for a very important reason: to do any and all - // operations that could require an exclusive write lock outside - // of the read lock! otherwise we get a deadlock, yikes. in other - // words, our first iteration through the certificate cache does NOT - // perform any operations--only queues them--so that more fine-grained - // write locks may be obtained during the actual operations. - var renewQueue, reloadQueue, deleteQueue []Certificate - - certCache.mu.RLock() - for certKey, cert := range certCache.cache { - if !cert.managed { - continue - } - - // the list of names on this cert should never be empty... programmer error? - if cert.Names == nil || len(cert.Names) == 0 { - log.Printf("[WARNING] Certificate keyed by '%s' has no names: %v - removing from cache", certKey, cert.Names) - deleteQueue = append(deleteQueue, cert) - continue - } - - // get the config associated with this certificate - cfg, err := certCache.getConfig(cert) - if err != nil { - log.Printf("[ERROR] Getting configuration to manage certificate for names %v; unable to renew: %v", cert.Names, err) - continue - } - if cfg == nil { - // this is bad if this happens, probably a programmer error (oops) - log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage", cert.Names) - continue - } - configs[cert.Names[0]] = cfg - - // if time is up or expires soon, we need to try to renew it - if cert.NeedsRenewal(cfg) { - // see if the certificate in storage has already been renewed, possibly by another - // instance that didn't coordinate with this one; if so, just load it (this - // might happen if another instance already renewed it - kinda sloppy but checking disk - // first is a simple way to possibly drastically reduce rate limit problems) - storedCertExpiring, err := cfg.managedCertInStorageExpiresSoon(cert) - if err != nil { - // hmm, weird, but not a big deal, maybe it was deleted or something - log.Printf("[NOTICE] Error while checking if certificate for %v in storage is also expiring soon: %v", - cert.Names, err) - } else if !storedCertExpiring { - // if the certificate is NOT expiring soon and there was no error, then we - // are good to just reload the certificate from storage instead of repeating - // a likely-unnecessary renewal procedure - reloadQueue = append(reloadQueue, cert) - continue - } - - // the certificate in storage has not been renewed yet, so we will do it - // NOTE: It is super-important to note that the TLS-ALPN challenge requires - // a write lock on the cache in order to complete its challenge, so it is extra - // vital that this renew operation does not happen inside our read lock! - renewQueue = append(renewQueue, cert) - } - } - certCache.mu.RUnlock() - - // Reload certificates that merely need to be updated in memory - for _, oldCert := range reloadQueue { - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] Certificate for %v expires in %v, but is already renewed in storage; reloading stored certificate", - oldCert.Names, timeLeft) - - cfg := configs[oldCert.Names[0]] - - err := cfg.reloadManagedCertificate(oldCert) - if err != nil { - if interactive { - return err // operator is present, so report error immediately - } - log.Printf("[ERROR] Loading renewed certificate: %v", err) - } - } - - // Renewal queue - for _, oldCert := range renewQueue { - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - log.Printf("[INFO] Certificate for %v expires in %v; attempting renewal", oldCert.Names, timeLeft) - - cfg := configs[oldCert.Names[0]] - - // Get the name which we should use to renew this certificate; - // we only support managing certificates with one name per cert, - // so this should be easy. - renewName := oldCert.Names[0] - - // perform renewal - err := cfg.RenewCert(renewName, interactive) - if err != nil { - if interactive { - // Certificate renewal failed and the operator is present. See a discussion about - // this in issue mholt/caddy#642. For a while, we only stopped if the certificate - // was expired, but in reality, there is no difference between reporting it now - // versus later, except that there's somebody present to deal with it right now. - // Follow-up: See issue mholt/caddy#1680. Only fail in this case if the certificate - // is dangerously close to expiration. - timeLeft := oldCert.NotAfter.Sub(time.Now().UTC()) - if timeLeft < cfg.RenewDurationBeforeAtStartup { - return err - } - } - log.Printf("[ERROR] %v", err) - if cfg.OnDemand != nil { - // loaded dynamically, remove dynamically - deleteQueue = append(deleteQueue, oldCert) - } - continue - } - - // successful renewal, so update in-memory cache by loading - // renewed certificate so it will be used with handshakes - err = cfg.reloadManagedCertificate(oldCert) - if err != nil { - if interactive { - return err // operator is present, so report error immediately - } - log.Printf("[ERROR] %v", err) - } - } - - // Deletion queue - for _, cert := range deleteQueue { - certCache.removeCertificate(cert) - } - - return nil -} - -// updateOCSPStaples updates the OCSP stapling in all -// eligible, cached certificates. -// -// OCSP maintenance strives to abide the relevant points on -// Ryan Sleevi's recommendations for good OCSP support: -// https://gist.github.com/sleevi/5efe9ef98961ecfb4da8 -func (certCache *Cache) updateOCSPStaples() { - // Create a temporary place to store updates - // until we release the potentially long-lived - // read lock and use a short-lived write lock - // on the certificate cache. - type ocspUpdate struct { - rawBytes []byte - parsed *ocsp.Response - } - updated := make(map[string]ocspUpdate) - - certCache.mu.RLock() - for certHash, cert := range certCache.cache { - // no point in updating OCSP for expired certificates - if time.Now().After(cert.NotAfter) { - continue - } - - var lastNextUpdate time.Time - if cert.OCSP != nil { - lastNextUpdate = cert.OCSP.NextUpdate - if freshOCSP(cert.OCSP) { - continue // no need to update staple if ours is still fresh - } - } - - cfg, err := certCache.getConfig(cert) - if err != nil { - log.Printf("[ERROR] Getting configuration to manage OCSP for certificate with names %v; unable to refresh: %v", cert.Names, err) - continue - } - if cfg == nil { - // this is bad if this happens, probably a programmer error (oops) - log.Printf("[ERROR] No configuration associated with certificate for names %v; unable to manage OCSP", cert.Names) - continue - } - - err = stapleOCSP(cfg.Storage, &cert, nil) - if err != nil { - if cert.OCSP != nil { - // if there was no staple before, that's fine; otherwise we should log the error - log.Printf("[ERROR] Checking OCSP: %v", err) - } - continue - } - - // By this point, we've obtained the latest OCSP response. - // If there was no staple before, or if the response is updated, make - // sure we apply the update to all names on the certificate. - if cert.OCSP != nil && (lastNextUpdate.IsZero() || lastNextUpdate != cert.OCSP.NextUpdate) { - log.Printf("[INFO] Advancing OCSP staple for %v from %s to %s", - cert.Names, lastNextUpdate, cert.OCSP.NextUpdate) - updated[certHash] = ocspUpdate{rawBytes: cert.Certificate.OCSPStaple, parsed: cert.OCSP} - } - } - certCache.mu.RUnlock() - - // These write locks should be brief since we have all the info we need now. - for certKey, update := range updated { - certCache.mu.Lock() - cert := certCache.cache[certKey] - cert.OCSP = update.parsed - cert.Certificate.OCSPStaple = update.rawBytes - certCache.cache[certKey] = cert - certCache.mu.Unlock() - } -} - -// CleanStorageOptions specifies how to clean up a storage unit. -type CleanStorageOptions struct { - OCSPStaples bool - // TODO: long-expired certificates -} - -// CleanStorage tidies up the given storage according to opts; this -// generally involves deleting assets which are no longer required. -// TODO: We should do this for long-expired certificates, too. -func CleanStorage(storage Storage, opts CleanStorageOptions) { - if opts.OCSPStaples { - err := deleteOldOCSPStaples(storage) - if err != nil { - log.Printf("[ERROR] Deleting old OCSP staples: %v", err) - } - } -} - -func deleteOldOCSPStaples(storage Storage) error { - ocspKeys, err := storage.List(prefixOCSP, false) - if err != nil { - // maybe just hasn't been created yet; no big deal - return nil - } - for _, key := range ocspKeys { - ocspBytes, err := storage.Load(key) - if err != nil { - log.Printf("[ERROR] While deleting old OCSP staples, unable to load staple file: %v", err) - continue - } - resp, err := ocsp.ParseResponse(ocspBytes, nil) - if err != nil { - // contents are invalid; delete it - err = storage.Delete(key) - if err != nil { - log.Printf("[ERROR] Purging corrupt staple file %s: %v", key, err) - } - continue - } - if time.Now().After(resp.NextUpdate) { - // response has expired; delete it - err = storage.Delete(key) - if err != nil { - log.Printf("[ERROR] Purging expired staple file %s: %v", key, err) - } - } - } - return nil -} - -const ( - // DefaultRenewCheckInterval is how often to check certificates for renewal. - DefaultRenewCheckInterval = 12 * time.Hour - - // DefaultRenewDurationBefore is how long before expiration to renew certificates. - DefaultRenewDurationBefore = (24 * time.Hour) * 30 - - // DefaultRenewDurationBeforeAtStartup is how long before expiration to require - // a renewed certificate when the process is first starting up (see mholt/caddy#1680). - DefaultRenewDurationBeforeAtStartup = (24 * time.Hour) * 7 - - // DefaultOCSPCheckInterval is how often to check if OCSP stapling needs updating. - DefaultOCSPCheckInterval = 1 * time.Hour -) diff --git a/vendor/github.com/mholt/certmagic/ocsp.go b/vendor/github.com/mholt/certmagic/ocsp.go deleted file mode 100644 index 823b7c868..000000000 --- a/vendor/github.com/mholt/certmagic/ocsp.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "fmt" - "io" - "io/ioutil" - "log" - "net/http" - "time" - - "golang.org/x/crypto/ocsp" -) - -// stapleOCSP staples OCSP information to cert for hostname name. -// If you have it handy, you should pass in the PEM-encoded certificate -// bundle; otherwise the DER-encoded cert will have to be PEM-encoded. -// If you don't have the PEM blocks already, just pass in nil. -// -// Errors here are not necessarily fatal, it could just be that the -// certificate doesn't have an issuer URL. -func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) error { - if pemBundle == nil { - // we need a PEM encoding only for some function calls below - bundle := new(bytes.Buffer) - for _, derBytes := range cert.Certificate.Certificate { - pem.Encode(bundle, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - } - pemBundle = bundle.Bytes() - } - - var ocspBytes []byte - var ocspResp *ocsp.Response - var ocspErr error - var gotNewOCSP bool - - // First try to load OCSP staple from storage and see if - // we can still use it. - ocspStapleKey := StorageKeys.OCSPStaple(cert, pemBundle) - cachedOCSP, err := storage.Load(ocspStapleKey) - if err == nil { - resp, err := ocsp.ParseResponse(cachedOCSP, nil) - if err == nil { - if freshOCSP(resp) { - // staple is still fresh; use it - ocspBytes = cachedOCSP - ocspResp = resp - } - } else { - // invalid contents; delete the file - // (we do this independently of the maintenance routine because - // in this case we know for sure this should be a staple file - // because we loaded it by name, whereas the maintenance routine - // just iterates the list of files, even if somehow a non-staple - // file gets in the folder. in this case we are sure it is corrupt.) - err := storage.Delete(ocspStapleKey) - if err != nil { - log.Printf("[WARNING] Unable to delete invalid OCSP staple file: %v", err) - } - } - } - - // If we couldn't get a fresh staple by reading the cache, - // then we need to request it from the OCSP responder - if ocspResp == nil || len(ocspBytes) == 0 { - ocspBytes, ocspResp, ocspErr = getOCSPForCert(pemBundle) - if ocspErr != nil { - // An error here is not a problem because a certificate may simply - // not contain a link to an OCSP server. But we should log it anyway. - // There's nothing else we can do to get OCSP for this certificate, - // so we can return here with the error. - return fmt.Errorf("no OCSP stapling for %v: %v", cert.Names, ocspErr) - } - gotNewOCSP = true - } - - // By now, we should have a response. If good, staple it to - // the certificate. If the OCSP response was not loaded from - // storage, we persist it for next time. - if ocspResp.Status == ocsp.Good { - if ocspResp.NextUpdate.After(cert.NotAfter) { - // uh oh, this OCSP response expires AFTER the certificate does, that's kinda bogus. - // it was the reason a lot of Symantec-validated sites (not Caddy) went down - // in October 2017. https://twitter.com/mattiasgeniar/status/919432824708648961 - return fmt.Errorf("invalid: OCSP response for %v valid after certificate expiration (%s)", - cert.Names, cert.NotAfter.Sub(ocspResp.NextUpdate)) - } - cert.Certificate.OCSPStaple = ocspBytes - cert.OCSP = ocspResp - if gotNewOCSP { - err := storage.Store(ocspStapleKey, ocspBytes) - if err != nil { - return fmt.Errorf("unable to write OCSP staple file for %v: %v", cert.Names, err) - } - } - } - - return nil -} - -// getOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response, -// the parsed response, and an error, if any. The returned []byte can be passed directly -// into the OCSPStaple property of a tls.Certificate. If the bundle only contains the -// issued certificate, this function will try to get the issuer certificate from the -// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return -// values are nil, the OCSP status may be assumed OCSPUnknown. -// -// Borrowed from github.com/go-acme/lego -func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) { - // TODO: Perhaps this should be synchronized too, with a Locker? - - certificates, err := parseCertsFromPEMBundle(bundle) - if err != nil { - return nil, nil, err - } - - // We expect the certificate slice to be ordered downwards the chain. - // SRV CRT -> CA. We need to pull the leaf and issuer certs out of it, - // which should always be the first two certificates. If there's no - // OCSP server listed in the leaf cert, there's nothing to do. And if - // we have only one certificate so far, we need to get the issuer cert. - issuedCert := certificates[0] - if len(issuedCert.OCSPServer) == 0 { - return nil, nil, fmt.Errorf("no OCSP server specified in certificate") - } - if len(certificates) == 1 { - if len(issuedCert.IssuingCertificateURL) == 0 { - return nil, nil, fmt.Errorf("no URL to issuing certificate") - } - - resp, err := http.Get(issuedCert.IssuingCertificateURL[0]) - if err != nil { - return nil, nil, fmt.Errorf("getting issuer certificate: %v", err) - } - defer resp.Body.Close() - - issuerBytes, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1024*1024)) - if err != nil { - return nil, nil, fmt.Errorf("reading issuer certificate: %v", err) - } - - issuerCert, err := x509.ParseCertificate(issuerBytes) - if err != nil { - return nil, nil, fmt.Errorf("parsing issuer certificate: %v", err) - } - - // insert it into the slice on position 0; - // we want it ordered right SRV CRT -> CA - certificates = append(certificates, issuerCert) - } - - issuerCert := certificates[1] - - ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil) - if err != nil { - return nil, nil, fmt.Errorf("creating OCSP request: %v", err) - } - - reader := bytes.NewReader(ocspReq) - req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader) - if err != nil { - return nil, nil, fmt.Errorf("making OCSP request: %v", err) - } - defer req.Body.Close() - - ocspResBytes, err := ioutil.ReadAll(io.LimitReader(req.Body, 1024*1024)) - if err != nil { - return nil, nil, fmt.Errorf("reading OCSP response: %v", err) - } - - ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert) - if err != nil { - return nil, nil, fmt.Errorf("parsing OCSP response: %v", err) - } - - return ocspResBytes, ocspRes, nil -} - -// freshOCSP returns true if resp is still fresh, -// meaning that it is not expedient to get an -// updated response from the OCSP server. -func freshOCSP(resp *ocsp.Response) bool { - nextUpdate := resp.NextUpdate - // If there is an OCSP responder certificate, and it expires before the - // OCSP response, use its expiration date as the end of the OCSP - // response's validity period. - if resp.Certificate != nil && resp.Certificate.NotAfter.Before(nextUpdate) { - nextUpdate = resp.Certificate.NotAfter - } - // start checking OCSP staple about halfway through validity period for good measure - refreshTime := resp.ThisUpdate.Add(nextUpdate.Sub(resp.ThisUpdate) / 2) - return time.Now().Before(refreshTime) -} diff --git a/vendor/github.com/mholt/certmagic/solvers.go b/vendor/github.com/mholt/certmagic/solvers.go deleted file mode 100644 index 743edfce7..000000000 --- a/vendor/github.com/mholt/certmagic/solvers.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "encoding/json" - "fmt" - "log" - "path/filepath" - - "github.com/go-acme/lego/challenge" - "github.com/go-acme/lego/challenge/tlsalpn01" -) - -// tlsALPNSolver is a type that can solve TLS-ALPN challenges using -// an existing listener and our custom, in-memory certificate cache. -type tlsALPNSolver struct { - certCache *Cache -} - -// Present adds the challenge certificate to the cache. -func (s tlsALPNSolver) Present(domain, token, keyAuth string) error { - cert, err := tlsalpn01.ChallengeCert(domain, keyAuth) - if err != nil { - return err - } - certHash := hashCertificateChain(cert.Certificate) - s.certCache.mu.Lock() - s.certCache.cache[tlsALPNCertKeyName(domain)] = Certificate{ - Certificate: *cert, - Names: []string{domain}, - Hash: certHash, // perhaps not necesssary - } - s.certCache.mu.Unlock() - return nil -} - -// CleanUp removes the challenge certificate from the cache. -func (s tlsALPNSolver) CleanUp(domain, token, keyAuth string) error { - s.certCache.mu.Lock() - delete(s.certCache.cache, tlsALPNCertKeyName(domain)) - s.certCache.mu.Unlock() - return nil -} - -// tlsALPNCertKeyName returns the key to use when caching a cert -// for use with the TLS-ALPN ACME challenge. It is simply to help -// avoid conflicts (although at time of writing, there shouldn't -// be, since the cert cache is keyed by hash of certificate chain). -func tlsALPNCertKeyName(sniName string) string { - return sniName + ":acme-tls-alpn" -} - -// distributedSolver allows the ACME HTTP-01 and TLS-ALPN challenges -// to be solved by an instance other than the one which initiated it. -// This is useful behind load balancers or in other cluster/fleet -// configurations. The only requirement is that the instance which -// initiates the challenge shares the same storage and locker with -// the others in the cluster. The storage backing the certificate -// cache in distributedSolver.config is crucial. -// -// Obviously, the instance which completes the challenge must be -// serving on the HTTPChallengePort for the HTTP-01 challenge or the -// TLSALPNChallengePort for the TLS-ALPN-01 challenge (or have all -// the packets port-forwarded) to receive and handle the request. The -// server which receives the challenge must handle it by checking to -// see if the challenge token exists in storage, and if so, decode it -// and use it to serve up the correct response. HTTPChallengeHandler -// in this package as well as the GetCertificate method implemented -// by a Config support and even require this behavior. -// -// In short: the only two requirements for cluster operation are -// sharing sync and storage, and using the facilities provided by -// this package for solving the challenges. -type distributedSolver struct { - // The config with a certificate cache - // with a reference to the storage to - // use which is shared among all the - // instances in the cluster - REQUIRED. - config *Config - - // Since the distributedSolver is only a - // wrapper over an actual solver, place - // the actual solver here. - providerServer challenge.Provider -} - -// Present invokes the underlying solver's Present method -// and also stores domain, token, and keyAuth to the storage -// backing the certificate cache of dhs.config. -func (dhs distributedSolver) Present(domain, token, keyAuth string) error { - if dhs.providerServer != nil { - err := dhs.providerServer.Present(domain, token, keyAuth) - if err != nil { - return fmt.Errorf("presenting with standard provider server: %v", err) - } - } - - infoBytes, err := json.Marshal(challengeInfo{ - Domain: domain, - Token: token, - KeyAuth: keyAuth, - }) - if err != nil { - return err - } - - return dhs.config.Storage.Store(dhs.challengeTokensKey(domain), infoBytes) -} - -// CleanUp invokes the underlying solver's CleanUp method -// and also cleans up any assets saved to storage. -func (dhs distributedSolver) CleanUp(domain, token, keyAuth string) error { - if dhs.providerServer != nil { - err := dhs.providerServer.CleanUp(domain, token, keyAuth) - if err != nil { - log.Printf("[ERROR] Cleaning up standard provider server: %v", err) - } - } - return dhs.config.Storage.Delete(dhs.challengeTokensKey(domain)) -} - -// challengeTokensPrefix returns the key prefix for challenge info. -func (dhs distributedSolver) challengeTokensPrefix() string { - return filepath.Join(StorageKeys.CAPrefix(dhs.config.CA), "challenge_tokens") -} - -// challengeTokensKey returns the key to use to store and access -// challenge info for domain. -func (dhs distributedSolver) challengeTokensKey(domain string) string { - return filepath.Join(dhs.challengeTokensPrefix(), StorageKeys.Safe(domain)+".json") -} - -type challengeInfo struct { - Domain, Token, KeyAuth string -} diff --git a/vendor/github.com/mholt/certmagic/storage.go b/vendor/github.com/mholt/certmagic/storage.go deleted file mode 100644 index 927ffef87..000000000 --- a/vendor/github.com/mholt/certmagic/storage.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "net/url" - "path" - "regexp" - "strings" - "time" -) - -// Storage is a type that implements a key-value store. -// Keys are prefix-based, with forward slash '/' as separators -// and without a leading slash. -// -// Processes running in a cluster will wish to use the -// same Storage value (its implementation and configuration) -// in order to share certificates and other TLS resources -// with the cluster. -// -// Implementations of Storage must be safe for concurrent use. -type Storage interface { - // Locker provides atomic synchronization - // operations, making Storage safe to share. - Locker - - // Store puts value at key. - Store(key string, value []byte) error - - // Load retrieves the value at key. - Load(key string) ([]byte, error) - - // Delete deletes key. - Delete(key string) error - - // Exists returns true if the key exists - // and there was no error checking. - Exists(key string) bool - - // List returns all keys that match prefix. - // If recursive is true, non-terminal keys - // will be enumerated (i.e. "directories" - // should be walked); otherwise, only keys - // prefixed exactly by prefix will be listed. - List(prefix string, recursive bool) ([]string, error) - - // Stat returns information about key. - Stat(key string) (KeyInfo, error) -} - -// Locker facilitates synchronization of certificate tasks across -// machines and networks. -type Locker interface { - // Lock acquires the lock for key, blocking until the lock - // can be obtained or an error is returned. Note that, even - // after acquiring a lock, an idempotent operation may have - // already been performed by another process that acquired - // the lock before - so always check to make sure idempotent - // operations still need to be performed after acquiring the - // lock. - // - // The actual implementation of obtaining of a lock must be - // an atomic operation so that multiple Lock calls at the - // same time always results in only one caller receiving the - // lock at any given time. - // - // To prevent deadlocks, all implementations (where this concern - // is relevant) should put a reasonable expiration on the lock in - // case Unlock is unable to be called due to some sort of network - // or system failure or crash. - Lock(key string) error - - // Unlock releases the lock for key. This method must ONLY be - // called after a successful call to Lock, and only after the - // critical section is finished, even if it errored or timed - // out. Unlock cleans up any resources allocated during Lock. - Unlock(key string) error -} - -// KeyInfo holds information about a key in storage. -type KeyInfo struct { - Key string - Modified time.Time - Size int64 - IsTerminal bool // false for keys that only contain other keys (like directories) -} - -// storeTx stores all the values or none at all. -func storeTx(s Storage, all []keyValue) error { - for i, kv := range all { - err := s.Store(kv.key, kv.value) - if err != nil { - for j := i - 1; j >= 0; j-- { - s.Delete(all[j].key) - } - return err - } - } - return nil -} - -// keyValue pairs a key and a value. -type keyValue struct { - key string - value []byte -} - -// KeyBuilder provides a namespace for methods that -// build keys and key prefixes, for addressing items -// in a Storage implementation. -type KeyBuilder struct{} - -// CAPrefix returns the storage key prefix for -// the given certificate authority URL. -func (keys KeyBuilder) CAPrefix(ca string) string { - caURL, err := url.Parse(ca) - if err != nil { - caURL = &url.URL{Host: ca} - } - return path.Join(prefixACME, keys.Safe(caURL.Host)) -} - -// SitePrefix returns a key prefix for items associated with -// the site using the given CA URL. -func (keys KeyBuilder) SitePrefix(ca, domain string) string { - return path.Join(keys.CAPrefix(ca), "sites", keys.Safe(domain)) -} - -// SiteCert returns the path to the certificate file for domain. -func (keys KeyBuilder) SiteCert(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".crt") -} - -// SitePrivateKey returns the path to domain's private key file. -func (keys KeyBuilder) SitePrivateKey(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".key") -} - -// SiteMeta returns the path to the domain's asset metadata file. -func (keys KeyBuilder) SiteMeta(ca, domain string) string { - return path.Join(keys.SitePrefix(ca, domain), keys.Safe(domain)+".json") -} - -// UsersPrefix returns a key prefix for items related to -// users associated with the given CA URL. -func (keys KeyBuilder) UsersPrefix(ca string) string { - return path.Join(keys.CAPrefix(ca), "users") -} - -// UserPrefix returns a key prefix for items related to -// the user with the given email for the given CA URL. -func (keys KeyBuilder) UserPrefix(ca, email string) string { - if email == "" { - email = emptyEmail - } - return path.Join(keys.UsersPrefix(ca), keys.Safe(email)) -} - -// UserReg gets the path to the registration file for the user -// with the given email address for the given CA URL. -func (keys KeyBuilder) UserReg(ca, email string) string { - return keys.safeUserKey(ca, email, "registration", ".json") -} - -// UserPrivateKey gets the path to the private key file for the -// user with the given email address on the given CA URL. -func (keys KeyBuilder) UserPrivateKey(ca, email string) string { - return keys.safeUserKey(ca, email, "private", ".key") -} - -// OCSPStaple returns a key for the OCSP staple associated -// with the given certificate. If you have the PEM bundle -// handy, pass that in to save an extra encoding step. -func (keys KeyBuilder) OCSPStaple(cert *Certificate, pemBundle []byte) string { - var ocspFileName string - if len(cert.Names) > 0 { - firstName := keys.Safe(cert.Names[0]) - ocspFileName = firstName + "-" - } - ocspFileName += fastHash(pemBundle) - return path.Join(prefixOCSP, ocspFileName) -} - -// safeUserKey returns a key for the given email, with the default -// filename, and the filename ending in the given extension. -func (keys KeyBuilder) safeUserKey(ca, email, defaultFilename, extension string) string { - if email == "" { - email = emptyEmail - } - email = strings.ToLower(email) - filename := keys.emailUsername(email) - if filename == "" { - filename = defaultFilename - } - filename = keys.Safe(filename) - return path.Join(keys.UserPrefix(ca, email), filename+extension) -} - -// emailUsername returns the username portion of an email address (part before -// '@') or the original input if it can't find the "@" symbol. -func (keys KeyBuilder) emailUsername(email string) string { - at := strings.Index(email, "@") - if at == -1 { - return email - } else if at == 0 { - return email[1:] - } - return email[:at] -} - -// Safe standardizes and sanitizes str for use as -// a storage key. This method is idempotent. -func (keys KeyBuilder) Safe(str string) string { - str = strings.ToLower(str) - str = strings.TrimSpace(str) - - // replace a few specific characters - repl := strings.NewReplacer( - " ", "_", - "+", "_plus_", - "*", "wildcard_", - "..", "", // prevent directory traversal (regex allows single dots) - ) - str = repl.Replace(str) - - // finally remove all non-word characters - return safeKeyRE.ReplaceAllLiteralString(str, "") -} - -// StorageKeys provides methods for accessing -// keys and key prefixes for items in a Storage. -// Typically, you will not need to use this -// because accessing storage is abstracted away -// for most cases. Only use this if you need to -// directly access TLS assets in your application. -var StorageKeys KeyBuilder - -const ( - prefixACME = "acme" - prefixOCSP = "ocsp" -) - -// safeKeyRE matches any undesirable characters in storage keys. -// Note that this allows dots, so you'll have to strip ".." manually. -var safeKeyRE = regexp.MustCompile(`[^\w@.-]`) - -// ErrNotExist is returned by Storage implementations when -// a resource is not found. It is similar to os.IsNotExist -// except this is a type, not a variable. -type ErrNotExist interface { - error -} - -// defaultFileStorage is a convenient, default storage -// implementation using the local file system. -var defaultFileStorage = &FileStorage{Path: dataDir()} diff --git a/vendor/github.com/mholt/certmagic/user.go b/vendor/github.com/mholt/certmagic/user.go deleted file mode 100644 index 2b5412447..000000000 --- a/vendor/github.com/mholt/certmagic/user.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2015 Matthew Holt -// -// 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 certmagic - -import ( - "bufio" - "crypto" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "encoding/json" - "fmt" - "io" - "net/http" - "os" - "path" - "sort" - "strings" - - "github.com/go-acme/lego/acme" - "github.com/go-acme/lego/registration" -) - -// user represents a Let's Encrypt user account. -type user struct { - Email string - Registration *registration.Resource - key crypto.PrivateKey -} - -// GetEmail gets u's email. -func (u user) GetEmail() string { - return u.Email -} - -// GetRegistration gets u's registration resource. -func (u user) GetRegistration() *registration.Resource { - return u.Registration -} - -// GetPrivateKey gets u's private key. -func (u user) GetPrivateKey() crypto.PrivateKey { - return u.key -} - -// newUser creates a new User for the given email address -// with a new private key. This function does NOT save the -// user to disk or register it via ACME. If you want to use -// a user account that might already exist, call getUser -// instead. It does NOT prompt the user. -func (cfg *Config) newUser(email string) (user, error) { - user := user{Email: email} - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return user, fmt.Errorf("generating private key: %v", err) - } - user.key = privateKey - return user, nil -} - -// getEmail does everything it can to obtain an email address -// from the user within the scope of memory and storage to use -// for ACME TLS. If it cannot get an email address, it does nothing -// (If user is prompted, it will warn the user of -// the consequences of an empty email.) This function MAY prompt -// the user for input. If allowPrompts is false, the user -// will NOT be prompted and an empty email may be returned. -func (cfg *Config) getEmail(allowPrompts bool) error { - leEmail := cfg.Email - - // First try package default email - if leEmail == "" { - leEmail = Default.Email - } - - // Then try to get most recent user email from storage - var gotRecentEmail bool - if leEmail == "" { - leEmail, gotRecentEmail = cfg.mostRecentUserEmail() - } - if !gotRecentEmail && leEmail == "" && allowPrompts { - // Looks like there is no email address readily available, - // so we will have to ask the user if we can. - var err error - leEmail, err = cfg.promptUserForEmail() - if err != nil { - return err - } - - // User might have just signified their agreement - cfg.Agreed = Default.Agreed - } - - // save the email for later and ensure it is consistent - // for repeated use; then update cfg with the email - Default.Email = strings.TrimSpace(strings.ToLower(leEmail)) - cfg.Email = Default.Email - - return nil -} - -func (cfg *Config) getAgreementURL() (string, error) { - if agreementTestURL != "" { - return agreementTestURL, nil - } - caURL := Default.CA - if cfg.CA != "" { - caURL = cfg.CA - } - response, err := http.Get(caURL) - if err != nil { - return "", err - } - defer response.Body.Close() - var dir acme.Directory - err = json.NewDecoder(response.Body).Decode(&dir) - if err != nil { - return "", err - } - return dir.Meta.TermsOfService, nil -} - -// promptUserForEmail prompts the user for an email address -// and returns the email address they entered (which could -// be the empty string). If no error is returned, then Agreed -// will also be set to true, since continuing through the -// prompt signifies agreement. -func (cfg *Config) promptUserForEmail() (string, error) { - agreementURL, err := cfg.getAgreementURL() - if err != nil { - return "", fmt.Errorf("get Agreement URL: %v", err) - } - // prompt the user for an email address and terms agreement - reader := bufio.NewReader(stdin) - cfg.promptUserAgreement(agreementURL) - fmt.Println("Please enter your email address to signify agreement and to be notified") - fmt.Println("in case of issues. You can leave it blank, but we don't recommend it.") - fmt.Print(" Email address: ") - leEmail, err := reader.ReadString('\n') - if err != nil && err != io.EOF { - return "", fmt.Errorf("reading email address: %v", err) - } - leEmail = strings.TrimSpace(leEmail) - Default.Agreed = true - return leEmail, nil -} - -// getUser loads the user with the given email from disk -// using the provided storage. If the user does not exist, -// it will create a new one, but it does NOT save new -// users to the disk or register them via ACME. It does -// NOT prompt the user. -func (cfg *Config) getUser(email string) (user, error) { - var user user - - regBytes, err := cfg.Storage.Load(StorageKeys.UserReg(cfg.CA, email)) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // create a new user - return cfg.newUser(email) - } - return user, err - } - keyBytes, err := cfg.Storage.Load(StorageKeys.UserPrivateKey(cfg.CA, email)) - if err != nil { - if _, ok := err.(ErrNotExist); ok { - // create a new user - return cfg.newUser(email) - } - return user, err - } - - err = json.Unmarshal(regBytes, &user) - if err != nil { - return user, err - } - user.key, err = decodePrivateKey(keyBytes) - return user, err -} - -// saveUser persists a user's key and account registration -// to the file system. It does NOT register the user via ACME -// or prompt the user. You must also pass in the storage -// wherein the user should be saved. It should be the storage -// for the CA with which user has an account. -func (cfg *Config) saveUser(user user) error { - regBytes, err := json.MarshalIndent(&user, "", "\t") - if err != nil { - return err - } - keyBytes, err := encodePrivateKey(user.key) - if err != nil { - return err - } - - all := []keyValue{ - { - key: StorageKeys.UserReg(cfg.CA, user.Email), - value: regBytes, - }, - { - key: StorageKeys.UserPrivateKey(cfg.CA, user.Email), - value: keyBytes, - }, - } - - return storeTx(cfg.Storage, all) -} - -// promptUserAgreement simply outputs the standard user -// agreement prompt with the given agreement URL. -// It outputs a newline after the message. -func (cfg *Config) promptUserAgreement(agreementURL string) { - const userAgreementPrompt = `Your sites will be served over HTTPS automatically using Let's Encrypt. -By continuing, you agree to the Let's Encrypt Subscriber Agreement at:` - fmt.Printf("\n\n%s\n %s\n", userAgreementPrompt, agreementURL) -} - -// askUserAgreement prompts the user to agree to the agreement -// at the given agreement URL via stdin. It returns whether the -// user agreed or not. -func (cfg *Config) askUserAgreement(agreementURL string) bool { - cfg.promptUserAgreement(agreementURL) - fmt.Print("Do you agree to the terms? (y/n): ") - - reader := bufio.NewReader(stdin) - answer, err := reader.ReadString('\n') - if err != nil { - return false - } - answer = strings.ToLower(strings.TrimSpace(answer)) - - return answer == "y" || answer == "yes" -} - -// mostRecentUserEmail finds the most recently-written user file -// in s. Since this is part of a complex sequence to get a user -// account, errors here are discarded to simplify code flow in -// the caller, and errors are not important here anyway. -func (cfg *Config) mostRecentUserEmail() (string, bool) { - userList, err := cfg.Storage.List(StorageKeys.UsersPrefix(cfg.CA), false) - if err != nil || len(userList) == 0 { - return "", false - } - sort.Slice(userList, func(i, j int) bool { - iInfo, _ := cfg.Storage.Stat(userList[i]) - jInfo, _ := cfg.Storage.Stat(userList[j]) - return jInfo.Modified.Before(iInfo.Modified) - }) - user, err := cfg.getUser(path.Base(userList[0])) - if err != nil { - return "", false - } - return user.Email, true -} - -// agreementTestURL is set during tests to skip requiring -// setting up an entire ACME CA endpoint. -var agreementTestURL string - -// stdin is used to read the user's input if prompted; -// this is changed by tests during tests. -var stdin = io.ReadWriter(os.Stdin) - -// The name of the folder for accounts where the email -// address was not provided; default 'username' if you will, -// but only for local/storage use, not with the CA. -const emptyEmail = "default" diff --git a/vendor/github.com/miekg/dns/.codecov.yml b/vendor/github.com/miekg/dns/.codecov.yml deleted file mode 100644 index f91e5c1fe..000000000 --- a/vendor/github.com/miekg/dns/.codecov.yml +++ /dev/null @@ -1,8 +0,0 @@ -coverage: - status: - project: - default: - target: 40% - threshold: null - patch: false - changes: false diff --git a/vendor/github.com/miekg/dns/.gitignore b/vendor/github.com/miekg/dns/.gitignore deleted file mode 100644 index 776cd950c..000000000 --- a/vendor/github.com/miekg/dns/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.6 -tags -test.out -a.out diff --git a/vendor/github.com/miekg/dns/.travis.yml b/vendor/github.com/miekg/dns/.travis.yml deleted file mode 100644 index 013b5ae44..000000000 --- a/vendor/github.com/miekg/dns/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: go -sudo: false - -go: - - 1.10.x - - 1.11.x - - 1.12.x - - tip - -before_install: - # don't use the miekg/dns when testing forks - - mkdir -p $GOPATH/src/github.com/miekg - - ln -s $TRAVIS_BUILD_DIR $GOPATH/src/github.com/miekg/ || true - -script: - - go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./... - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/miekg/dns/AUTHORS b/vendor/github.com/miekg/dns/AUTHORS deleted file mode 100644 index 196568352..000000000 --- a/vendor/github.com/miekg/dns/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Miek Gieben diff --git a/vendor/github.com/miekg/dns/CONTRIBUTORS b/vendor/github.com/miekg/dns/CONTRIBUTORS deleted file mode 100644 index 5903779d8..000000000 --- a/vendor/github.com/miekg/dns/CONTRIBUTORS +++ /dev/null @@ -1,10 +0,0 @@ -Alex A. Skinner -Andrew Tunnell-Jones -Ask Bjørn Hansen -Dave Cheney -Dusty Wilson -Marek Majkowski -Peter van Dijk -Omri Bahumi -Alex Sergeyev -James Hartig diff --git a/vendor/github.com/miekg/dns/COPYRIGHT b/vendor/github.com/miekg/dns/COPYRIGHT deleted file mode 100644 index 35702b10e..000000000 --- a/vendor/github.com/miekg/dns/COPYRIGHT +++ /dev/null @@ -1,9 +0,0 @@ -Copyright 2009 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. -Extensions of the original work are copyright (c) 2011 Miek Gieben - -Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is -governed by a BSD-style license that can be found in the LICENSE file. - -Copyright 2014 CloudFlare. All rights reserved. Use of this source code is -governed by a BSD-style license that can be found in the LICENSE file. diff --git a/vendor/github.com/miekg/dns/Gopkg.lock b/vendor/github.com/miekg/dns/Gopkg.lock deleted file mode 100644 index 686632207..000000000 --- a/vendor/github.com/miekg/dns/Gopkg.lock +++ /dev/null @@ -1,57 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:6914c49eed986dfb8dffb33516fa129c49929d4d873f41e073c83c11c372b870" - name = "golang.org/x/crypto" - packages = [ - "ed25519", - "ed25519/internal/edwards25519", - ] - pruneopts = "" - revision = "e3636079e1a4c1f337f212cc5cd2aca108f6c900" - -[[projects]] - branch = "master" - digest = "1:08e41d63f8dac84d83797368b56cf0b339e42d0224e5e56668963c28aec95685" - name = "golang.org/x/net" - packages = [ - "bpf", - "context", - "internal/iana", - "internal/socket", - "ipv4", - "ipv6", - ] - pruneopts = "" - revision = "4dfa2610cdf3b287375bbba5b8f2a14d3b01d8de" - -[[projects]] - branch = "master" - digest = "1:b2ea75de0ccb2db2ac79356407f8a4cd8f798fe15d41b381c00abf3ae8e55ed1" - name = "golang.org/x/sync" - packages = ["errgroup"] - pruneopts = "" - revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" - -[[projects]] - branch = "master" - digest = "1:149a432fabebb8221a80f77731b1cd63597197ded4f14af606ebe3a0959004ec" - name = "golang.org/x/sys" - packages = ["unix"] - pruneopts = "" - revision = "e4b3c5e9061176387e7cea65e4dc5853801f3fb7" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "golang.org/x/crypto/ed25519", - "golang.org/x/net/ipv4", - "golang.org/x/net/ipv6", - "golang.org/x/sync/errgroup", - "golang.org/x/sys/unix", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/miekg/dns/Gopkg.toml b/vendor/github.com/miekg/dns/Gopkg.toml deleted file mode 100644 index 85e6ff31b..000000000 --- a/vendor/github.com/miekg/dns/Gopkg.toml +++ /dev/null @@ -1,38 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "golang.org/x/crypto" - -[[constraint]] - branch = "master" - name = "golang.org/x/net" - -[[constraint]] - branch = "master" - name = "golang.org/x/sys" - -[[constraint]] - branch = "master" - name = "golang.org/x/sync" diff --git a/vendor/github.com/miekg/dns/LICENSE b/vendor/github.com/miekg/dns/LICENSE deleted file mode 100644 index 5763fa7fe..000000000 --- a/vendor/github.com/miekg/dns/LICENSE +++ /dev/null @@ -1,32 +0,0 @@ -Extensions of the original work are copyright (c) 2011 Miek Gieben - -As this is fork of the official Go code the same license applies: - -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/github.com/miekg/dns/Makefile.fuzz b/vendor/github.com/miekg/dns/Makefile.fuzz deleted file mode 100644 index dc158c4ac..000000000 --- a/vendor/github.com/miekg/dns/Makefile.fuzz +++ /dev/null @@ -1,33 +0,0 @@ -# Makefile for fuzzing -# -# Use go-fuzz and needs the tools installed. -# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/ -# -# Installing go-fuzz: -# $ make -f Makefile.fuzz get -# Installs: -# * github.com/dvyukov/go-fuzz/go-fuzz -# * get github.com/dvyukov/go-fuzz/go-fuzz-build - -all: build - -.PHONY: build -build: - go-fuzz-build -tags fuzz github.com/miekg/dns - -.PHONY: build-newrr -build-newrr: - go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns - -.PHONY: fuzz -fuzz: - go-fuzz -bin=dns-fuzz.zip -workdir=fuzz - -.PHONY: get -get: - go get github.com/dvyukov/go-fuzz/go-fuzz - go get github.com/dvyukov/go-fuzz/go-fuzz-build - -.PHONY: clean -clean: - rm *-fuzz.zip diff --git a/vendor/github.com/miekg/dns/Makefile.release b/vendor/github.com/miekg/dns/Makefile.release deleted file mode 100644 index 8fb748e8a..000000000 --- a/vendor/github.com/miekg/dns/Makefile.release +++ /dev/null @@ -1,52 +0,0 @@ -# Makefile for releasing. -# -# The release is controlled from version.go. The version found there is -# used to tag the git repo, we're not building any artifects so there is nothing -# to upload to github. -# -# * Up the version in version.go -# * Run: make -f Makefile.release release -# * will *commit* your change with 'Release $VERSION' -# * push to github -# - -define GO -//+build ignore - -package main - -import ( - "fmt" - - "github.com/miekg/dns" -) - -func main() { - fmt.Println(dns.Version.String()) -} -endef - -$(file > version_release.go,$(GO)) -VERSION:=$(shell go run version_release.go) -TAG="v$(VERSION)" - -all: - @echo Use the \'release\' target to start a release $(VERSION) - rm -f version_release.go - -.PHONY: release -release: commit push - @echo Released $(VERSION) - rm -f version_release.go - -.PHONY: commit -commit: - @echo Committing release $(VERSION) - git commit -am"Release $(VERSION)" - git tag $(TAG) - -.PHONY: push -push: - @echo Pushing release $(VERSION) to master - git push --tags - git push diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md deleted file mode 100644 index dc2a8228f..000000000 --- a/vendor/github.com/miekg/dns/README.md +++ /dev/null @@ -1,172 +0,0 @@ -[![Build Status](https://travis-ci.org/miekg/dns.svg?branch=master)](https://travis-ci.org/miekg/dns) -[![Code Coverage](https://img.shields.io/codecov/c/github/miekg/dns/master.svg)](https://codecov.io/github/miekg/dns?branch=master) -[![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns) -[![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns) - -# Alternative (more granular) approach to a DNS library - -> Less is more. - -Complete and usable DNS library. All Resource Records are supported, including the DNSSEC types. -It follows a lean and mean philosophy. If there is stuff you should know as a DNS programmer there -isn't a convenience function for it. Server side and client side programming is supported, i.e. you -can build servers and resolvers with it. - -We try to keep the "master" branch as sane as possible and at the bleeding edge of standards, -avoiding breaking changes wherever reasonable. We support the last two versions of Go. - -# Goals - -* KISS; -* Fast; -* Small API. If it's easy to code in Go, don't make a function for it. - -# Users - -A not-so-up-to-date-list-that-may-be-actually-current: - -* https://github.com/coredns/coredns -* https://cloudflare.com -* https://github.com/abh/geodns -* http://www.statdns.com/ -* http://www.dnsinspect.com/ -* https://github.com/chuangbo/jianbing-dictionary-dns -* http://www.dns-lg.com/ -* https://github.com/fcambus/rrda -* https://github.com/kenshinx/godns -* https://github.com/skynetservices/skydns -* https://github.com/hashicorp/consul -* https://github.com/DevelopersPL/godnsagent -* https://github.com/duedil-ltd/discodns -* https://github.com/StalkR/dns-reverse-proxy -* https://github.com/tianon/rawdns -* https://mesosphere.github.io/mesos-dns/ -* https://pulse.turbobytes.com/ -* https://github.com/fcambus/statzone -* https://github.com/benschw/dns-clb-go -* https://github.com/corny/dnscheck for -* https://namesmith.io -* https://github.com/miekg/unbound -* https://github.com/miekg/exdns -* https://dnslookup.org -* https://github.com/looterz/grimd -* https://github.com/phamhongviet/serf-dns -* https://github.com/mehrdadrad/mylg -* https://github.com/bamarni/dockness -* https://github.com/fffaraz/microdns -* http://kelda.io -* https://github.com/ipdcode/hades -* https://github.com/StackExchange/dnscontrol/ -* https://www.dnsperf.com/ -* https://dnssectest.net/ -* https://dns.apebits.com -* https://github.com/oif/apex -* https://github.com/jedisct1/dnscrypt-proxy -* https://github.com/jedisct1/rpdns -* https://github.com/xor-gate/sshfp -* https://github.com/rs/dnstrace -* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) -* https://github.com/semihalev/sdns -* https://render.com -* https://github.com/peterzen/goresolver - -Send pull request if you want to be listed here. - -# Features - -* UDP/TCP queries, IPv4 and IPv6 -* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported -* Fast -* Server side programming (mimicking the net/http package) -* Client side programming -* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519 -* EDNS0, NSID, Cookies -* AXFR/IXFR -* TSIG, SIG(0) -* DNS over TLS (DoT): encrypted connection between client and server over TCP -* DNS name compression - -Have fun! - -Miek Gieben - 2010-2012 - -DNS Authors 2012- - -# Building - -Building is done with the `go` tool. If you have setup your GOPATH correctly, the following should -work: - - go get github.com/miekg/dns - go build github.com/miekg/dns - -## Examples - -A short "how to use the API" is at the beginning of doc.go (this also will show when you call `godoc -github.com/miekg/dns`). - -Example programs can be found in the `github.com/miekg/exdns` repository. - -## Supported RFCs - -*all of them* - -* 103{4,5} - DNS standard -* 1348 - NSAP record (removed the record) -* 1982 - Serial Arithmetic -* 1876 - LOC record -* 1995 - IXFR -* 1996 - DNS notify -* 2136 - DNS Update (dynamic updates) -* 2181 - RRset definition - there is no RRset type though, just []RR -* 2537 - RSAMD5 DNS keys -* 2065 - DNSSEC (updated in later RFCs) -* 2671 - EDNS record -* 2782 - SRV record -* 2845 - TSIG record -* 2915 - NAPTR record -* 2929 - DNS IANA Considerations -* 3110 - RSASHA1 DNS keys -* 3225 - DO bit (DNSSEC OK) -* 340{1,2,3} - NAPTR record -* 3445 - Limiting the scope of (DNS)KEY -* 3597 - Unknown RRs -* 403{3,4,5} - DNSSEC + validation functions -* 4255 - SSHFP record -* 4343 - Case insensitivity -* 4408 - SPF record -* 4509 - SHA256 Hash in DS -* 4592 - Wildcards in the DNS -* 4635 - HMAC SHA TSIG -* 4701 - DHCID -* 4892 - id.server -* 5001 - NSID -* 5155 - NSEC3 record -* 5205 - HIP record -* 5702 - SHA2 in the DNS -* 5936 - AXFR -* 5966 - TCP implementation recommendations -* 6605 - ECDSA -* 6725 - IANA Registry Update -* 6742 - ILNP DNS -* 6840 - Clarifications and Implementation Notes for DNS Security -* 6844 - CAA record -* 6891 - EDNS0 update -* 6895 - DNS IANA considerations -* 6975 - Algorithm Understanding in DNSSEC -* 7043 - EUI48/EUI64 records -* 7314 - DNS (EDNS) EXPIRE Option -* 7477 - CSYNC RR -* 7828 - edns-tcp-keepalive EDNS0 Option -* 7553 - URI record -* 7858 - DNS over TLS: Initiation and Performance Considerations -* 7871 - EDNS0 Client Subnet -* 7873 - Domain Name System (DNS) Cookies -* 8080 - EdDSA for DNSSEC -* 8499 - DNS Terminology - -## Loosely Based Upon - -* ldns - -* NSD - -* Net::DNS - -* GRONG - diff --git a/vendor/github.com/miekg/dns/acceptfunc.go b/vendor/github.com/miekg/dns/acceptfunc.go deleted file mode 100644 index 78c076c25..000000000 --- a/vendor/github.com/miekg/dns/acceptfunc.go +++ /dev/null @@ -1,56 +0,0 @@ -package dns - -// MsgAcceptFunc is used early in the server code to accept or reject a message with RcodeFormatError. -// It returns a MsgAcceptAction to indicate what should happen with the message. -type MsgAcceptFunc func(dh Header) MsgAcceptAction - -// DefaultMsgAcceptFunc checks the request and will reject if: -// -// * isn't a request (don't respond in that case). -// * opcode isn't OpcodeQuery or OpcodeNotify -// * Zero bit isn't zero -// * has more than 1 question in the question section -// * has more than 1 RR in the Answer section -// * has more than 0 RRs in the Authority section -// * has more than 2 RRs in the Additional section -var DefaultMsgAcceptFunc MsgAcceptFunc = defaultMsgAcceptFunc - -// MsgAcceptAction represents the action to be taken. -type MsgAcceptAction int - -const ( - MsgAccept MsgAcceptAction = iota // Accept the message - MsgReject // Reject the message with a RcodeFormatError - MsgIgnore // Ignore the error and send nothing back. -) - -func defaultMsgAcceptFunc(dh Header) MsgAcceptAction { - if isResponse := dh.Bits&_QR != 0; isResponse { - return MsgIgnore - } - - // Don't allow dynamic updates, because then the sections can contain a whole bunch of RRs. - opcode := int(dh.Bits>>11) & 0xF - if opcode != OpcodeQuery && opcode != OpcodeNotify { - return MsgReject - } - - if isZero := dh.Bits&_Z != 0; isZero { - return MsgReject - } - if dh.Qdcount != 1 { - return MsgReject - } - // NOTIFY requests can have a SOA in the ANSWER section. See RFC 1996 Section 3.7 and 3.11. - if dh.Ancount > 1 { - return MsgReject - } - // IXFR request could have one SOA RR in the NS section. See RFC 1995, section 3. - if dh.Nscount > 1 { - return MsgReject - } - if dh.Arcount > 2 { - return MsgReject - } - return MsgAccept -} diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go deleted file mode 100644 index b14c8d908..000000000 --- a/vendor/github.com/miekg/dns/client.go +++ /dev/null @@ -1,417 +0,0 @@ -package dns - -// A client implementation. - -import ( - "context" - "crypto/tls" - "encoding/binary" - "fmt" - "io" - "net" - "strings" - "time" -) - -const ( - dnsTimeout time.Duration = 2 * time.Second - tcpIdleTimeout time.Duration = 8 * time.Second -) - -// A Conn represents a connection to a DNS server. -type Conn struct { - net.Conn // a net.Conn holding the connection - UDPSize uint16 // minimum receive buffer for UDP messages - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - tsigRequestMAC string -} - -// A Client defines parameters for a DNS client. -type Client struct { - Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) - UDPSize uint16 // minimum receive buffer for UDP messages - TLSConfig *tls.Config // TLS connection configuration - Dialer *net.Dialer // a net.Dialer used to set local address, timeouts and more - // Timeout is a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout, - // WriteTimeout when non-zero. Can be overridden with net.Dialer.Timeout (see Client.ExchangeWithDialer and - // Client.Dialer) or context.Context.Deadline (see the deprecated ExchangeContext) - Timeout time.Duration - DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero - TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass - group singleflight -} - -// Exchange performs a synchronous UDP query. It sends the message m to the address -// contained in a and waits for a reply. Exchange does not retry a failed query, nor -// will it fall back to TCP in case of truncation. -// See client.Exchange for more information on setting larger buffer sizes. -func Exchange(m *Msg, a string) (r *Msg, err error) { - client := Client{Net: "udp"} - r, _, err = client.Exchange(m, a) - return r, err -} - -func (c *Client) dialTimeout() time.Duration { - if c.Timeout != 0 { - return c.Timeout - } - if c.DialTimeout != 0 { - return c.DialTimeout - } - return dnsTimeout -} - -func (c *Client) readTimeout() time.Duration { - if c.ReadTimeout != 0 { - return c.ReadTimeout - } - return dnsTimeout -} - -func (c *Client) writeTimeout() time.Duration { - if c.WriteTimeout != 0 { - return c.WriteTimeout - } - return dnsTimeout -} - -// Dial connects to the address on the named network. -func (c *Client) Dial(address string) (conn *Conn, err error) { - // create a new dialer with the appropriate timeout - var d net.Dialer - if c.Dialer == nil { - d = net.Dialer{Timeout: c.getTimeoutForRequest(c.dialTimeout())} - } else { - d = *c.Dialer - } - - network := c.Net - if network == "" { - network = "udp" - } - - useTLS := strings.HasPrefix(network, "tcp") && strings.HasSuffix(network, "-tls") - - conn = new(Conn) - if useTLS { - network = strings.TrimSuffix(network, "-tls") - - conn.Conn, err = tls.DialWithDialer(&d, network, address, c.TLSConfig) - } else { - conn.Conn, err = d.Dial(network, address) - } - if err != nil { - return nil, err - } - - return conn, nil -} - -// Exchange performs a synchronous query. It sends the message m to the address -// contained in a and waits for a reply. Basic use pattern with a *dns.Client: -// -// c := new(dns.Client) -// in, rtt, err := c.Exchange(message, "127.0.0.1:53") -// -// Exchange does not retry a failed query, nor will it fall back to TCP in -// case of truncation. -// It is up to the caller to create a message that allows for larger responses to be -// returned. Specifically this means adding an EDNS0 OPT RR that will advertise a larger -// buffer, see SetEdns0. Messages without an OPT RR will fallback to the historic limit -// of 512 bytes -// To specify a local address or a timeout, the caller has to set the `Client.Dialer` -// attribute appropriately -func (c *Client) Exchange(m *Msg, address string) (r *Msg, rtt time.Duration, err error) { - if !c.SingleInflight { - return c.exchange(m, address) - } - - q := m.Question[0] - key := fmt.Sprintf("%s:%d:%d", q.Name, q.Qtype, q.Qclass) - r, rtt, err, shared := c.group.Do(key, func() (*Msg, time.Duration, error) { - return c.exchange(m, address) - }) - if r != nil && shared { - r = r.Copy() - } - - return r, rtt, err -} - -func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - var co *Conn - - co, err = c.Dial(a) - - if err != nil { - return nil, 0, err - } - defer co.Close() - - opt := m.IsEdns0() - // If EDNS0 is used use that for size. - if opt != nil && opt.UDPSize() >= MinMsgSize { - co.UDPSize = opt.UDPSize() - } - // Otherwise use the client's configured UDP size. - if opt == nil && c.UDPSize >= MinMsgSize { - co.UDPSize = c.UDPSize - } - - co.TsigSecret = c.TsigSecret - t := time.Now() - // write with the appropriate write timeout - co.SetWriteDeadline(t.Add(c.getTimeoutForRequest(c.writeTimeout()))) - if err = co.WriteMsg(m); err != nil { - return nil, 0, err - } - - co.SetReadDeadline(time.Now().Add(c.getTimeoutForRequest(c.readTimeout()))) - r, err = co.ReadMsg() - if err == nil && r.Id != m.Id { - err = ErrId - } - rtt = time.Since(t) - return r, rtt, err -} - -// ReadMsg reads a message from the connection co. -// If the received message contains a TSIG record the transaction signature -// is verified. This method always tries to return the message, however if an -// error is returned there are no guarantees that the returned message is a -// valid representation of the packet read. -func (co *Conn) ReadMsg() (*Msg, error) { - p, err := co.ReadMsgHeader(nil) - if err != nil { - return nil, err - } - - m := new(Msg) - if err := m.Unpack(p); err != nil { - // If an error was returned, we still want to allow the user to use - // the message, but naively they can just check err if they don't want - // to use an erroneous message - return m, err - } - if t := m.IsTsig(); t != nil { - if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { - return m, ErrSecret - } - // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) - } - return m, err -} - -// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil). -// Returns message as a byte slice to be parsed with Msg.Unpack later on. -// Note that error handling on the message body is not possible as only the header is parsed. -func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) { - var ( - p []byte - n int - err error - ) - switch co.Conn.(type) { - case *net.TCPConn, *tls.Conn: - var length uint16 - if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil { - return nil, err - } - - p = make([]byte, length) - n, err = io.ReadFull(co.Conn, p) - default: - if co.UDPSize > MinMsgSize { - p = make([]byte, co.UDPSize) - } else { - p = make([]byte, MinMsgSize) - } - n, err = co.Read(p) - } - - if err != nil { - return nil, err - } else if n < headerSize { - return nil, ErrShortRead - } - - p = p[:n] - if hdr != nil { - dh, _, err := unpackMsgHdr(p, 0) - if err != nil { - return nil, err - } - *hdr = dh - } - return p, err -} - -// Read implements the net.Conn read method. -func (co *Conn) Read(p []byte) (n int, err error) { - if co.Conn == nil { - return 0, ErrConnEmpty - } - - switch co.Conn.(type) { - case *net.TCPConn, *tls.Conn: - var length uint16 - if err := binary.Read(co.Conn, binary.BigEndian, &length); err != nil { - return 0, err - } - if int(length) > len(p) { - return 0, io.ErrShortBuffer - } - - return io.ReadFull(co.Conn, p[:length]) - } - - // UDP connection - return co.Conn.Read(p) -} - -// WriteMsg sends a message through the connection co. -// If the message m contains a TSIG record the transaction -// signature is calculated. -func (co *Conn) WriteMsg(m *Msg) (err error) { - var out []byte - if t := m.IsTsig(); t != nil { - mac := "" - if _, ok := co.TsigSecret[t.Hdr.Name]; !ok { - return ErrSecret - } - out, mac, err = TsigGenerate(m, co.TsigSecret[t.Hdr.Name], co.tsigRequestMAC, false) - // Set for the next read, although only used in zone transfers - co.tsigRequestMAC = mac - } else { - out, err = m.Pack() - } - if err != nil { - return err - } - _, err = co.Write(out) - return err -} - -// Write implements the net.Conn Write method. -func (co *Conn) Write(p []byte) (n int, err error) { - switch co.Conn.(type) { - case *net.TCPConn, *tls.Conn: - if len(p) > MaxMsgSize { - return 0, &Error{err: "message too large"} - } - - l := make([]byte, 2) - binary.BigEndian.PutUint16(l, uint16(len(p))) - - n, err := (&net.Buffers{l, p}).WriteTo(co.Conn) - return int(n), err - } - - return co.Conn.Write(p) -} - -// Return the appropriate timeout for a specific request -func (c *Client) getTimeoutForRequest(timeout time.Duration) time.Duration { - var requestTimeout time.Duration - if c.Timeout != 0 { - requestTimeout = c.Timeout - } else { - requestTimeout = timeout - } - // net.Dialer.Timeout has priority if smaller than the timeouts computed so - // far - if c.Dialer != nil && c.Dialer.Timeout != 0 { - if c.Dialer.Timeout < requestTimeout { - requestTimeout = c.Dialer.Timeout - } - } - return requestTimeout -} - -// Dial connects to the address on the named network. -func Dial(network, address string) (conn *Conn, err error) { - conn = new(Conn) - conn.Conn, err = net.Dial(network, address) - if err != nil { - return nil, err - } - return conn, nil -} - -// ExchangeContext performs a synchronous UDP query, like Exchange. It -// additionally obeys deadlines from the passed Context. -func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) { - client := Client{Net: "udp"} - r, _, err = client.ExchangeContext(ctx, m, a) - // ignorint rtt to leave the original ExchangeContext API unchanged, but - // this function will go away - return r, err -} - -// ExchangeConn performs a synchronous query. It sends the message m via the connection -// c and waits for a reply. The connection c is not closed by ExchangeConn. -// Deprecated: This function is going away, but can easily be mimicked: -// -// co := &dns.Conn{Conn: c} // c is your net.Conn -// co.WriteMsg(m) -// in, _ := co.ReadMsg() -// co.Close() -// -func ExchangeConn(c net.Conn, m *Msg) (r *Msg, err error) { - println("dns: ExchangeConn: this function is deprecated") - co := new(Conn) - co.Conn = c - if err = co.WriteMsg(m); err != nil { - return nil, err - } - r, err = co.ReadMsg() - if err == nil && r.Id != m.Id { - err = ErrId - } - return r, err -} - -// DialTimeout acts like Dial but takes a timeout. -func DialTimeout(network, address string, timeout time.Duration) (conn *Conn, err error) { - client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}} - return client.Dial(address) -} - -// DialWithTLS connects to the address on the named network with TLS. -func DialWithTLS(network, address string, tlsConfig *tls.Config) (conn *Conn, err error) { - if !strings.HasSuffix(network, "-tls") { - network += "-tls" - } - client := Client{Net: network, TLSConfig: tlsConfig} - return client.Dial(address) -} - -// DialTimeoutWithTLS acts like DialWithTLS but takes a timeout. -func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout time.Duration) (conn *Conn, err error) { - if !strings.HasSuffix(network, "-tls") { - network += "-tls" - } - client := Client{Net: network, Dialer: &net.Dialer{Timeout: timeout}, TLSConfig: tlsConfig} - return client.Dial(address) -} - -// ExchangeContext acts like Exchange, but honors the deadline on the provided -// context, if present. If there is both a context deadline and a configured -// timeout on the client, the earliest of the two takes effect. -func (c *Client) ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, rtt time.Duration, err error) { - var timeout time.Duration - if deadline, ok := ctx.Deadline(); !ok { - timeout = 0 - } else { - timeout = time.Until(deadline) - } - // not passing the context to the underlying calls, as the API does not support - // context. For timeouts you should set up Client.Dialer and call Client.Exchange. - // TODO(tmthrgd,miekg): this is a race condition. - c.Dialer = &net.Dialer{Timeout: timeout} - return c.Exchange(m, a) -} diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go deleted file mode 100644 index e11b630df..000000000 --- a/vendor/github.com/miekg/dns/clientconfig.go +++ /dev/null @@ -1,135 +0,0 @@ -package dns - -import ( - "bufio" - "io" - "os" - "strconv" - "strings" -) - -// ClientConfig wraps the contents of the /etc/resolv.conf file. -type ClientConfig struct { - Servers []string // servers to use - Search []string // suffixes to append to local name - Port string // what port to use - Ndots int // number of dots in name to trigger absolute lookup - Timeout int // seconds before giving up on packet - Attempts int // lost packets before giving up on server, not used in the package dns -} - -// ClientConfigFromFile parses a resolv.conf(5) like file and returns -// a *ClientConfig. -func ClientConfigFromFile(resolvconf string) (*ClientConfig, error) { - file, err := os.Open(resolvconf) - if err != nil { - return nil, err - } - defer file.Close() - return ClientConfigFromReader(file) -} - -// ClientConfigFromReader works like ClientConfigFromFile but takes an io.Reader as argument -func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) { - c := new(ClientConfig) - scanner := bufio.NewScanner(resolvconf) - c.Servers = make([]string, 0) - c.Search = make([]string, 0) - c.Port = "53" - c.Ndots = 1 - c.Timeout = 5 - c.Attempts = 2 - - for scanner.Scan() { - if err := scanner.Err(); err != nil { - return nil, err - } - line := scanner.Text() - f := strings.Fields(line) - if len(f) < 1 { - continue - } - switch f[0] { - case "nameserver": // add one name server - if len(f) > 1 { - // One more check: make sure server name is - // just an IP address. Otherwise we need DNS - // to look it up. - name := f[1] - c.Servers = append(c.Servers, name) - } - - case "domain": // set search path to just this domain - if len(f) > 1 { - c.Search = make([]string, 1) - c.Search[0] = f[1] - } else { - c.Search = make([]string, 0) - } - - case "search": // set search path to given servers - c.Search = append([]string(nil), f[1:]...) - - case "options": // magic options - for _, s := range f[1:] { - switch { - case len(s) >= 6 && s[:6] == "ndots:": - n, _ := strconv.Atoi(s[6:]) - if n < 0 { - n = 0 - } else if n > 15 { - n = 15 - } - c.Ndots = n - case len(s) >= 8 && s[:8] == "timeout:": - n, _ := strconv.Atoi(s[8:]) - if n < 1 { - n = 1 - } - c.Timeout = n - case len(s) >= 9 && s[:9] == "attempts:": - n, _ := strconv.Atoi(s[9:]) - if n < 1 { - n = 1 - } - c.Attempts = n - case s == "rotate": - /* not imp */ - } - } - } - } - return c, nil -} - -// NameList returns all of the names that should be queried based on the -// config. It is based off of go's net/dns name building, but it does not -// check the length of the resulting names. -func (c *ClientConfig) NameList(name string) []string { - // if this domain is already fully qualified, no append needed. - if IsFqdn(name) { - return []string{name} - } - - // Check to see if the name has more labels than Ndots. Do this before making - // the domain fully qualified. - hasNdots := CountLabel(name) > c.Ndots - // Make the domain fully qualified. - name = Fqdn(name) - - // Make a list of names based off search. - names := []string{} - - // If name has enough dots, try that first. - if hasNdots { - names = append(names, name) - } - for _, s := range c.Search { - names = append(names, Fqdn(name+s)) - } - // If we didn't have enough dots, try after suffixes. - if !hasNdots { - names = append(names, name) - } - return names -} diff --git a/vendor/github.com/miekg/dns/dane.go b/vendor/github.com/miekg/dns/dane.go deleted file mode 100644 index 8c4a14ef1..000000000 --- a/vendor/github.com/miekg/dns/dane.go +++ /dev/null @@ -1,43 +0,0 @@ -package dns - -import ( - "crypto/sha256" - "crypto/sha512" - "crypto/x509" - "encoding/hex" - "errors" -) - -// CertificateToDANE converts a certificate to a hex string as used in the TLSA or SMIMEA records. -func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) (string, error) { - switch matchingType { - case 0: - switch selector { - case 0: - return hex.EncodeToString(cert.Raw), nil - case 1: - return hex.EncodeToString(cert.RawSubjectPublicKeyInfo), nil - } - case 1: - h := sha256.New() - switch selector { - case 0: - h.Write(cert.Raw) - return hex.EncodeToString(h.Sum(nil)), nil - case 1: - h.Write(cert.RawSubjectPublicKeyInfo) - return hex.EncodeToString(h.Sum(nil)), nil - } - case 2: - h := sha512.New() - switch selector { - case 0: - h.Write(cert.Raw) - return hex.EncodeToString(h.Sum(nil)), nil - case 1: - h.Write(cert.RawSubjectPublicKeyInfo) - return hex.EncodeToString(h.Sum(nil)), nil - } - } - return "", errors.New("dns: bad MatchingType or Selector") -} diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go deleted file mode 100644 index b059f6fc6..000000000 --- a/vendor/github.com/miekg/dns/defaults.go +++ /dev/null @@ -1,378 +0,0 @@ -package dns - -import ( - "errors" - "net" - "strconv" - "strings" -) - -const hexDigit = "0123456789abcdef" - -// Everything is assumed in ClassINET. - -// SetReply creates a reply message from a request message. -func (dns *Msg) SetReply(request *Msg) *Msg { - dns.Id = request.Id - dns.Response = true - dns.Opcode = request.Opcode - if dns.Opcode == OpcodeQuery { - dns.RecursionDesired = request.RecursionDesired // Copy rd bit - dns.CheckingDisabled = request.CheckingDisabled // Copy cd bit - } - dns.Rcode = RcodeSuccess - if len(request.Question) > 0 { - dns.Question = make([]Question, 1) - dns.Question[0] = request.Question[0] - } - return dns -} - -// SetQuestion creates a question message, it sets the Question -// section, generates an Id and sets the RecursionDesired (RD) -// bit to true. -func (dns *Msg) SetQuestion(z string, t uint16) *Msg { - dns.Id = Id() - dns.RecursionDesired = true - dns.Question = make([]Question, 1) - dns.Question[0] = Question{z, t, ClassINET} - return dns -} - -// SetNotify creates a notify message, it sets the Question -// section, generates an Id and sets the Authoritative (AA) -// bit to true. -func (dns *Msg) SetNotify(z string) *Msg { - dns.Opcode = OpcodeNotify - dns.Authoritative = true - dns.Id = Id() - dns.Question = make([]Question, 1) - dns.Question[0] = Question{z, TypeSOA, ClassINET} - return dns -} - -// SetRcode creates an error message suitable for the request. -func (dns *Msg) SetRcode(request *Msg, rcode int) *Msg { - dns.SetReply(request) - dns.Rcode = rcode - return dns -} - -// SetRcodeFormatError creates a message with FormError set. -func (dns *Msg) SetRcodeFormatError(request *Msg) *Msg { - dns.Rcode = RcodeFormatError - dns.Opcode = OpcodeQuery - dns.Response = true - dns.Authoritative = false - dns.Id = request.Id - return dns -} - -// SetUpdate makes the message a dynamic update message. It -// sets the ZONE section to: z, TypeSOA, ClassINET. -func (dns *Msg) SetUpdate(z string) *Msg { - dns.Id = Id() - dns.Response = false - dns.Opcode = OpcodeUpdate - dns.Compress = false // BIND9 cannot handle compression - dns.Question = make([]Question, 1) - dns.Question[0] = Question{z, TypeSOA, ClassINET} - return dns -} - -// SetIxfr creates message for requesting an IXFR. -func (dns *Msg) SetIxfr(z string, serial uint32, ns, mbox string) *Msg { - dns.Id = Id() - dns.Question = make([]Question, 1) - dns.Ns = make([]RR, 1) - s := new(SOA) - s.Hdr = RR_Header{z, TypeSOA, ClassINET, defaultTtl, 0} - s.Serial = serial - s.Ns = ns - s.Mbox = mbox - dns.Question[0] = Question{z, TypeIXFR, ClassINET} - dns.Ns[0] = s - return dns -} - -// SetAxfr creates message for requesting an AXFR. -func (dns *Msg) SetAxfr(z string) *Msg { - dns.Id = Id() - dns.Question = make([]Question, 1) - dns.Question[0] = Question{z, TypeAXFR, ClassINET} - return dns -} - -// SetTsig appends a TSIG RR to the message. -// This is only a skeleton TSIG RR that is added as the last RR in the -// additional section. The Tsig is calculated when the message is being send. -func (dns *Msg) SetTsig(z, algo string, fudge uint16, timesigned int64) *Msg { - t := new(TSIG) - t.Hdr = RR_Header{z, TypeTSIG, ClassANY, 0, 0} - t.Algorithm = algo - t.Fudge = fudge - t.TimeSigned = uint64(timesigned) - t.OrigId = dns.Id - dns.Extra = append(dns.Extra, t) - return dns -} - -// SetEdns0 appends a EDNS0 OPT RR to the message. -// TSIG should always the last RR in a message. -func (dns *Msg) SetEdns0(udpsize uint16, do bool) *Msg { - e := new(OPT) - e.Hdr.Name = "." - e.Hdr.Rrtype = TypeOPT - e.SetUDPSize(udpsize) - if do { - e.SetDo() - } - dns.Extra = append(dns.Extra, e) - return dns -} - -// IsTsig checks if the message has a TSIG record as the last record -// in the additional section. It returns the TSIG record found or nil. -func (dns *Msg) IsTsig() *TSIG { - if len(dns.Extra) > 0 { - if dns.Extra[len(dns.Extra)-1].Header().Rrtype == TypeTSIG { - return dns.Extra[len(dns.Extra)-1].(*TSIG) - } - } - return nil -} - -// IsEdns0 checks if the message has a EDNS0 (OPT) record, any EDNS0 -// record in the additional section will do. It returns the OPT record -// found or nil. -func (dns *Msg) IsEdns0() *OPT { - // RFC 6891, Section 6.1.1 allows the OPT record to appear - // anywhere in the additional record section, but it's usually at - // the end so start there. - for i := len(dns.Extra) - 1; i >= 0; i-- { - if dns.Extra[i].Header().Rrtype == TypeOPT { - return dns.Extra[i].(*OPT) - } - } - return nil -} - -// popEdns0 is like IsEdns0, but it removes the record from the message. -func (dns *Msg) popEdns0() *OPT { - // RFC 6891, Section 6.1.1 allows the OPT record to appear - // anywhere in the additional record section, but it's usually at - // the end so start there. - for i := len(dns.Extra) - 1; i >= 0; i-- { - if dns.Extra[i].Header().Rrtype == TypeOPT { - opt := dns.Extra[i].(*OPT) - dns.Extra = append(dns.Extra[:i], dns.Extra[i+1:]...) - return opt - } - } - return nil -} - -// IsDomainName checks if s is a valid domain name, it returns the number of -// labels and true, when a domain name is valid. Note that non fully qualified -// domain name is considered valid, in this case the last label is counted in -// the number of labels. When false is returned the number of labels is not -// defined. Also note that this function is extremely liberal; almost any -// string is a valid domain name as the DNS is 8 bit protocol. It checks if each -// label fits in 63 characters and that the entire name will fit into the 255 -// octet wire format limit. -func IsDomainName(s string) (labels int, ok bool) { - // XXX: The logic in this function was copied from packDomainName and - // should be kept in sync with that function. - - const lenmsg = 256 - - if len(s) == 0 { // Ok, for instance when dealing with update RR without any rdata. - return 0, false - } - - s = Fqdn(s) - - // Each dot ends a segment of the name. Except for escaped dots (\.), which - // are normal dots. - - var ( - off int - begin int - wasDot bool - ) - for i := 0; i < len(s); i++ { - switch s[i] { - case '\\': - if off+1 > lenmsg { - return labels, false - } - - // check for \DDD - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { - i += 3 - begin += 3 - } else { - i++ - begin++ - } - - wasDot = false - case '.': - if wasDot { - // two dots back to back is not legal - return labels, false - } - wasDot = true - - labelLen := i - begin - if labelLen >= 1<<6 { // top two bits of length must be clear - return labels, false - } - - // off can already (we're in a loop) be bigger than lenmsg - // this happens when a name isn't fully qualified - off += 1 + labelLen - if off > lenmsg { - return labels, false - } - - labels++ - begin = i + 1 - default: - wasDot = false - } - } - - return labels, true -} - -// IsSubDomain checks if child is indeed a child of the parent. If child and parent -// are the same domain true is returned as well. -func IsSubDomain(parent, child string) bool { - // Entire child is contained in parent - return CompareDomainName(parent, child) == CountLabel(parent) -} - -// IsMsg sanity checks buf and returns an error if it isn't a valid DNS packet. -// The checking is performed on the binary payload. -func IsMsg(buf []byte) error { - // Header - if len(buf) < headerSize { - return errors.New("dns: bad message header") - } - // Header: Opcode - // TODO(miek): more checks here, e.g. check all header bits. - return nil -} - -// IsFqdn checks if a domain name is fully qualified. -func IsFqdn(s string) bool { - s2 := strings.TrimSuffix(s, ".") - if s == s2 { - return false - } - - i := strings.LastIndexFunc(s2, func(r rune) bool { - return r != '\\' - }) - - // Test whether we have an even number of escape sequences before - // the dot or none. - return (len(s2)-i)%2 != 0 -} - -// IsRRset checks if a set of RRs is a valid RRset as defined by RFC 2181. -// This means the RRs need to have the same type, name, and class. Returns true -// if the RR set is valid, otherwise false. -func IsRRset(rrset []RR) bool { - if len(rrset) == 0 { - return false - } - if len(rrset) == 1 { - return true - } - rrHeader := rrset[0].Header() - rrType := rrHeader.Rrtype - rrClass := rrHeader.Class - rrName := rrHeader.Name - - for _, rr := range rrset[1:] { - curRRHeader := rr.Header() - if curRRHeader.Rrtype != rrType || curRRHeader.Class != rrClass || curRRHeader.Name != rrName { - // Mismatch between the records, so this is not a valid rrset for - //signing/verifying - return false - } - } - - return true -} - -// Fqdn return the fully qualified domain name from s. -// If s is already fully qualified, it behaves as the identity function. -func Fqdn(s string) string { - if IsFqdn(s) { - return s - } - return s + "." -} - -// Copied from the official Go code. - -// ReverseAddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP -// address suitable for reverse DNS (PTR) record lookups or an error if it fails -// to parse the IP address. -func ReverseAddr(addr string) (arpa string, err error) { - ip := net.ParseIP(addr) - if ip == nil { - return "", &Error{err: "unrecognized address: " + addr} - } - if v4 := ip.To4(); v4 != nil { - buf := make([]byte, 0, net.IPv4len*4+len("in-addr.arpa.")) - // Add it, in reverse, to the buffer - for i := len(v4) - 1; i >= 0; i-- { - buf = strconv.AppendInt(buf, int64(v4[i]), 10) - buf = append(buf, '.') - } - // Append "in-addr.arpa." and return (buf already has the final .) - buf = append(buf, "in-addr.arpa."...) - return string(buf), nil - } - // Must be IPv6 - buf := make([]byte, 0, net.IPv6len*4+len("ip6.arpa.")) - // Add it, in reverse, to the buffer - for i := len(ip) - 1; i >= 0; i-- { - v := ip[i] - buf = append(buf, hexDigit[v&0xF]) - buf = append(buf, '.') - buf = append(buf, hexDigit[v>>4]) - buf = append(buf, '.') - } - // Append "ip6.arpa." and return (buf already has the final .) - buf = append(buf, "ip6.arpa."...) - return string(buf), nil -} - -// String returns the string representation for the type t. -func (t Type) String() string { - if t1, ok := TypeToString[uint16(t)]; ok { - return t1 - } - return "TYPE" + strconv.Itoa(int(t)) -} - -// String returns the string representation for the class c. -func (c Class) String() string { - if s, ok := ClassToString[uint16(c)]; ok { - // Only emit mnemonics when they are unambiguous, specically ANY is in both. - if _, ok := StringToType[s]; !ok { - return s - } - } - return "CLASS" + strconv.Itoa(int(c)) -} - -// String returns the string representation for the name n. -func (n Name) String() string { - return sprintName(string(n)) -} diff --git a/vendor/github.com/miekg/dns/dns.go b/vendor/github.com/miekg/dns/dns.go deleted file mode 100644 index f57337b89..000000000 --- a/vendor/github.com/miekg/dns/dns.go +++ /dev/null @@ -1,134 +0,0 @@ -package dns - -import "strconv" - -const ( - year68 = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits. - defaultTtl = 3600 // Default internal TTL. - - // DefaultMsgSize is the standard default for messages larger than 512 bytes. - DefaultMsgSize = 4096 - // MinMsgSize is the minimal size of a DNS packet. - MinMsgSize = 512 - // MaxMsgSize is the largest possible DNS packet. - MaxMsgSize = 65535 -) - -// Error represents a DNS error. -type Error struct{ err string } - -func (e *Error) Error() string { - if e == nil { - return "dns: " - } - return "dns: " + e.err -} - -// An RR represents a resource record. -type RR interface { - // Header returns the header of an resource record. The header contains - // everything up to the rdata. - Header() *RR_Header - // String returns the text representation of the resource record. - String() string - - // copy returns a copy of the RR - copy() RR - - // len returns the length (in octets) of the compressed or uncompressed RR in wire format. - // - // If compression is nil, the uncompressed size will be returned, otherwise the compressed - // size will be returned and domain names will be added to the map for future compression. - len(off int, compression map[string]struct{}) int - - // pack packs the records RDATA into wire format. The header will - // already have been packed into msg. - pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) - - // unpack unpacks an RR from wire format. - // - // This will only be called on a new and empty RR type with only the header populated. It - // will only be called if the record's RDATA is non-empty. - unpack(msg []byte, off int) (off1 int, err error) - - // parse parses an RR from zone file format. - // - // This will only be called on a new and empty RR type with only the header populated. - parse(c *zlexer, origin, file string) *ParseError - - // isDuplicate returns whether the two RRs are duplicates. - isDuplicate(r2 RR) bool -} - -// RR_Header is the header all DNS resource records share. -type RR_Header struct { - Name string `dns:"cdomain-name"` - Rrtype uint16 - Class uint16 - Ttl uint32 - Rdlength uint16 // Length of data after header. -} - -// Header returns itself. This is here to make RR_Header implements the RR interface. -func (h *RR_Header) Header() *RR_Header { return h } - -// Just to implement the RR interface. -func (h *RR_Header) copy() RR { return nil } - -func (h *RR_Header) String() string { - var s string - - if h.Rrtype == TypeOPT { - s = ";" - // and maybe other things - } - - s += sprintName(h.Name) + "\t" - s += strconv.FormatInt(int64(h.Ttl), 10) + "\t" - s += Class(h.Class).String() + "\t" - s += Type(h.Rrtype).String() + "\t" - return s -} - -func (h *RR_Header) len(off int, compression map[string]struct{}) int { - l := domainNameLen(h.Name, off, compression, true) - l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2) - return l -} - -func (h *RR_Header) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - // RR_Header has no RDATA to pack. - return off, nil -} - -func (h *RR_Header) unpack(msg []byte, off int) (int, error) { - panic("dns: internal error: unpack should never be called on RR_Header") -} - -func (h *RR_Header) parse(c *zlexer, origin, file string) *ParseError { - panic("dns: internal error: parse should never be called on RR_Header") -} - -// ToRFC3597 converts a known RR to the unknown RR representation from RFC 3597. -func (rr *RFC3597) ToRFC3597(r RR) error { - buf := make([]byte, Len(r)*2) - headerEnd, off, err := packRR(r, buf, 0, compressionMap{}, false) - if err != nil { - return err - } - buf = buf[:off] - - *rr = RFC3597{Hdr: *r.Header()} - rr.Hdr.Rdlength = uint16(off - headerEnd) - - if noRdata(rr.Hdr) { - return nil - } - - _, err = rr.unpack(buf, headerEnd) - if err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go deleted file mode 100644 index faf1dc659..000000000 --- a/vendor/github.com/miekg/dns/dnssec.go +++ /dev/null @@ -1,791 +0,0 @@ -package dns - -import ( - "bytes" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - _ "crypto/md5" - "crypto/rand" - "crypto/rsa" - _ "crypto/sha1" - _ "crypto/sha256" - _ "crypto/sha512" - "encoding/asn1" - "encoding/binary" - "encoding/hex" - "math/big" - "sort" - "strings" - "time" - - "golang.org/x/crypto/ed25519" -) - -// DNSSEC encryption algorithm codes. -const ( - _ uint8 = iota - RSAMD5 - DH - DSA - _ // Skip 4, RFC 6725, section 2.1 - RSASHA1 - DSANSEC3SHA1 - RSASHA1NSEC3SHA1 - RSASHA256 - _ // Skip 9, RFC 6725, section 2.1 - RSASHA512 - _ // Skip 11, RFC 6725, section 2.1 - ECCGOST - ECDSAP256SHA256 - ECDSAP384SHA384 - ED25519 - ED448 - INDIRECT uint8 = 252 - PRIVATEDNS uint8 = 253 // Private (experimental keys) - PRIVATEOID uint8 = 254 -) - -// AlgorithmToString is a map of algorithm IDs to algorithm names. -var AlgorithmToString = map[uint8]string{ - RSAMD5: "RSAMD5", - DH: "DH", - DSA: "DSA", - RSASHA1: "RSASHA1", - DSANSEC3SHA1: "DSA-NSEC3-SHA1", - RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1", - RSASHA256: "RSASHA256", - RSASHA512: "RSASHA512", - ECCGOST: "ECC-GOST", - ECDSAP256SHA256: "ECDSAP256SHA256", - ECDSAP384SHA384: "ECDSAP384SHA384", - ED25519: "ED25519", - ED448: "ED448", - INDIRECT: "INDIRECT", - PRIVATEDNS: "PRIVATEDNS", - PRIVATEOID: "PRIVATEOID", -} - -// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's. -var AlgorithmToHash = map[uint8]crypto.Hash{ - RSAMD5: crypto.MD5, // Deprecated in RFC 6725 - DSA: crypto.SHA1, - RSASHA1: crypto.SHA1, - RSASHA1NSEC3SHA1: crypto.SHA1, - RSASHA256: crypto.SHA256, - ECDSAP256SHA256: crypto.SHA256, - ECDSAP384SHA384: crypto.SHA384, - RSASHA512: crypto.SHA512, - ED25519: crypto.Hash(0), -} - -// DNSSEC hashing algorithm codes. -const ( - _ uint8 = iota - SHA1 // RFC 4034 - SHA256 // RFC 4509 - GOST94 // RFC 5933 - SHA384 // Experimental - SHA512 // Experimental -) - -// HashToString is a map of hash IDs to names. -var HashToString = map[uint8]string{ - SHA1: "SHA1", - SHA256: "SHA256", - GOST94: "GOST94", - SHA384: "SHA384", - SHA512: "SHA512", -} - -// DNSKEY flag values. -const ( - SEP = 1 - REVOKE = 1 << 7 - ZONE = 1 << 8 -) - -// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing. -type rrsigWireFmt struct { - TypeCovered uint16 - Algorithm uint8 - Labels uint8 - OrigTtl uint32 - Expiration uint32 - Inception uint32 - KeyTag uint16 - SignerName string `dns:"domain-name"` - /* No Signature */ -} - -// Used for converting DNSKEY's rdata to wirefmt. -type dnskeyWireFmt struct { - Flags uint16 - Protocol uint8 - Algorithm uint8 - PublicKey string `dns:"base64"` - /* Nothing is left out */ -} - -func divRoundUp(a, b int) int { - return (a + b - 1) / b -} - -// KeyTag calculates the keytag (or key-id) of the DNSKEY. -func (k *DNSKEY) KeyTag() uint16 { - if k == nil { - return 0 - } - var keytag int - switch k.Algorithm { - case RSAMD5: - // Look at the bottom two bytes of the modules, which the last - // item in the pubkey. We could do this faster by looking directly - // at the base64 values. But I'm lazy. - modulus, _ := fromBase64([]byte(k.PublicKey)) - if len(modulus) > 1 { - x := binary.BigEndian.Uint16(modulus[len(modulus)-2:]) - keytag = int(x) - } - default: - keywire := new(dnskeyWireFmt) - keywire.Flags = k.Flags - keywire.Protocol = k.Protocol - keywire.Algorithm = k.Algorithm - keywire.PublicKey = k.PublicKey - wire := make([]byte, DefaultMsgSize) - n, err := packKeyWire(keywire, wire) - if err != nil { - return 0 - } - wire = wire[:n] - for i, v := range wire { - if i&1 != 0 { - keytag += int(v) // must be larger than uint32 - } else { - keytag += int(v) << 8 - } - } - keytag += keytag >> 16 & 0xFFFF - keytag &= 0xFFFF - } - return uint16(keytag) -} - -// ToDS converts a DNSKEY record to a DS record. -func (k *DNSKEY) ToDS(h uint8) *DS { - if k == nil { - return nil - } - ds := new(DS) - ds.Hdr.Name = k.Hdr.Name - ds.Hdr.Class = k.Hdr.Class - ds.Hdr.Rrtype = TypeDS - ds.Hdr.Ttl = k.Hdr.Ttl - ds.Algorithm = k.Algorithm - ds.DigestType = h - ds.KeyTag = k.KeyTag() - - keywire := new(dnskeyWireFmt) - keywire.Flags = k.Flags - keywire.Protocol = k.Protocol - keywire.Algorithm = k.Algorithm - keywire.PublicKey = k.PublicKey - wire := make([]byte, DefaultMsgSize) - n, err := packKeyWire(keywire, wire) - if err != nil { - return nil - } - wire = wire[:n] - - owner := make([]byte, 255) - off, err1 := PackDomainName(strings.ToLower(k.Hdr.Name), owner, 0, nil, false) - if err1 != nil { - return nil - } - owner = owner[:off] - // RFC4034: - // digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); - // "|" denotes concatenation - // DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. - - var hash crypto.Hash - switch h { - case SHA1: - hash = crypto.SHA1 - case SHA256: - hash = crypto.SHA256 - case SHA384: - hash = crypto.SHA384 - case SHA512: - hash = crypto.SHA512 - default: - return nil - } - - s := hash.New() - s.Write(owner) - s.Write(wire) - ds.Digest = hex.EncodeToString(s.Sum(nil)) - return ds -} - -// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record. -func (k *DNSKEY) ToCDNSKEY() *CDNSKEY { - c := &CDNSKEY{DNSKEY: *k} - c.Hdr = k.Hdr - c.Hdr.Rrtype = TypeCDNSKEY - return c -} - -// ToCDS converts a DS record to a CDS record. -func (d *DS) ToCDS() *CDS { - c := &CDS{DS: *d} - c.Hdr = d.Hdr - c.Hdr.Rrtype = TypeCDS - return c -} - -// Sign signs an RRSet. The signature needs to be filled in with the values: -// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied -// from the RRset. Sign returns a non-nill error when the signing went OK. -// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non -// zero, it is used as-is, otherwise the TTL of the RRset is used as the -// OrigTTL. -func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error { - if k == nil { - return ErrPrivKey - } - // s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set - if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { - return ErrKey - } - - h0 := rrset[0].Header() - rr.Hdr.Rrtype = TypeRRSIG - rr.Hdr.Name = h0.Name - rr.Hdr.Class = h0.Class - if rr.OrigTtl == 0 { // If set don't override - rr.OrigTtl = h0.Ttl - } - rr.TypeCovered = h0.Rrtype - rr.Labels = uint8(CountLabel(h0.Name)) - - if strings.HasPrefix(h0.Name, "*") { - rr.Labels-- // wildcard, remove from label count - } - - sigwire := new(rrsigWireFmt) - sigwire.TypeCovered = rr.TypeCovered - sigwire.Algorithm = rr.Algorithm - sigwire.Labels = rr.Labels - sigwire.OrigTtl = rr.OrigTtl - sigwire.Expiration = rr.Expiration - sigwire.Inception = rr.Inception - sigwire.KeyTag = rr.KeyTag - // For signing, lowercase this name - sigwire.SignerName = strings.ToLower(rr.SignerName) - - // Create the desired binary blob - signdata := make([]byte, DefaultMsgSize) - n, err := packSigWire(sigwire, signdata) - if err != nil { - return err - } - signdata = signdata[:n] - wire, err := rawSignatureData(rrset, rr) - if err != nil { - return err - } - - hash, ok := AlgorithmToHash[rr.Algorithm] - if !ok { - return ErrAlg - } - - switch rr.Algorithm { - case ED25519: - // ed25519 signs the raw message and performs hashing internally. - // All other supported signature schemes operate over the pre-hashed - // message, and thus ed25519 must be handled separately here. - // - // The raw message is passed directly into sign and crypto.Hash(0) is - // used to signal to the crypto.Signer that the data has not been hashed. - signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm) - if err != nil { - return err - } - - rr.Signature = toBase64(signature) - default: - h := hash.New() - h.Write(signdata) - h.Write(wire) - - signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm) - if err != nil { - return err - } - - rr.Signature = toBase64(signature) - } - - return nil -} - -func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) { - signature, err := k.Sign(rand.Reader, hashed, hash) - if err != nil { - return nil, err - } - - switch alg { - case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512: - return signature, nil - - case ECDSAP256SHA256, ECDSAP384SHA384: - ecdsaSignature := &struct { - R, S *big.Int - }{} - if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil { - return nil, err - } - - var intlen int - switch alg { - case ECDSAP256SHA256: - intlen = 32 - case ECDSAP384SHA384: - intlen = 48 - } - - signature := intToBytes(ecdsaSignature.R, intlen) - signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...) - return signature, nil - - // There is no defined interface for what a DSA backed crypto.Signer returns - case DSA, DSANSEC3SHA1: - // t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8) - // signature := []byte{byte(t)} - // signature = append(signature, intToBytes(r1, 20)...) - // signature = append(signature, intToBytes(s1, 20)...) - // rr.Signature = signature - - case ED25519: - return signature, nil - } - - return nil, ErrAlg -} - -// Verify validates an RRSet with the signature and key. This is only the -// cryptographic test, the signature validity period must be checked separately. -// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work. -func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error { - // First the easy checks - if !IsRRset(rrset) { - return ErrRRset - } - if rr.KeyTag != k.KeyTag() { - return ErrKey - } - if rr.Hdr.Class != k.Hdr.Class { - return ErrKey - } - if rr.Algorithm != k.Algorithm { - return ErrKey - } - if !strings.EqualFold(rr.SignerName, k.Hdr.Name) { - return ErrKey - } - if k.Protocol != 3 { - return ErrKey - } - - // IsRRset checked that we have at least one RR and that the RRs in - // the set have consistent type, class, and name. Also check that type and - // class matches the RRSIG record. - if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered { - return ErrRRset - } - - // RFC 4035 5.3.2. Reconstructing the Signed Data - // Copy the sig, except the rrsig data - sigwire := new(rrsigWireFmt) - sigwire.TypeCovered = rr.TypeCovered - sigwire.Algorithm = rr.Algorithm - sigwire.Labels = rr.Labels - sigwire.OrigTtl = rr.OrigTtl - sigwire.Expiration = rr.Expiration - sigwire.Inception = rr.Inception - sigwire.KeyTag = rr.KeyTag - sigwire.SignerName = strings.ToLower(rr.SignerName) - // Create the desired binary blob - signeddata := make([]byte, DefaultMsgSize) - n, err := packSigWire(sigwire, signeddata) - if err != nil { - return err - } - signeddata = signeddata[:n] - wire, err := rawSignatureData(rrset, rr) - if err != nil { - return err - } - - sigbuf := rr.sigBuf() // Get the binary signature data - if rr.Algorithm == PRIVATEDNS { // PRIVATEOID - // TODO(miek) - // remove the domain name and assume its ours? - } - - hash, ok := AlgorithmToHash[rr.Algorithm] - if !ok { - return ErrAlg - } - - switch rr.Algorithm { - case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5: - // TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere?? - pubkey := k.publicKeyRSA() // Get the key - if pubkey == nil { - return ErrKey - } - - h := hash.New() - h.Write(signeddata) - h.Write(wire) - return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf) - - case ECDSAP256SHA256, ECDSAP384SHA384: - pubkey := k.publicKeyECDSA() - if pubkey == nil { - return ErrKey - } - - // Split sigbuf into the r and s coordinates - r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2]) - s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:]) - - h := hash.New() - h.Write(signeddata) - h.Write(wire) - if ecdsa.Verify(pubkey, h.Sum(nil), r, s) { - return nil - } - return ErrSig - - case ED25519: - pubkey := k.publicKeyED25519() - if pubkey == nil { - return ErrKey - } - - if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) { - return nil - } - return ErrSig - - default: - return ErrAlg - } -} - -// ValidityPeriod uses RFC1982 serial arithmetic to calculate -// if a signature period is valid. If t is the zero time, the -// current time is taken other t is. Returns true if the signature -// is valid at the given time, otherwise returns false. -func (rr *RRSIG) ValidityPeriod(t time.Time) bool { - var utc int64 - if t.IsZero() { - utc = time.Now().UTC().Unix() - } else { - utc = t.UTC().Unix() - } - modi := (int64(rr.Inception) - utc) / year68 - mode := (int64(rr.Expiration) - utc) / year68 - ti := int64(rr.Inception) + modi*year68 - te := int64(rr.Expiration) + mode*year68 - return ti <= utc && utc <= te -} - -// Return the signatures base64 encodedig sigdata as a byte slice. -func (rr *RRSIG) sigBuf() []byte { - sigbuf, err := fromBase64([]byte(rr.Signature)) - if err != nil { - return nil - } - return sigbuf -} - -// publicKeyRSA returns the RSA public key from a DNSKEY record. -func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey { - keybuf, err := fromBase64([]byte(k.PublicKey)) - if err != nil { - return nil - } - - if len(keybuf) < 1+1+64 { - // Exponent must be at least 1 byte and modulus at least 64 - return nil - } - - // RFC 2537/3110, section 2. RSA Public KEY Resource Records - // Length is in the 0th byte, unless its zero, then it - // it in bytes 1 and 2 and its a 16 bit number - explen := uint16(keybuf[0]) - keyoff := 1 - if explen == 0 { - explen = uint16(keybuf[1])<<8 | uint16(keybuf[2]) - keyoff = 3 - } - - if explen > 4 || explen == 0 || keybuf[keyoff] == 0 { - // Exponent larger than supported by the crypto package, - // empty, or contains prohibited leading zero. - return nil - } - - modoff := keyoff + int(explen) - modlen := len(keybuf) - modoff - if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 { - // Modulus is too small, large, or contains prohibited leading zero. - return nil - } - - pubkey := new(rsa.PublicKey) - - var expo uint64 - // The exponent of length explen is between keyoff and modoff. - for _, v := range keybuf[keyoff:modoff] { - expo <<= 8 - expo |= uint64(v) - } - if expo > 1<<31-1 { - // Larger exponent than supported by the crypto package. - return nil - } - - pubkey.E = int(expo) - pubkey.N = new(big.Int).SetBytes(keybuf[modoff:]) - return pubkey -} - -// publicKeyECDSA returns the Curve public key from the DNSKEY record. -func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey { - keybuf, err := fromBase64([]byte(k.PublicKey)) - if err != nil { - return nil - } - pubkey := new(ecdsa.PublicKey) - switch k.Algorithm { - case ECDSAP256SHA256: - pubkey.Curve = elliptic.P256() - if len(keybuf) != 64 { - // wrongly encoded key - return nil - } - case ECDSAP384SHA384: - pubkey.Curve = elliptic.P384() - if len(keybuf) != 96 { - // Wrongly encoded key - return nil - } - } - pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2]) - pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:]) - return pubkey -} - -func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey { - keybuf, err := fromBase64([]byte(k.PublicKey)) - if err != nil { - return nil - } - if len(keybuf) < 22 { - return nil - } - t, keybuf := int(keybuf[0]), keybuf[1:] - size := 64 + t*8 - q, keybuf := keybuf[:20], keybuf[20:] - if len(keybuf) != 3*size { - return nil - } - p, keybuf := keybuf[:size], keybuf[size:] - g, y := keybuf[:size], keybuf[size:] - pubkey := new(dsa.PublicKey) - pubkey.Parameters.Q = new(big.Int).SetBytes(q) - pubkey.Parameters.P = new(big.Int).SetBytes(p) - pubkey.Parameters.G = new(big.Int).SetBytes(g) - pubkey.Y = new(big.Int).SetBytes(y) - return pubkey -} - -func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey { - keybuf, err := fromBase64([]byte(k.PublicKey)) - if err != nil { - return nil - } - if len(keybuf) != ed25519.PublicKeySize { - return nil - } - return keybuf -} - -type wireSlice [][]byte - -func (p wireSlice) Len() int { return len(p) } -func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p wireSlice) Less(i, j int) bool { - _, ioff, _ := UnpackDomainName(p[i], 0) - _, joff, _ := UnpackDomainName(p[j], 0) - return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0 -} - -// Return the raw signature data. -func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) { - wires := make(wireSlice, len(rrset)) - for i, r := range rrset { - r1 := r.copy() - h := r1.Header() - h.Ttl = s.OrigTtl - labels := SplitDomainName(h.Name) - // 6.2. Canonical RR Form. (4) - wildcards - if len(labels) > int(s.Labels) { - // Wildcard - h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "." - } - // RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase - h.Name = strings.ToLower(h.Name) - // 6.2. Canonical RR Form. (3) - domain rdata to lowercase. - // NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, - // HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, - // SRV, DNAME, A6 - // - // RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC): - // Section 6.2 of [RFC4034] also erroneously lists HINFO as a record - // that needs conversion to lowercase, and twice at that. Since HINFO - // records contain no domain names, they are not subject to case - // conversion. - switch x := r1.(type) { - case *NS: - x.Ns = strings.ToLower(x.Ns) - case *MD: - x.Md = strings.ToLower(x.Md) - case *MF: - x.Mf = strings.ToLower(x.Mf) - case *CNAME: - x.Target = strings.ToLower(x.Target) - case *SOA: - x.Ns = strings.ToLower(x.Ns) - x.Mbox = strings.ToLower(x.Mbox) - case *MB: - x.Mb = strings.ToLower(x.Mb) - case *MG: - x.Mg = strings.ToLower(x.Mg) - case *MR: - x.Mr = strings.ToLower(x.Mr) - case *PTR: - x.Ptr = strings.ToLower(x.Ptr) - case *MINFO: - x.Rmail = strings.ToLower(x.Rmail) - x.Email = strings.ToLower(x.Email) - case *MX: - x.Mx = strings.ToLower(x.Mx) - case *RP: - x.Mbox = strings.ToLower(x.Mbox) - x.Txt = strings.ToLower(x.Txt) - case *AFSDB: - x.Hostname = strings.ToLower(x.Hostname) - case *RT: - x.Host = strings.ToLower(x.Host) - case *SIG: - x.SignerName = strings.ToLower(x.SignerName) - case *PX: - x.Map822 = strings.ToLower(x.Map822) - x.Mapx400 = strings.ToLower(x.Mapx400) - case *NAPTR: - x.Replacement = strings.ToLower(x.Replacement) - case *KX: - x.Exchanger = strings.ToLower(x.Exchanger) - case *SRV: - x.Target = strings.ToLower(x.Target) - case *DNAME: - x.Target = strings.ToLower(x.Target) - } - // 6.2. Canonical RR Form. (5) - origTTL - wire := make([]byte, Len(r1)+1) // +1 to be safe(r) - off, err1 := PackRR(r1, wire, 0, nil, false) - if err1 != nil { - return nil, err1 - } - wire = wire[:off] - wires[i] = wire - } - sort.Sort(wires) - for i, wire := range wires { - if i > 0 && bytes.Equal(wire, wires[i-1]) { - continue - } - buf = append(buf, wire...) - } - return buf, nil -} - -func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) { - // copied from zmsg.go RRSIG packing - off, err := packUint16(sw.TypeCovered, msg, 0) - if err != nil { - return off, err - } - off, err = packUint8(sw.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(sw.Labels, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(sw.OrigTtl, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(sw.Expiration, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(sw.Inception, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(sw.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = PackDomainName(sw.SignerName, msg, off, nil, false) - if err != nil { - return off, err - } - return off, nil -} - -func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) { - // copied from zmsg.go DNSKEY packing - off, err := packUint16(dw.Flags, msg, 0) - if err != nil { - return off, err - } - off, err = packUint8(dw.Protocol, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(dw.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(dw.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go deleted file mode 100644 index 33e913ac5..000000000 --- a/vendor/github.com/miekg/dns/dnssec_keygen.go +++ /dev/null @@ -1,178 +0,0 @@ -package dns - -import ( - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "math/big" - - "golang.org/x/crypto/ed25519" -) - -// Generate generates a DNSKEY of the given bit size. -// The public part is put inside the DNSKEY record. -// The Algorithm in the key must be set as this will define -// what kind of DNSKEY will be generated. -// The ECDSA algorithms imply a fixed keysize, in that case -// bits should be set to the size of the algorithm. -func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) { - switch k.Algorithm { - case DSA, DSANSEC3SHA1: - if bits != 1024 { - return nil, ErrKeySize - } - case RSAMD5, RSASHA1, RSASHA256, RSASHA1NSEC3SHA1: - if bits < 512 || bits > 4096 { - return nil, ErrKeySize - } - case RSASHA512: - if bits < 1024 || bits > 4096 { - return nil, ErrKeySize - } - case ECDSAP256SHA256: - if bits != 256 { - return nil, ErrKeySize - } - case ECDSAP384SHA384: - if bits != 384 { - return nil, ErrKeySize - } - case ED25519: - if bits != 256 { - return nil, ErrKeySize - } - } - - switch k.Algorithm { - case DSA, DSANSEC3SHA1: - params := new(dsa.Parameters) - if err := dsa.GenerateParameters(params, rand.Reader, dsa.L1024N160); err != nil { - return nil, err - } - priv := new(dsa.PrivateKey) - priv.PublicKey.Parameters = *params - err := dsa.GenerateKey(priv, rand.Reader) - if err != nil { - return nil, err - } - k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y) - return priv, nil - case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1: - priv, err := rsa.GenerateKey(rand.Reader, bits) - if err != nil { - return nil, err - } - k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N) - return priv, nil - case ECDSAP256SHA256, ECDSAP384SHA384: - var c elliptic.Curve - switch k.Algorithm { - case ECDSAP256SHA256: - c = elliptic.P256() - case ECDSAP384SHA384: - c = elliptic.P384() - } - priv, err := ecdsa.GenerateKey(c, rand.Reader) - if err != nil { - return nil, err - } - k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y) - return priv, nil - case ED25519: - pub, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, err - } - k.setPublicKeyED25519(pub) - return priv, nil - default: - return nil, ErrAlg - } -} - -// Set the public key (the value E and N) -func (k *DNSKEY) setPublicKeyRSA(_E int, _N *big.Int) bool { - if _E == 0 || _N == nil { - return false - } - buf := exponentToBuf(_E) - buf = append(buf, _N.Bytes()...) - k.PublicKey = toBase64(buf) - return true -} - -// Set the public key for Elliptic Curves -func (k *DNSKEY) setPublicKeyECDSA(_X, _Y *big.Int) bool { - if _X == nil || _Y == nil { - return false - } - var intlen int - switch k.Algorithm { - case ECDSAP256SHA256: - intlen = 32 - case ECDSAP384SHA384: - intlen = 48 - } - k.PublicKey = toBase64(curveToBuf(_X, _Y, intlen)) - return true -} - -// Set the public key for DSA -func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool { - if _Q == nil || _P == nil || _G == nil || _Y == nil { - return false - } - buf := dsaToBuf(_Q, _P, _G, _Y) - k.PublicKey = toBase64(buf) - return true -} - -// Set the public key for Ed25519 -func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool { - if _K == nil { - return false - } - k.PublicKey = toBase64(_K) - return true -} - -// Set the public key (the values E and N) for RSA -// RFC 3110: Section 2. RSA Public KEY Resource Records -func exponentToBuf(_E int) []byte { - var buf []byte - i := big.NewInt(int64(_E)).Bytes() - if len(i) < 256 { - buf = make([]byte, 1, 1+len(i)) - buf[0] = uint8(len(i)) - } else { - buf = make([]byte, 3, 3+len(i)) - buf[0] = 0 - buf[1] = uint8(len(i) >> 8) - buf[2] = uint8(len(i)) - } - buf = append(buf, i...) - return buf -} - -// Set the public key for X and Y for Curve. The two -// values are just concatenated. -func curveToBuf(_X, _Y *big.Int, intlen int) []byte { - buf := intToBytes(_X, intlen) - buf = append(buf, intToBytes(_Y, intlen)...) - return buf -} - -// Set the public key for X and Y for Curve. The two -// values are just concatenated. -func dsaToBuf(_Q, _P, _G, _Y *big.Int) []byte { - t := divRoundUp(divRoundUp(_G.BitLen(), 8)-64, 8) - buf := []byte{byte(t)} - buf = append(buf, intToBytes(_Q, 20)...) - buf = append(buf, intToBytes(_P, 64+t*8)...) - buf = append(buf, intToBytes(_G, 64+t*8)...) - buf = append(buf, intToBytes(_Y, 64+t*8)...) - return buf -} diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go deleted file mode 100644 index e9dd69573..000000000 --- a/vendor/github.com/miekg/dns/dnssec_keyscan.go +++ /dev/null @@ -1,352 +0,0 @@ -package dns - -import ( - "bufio" - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/rsa" - "io" - "math/big" - "strconv" - "strings" - - "golang.org/x/crypto/ed25519" -) - -// NewPrivateKey returns a PrivateKey by parsing the string s. -// s should be in the same form of the BIND private key files. -func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) { - if s == "" || s[len(s)-1] != '\n' { // We need a closing newline - return k.ReadPrivateKey(strings.NewReader(s+"\n"), "") - } - return k.ReadPrivateKey(strings.NewReader(s), "") -} - -// ReadPrivateKey reads a private key from the io.Reader q. The string file is -// only used in error reporting. -// The public key must be known, because some cryptographic algorithms embed -// the public inside the privatekey. -func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) { - m, err := parseKey(q, file) - if m == nil { - return nil, err - } - if _, ok := m["private-key-format"]; !ok { - return nil, ErrPrivKey - } - if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" { - return nil, ErrPrivKey - } - // TODO(mg): check if the pubkey matches the private key - algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8) - if err != nil { - return nil, ErrPrivKey - } - switch uint8(algo) { - case DSA: - priv, err := readPrivateKeyDSA(m) - if err != nil { - return nil, err - } - pub := k.publicKeyDSA() - if pub == nil { - return nil, ErrKey - } - priv.PublicKey = *pub - return priv, nil - case RSAMD5: - fallthrough - case RSASHA1: - fallthrough - case RSASHA1NSEC3SHA1: - fallthrough - case RSASHA256: - fallthrough - case RSASHA512: - priv, err := readPrivateKeyRSA(m) - if err != nil { - return nil, err - } - pub := k.publicKeyRSA() - if pub == nil { - return nil, ErrKey - } - priv.PublicKey = *pub - return priv, nil - case ECCGOST: - return nil, ErrPrivKey - case ECDSAP256SHA256: - fallthrough - case ECDSAP384SHA384: - priv, err := readPrivateKeyECDSA(m) - if err != nil { - return nil, err - } - pub := k.publicKeyECDSA() - if pub == nil { - return nil, ErrKey - } - priv.PublicKey = *pub - return priv, nil - case ED25519: - return readPrivateKeyED25519(m) - default: - return nil, ErrPrivKey - } -} - -// Read a private key (file) string and create a public key. Return the private key. -func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) { - p := new(rsa.PrivateKey) - p.Primes = []*big.Int{nil, nil} - for k, v := range m { - switch k { - case "modulus", "publicexponent", "privateexponent", "prime1", "prime2": - v1, err := fromBase64([]byte(v)) - if err != nil { - return nil, err - } - switch k { - case "modulus": - p.PublicKey.N = new(big.Int).SetBytes(v1) - case "publicexponent": - i := new(big.Int).SetBytes(v1) - p.PublicKey.E = int(i.Int64()) // int64 should be large enough - case "privateexponent": - p.D = new(big.Int).SetBytes(v1) - case "prime1": - p.Primes[0] = new(big.Int).SetBytes(v1) - case "prime2": - p.Primes[1] = new(big.Int).SetBytes(v1) - } - case "exponent1", "exponent2", "coefficient": - // not used in Go (yet) - case "created", "publish", "activate": - // not used in Go (yet) - } - } - return p, nil -} - -func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) { - p := new(dsa.PrivateKey) - p.X = new(big.Int) - for k, v := range m { - switch k { - case "private_value(x)": - v1, err := fromBase64([]byte(v)) - if err != nil { - return nil, err - } - p.X.SetBytes(v1) - case "created", "publish", "activate": - /* not used in Go (yet) */ - } - } - return p, nil -} - -func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) { - p := new(ecdsa.PrivateKey) - p.D = new(big.Int) - // TODO: validate that the required flags are present - for k, v := range m { - switch k { - case "privatekey": - v1, err := fromBase64([]byte(v)) - if err != nil { - return nil, err - } - p.D.SetBytes(v1) - case "created", "publish", "activate": - /* not used in Go (yet) */ - } - } - return p, nil -} - -func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) { - var p ed25519.PrivateKey - // TODO: validate that the required flags are present - for k, v := range m { - switch k { - case "privatekey": - p1, err := fromBase64([]byte(v)) - if err != nil { - return nil, err - } - if len(p1) != ed25519.SeedSize { - return nil, ErrPrivKey - } - p = ed25519.NewKeyFromSeed(p1) - case "created", "publish", "activate": - /* not used in Go (yet) */ - } - } - return p, nil -} - -// parseKey reads a private key from r. It returns a map[string]string, -// with the key-value pairs, or an error when the file is not correct. -func parseKey(r io.Reader, file string) (map[string]string, error) { - m := make(map[string]string) - var k string - - c := newKLexer(r) - - for l, ok := c.Next(); ok; l, ok = c.Next() { - // It should alternate - switch l.value { - case zKey: - k = l.token - case zValue: - if k == "" { - return nil, &ParseError{file, "no private key seen", l} - } - - m[strings.ToLower(k)] = l.token - k = "" - } - } - - // Surface any read errors from r. - if err := c.Err(); err != nil { - return nil, &ParseError{file: file, err: err.Error()} - } - - return m, nil -} - -type klexer struct { - br io.ByteReader - - readErr error - - line int - column int - - key bool - - eol bool // end-of-line -} - -func newKLexer(r io.Reader) *klexer { - br, ok := r.(io.ByteReader) - if !ok { - br = bufio.NewReaderSize(r, 1024) - } - - return &klexer{ - br: br, - - line: 1, - - key: true, - } -} - -func (kl *klexer) Err() error { - if kl.readErr == io.EOF { - return nil - } - - return kl.readErr -} - -// readByte returns the next byte from the input -func (kl *klexer) readByte() (byte, bool) { - if kl.readErr != nil { - return 0, false - } - - c, err := kl.br.ReadByte() - if err != nil { - kl.readErr = err - return 0, false - } - - // delay the newline handling until the next token is delivered, - // fixes off-by-one errors when reporting a parse error. - if kl.eol { - kl.line++ - kl.column = 0 - kl.eol = false - } - - if c == '\n' { - kl.eol = true - } else { - kl.column++ - } - - return c, true -} - -func (kl *klexer) Next() (lex, bool) { - var ( - l lex - - str strings.Builder - - commt bool - ) - - for x, ok := kl.readByte(); ok; x, ok = kl.readByte() { - l.line, l.column = kl.line, kl.column - - switch x { - case ':': - if commt || !kl.key { - break - } - - kl.key = false - - // Next token is a space, eat it - kl.readByte() - - l.value = zKey - l.token = str.String() - return l, true - case ';': - commt = true - case '\n': - if commt { - // Reset a comment - commt = false - } - - if kl.key && str.Len() == 0 { - // ignore empty lines - break - } - - kl.key = true - - l.value = zValue - l.token = str.String() - return l, true - default: - if commt { - break - } - - str.WriteByte(x) - } - } - - if kl.readErr != nil && kl.readErr != io.EOF { - // Don't return any tokens after a read error occurs. - return lex{value: zEOF}, false - } - - if str.Len() > 0 { - // Send remainder - l.value = zValue - l.token = str.String() - return l, true - } - - return lex{value: zEOF}, false -} diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go deleted file mode 100644 index 4493c9d57..000000000 --- a/vendor/github.com/miekg/dns/dnssec_privkey.go +++ /dev/null @@ -1,94 +0,0 @@ -package dns - -import ( - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/rsa" - "math/big" - "strconv" - - "golang.org/x/crypto/ed25519" -) - -const format = "Private-key-format: v1.3\n" - -var bigIntOne = big.NewInt(1) - -// PrivateKeyString converts a PrivateKey to a string. This string has the same -// format as the private-key-file of BIND9 (Private-key-format: v1.3). -// It needs some info from the key (the algorithm), so its a method of the DNSKEY -// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey -func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string { - algorithm := strconv.Itoa(int(r.Algorithm)) - algorithm += " (" + AlgorithmToString[r.Algorithm] + ")" - - switch p := p.(type) { - case *rsa.PrivateKey: - modulus := toBase64(p.PublicKey.N.Bytes()) - e := big.NewInt(int64(p.PublicKey.E)) - publicExponent := toBase64(e.Bytes()) - privateExponent := toBase64(p.D.Bytes()) - prime1 := toBase64(p.Primes[0].Bytes()) - prime2 := toBase64(p.Primes[1].Bytes()) - // Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm - // and from: http://code.google.com/p/go/issues/detail?id=987 - p1 := new(big.Int).Sub(p.Primes[0], bigIntOne) - q1 := new(big.Int).Sub(p.Primes[1], bigIntOne) - exp1 := new(big.Int).Mod(p.D, p1) - exp2 := new(big.Int).Mod(p.D, q1) - coeff := new(big.Int).ModInverse(p.Primes[1], p.Primes[0]) - - exponent1 := toBase64(exp1.Bytes()) - exponent2 := toBase64(exp2.Bytes()) - coefficient := toBase64(coeff.Bytes()) - - return format + - "Algorithm: " + algorithm + "\n" + - "Modulus: " + modulus + "\n" + - "PublicExponent: " + publicExponent + "\n" + - "PrivateExponent: " + privateExponent + "\n" + - "Prime1: " + prime1 + "\n" + - "Prime2: " + prime2 + "\n" + - "Exponent1: " + exponent1 + "\n" + - "Exponent2: " + exponent2 + "\n" + - "Coefficient: " + coefficient + "\n" - - case *ecdsa.PrivateKey: - var intlen int - switch r.Algorithm { - case ECDSAP256SHA256: - intlen = 32 - case ECDSAP384SHA384: - intlen = 48 - } - private := toBase64(intToBytes(p.D, intlen)) - return format + - "Algorithm: " + algorithm + "\n" + - "PrivateKey: " + private + "\n" - - case *dsa.PrivateKey: - T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8) - prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8)) - subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20)) - base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8)) - priv := toBase64(intToBytes(p.X, 20)) - pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8)) - return format + - "Algorithm: " + algorithm + "\n" + - "Prime(p): " + prime + "\n" + - "Subprime(q): " + subprime + "\n" + - "Base(g): " + base + "\n" + - "Private_value(x): " + priv + "\n" + - "Public_value(y): " + pub + "\n" - - case ed25519.PrivateKey: - private := toBase64(p.Seed()) - return format + - "Algorithm: " + algorithm + "\n" + - "PrivateKey: " + private + "\n" - - default: - return "" - } -} diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go deleted file mode 100644 index d3d7cec9e..000000000 --- a/vendor/github.com/miekg/dns/doc.go +++ /dev/null @@ -1,269 +0,0 @@ -/* -Package dns implements a full featured interface to the Domain Name System. -Both server- and client-side programming is supported. The package allows -complete control over what is sent out to the DNS. The API follows the -less-is-more principle, by presenting a small, clean interface. - -It supports (asynchronous) querying/replying, incoming/outgoing zone transfers, -TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing. - -Note that domain names MUST be fully qualified before sending them, unqualified -names in a message will result in a packing failure. - -Resource records are native types. They are not stored in wire format. Basic -usage pattern for creating a new resource record: - - r := new(dns.MX) - r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600} - r.Preference = 10 - r.Mx = "mx.miek.nl." - -Or directly from a string: - - mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.") - -Or when the default origin (.) and TTL (3600) and class (IN) suit you: - - mx, err := dns.NewRR("miek.nl MX 10 mx.miek.nl") - -Or even: - - mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek") - -In the DNS messages are exchanged, these messages contain resource records -(sets). Use pattern for creating a message: - - m := new(dns.Msg) - m.SetQuestion("miek.nl.", dns.TypeMX) - -Or when not certain if the domain name is fully qualified: - - m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX) - -The message m is now a message with the question section set to ask the MX -records for the miek.nl. zone. - -The following is slightly more verbose, but more flexible: - - m1 := new(dns.Msg) - m1.Id = dns.Id() - m1.RecursionDesired = true - m1.Question = make([]dns.Question, 1) - m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET} - -After creating a message it can be sent. Basic use pattern for synchronous -querying the DNS at a server configured on 127.0.0.1 and port 53: - - c := new(dns.Client) - in, rtt, err := c.Exchange(m1, "127.0.0.1:53") - -Suppressing multiple outstanding queries (with the same question, type and -class) is as easy as setting: - - c.SingleInflight = true - -More advanced options are available using a net.Dialer and the corresponding API. -For example it is possible to set a timeout, or to specify a source IP address -and port to use for the connection: - - c := new(dns.Client) - laddr := net.UDPAddr{ - IP: net.ParseIP("[::1]"), - Port: 12345, - Zone: "", - } - c.Dialer := &net.Dialer{ - Timeout: 200 * time.Millisecond, - LocalAddr: &laddr, - } - in, rtt, err := c.Exchange(m1, "8.8.8.8:53") - -If these "advanced" features are not needed, a simple UDP query can be sent, -with: - - in, err := dns.Exchange(m1, "127.0.0.1:53") - -When this functions returns you will get dns message. A dns message consists -out of four sections. -The question section: in.Question, the answer section: in.Answer, -the authority section: in.Ns and the additional section: in.Extra. - -Each of these sections (except the Question section) contain a []RR. Basic -use pattern for accessing the rdata of a TXT RR as the first RR in -the Answer section: - - if t, ok := in.Answer[0].(*dns.TXT); ok { - // do something with t.Txt - } - -Domain Name and TXT Character String Representations - -Both domain names and TXT character strings are converted to presentation form -both when unpacked and when converted to strings. - -For TXT character strings, tabs, carriage returns and line feeds will be -converted to \t, \r and \n respectively. Back slashes and quotations marks will -be escaped. Bytes below 32 and above 127 will be converted to \DDD form. - -For domain names, in addition to the above rules brackets, periods, spaces, -semicolons and the at symbol are escaped. - -DNSSEC - -DNSSEC (DNS Security Extension) adds a layer of security to the DNS. It uses -public key cryptography to sign resource records. The public keys are stored in -DNSKEY records and the signatures in RRSIG records. - -Requesting DNSSEC information for a zone is done by adding the DO (DNSSEC OK) -bit to a request. - - m := new(dns.Msg) - m.SetEdns0(4096, true) - -Signature generation, signature verification and key generation are all supported. - -DYNAMIC UPDATES - -Dynamic updates reuses the DNS message format, but renames three of the -sections. Question is Zone, Answer is Prerequisite, Authority is Update, only -the Additional is not renamed. See RFC 2136 for the gory details. - -You can set a rather complex set of rules for the existence of absence of -certain resource records or names in a zone to specify if resource records -should be added or removed. The table from RFC 2136 supplemented with the Go -DNS function shows which functions exist to specify the prerequisites. - - 3.2.4 - Table Of Metavalues Used In Prerequisite Section - - CLASS TYPE RDATA Meaning Function - -------------------------------------------------------------- - ANY ANY empty Name is in use dns.NameUsed - ANY rrset empty RRset exists (value indep) dns.RRsetUsed - NONE ANY empty Name is not in use dns.NameNotUsed - NONE rrset empty RRset does not exist dns.RRsetNotUsed - zone rrset rr RRset exists (value dep) dns.Used - -The prerequisite section can also be left empty. If you have decided on the -prerequisites you can tell what RRs should be added or deleted. The next table -shows the options you have and what functions to call. - - 3.4.2.6 - Table Of Metavalues Used In Update Section - - CLASS TYPE RDATA Meaning Function - --------------------------------------------------------------- - ANY ANY empty Delete all RRsets from name dns.RemoveName - ANY rrset empty Delete an RRset dns.RemoveRRset - NONE rrset rr Delete an RR from RRset dns.Remove - zone rrset rr Add to an RRset dns.Insert - -TRANSACTION SIGNATURE - -An TSIG or transaction signature adds a HMAC TSIG record to each message sent. -The supported algorithms include: HmacMD5, HmacSHA1, HmacSHA256 and HmacSHA512. - -Basic use pattern when querying with a TSIG name "axfr." (note that these key names -must be fully qualified - as they are domain names) and the base64 secret -"so6ZGir4GPAqINNh9U5c3A==": - -If an incoming message contains a TSIG record it MUST be the last record in -the additional section (RFC2845 3.2). This means that you should make the -call to SetTsig last, right before executing the query. If you make any -changes to the RRset after calling SetTsig() the signature will be incorrect. - - c := new(dns.Client) - c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - m := new(dns.Msg) - m.SetQuestion("miek.nl.", dns.TypeMX) - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) - ... - // When sending the TSIG RR is calculated and filled in before sending - -When requesting an zone transfer (almost all TSIG usage is when requesting zone -transfers), with TSIG, this is the basic use pattern. In this example we -request an AXFR for miek.nl. with TSIG key named "axfr." and secret -"so6ZGir4GPAqINNh9U5c3A==" and using the server 176.58.119.54: - - t := new(dns.Transfer) - m := new(dns.Msg) - t.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - m.SetAxfr("miek.nl.") - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) - c, err := t.In(m, "176.58.119.54:53") - for r := range c { ... } - -You can now read the records from the transfer as they come in. Each envelope -is checked with TSIG. If something is not correct an error is returned. - -Basic use pattern validating and replying to a message that has TSIG set. - - server := &dns.Server{Addr: ":53", Net: "udp"} - server.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="} - go server.ListenAndServe() - dns.HandleFunc(".", handleRequest) - - func handleRequest(w dns.ResponseWriter, r *dns.Msg) { - m := new(dns.Msg) - m.SetReply(r) - if r.IsTsig() != nil { - if w.TsigStatus() == nil { - // *Msg r has an TSIG record and it was validated - m.SetTsig("axfr.", dns.HmacMD5, 300, time.Now().Unix()) - } else { - // *Msg r has an TSIG records and it was not valided - } - } - w.WriteMsg(m) - } - -PRIVATE RRS - -RFC 6895 sets aside a range of type codes for private use. This range is 65,280 -- 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these -can be used, before requesting an official type code from IANA. - -See https://miek.nl/2014/September/21/idn-and-private-rr-in-go-dns/ for more -information. - -EDNS0 - -EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated by -RFC 6891. It defines an new RR type, the OPT RR, which is then completely -abused. - -Basic use pattern for creating an (empty) OPT RR: - - o := new(dns.OPT) - o.Hdr.Name = "." // MUST be the root zone, per definition. - o.Hdr.Rrtype = dns.TypeOPT - -The rdata of an OPT RR consists out of a slice of EDNS0 (RFC 6891) interfaces. -Currently only a few have been standardized: EDNS0_NSID (RFC 5001) and -EDNS0_SUBNET (draft-vandergaast-edns-client-subnet-02). Note that these options -may be combined in an OPT RR. Basic use pattern for a server to check if (and -which) options are set: - - // o is a dns.OPT - for _, s := range o.Option { - switch e := s.(type) { - case *dns.EDNS0_NSID: - // do stuff with e.Nsid - case *dns.EDNS0_SUBNET: - // access e.Family, e.Address, etc. - } - } - -SIG(0) - -From RFC 2931: - - SIG(0) provides protection for DNS transactions and requests .... - ... protection for glue records, DNS requests, protection for message headers - on requests and responses, and protection of the overall integrity of a response. - -It works like TSIG, except that SIG(0) uses public key cryptography, instead of -the shared secret approach in TSIG. Supported algorithms: DSA, ECDSAP256SHA256, -ECDSAP384SHA384, RSASHA1, RSASHA256 and RSASHA512. - -Signing subsequent messages in multi-message sessions is not implemented. -*/ -package dns diff --git a/vendor/github.com/miekg/dns/duplicate.go b/vendor/github.com/miekg/dns/duplicate.go deleted file mode 100644 index 00cda0aa2..000000000 --- a/vendor/github.com/miekg/dns/duplicate.go +++ /dev/null @@ -1,38 +0,0 @@ -package dns - -//go:generate go run duplicate_generate.go - -// IsDuplicate checks of r1 and r2 are duplicates of each other, excluding the TTL. -// So this means the header data is equal *and* the RDATA is the same. Return true -// is so, otherwise false. -// It's is a protocol violation to have identical RRs in a message. -func IsDuplicate(r1, r2 RR) bool { - // Check whether the record header is identical. - if !r1.Header().isDuplicate(r2.Header()) { - return false - } - - // Check whether the RDATA is identical. - return r1.isDuplicate(r2) -} - -func (r1 *RR_Header) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RR_Header) - if !ok { - return false - } - if r1.Class != r2.Class { - return false - } - if r1.Rrtype != r2.Rrtype { - return false - } - if !isDuplicateName(r1.Name, r2.Name) { - return false - } - // ignore TTL - return true -} - -// isDuplicateName checks if the domain names s1 and s2 are equal. -func isDuplicateName(s1, s2 string) bool { return equal(s1, s2) } diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go deleted file mode 100644 index ca8873e14..000000000 --- a/vendor/github.com/miekg/dns/edns.go +++ /dev/null @@ -1,659 +0,0 @@ -package dns - -import ( - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "net" - "strconv" -) - -// EDNS0 Option codes. -const ( - EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 - EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt - EDNS0NSID = 0x3 // nsid (See RFC 5001) - EDNS0DAU = 0x5 // DNSSEC Algorithm Understood - EDNS0DHU = 0x6 // DS Hash Understood - EDNS0N3U = 0x7 // NSEC3 Hash Understood - EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871) - EDNS0EXPIRE = 0x9 // EDNS0 expire - EDNS0COOKIE = 0xa // EDNS0 Cookie - EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) - EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) - EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) - EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) - _DO = 1 << 15 // DNSSEC OK -) - -// OPT is the EDNS0 RR appended to messages to convey extra (meta) information. -// See RFC 6891. -type OPT struct { - Hdr RR_Header - Option []EDNS0 `dns:"opt"` -} - -func (rr *OPT) String() string { - s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " - if rr.Do() { - s += "flags: do; " - } else { - s += "flags: ; " - } - s += "udp: " + strconv.Itoa(int(rr.UDPSize())) - - for _, o := range rr.Option { - switch o.(type) { - case *EDNS0_NSID: - s += "\n; NSID: " + o.String() - h, e := o.pack() - var r string - if e == nil { - for _, c := range h { - r += "(" + string(c) + ")" - } - s += " " + r - } - case *EDNS0_SUBNET: - s += "\n; SUBNET: " + o.String() - case *EDNS0_COOKIE: - s += "\n; COOKIE: " + o.String() - case *EDNS0_UL: - s += "\n; UPDATE LEASE: " + o.String() - case *EDNS0_LLQ: - s += "\n; LONG LIVED QUERIES: " + o.String() - case *EDNS0_DAU: - s += "\n; DNSSEC ALGORITHM UNDERSTOOD: " + o.String() - case *EDNS0_DHU: - s += "\n; DS HASH UNDERSTOOD: " + o.String() - case *EDNS0_N3U: - s += "\n; NSEC3 HASH UNDERSTOOD: " + o.String() - case *EDNS0_LOCAL: - s += "\n; LOCAL OPT: " + o.String() - case *EDNS0_PADDING: - s += "\n; PADDING: " + o.String() - } - } - return s -} - -func (rr *OPT) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - for _, o := range rr.Option { - l += 4 // Account for 2-byte option code and 2-byte option length. - lo, _ := o.pack() - l += len(lo) - } - return l -} - -func (rr *OPT) parse(c *zlexer, origin, file string) *ParseError { - panic("dns: internal error: parse should never be called on OPT") -} - -func (r1 *OPT) isDuplicate(r2 RR) bool { return false } - -// return the old value -> delete SetVersion? - -// Version returns the EDNS version used. Only zero is defined. -func (rr *OPT) Version() uint8 { - return uint8(rr.Hdr.Ttl & 0x00FF0000 >> 16) -} - -// SetVersion sets the version of EDNS. This is usually zero. -func (rr *OPT) SetVersion(v uint8) { - rr.Hdr.Ttl = rr.Hdr.Ttl&0xFF00FFFF | uint32(v)<<16 -} - -// ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). -func (rr *OPT) ExtendedRcode() int { - return int(rr.Hdr.Ttl&0xFF000000>>24) << 4 -} - -// SetExtendedRcode sets the EDNS extended RCODE field. -// -// If the RCODE is not an extended RCODE, will reset the extended RCODE field to 0. -func (rr *OPT) SetExtendedRcode(v uint16) { - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | uint32(v>>4)<<24 -} - -// UDPSize returns the UDP buffer size. -func (rr *OPT) UDPSize() uint16 { - return rr.Hdr.Class -} - -// SetUDPSize sets the UDP buffer size. -func (rr *OPT) SetUDPSize(size uint16) { - rr.Hdr.Class = size -} - -// Do returns the value of the DO (DNSSEC OK) bit. -func (rr *OPT) Do() bool { - return rr.Hdr.Ttl&_DO == _DO -} - -// SetDo sets the DO (DNSSEC OK) bit. -// If we pass an argument, set the DO bit to that value. -// It is possible to pass 2 or more arguments. Any arguments after the 1st is silently ignored. -func (rr *OPT) SetDo(do ...bool) { - if len(do) == 1 { - if do[0] { - rr.Hdr.Ttl |= _DO - } else { - rr.Hdr.Ttl &^= _DO - } - } else { - rr.Hdr.Ttl |= _DO - } -} - -// EDNS0 defines an EDNS0 Option. An OPT RR can have multiple options appended to it. -type EDNS0 interface { - // Option returns the option code for the option. - Option() uint16 - // pack returns the bytes of the option data. - pack() ([]byte, error) - // unpack sets the data as found in the buffer. Is also sets - // the length of the slice as the length of the option data. - unpack([]byte) error - // String returns the string representation of the option. - String() string - // copy returns a deep-copy of the option. - copy() EDNS0 -} - -// EDNS0_NSID option is used to retrieve a nameserver -// identifier. When sending a request Nsid must be set to the empty string -// The identifier is an opaque string encoded as hex. -// Basic use pattern for creating an nsid option: -// -// o := new(dns.OPT) -// o.Hdr.Name = "." -// o.Hdr.Rrtype = dns.TypeOPT -// e := new(dns.EDNS0_NSID) -// e.Code = dns.EDNS0NSID -// e.Nsid = "AA" -// o.Option = append(o.Option, e) -type EDNS0_NSID struct { - Code uint16 // Always EDNS0NSID - Nsid string // This string needs to be hex encoded -} - -func (e *EDNS0_NSID) pack() ([]byte, error) { - h, err := hex.DecodeString(e.Nsid) - if err != nil { - return nil, err - } - return h, nil -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID } // Option returns the option code. -func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil } -func (e *EDNS0_NSID) String() string { return e.Nsid } -func (e *EDNS0_NSID) copy() EDNS0 { return &EDNS0_NSID{e.Code, e.Nsid} } - -// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver -// an idea of where the client lives. See RFC 7871. It can then give back a different -// answer depending on the location or network topology. -// Basic use pattern for creating an subnet option: -// -// o := new(dns.OPT) -// o.Hdr.Name = "." -// o.Hdr.Rrtype = dns.TypeOPT -// e := new(dns.EDNS0_SUBNET) -// e.Code = dns.EDNS0SUBNET -// e.Family = 1 // 1 for IPv4 source address, 2 for IPv6 -// e.SourceNetmask = 32 // 32 for IPV4, 128 for IPv6 -// e.SourceScope = 0 -// e.Address = net.ParseIP("127.0.0.1").To4() // for IPv4 -// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6 -// o.Option = append(o.Option, e) -// -// This code will parse all the available bits when unpacking (up to optlen). -// When packing it will apply SourceNetmask. If you need more advanced logic, -// patches welcome and good luck. -type EDNS0_SUBNET struct { - Code uint16 // Always EDNS0SUBNET - Family uint16 // 1 for IP, 2 for IP6 - SourceNetmask uint8 - SourceScope uint8 - Address net.IP -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET } - -func (e *EDNS0_SUBNET) pack() ([]byte, error) { - b := make([]byte, 4) - binary.BigEndian.PutUint16(b[0:], e.Family) - b[2] = e.SourceNetmask - b[3] = e.SourceScope - switch e.Family { - case 0: - // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 - // We might don't need to complain either - if e.SourceNetmask != 0 { - return nil, errors.New("dns: bad address family") - } - case 1: - if e.SourceNetmask > net.IPv4len*8 { - return nil, errors.New("dns: bad netmask") - } - if len(e.Address.To4()) != net.IPv4len { - return nil, errors.New("dns: bad address") - } - ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) - needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) - case 2: - if e.SourceNetmask > net.IPv6len*8 { - return nil, errors.New("dns: bad netmask") - } - if len(e.Address) != net.IPv6len { - return nil, errors.New("dns: bad address") - } - ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) - needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) - default: - return nil, errors.New("dns: bad address family") - } - return b, nil -} - -func (e *EDNS0_SUBNET) unpack(b []byte) error { - if len(b) < 4 { - return ErrBuf - } - e.Family = binary.BigEndian.Uint16(b) - e.SourceNetmask = b[2] - e.SourceScope = b[3] - switch e.Family { - case 0: - // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 - // It's okay to accept such a packet - if e.SourceNetmask != 0 { - return errors.New("dns: bad address family") - } - e.Address = net.IPv4(0, 0, 0, 0) - case 1: - if e.SourceNetmask > net.IPv4len*8 || e.SourceScope > net.IPv4len*8 { - return errors.New("dns: bad netmask") - } - addr := make(net.IP, net.IPv4len) - copy(addr, b[4:]) - e.Address = addr.To16() - case 2: - if e.SourceNetmask > net.IPv6len*8 || e.SourceScope > net.IPv6len*8 { - return errors.New("dns: bad netmask") - } - addr := make(net.IP, net.IPv6len) - copy(addr, b[4:]) - e.Address = addr - default: - return errors.New("dns: bad address family") - } - return nil -} - -func (e *EDNS0_SUBNET) String() (s string) { - if e.Address == nil { - s = "" - } else if e.Address.To4() != nil { - s = e.Address.String() - } else { - s = "[" + e.Address.String() + "]" - } - s += "/" + strconv.Itoa(int(e.SourceNetmask)) + "/" + strconv.Itoa(int(e.SourceScope)) - return -} - -func (e *EDNS0_SUBNET) copy() EDNS0 { - return &EDNS0_SUBNET{ - e.Code, - e.Family, - e.SourceNetmask, - e.SourceScope, - e.Address, - } -} - -// The EDNS0_COOKIE option is used to add a DNS Cookie to a message. -// -// o := new(dns.OPT) -// o.Hdr.Name = "." -// o.Hdr.Rrtype = dns.TypeOPT -// e := new(dns.EDNS0_COOKIE) -// e.Code = dns.EDNS0COOKIE -// e.Cookie = "24a5ac.." -// o.Option = append(o.Option, e) -// -// The Cookie field consists out of a client cookie (RFC 7873 Section 4), that is -// always 8 bytes. It may then optionally be followed by the server cookie. The server -// cookie is of variable length, 8 to a maximum of 32 bytes. In other words: -// -// cCookie := o.Cookie[:16] -// sCookie := o.Cookie[16:] -// -// There is no guarantee that the Cookie string has a specific length. -type EDNS0_COOKIE struct { - Code uint16 // Always EDNS0COOKIE - Cookie string // Hex-encoded cookie data -} - -func (e *EDNS0_COOKIE) pack() ([]byte, error) { - h, err := hex.DecodeString(e.Cookie) - if err != nil { - return nil, err - } - return h, nil -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE } -func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil } -func (e *EDNS0_COOKIE) String() string { return e.Cookie } -func (e *EDNS0_COOKIE) copy() EDNS0 { return &EDNS0_COOKIE{e.Code, e.Cookie} } - -// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set -// an expiration on an update RR. This is helpful for clients that cannot clean -// up after themselves. This is a draft RFC and more information can be found at -// http://files.dns-sd.org/draft-sekar-dns-ul.txt -// -// o := new(dns.OPT) -// o.Hdr.Name = "." -// o.Hdr.Rrtype = dns.TypeOPT -// e := new(dns.EDNS0_UL) -// e.Code = dns.EDNS0UL -// e.Lease = 120 // in seconds -// o.Option = append(o.Option, e) -type EDNS0_UL struct { - Code uint16 // Always EDNS0UL - Lease uint32 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_UL) Option() uint16 { return EDNS0UL } -func (e *EDNS0_UL) String() string { return strconv.FormatUint(uint64(e.Lease), 10) } -func (e *EDNS0_UL) copy() EDNS0 { return &EDNS0_UL{e.Code, e.Lease} } - -// Copied: http://golang.org/src/pkg/net/dnsmsg.go -func (e *EDNS0_UL) pack() ([]byte, error) { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, e.Lease) - return b, nil -} - -func (e *EDNS0_UL) unpack(b []byte) error { - if len(b) < 4 { - return ErrBuf - } - e.Lease = binary.BigEndian.Uint32(b) - return nil -} - -// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01 -// Implemented for completeness, as the EDNS0 type code is assigned. -type EDNS0_LLQ struct { - Code uint16 // Always EDNS0LLQ - Version uint16 - Opcode uint16 - Error uint16 - Id uint64 - LeaseLife uint32 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_LLQ) Option() uint16 { return EDNS0LLQ } - -func (e *EDNS0_LLQ) pack() ([]byte, error) { - b := make([]byte, 18) - binary.BigEndian.PutUint16(b[0:], e.Version) - binary.BigEndian.PutUint16(b[2:], e.Opcode) - binary.BigEndian.PutUint16(b[4:], e.Error) - binary.BigEndian.PutUint64(b[6:], e.Id) - binary.BigEndian.PutUint32(b[14:], e.LeaseLife) - return b, nil -} - -func (e *EDNS0_LLQ) unpack(b []byte) error { - if len(b) < 18 { - return ErrBuf - } - e.Version = binary.BigEndian.Uint16(b[0:]) - e.Opcode = binary.BigEndian.Uint16(b[2:]) - e.Error = binary.BigEndian.Uint16(b[4:]) - e.Id = binary.BigEndian.Uint64(b[6:]) - e.LeaseLife = binary.BigEndian.Uint32(b[14:]) - return nil -} - -func (e *EDNS0_LLQ) String() string { - s := strconv.FormatUint(uint64(e.Version), 10) + " " + strconv.FormatUint(uint64(e.Opcode), 10) + - " " + strconv.FormatUint(uint64(e.Error), 10) + " " + strconv.FormatUint(e.Id, 10) + - " " + strconv.FormatUint(uint64(e.LeaseLife), 10) - return s -} -func (e *EDNS0_LLQ) copy() EDNS0 { - return &EDNS0_LLQ{e.Code, e.Version, e.Opcode, e.Error, e.Id, e.LeaseLife} -} - -// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975. -type EDNS0_DAU struct { - Code uint16 // Always EDNS0DAU - AlgCode []uint8 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_DAU) Option() uint16 { return EDNS0DAU } -func (e *EDNS0_DAU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DAU) unpack(b []byte) error { e.AlgCode = b; return nil } - -func (e *EDNS0_DAU) String() string { - s := "" - for _, alg := range e.AlgCode { - if a, ok := AlgorithmToString[alg]; ok { - s += " " + a - } else { - s += " " + strconv.Itoa(int(alg)) - } - } - return s -} -func (e *EDNS0_DAU) copy() EDNS0 { return &EDNS0_DAU{e.Code, e.AlgCode} } - -// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975. -type EDNS0_DHU struct { - Code uint16 // Always EDNS0DHU - AlgCode []uint8 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_DHU) Option() uint16 { return EDNS0DHU } -func (e *EDNS0_DHU) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_DHU) unpack(b []byte) error { e.AlgCode = b; return nil } - -func (e *EDNS0_DHU) String() string { - s := "" - for _, alg := range e.AlgCode { - if a, ok := HashToString[alg]; ok { - s += " " + a - } else { - s += " " + strconv.Itoa(int(alg)) - } - } - return s -} -func (e *EDNS0_DHU) copy() EDNS0 { return &EDNS0_DHU{e.Code, e.AlgCode} } - -// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975. -type EDNS0_N3U struct { - Code uint16 // Always EDNS0N3U - AlgCode []uint8 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_N3U) Option() uint16 { return EDNS0N3U } -func (e *EDNS0_N3U) pack() ([]byte, error) { return e.AlgCode, nil } -func (e *EDNS0_N3U) unpack(b []byte) error { e.AlgCode = b; return nil } - -func (e *EDNS0_N3U) String() string { - // Re-use the hash map - s := "" - for _, alg := range e.AlgCode { - if a, ok := HashToString[alg]; ok { - s += " " + a - } else { - s += " " + strconv.Itoa(int(alg)) - } - } - return s -} -func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} } - -// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314. -type EDNS0_EXPIRE struct { - Code uint16 // Always EDNS0EXPIRE - Expire uint32 -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_EXPIRE) Option() uint16 { return EDNS0EXPIRE } -func (e *EDNS0_EXPIRE) String() string { return strconv.FormatUint(uint64(e.Expire), 10) } -func (e *EDNS0_EXPIRE) copy() EDNS0 { return &EDNS0_EXPIRE{e.Code, e.Expire} } - -func (e *EDNS0_EXPIRE) pack() ([]byte, error) { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, e.Expire) - return b, nil -} - -func (e *EDNS0_EXPIRE) unpack(b []byte) error { - if len(b) < 4 { - return ErrBuf - } - e.Expire = binary.BigEndian.Uint32(b) - return nil -} - -// The EDNS0_LOCAL option is used for local/experimental purposes. The option -// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND] -// (RFC6891), although any unassigned code can actually be used. The content of -// the option is made available in Data, unaltered. -// Basic use pattern for creating a local option: -// -// o := new(dns.OPT) -// o.Hdr.Name = "." -// o.Hdr.Rrtype = dns.TypeOPT -// e := new(dns.EDNS0_LOCAL) -// e.Code = dns.EDNS0LOCALSTART -// e.Data = []byte{72, 82, 74} -// o.Option = append(o.Option, e) -type EDNS0_LOCAL struct { - Code uint16 - Data []byte -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_LOCAL) Option() uint16 { return e.Code } -func (e *EDNS0_LOCAL) String() string { - return strconv.FormatInt(int64(e.Code), 10) + ":0x" + hex.EncodeToString(e.Data) -} -func (e *EDNS0_LOCAL) copy() EDNS0 { - b := make([]byte, len(e.Data)) - copy(b, e.Data) - return &EDNS0_LOCAL{e.Code, b} -} - -func (e *EDNS0_LOCAL) pack() ([]byte, error) { - b := make([]byte, len(e.Data)) - copied := copy(b, e.Data) - if copied != len(e.Data) { - return nil, ErrBuf - } - return b, nil -} - -func (e *EDNS0_LOCAL) unpack(b []byte) error { - e.Data = make([]byte, len(b)) - copied := copy(e.Data, b) - if copied != len(b) { - return ErrBuf - } - return nil -} - -// EDNS0_TCP_KEEPALIVE is an EDNS0 option that instructs the server to keep -// the TCP connection alive. See RFC 7828. -type EDNS0_TCP_KEEPALIVE struct { - Code uint16 // Always EDNSTCPKEEPALIVE - Length uint16 // the value 0 if the TIMEOUT is omitted, the value 2 if it is present; - Timeout uint16 // an idle timeout value for the TCP connection, specified in units of 100 milliseconds, encoded in network byte order. -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_TCP_KEEPALIVE) Option() uint16 { return EDNS0TCPKEEPALIVE } - -func (e *EDNS0_TCP_KEEPALIVE) pack() ([]byte, error) { - if e.Timeout != 0 && e.Length != 2 { - return nil, errors.New("dns: timeout specified but length is not 2") - } - if e.Timeout == 0 && e.Length != 0 { - return nil, errors.New("dns: timeout not specified but length is not 0") - } - b := make([]byte, 4+e.Length) - binary.BigEndian.PutUint16(b[0:], e.Code) - binary.BigEndian.PutUint16(b[2:], e.Length) - if e.Length == 2 { - binary.BigEndian.PutUint16(b[4:], e.Timeout) - } - return b, nil -} - -func (e *EDNS0_TCP_KEEPALIVE) unpack(b []byte) error { - if len(b) < 4 { - return ErrBuf - } - e.Length = binary.BigEndian.Uint16(b[2:4]) - if e.Length != 0 && e.Length != 2 { - return errors.New("dns: length mismatch, want 0/2 but got " + strconv.FormatUint(uint64(e.Length), 10)) - } - if e.Length == 2 { - if len(b) < 6 { - return ErrBuf - } - e.Timeout = binary.BigEndian.Uint16(b[4:6]) - } - return nil -} - -func (e *EDNS0_TCP_KEEPALIVE) String() (s string) { - s = "use tcp keep-alive" - if e.Length == 0 { - s += ", timeout omitted" - } else { - s += fmt.Sprintf(", timeout %dms", e.Timeout*100) - } - return -} -func (e *EDNS0_TCP_KEEPALIVE) copy() EDNS0 { return &EDNS0_TCP_KEEPALIVE{e.Code, e.Length, e.Timeout} } - -// EDNS0_PADDING option is used to add padding to a request/response. The default -// value of padding SHOULD be 0x0 but other values MAY be used, for instance if -// compression is applied before encryption which may break signatures. -type EDNS0_PADDING struct { - Padding []byte -} - -// Option implements the EDNS0 interface. -func (e *EDNS0_PADDING) Option() uint16 { return EDNS0PADDING } -func (e *EDNS0_PADDING) pack() ([]byte, error) { return e.Padding, nil } -func (e *EDNS0_PADDING) unpack(b []byte) error { e.Padding = b; return nil } -func (e *EDNS0_PADDING) String() string { return fmt.Sprintf("%0X", e.Padding) } -func (e *EDNS0_PADDING) copy() EDNS0 { - b := make([]byte, len(e.Padding)) - copy(b, e.Padding) - return &EDNS0_PADDING{b} -} diff --git a/vendor/github.com/miekg/dns/format.go b/vendor/github.com/miekg/dns/format.go deleted file mode 100644 index 0ec79f2fc..000000000 --- a/vendor/github.com/miekg/dns/format.go +++ /dev/null @@ -1,93 +0,0 @@ -package dns - -import ( - "net" - "reflect" - "strconv" -) - -// NumField returns the number of rdata fields r has. -func NumField(r RR) int { - return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header -} - -// Field returns the rdata field i as a string. Fields are indexed starting from 1. -// RR types that holds slice data, for instance the NSEC type bitmap will return a single -// string where the types are concatenated using a space. -// Accessing non existing fields will cause a panic. -func Field(r RR, i int) string { - if i == 0 { - return "" - } - d := reflect.ValueOf(r).Elem().Field(i) - switch d.Kind() { - case reflect.String: - return d.String() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return strconv.FormatInt(d.Int(), 10) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return strconv.FormatUint(d.Uint(), 10) - case reflect.Slice: - switch reflect.ValueOf(r).Elem().Type().Field(i).Tag { - case `dns:"a"`: - // TODO(miek): Hmm store this as 16 bytes - if d.Len() < net.IPv4len { - return "" - } - if d.Len() < net.IPv6len { - return net.IPv4(byte(d.Index(0).Uint()), - byte(d.Index(1).Uint()), - byte(d.Index(2).Uint()), - byte(d.Index(3).Uint())).String() - } - return net.IPv4(byte(d.Index(12).Uint()), - byte(d.Index(13).Uint()), - byte(d.Index(14).Uint()), - byte(d.Index(15).Uint())).String() - case `dns:"aaaa"`: - if d.Len() < net.IPv6len { - return "" - } - return net.IP{ - byte(d.Index(0).Uint()), - byte(d.Index(1).Uint()), - byte(d.Index(2).Uint()), - byte(d.Index(3).Uint()), - byte(d.Index(4).Uint()), - byte(d.Index(5).Uint()), - byte(d.Index(6).Uint()), - byte(d.Index(7).Uint()), - byte(d.Index(8).Uint()), - byte(d.Index(9).Uint()), - byte(d.Index(10).Uint()), - byte(d.Index(11).Uint()), - byte(d.Index(12).Uint()), - byte(d.Index(13).Uint()), - byte(d.Index(14).Uint()), - byte(d.Index(15).Uint()), - }.String() - case `dns:"nsec"`: - if d.Len() == 0 { - return "" - } - s := Type(d.Index(0).Uint()).String() - for i := 1; i < d.Len(); i++ { - s += " " + Type(d.Index(i).Uint()).String() - } - return s - default: - // if it does not have a tag its a string slice - fallthrough - case `dns:"txt"`: - if d.Len() == 0 { - return "" - } - s := d.Index(0).String() - for i := 1; i < d.Len(); i++ { - s += " " + d.Index(i).String() - } - return s - } - } - return "" -} diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go deleted file mode 100644 index a8a09184d..000000000 --- a/vendor/github.com/miekg/dns/fuzz.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build fuzz - -package dns - -func Fuzz(data []byte) int { - msg := new(Msg) - - if err := msg.Unpack(data); err != nil { - return 0 - } - if _, err := msg.Pack(); err != nil { - return 0 - } - - return 1 -} - -func FuzzNewRR(data []byte) int { - if _, err := NewRR(string(data)); err != nil { - return 0 - } - return 1 -} diff --git a/vendor/github.com/miekg/dns/generate.go b/vendor/github.com/miekg/dns/generate.go deleted file mode 100644 index 97bc39f58..000000000 --- a/vendor/github.com/miekg/dns/generate.go +++ /dev/null @@ -1,242 +0,0 @@ -package dns - -import ( - "bytes" - "fmt" - "io" - "strconv" - "strings" -) - -// Parse the $GENERATE statement as used in BIND9 zones. -// See http://www.zytrax.com/books/dns/ch8/generate.html for instance. -// We are called after '$GENERATE '. After which we expect: -// * the range (12-24/2) -// * lhs (ownername) -// * [[ttl][class]] -// * type -// * rhs (rdata) -// But we are lazy here, only the range is parsed *all* occurrences -// of $ after that are interpreted. -func (zp *ZoneParser) generate(l lex) (RR, bool) { - token := l.token - step := 1 - if i := strings.IndexByte(token, '/'); i >= 0 { - if i+1 == len(token) { - return zp.setParseError("bad step in $GENERATE range", l) - } - - s, err := strconv.Atoi(token[i+1:]) - if err != nil || s <= 0 { - return zp.setParseError("bad step in $GENERATE range", l) - } - - step = s - token = token[:i] - } - - sx := strings.SplitN(token, "-", 2) - if len(sx) != 2 { - return zp.setParseError("bad start-stop in $GENERATE range", l) - } - - start, err := strconv.Atoi(sx[0]) - if err != nil { - return zp.setParseError("bad start in $GENERATE range", l) - } - - end, err := strconv.Atoi(sx[1]) - if err != nil { - return zp.setParseError("bad stop in $GENERATE range", l) - } - if end < 0 || start < 0 || end < start { - return zp.setParseError("bad range in $GENERATE range", l) - } - - zp.c.Next() // _BLANK - - // Create a complete new string, which we then parse again. - var s string - for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { - if l.err { - return zp.setParseError("bad data in $GENERATE directive", l) - } - if l.value == zNewline { - break - } - - s += l.token - } - - r := &generateReader{ - s: s, - - cur: start, - start: start, - end: end, - step: step, - - file: zp.file, - lex: &l, - } - zp.sub = NewZoneParser(r, zp.origin, zp.file) - zp.sub.includeDepth, zp.sub.includeAllowed = zp.includeDepth, zp.includeAllowed - zp.sub.SetDefaultTTL(defaultTtl) - return zp.subNext() -} - -type generateReader struct { - s string - si int - - cur int - start int - end int - step int - - mod bytes.Buffer - - escape bool - - eof bool - - file string - lex *lex -} - -func (r *generateReader) parseError(msg string, end int) *ParseError { - r.eof = true // Make errors sticky. - - l := *r.lex - l.token = r.s[r.si-1 : end] - l.column += r.si // l.column starts one zBLANK before r.s - - return &ParseError{r.file, msg, l} -} - -func (r *generateReader) Read(p []byte) (int, error) { - // NewZLexer, through NewZoneParser, should use ReadByte and - // not end up here. - - panic("not implemented") -} - -func (r *generateReader) ReadByte() (byte, error) { - if r.eof { - return 0, io.EOF - } - if r.mod.Len() > 0 { - return r.mod.ReadByte() - } - - if r.si >= len(r.s) { - r.si = 0 - r.cur += r.step - - r.eof = r.cur > r.end || r.cur < 0 - return '\n', nil - } - - si := r.si - r.si++ - - switch r.s[si] { - case '\\': - if r.escape { - r.escape = false - return '\\', nil - } - - r.escape = true - return r.ReadByte() - case '$': - if r.escape { - r.escape = false - return '$', nil - } - - mod := "%d" - - if si >= len(r.s)-1 { - // End of the string - fmt.Fprintf(&r.mod, mod, r.cur) - return r.mod.ReadByte() - } - - if r.s[si+1] == '$' { - r.si++ - return '$', nil - } - - var offset int - - // Search for { and } - if r.s[si+1] == '{' { - // Modifier block - sep := strings.Index(r.s[si+2:], "}") - if sep < 0 { - return 0, r.parseError("bad modifier in $GENERATE", len(r.s)) - } - - var errMsg string - mod, offset, errMsg = modToPrintf(r.s[si+2 : si+2+sep]) - if errMsg != "" { - return 0, r.parseError(errMsg, si+3+sep) - } - if r.start+offset < 0 || r.end+offset > 1<<31-1 { - return 0, r.parseError("bad offset in $GENERATE", si+3+sep) - } - - r.si += 2 + sep // Jump to it - } - - fmt.Fprintf(&r.mod, mod, r.cur+offset) - return r.mod.ReadByte() - default: - if r.escape { // Pretty useless here - r.escape = false - return r.ReadByte() - } - - return r.s[si], nil - } -} - -// Convert a $GENERATE modifier 0,0,d to something Printf can deal with. -func modToPrintf(s string) (string, int, string) { - // Modifier is { offset [ ,width [ ,base ] ] } - provide default - // values for optional width and type, if necessary. - var offStr, widthStr, base string - switch xs := strings.Split(s, ","); len(xs) { - case 1: - offStr, widthStr, base = xs[0], "0", "d" - case 2: - offStr, widthStr, base = xs[0], xs[1], "d" - case 3: - offStr, widthStr, base = xs[0], xs[1], xs[2] - default: - return "", 0, "bad modifier in $GENERATE" - } - - switch base { - case "o", "d", "x", "X": - default: - return "", 0, "bad base in $GENERATE" - } - - offset, err := strconv.Atoi(offStr) - if err != nil { - return "", 0, "bad offset in $GENERATE" - } - - width, err := strconv.Atoi(widthStr) - if err != nil || width < 0 || width > 255 { - return "", 0, "bad width in $GENERATE" - } - - if width == 0 { - return "%" + base, offset, "" - } - - return "%0" + widthStr + base, offset, "" -} diff --git a/vendor/github.com/miekg/dns/labels.go b/vendor/github.com/miekg/dns/labels.go deleted file mode 100644 index e32d2a1d2..000000000 --- a/vendor/github.com/miekg/dns/labels.go +++ /dev/null @@ -1,188 +0,0 @@ -package dns - -// Holds a bunch of helper functions for dealing with labels. - -// SplitDomainName splits a name string into it's labels. -// www.miek.nl. returns []string{"www", "miek", "nl"} -// .www.miek.nl. returns []string{"", "www", "miek", "nl"}, -// The root label (.) returns nil. Note that using -// strings.Split(s) will work in most cases, but does not handle -// escaped dots (\.) for instance. -// s must be a syntactically valid domain name, see IsDomainName. -func SplitDomainName(s string) (labels []string) { - if len(s) == 0 { - return nil - } - fqdnEnd := 0 // offset of the final '.' or the length of the name - idx := Split(s) - begin := 0 - if IsFqdn(s) { - fqdnEnd = len(s) - 1 - } else { - fqdnEnd = len(s) - } - - switch len(idx) { - case 0: - return nil - case 1: - // no-op - default: - for _, end := range idx[1:] { - labels = append(labels, s[begin:end-1]) - begin = end - } - } - - return append(labels, s[begin:fqdnEnd]) -} - -// CompareDomainName compares the names s1 and s2 and -// returns how many labels they have in common starting from the *right*. -// The comparison stops at the first inequality. The names are downcased -// before the comparison. -// -// www.miek.nl. and miek.nl. have two labels in common: miek and nl -// www.miek.nl. and www.bla.nl. have one label in common: nl -// -// s1 and s2 must be syntactically valid domain names. -func CompareDomainName(s1, s2 string) (n int) { - // the first check: root label - if s1 == "." || s2 == "." { - return 0 - } - - l1 := Split(s1) - l2 := Split(s2) - - j1 := len(l1) - 1 // end - i1 := len(l1) - 2 // start - j2 := len(l2) - 1 - i2 := len(l2) - 2 - // the second check can be done here: last/only label - // before we fall through into the for-loop below - if equal(s1[l1[j1]:], s2[l2[j2]:]) { - n++ - } else { - return - } - for { - if i1 < 0 || i2 < 0 { - break - } - if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) { - n++ - } else { - break - } - j1-- - i1-- - j2-- - i2-- - } - return -} - -// CountLabel counts the the number of labels in the string s. -// s must be a syntactically valid domain name. -func CountLabel(s string) (labels int) { - if s == "." { - return - } - off := 0 - end := false - for { - off, end = NextLabel(s, off) - labels++ - if end { - return - } - } -} - -// Split splits a name s into its label indexes. -// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. -// The root name (.) returns nil. Also see SplitDomainName. -// s must be a syntactically valid domain name. -func Split(s string) []int { - if s == "." { - return nil - } - idx := make([]int, 1, 3) - off := 0 - end := false - - for { - off, end = NextLabel(s, off) - if end { - return idx - } - idx = append(idx, off) - } -} - -// NextLabel returns the index of the start of the next label in the -// string s starting at offset. -// The bool end is true when the end of the string has been reached. -// Also see PrevLabel. -func NextLabel(s string, offset int) (i int, end bool) { - quote := false - for i = offset; i < len(s)-1; i++ { - switch s[i] { - case '\\': - quote = !quote - default: - quote = false - case '.': - if quote { - quote = !quote - continue - } - return i + 1, false - } - } - return i + 1, true -} - -// PrevLabel returns the index of the label when starting from the right and -// jumping n labels to the left. -// The bool start is true when the start of the string has been overshot. -// Also see NextLabel. -func PrevLabel(s string, n int) (i int, start bool) { - if n == 0 { - return len(s), false - } - lab := Split(s) - if lab == nil { - return 0, true - } - if n > len(lab) { - return 0, true - } - return lab[len(lab)-n], false -} - -// equal compares a and b while ignoring case. It returns true when equal otherwise false. -func equal(a, b string) bool { - // might be lifted into API function. - la := len(a) - lb := len(b) - if la != lb { - return false - } - - for i := la - 1; i >= 0; i-- { - ai := a[i] - bi := b[i] - if ai >= 'A' && ai <= 'Z' { - ai |= 'a' - 'A' - } - if bi >= 'A' && bi <= 'Z' { - bi |= 'a' - 'A' - } - if ai != bi { - return false - } - } - return true -} diff --git a/vendor/github.com/miekg/dns/listen_go111.go b/vendor/github.com/miekg/dns/listen_go111.go deleted file mode 100644 index fad195cfe..000000000 --- a/vendor/github.com/miekg/dns/listen_go111.go +++ /dev/null @@ -1,44 +0,0 @@ -// +build go1.11 -// +build aix darwin dragonfly freebsd linux netbsd openbsd - -package dns - -import ( - "context" - "net" - "syscall" - - "golang.org/x/sys/unix" -) - -const supportsReusePort = true - -func reuseportControl(network, address string, c syscall.RawConn) error { - var opErr error - err := c.Control(func(fd uintptr) { - opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) - }) - if err != nil { - return err - } - - return opErr -} - -func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { - var lc net.ListenConfig - if reuseport { - lc.Control = reuseportControl - } - - return lc.Listen(context.Background(), network, addr) -} - -func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { - var lc net.ListenConfig - if reuseport { - lc.Control = reuseportControl - } - - return lc.ListenPacket(context.Background(), network, addr) -} diff --git a/vendor/github.com/miekg/dns/listen_go_not111.go b/vendor/github.com/miekg/dns/listen_go_not111.go deleted file mode 100644 index b9201417a..000000000 --- a/vendor/github.com/miekg/dns/listen_go_not111.go +++ /dev/null @@ -1,23 +0,0 @@ -// +build !go1.11 !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd - -package dns - -import "net" - -const supportsReusePort = false - -func listenTCP(network, addr string, reuseport bool) (net.Listener, error) { - if reuseport { - // TODO(tmthrgd): return an error? - } - - return net.Listen(network, addr) -} - -func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) { - if reuseport { - // TODO(tmthrgd): return an error? - } - - return net.ListenPacket(network, addr) -} diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go deleted file mode 100644 index e04fb5d77..000000000 --- a/vendor/github.com/miekg/dns/msg.go +++ /dev/null @@ -1,1228 +0,0 @@ -// DNS packet assembly, see RFC 1035. Converting from - Unpack() - -// and to - Pack() - wire format. -// All the packers and unpackers take a (msg []byte, off int) -// and return (off1 int, ok bool). If they return ok==false, they -// also return off1==len(msg), so that the next unpacker will -// also fail. This lets us avoid checks of ok until the end of a -// packing sequence. - -package dns - -//go:generate go run msg_generate.go - -import ( - crand "crypto/rand" - "encoding/binary" - "fmt" - "math/big" - "math/rand" - "strconv" - "strings" - "sync" -) - -const ( - maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer - maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 - - // This is the maximum number of compression pointers that should occur in a - // semantically valid message. Each label in a domain name must be at least one - // octet and is separated by a period. The root label won't be represented by a - // compression pointer to a compression pointer, hence the -2 to exclude the - // smallest valid root label. - // - // It is possible to construct a valid message that has more compression pointers - // than this, and still doesn't loop, by pointing to a previous pointer. This is - // not something a well written implementation should ever do, so we leave them - // to trip the maximum compression pointer check. - maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2 - - // This is the maximum length of a domain name in presentation format. The - // maximum wire length of a domain name is 255 octets (see above), with the - // maximum label length being 63. The wire format requires one extra byte over - // the presentation format, reducing the number of octets by 1. Each label in - // the name will be separated by a single period, with each octet in the label - // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum - // length, then the final label can only be 61 octets long to not exceed the - // maximum allowed wire length. - maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1 -) - -// Errors defined in this package. -var ( - ErrAlg error = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm. - ErrAuth error = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication. - ErrBuf error = &Error{err: "buffer size too small"} // ErrBuf indicates that the buffer used is too small for the message. - ErrConnEmpty error = &Error{err: "conn has no connection"} // ErrConnEmpty indicates a connection is being used before it is initialized. - ErrExtendedRcode error = &Error{err: "bad extended rcode"} // ErrExtendedRcode ... - ErrFqdn error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot. - ErrId error = &Error{err: "id mismatch"} // ErrId indicates there is a mismatch with the message's ID. - ErrKeyAlg error = &Error{err: "bad key algorithm"} // ErrKeyAlg indicates that the algorithm in the key is not valid. - ErrKey error = &Error{err: "bad key"} - ErrKeySize error = &Error{err: "bad key size"} - ErrLongDomain error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)} - ErrNoSig error = &Error{err: "no signature found"} - ErrPrivKey error = &Error{err: "bad private key"} - ErrRcode error = &Error{err: "bad rcode"} - ErrRdata error = &Error{err: "bad rdata"} - ErrRRset error = &Error{err: "bad rrset"} - ErrSecret error = &Error{err: "no secrets defined"} - ErrShortRead error = &Error{err: "short read"} - ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. - ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. - ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. -) - -// Id by default, returns a 16 bits random number to be used as a -// message id. The random provided should be good enough. This being a -// variable the function can be reassigned to a custom function. -// For instance, to make it return a static value: -// -// dns.Id = func() uint16 { return 3 } -var Id = id - -var ( - idLock sync.Mutex - idRand *rand.Rand -) - -// id returns a 16 bits random number to be used as a -// message id. The random provided should be good enough. -func id() uint16 { - idLock.Lock() - - if idRand == nil { - // This (partially) works around - // https://github.com/golang/go/issues/11833 by only - // seeding idRand upon the first call to id. - - var seed int64 - var buf [8]byte - - if _, err := crand.Read(buf[:]); err == nil { - seed = int64(binary.LittleEndian.Uint64(buf[:])) - } else { - seed = rand.Int63() - } - - idRand = rand.New(rand.NewSource(seed)) - } - - // The call to idRand.Uint32 must be within the - // mutex lock because *rand.Rand is not safe for - // concurrent use. - // - // There is no added performance overhead to calling - // idRand.Uint32 inside a mutex lock over just - // calling rand.Uint32 as the global math/rand rng - // is internally protected by a sync.Mutex. - id := uint16(idRand.Uint32()) - - idLock.Unlock() - return id -} - -// MsgHdr is a a manually-unpacked version of (id, bits). -type MsgHdr struct { - Id uint16 - Response bool - Opcode int - Authoritative bool - Truncated bool - RecursionDesired bool - RecursionAvailable bool - Zero bool - AuthenticatedData bool - CheckingDisabled bool - Rcode int -} - -// Msg contains the layout of a DNS message. -type Msg struct { - MsgHdr - Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format. - Question []Question // Holds the RR(s) of the question section. - Answer []RR // Holds the RR(s) of the answer section. - Ns []RR // Holds the RR(s) of the authority section. - Extra []RR // Holds the RR(s) of the additional section. -} - -// ClassToString is a maps Classes to strings for each CLASS wire type. -var ClassToString = map[uint16]string{ - ClassINET: "IN", - ClassCSNET: "CS", - ClassCHAOS: "CH", - ClassHESIOD: "HS", - ClassNONE: "NONE", - ClassANY: "ANY", -} - -// OpcodeToString maps Opcodes to strings. -var OpcodeToString = map[int]string{ - OpcodeQuery: "QUERY", - OpcodeIQuery: "IQUERY", - OpcodeStatus: "STATUS", - OpcodeNotify: "NOTIFY", - OpcodeUpdate: "UPDATE", -} - -// RcodeToString maps Rcodes to strings. -var RcodeToString = map[int]string{ - RcodeSuccess: "NOERROR", - RcodeFormatError: "FORMERR", - RcodeServerFailure: "SERVFAIL", - RcodeNameError: "NXDOMAIN", - RcodeNotImplemented: "NOTIMP", - RcodeRefused: "REFUSED", - RcodeYXDomain: "YXDOMAIN", // See RFC 2136 - RcodeYXRrset: "YXRRSET", - RcodeNXRrset: "NXRRSET", - RcodeNotAuth: "NOTAUTH", - RcodeNotZone: "NOTZONE", - RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891 - // RcodeBadVers: "BADVERS", - RcodeBadKey: "BADKEY", - RcodeBadTime: "BADTIME", - RcodeBadMode: "BADMODE", - RcodeBadName: "BADNAME", - RcodeBadAlg: "BADALG", - RcodeBadTrunc: "BADTRUNC", - RcodeBadCookie: "BADCOOKIE", -} - -// compressionMap is used to allow a more efficient compression map -// to be used for internal packDomainName calls without changing the -// signature or functionality of public API. -// -// In particular, map[string]uint16 uses 25% less per-entry memory -// than does map[string]int. -type compressionMap struct { - ext map[string]int // external callers - int map[string]uint16 // internal callers -} - -func (m compressionMap) valid() bool { - return m.int != nil || m.ext != nil -} - -func (m compressionMap) insert(s string, pos int) { - if m.ext != nil { - m.ext[s] = pos - } else { - m.int[s] = uint16(pos) - } -} - -func (m compressionMap) find(s string) (int, bool) { - if m.ext != nil { - pos, ok := m.ext[s] - return pos, ok - } - - pos, ok := m.int[s] - return int(pos), ok -} - -// Domain names are a sequence of counted strings -// split at the dots. They end with a zero-length string. - -// PackDomainName packs a domain name s into msg[off:]. -// If compression is wanted compress must be true and the compression -// map needs to hold a mapping between domain names and offsets -// pointing into msg. -func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { - return packDomainName(s, msg, off, compressionMap{ext: compression}, compress) -} - -func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - // XXX: A logical copy of this function exists in IsDomainName and - // should be kept in sync with this function. - - ls := len(s) - if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. - return off, nil - } - - // If not fully qualified, error out. - if !IsFqdn(s) { - return len(msg), ErrFqdn - } - - // Each dot ends a segment of the name. - // We trade each dot byte for a length byte. - // Except for escaped dots (\.), which are normal dots. - // There is also a trailing zero. - - // Compression - pointer := -1 - - // Emit sequence of counted strings, chopping at dots. - var ( - begin int - compBegin int - compOff int - bs []byte - wasDot bool - ) -loop: - for i := 0; i < ls; i++ { - var c byte - if bs == nil { - c = s[i] - } else { - c = bs[i] - } - - switch c { - case '\\': - if off+1 > len(msg) { - return len(msg), ErrBuf - } - - if bs == nil { - bs = []byte(s) - } - - // check for \DDD - if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { - bs[i] = dddToByte(bs[i+1:]) - copy(bs[i+1:ls-3], bs[i+4:]) - ls -= 3 - compOff += 3 - } else { - copy(bs[i:ls-1], bs[i+1:]) - ls-- - compOff++ - } - - wasDot = false - case '.': - if wasDot { - // two dots back to back is not legal - return len(msg), ErrRdata - } - wasDot = true - - labelLen := i - begin - if labelLen >= 1<<6 { // top two bits of length must be clear - return len(msg), ErrRdata - } - - // off can already (we're in a loop) be bigger than len(msg) - // this happens when a name isn't fully qualified - if off+1+labelLen > len(msg) { - return len(msg), ErrBuf - } - - // Don't try to compress '.' - // We should only compress when compress is true, but we should also still pick - // up names that can be used for *future* compression(s). - if compression.valid() && !isRootLabel(s, bs, begin, ls) { - if p, ok := compression.find(s[compBegin:]); ok { - // The first hit is the longest matching dname - // keep the pointer offset we get back and store - // the offset of the current name, because that's - // where we need to insert the pointer later - - // If compress is true, we're allowed to compress this dname - if compress { - pointer = p // Where to point to - break loop - } - } else if off < maxCompressionOffset { - // Only offsets smaller than maxCompressionOffset can be used. - compression.insert(s[compBegin:], off) - } - } - - // The following is covered by the length check above. - msg[off] = byte(labelLen) - - if bs == nil { - copy(msg[off+1:], s[begin:i]) - } else { - copy(msg[off+1:], bs[begin:i]) - } - off += 1 + labelLen - - begin = i + 1 - compBegin = begin + compOff - default: - wasDot = false - } - } - - // Root label is special - if isRootLabel(s, bs, 0, ls) { - return off, nil - } - - // If we did compression and we find something add the pointer here - if pointer != -1 { - // We have two bytes (14 bits) to put the pointer in - binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000)) - return off + 2, nil - } - - if off < len(msg) { - msg[off] = 0 - } - - return off + 1, nil -} - -// isRootLabel returns whether s or bs, from off to end, is the root -// label ".". -// -// If bs is nil, s will be checked, otherwise bs will be checked. -func isRootLabel(s string, bs []byte, off, end int) bool { - if bs == nil { - return s[off:end] == "." - } - - return end-off == 1 && bs[off] == '.' -} - -// Unpack a domain name. -// In addition to the simple sequences of counted strings above, -// domain names are allowed to refer to strings elsewhere in the -// packet, to avoid repeating common suffixes when returning -// many entries in a single domain. The pointers are marked -// by a length byte with the top two bits set. Ignoring those -// two bits, that byte and the next give a 14 bit offset from msg[0] -// where we should pick up the trail. -// Note that if we jump elsewhere in the packet, -// we return off1 == the offset after the first pointer we found, -// which is where the next record will start. -// In theory, the pointers are only allowed to jump backward. -// We let them jump anywhere and stop jumping after a while. - -// UnpackDomainName unpacks a domain name into a string. It returns -// the name, the new offset into msg and any error that occurred. -// -// When an error is encountered, the unpacked name will be discarded -// and len(msg) will be returned as the offset. -func UnpackDomainName(msg []byte, off int) (string, int, error) { - s := make([]byte, 0, maxDomainNamePresentationLength) - off1 := 0 - lenmsg := len(msg) - budget := maxDomainNameWireOctets - ptr := 0 // number of pointers followed -Loop: - for { - if off >= lenmsg { - return "", lenmsg, ErrBuf - } - c := int(msg[off]) - off++ - switch c & 0xC0 { - case 0x00: - if c == 0x00 { - // end of name - break Loop - } - // literal string - if off+c > lenmsg { - return "", lenmsg, ErrBuf - } - budget -= c + 1 // +1 for the label separator - if budget <= 0 { - return "", lenmsg, ErrLongDomain - } - for _, b := range msg[off : off+c] { - switch b { - case '.', '(', ')', ';', ' ', '@': - fallthrough - case '"', '\\': - s = append(s, '\\', b) - default: - if b < ' ' || b > '~' { // unprintable, use \DDD - s = append(s, escapeByte(b)...) - } else { - s = append(s, b) - } - } - } - s = append(s, '.') - off += c - case 0xC0: - // pointer to somewhere else in msg. - // remember location after first ptr, - // since that's how many bytes we consumed. - // also, don't follow too many pointers -- - // maybe there's a loop. - if off >= lenmsg { - return "", lenmsg, ErrBuf - } - c1 := msg[off] - off++ - if ptr == 0 { - off1 = off - } - if ptr++; ptr > maxCompressionPointers { - return "", lenmsg, &Error{err: "too many compression pointers"} - } - // pointer should guarantee that it advances and points forwards at least - // but the condition on previous three lines guarantees that it's - // at least loop-free - off = (c^0xC0)<<8 | int(c1) - default: - // 0x80 and 0x40 are reserved - return "", lenmsg, ErrRdata - } - } - if ptr == 0 { - off1 = off - } - if len(s) == 0 { - return ".", off1, nil - } - return string(s), off1, nil -} - -func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { - if len(txt) == 0 { - if offset >= len(msg) { - return offset, ErrBuf - } - msg[offset] = 0 - return offset, nil - } - var err error - for _, s := range txt { - if len(s) > len(tmp) { - return offset, ErrBuf - } - offset, err = packTxtString(s, msg, offset, tmp) - if err != nil { - return offset, err - } - } - return offset, nil -} - -func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { - lenByteOffset := offset - if offset >= len(msg) || len(s) > len(tmp) { - return offset, ErrBuf - } - offset++ - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { - if len(msg) <= offset { - return offset, ErrBuf - } - if bs[i] == '\\' { - i++ - if i == len(bs) { - break - } - // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) - i += 2 - } else { - msg[offset] = bs[i] - } - } else { - msg[offset] = bs[i] - } - offset++ - } - l := offset - lenByteOffset - 1 - if l > 255 { - return offset, &Error{err: "string exceeded 255 bytes in txt"} - } - msg[lenByteOffset] = byte(l) - return offset, nil -} - -func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { - if offset >= len(msg) || len(s) > len(tmp) { - return offset, ErrBuf - } - bs := tmp[:len(s)] - copy(bs, s) - for i := 0; i < len(bs); i++ { - if len(msg) <= offset { - return offset, ErrBuf - } - if bs[i] == '\\' { - i++ - if i == len(bs) { - break - } - // check for \DDD - if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { - msg[offset] = dddToByte(bs[i:]) - i += 2 - } else { - msg[offset] = bs[i] - } - } else { - msg[offset] = bs[i] - } - offset++ - } - return offset, nil -} - -func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { - off = off0 - var s string - for off < len(msg) && err == nil { - s, off, err = unpackString(msg, off) - if err == nil { - ss = append(ss, s) - } - } - return -} - -// Helpers for dealing with escaped bytes -func isDigit(b byte) bool { return b >= '0' && b <= '9' } - -func dddToByte(s []byte) byte { - _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 - return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) -} - -func dddStringToByte(s string) byte { - _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 - return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) -} - -// Helper function for packing and unpacking -func intToBytes(i *big.Int, length int) []byte { - buf := i.Bytes() - if len(buf) < length { - b := make([]byte, length) - copy(b[length-len(buf):], buf) - return b - } - return buf -} - -// PackRR packs a resource record rr into msg[off:]. -// See PackDomainName for documentation about the compression. -func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { - headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress) - if err == nil { - // packRR no longer sets the Rdlength field on the rr, but - // callers might be expecting it so we set it here. - rr.Header().Rdlength = uint16(off1 - headerEnd) - } - return off1, err -} - -func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) { - if rr == nil { - return len(msg), len(msg), &Error{err: "nil rr"} - } - - headerEnd, err = rr.Header().packHeader(msg, off, compression, compress) - if err != nil { - return headerEnd, len(msg), err - } - - off1, err = rr.pack(msg, headerEnd, compression, compress) - if err != nil { - return headerEnd, len(msg), err - } - - rdlength := off1 - headerEnd - if int(uint16(rdlength)) != rdlength { // overflow - return headerEnd, len(msg), ErrRdata - } - - // The RDLENGTH field is the last field in the header and we set it here. - binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength)) - return headerEnd, off1, nil -} - -// UnpackRR unpacks msg[off:] into an RR. -func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { - h, off, msg, err := unpackHeader(msg, off) - if err != nil { - return nil, len(msg), err - } - - return UnpackRRWithHeader(h, msg, off) -} - -// UnpackRRWithHeader unpacks the record type specific payload given an existing -// RR_Header. -func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) { - if newFn, ok := TypeToRR[h.Rrtype]; ok { - rr = newFn() - *rr.Header() = h - } else { - rr = &RFC3597{Hdr: h} - } - - if noRdata(h) { - return rr, off, nil - } - - end := off + int(h.Rdlength) - - off, err = rr.unpack(msg, off) - if err != nil { - return nil, end, err - } - if off != end { - return &h, end, &Error{err: "bad rdlength"} - } - - return rr, off, nil -} - -// unpackRRslice unpacks msg[off:] into an []RR. -// If we cannot unpack the whole array, then it will return nil -func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) { - var r RR - // Don't pre-allocate, l may be under attacker control - var dst []RR - for i := 0; i < l; i++ { - off1 := off - r, off, err = UnpackRR(msg, off) - if err != nil { - off = len(msg) - break - } - // If offset does not increase anymore, l is a lie - if off1 == off { - l = i - break - } - dst = append(dst, r) - } - if err != nil && off == len(msg) { - dst = nil - } - return dst, off, err -} - -// Convert a MsgHdr to a string, with dig-like headers: -// -//;; opcode: QUERY, status: NOERROR, id: 48404 -// -//;; flags: qr aa rd ra; -func (h *MsgHdr) String() string { - if h == nil { - return " MsgHdr" - } - - s := ";; opcode: " + OpcodeToString[h.Opcode] - s += ", status: " + RcodeToString[h.Rcode] - s += ", id: " + strconv.Itoa(int(h.Id)) + "\n" - - s += ";; flags:" - if h.Response { - s += " qr" - } - if h.Authoritative { - s += " aa" - } - if h.Truncated { - s += " tc" - } - if h.RecursionDesired { - s += " rd" - } - if h.RecursionAvailable { - s += " ra" - } - if h.Zero { // Hmm - s += " z" - } - if h.AuthenticatedData { - s += " ad" - } - if h.CheckingDisabled { - s += " cd" - } - - s += ";" - return s -} - -// Pack packs a Msg: it is converted to to wire format. -// If the dns.Compress is true the message will be in compressed wire format. -func (dns *Msg) Pack() (msg []byte, err error) { - return dns.PackBuffer(nil) -} - -// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated. -func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { - // If this message can't be compressed, avoid filling the - // compression map and creating garbage. - if dns.Compress && dns.isCompressible() { - compression := make(map[string]uint16) // Compression pointer mappings. - return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true) - } - - return dns.packBufferWithCompressionMap(buf, compressionMap{}, false) -} - -// packBufferWithCompressionMap packs a Msg, using the given buffer buf. -func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) { - if dns.Rcode < 0 || dns.Rcode > 0xFFF { - return nil, ErrRcode - } - - // Set extended rcode unconditionally if we have an opt, this will allow - // reseting the extended rcode bits if they need to. - if opt := dns.IsEdns0(); opt != nil { - opt.SetExtendedRcode(uint16(dns.Rcode)) - } else if dns.Rcode > 0xF { - // If Rcode is an extended one and opt is nil, error out. - return nil, ErrExtendedRcode - } - - // Convert convenient Msg into wire-like Header. - var dh Header - dh.Id = dns.Id - dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF) - if dns.Response { - dh.Bits |= _QR - } - if dns.Authoritative { - dh.Bits |= _AA - } - if dns.Truncated { - dh.Bits |= _TC - } - if dns.RecursionDesired { - dh.Bits |= _RD - } - if dns.RecursionAvailable { - dh.Bits |= _RA - } - if dns.Zero { - dh.Bits |= _Z - } - if dns.AuthenticatedData { - dh.Bits |= _AD - } - if dns.CheckingDisabled { - dh.Bits |= _CD - } - - dh.Qdcount = uint16(len(dns.Question)) - dh.Ancount = uint16(len(dns.Answer)) - dh.Nscount = uint16(len(dns.Ns)) - dh.Arcount = uint16(len(dns.Extra)) - - // We need the uncompressed length here, because we first pack it and then compress it. - msg = buf - uncompressedLen := msgLenWithCompressionMap(dns, nil) - if packLen := uncompressedLen + 1; len(msg) < packLen { - msg = make([]byte, packLen) - } - - // Pack it in: header and then the pieces. - off := 0 - off, err = dh.pack(msg, off, compression, compress) - if err != nil { - return nil, err - } - for _, r := range dns.Question { - off, err = r.pack(msg, off, compression, compress) - if err != nil { - return nil, err - } - } - for _, r := range dns.Answer { - _, off, err = packRR(r, msg, off, compression, compress) - if err != nil { - return nil, err - } - } - for _, r := range dns.Ns { - _, off, err = packRR(r, msg, off, compression, compress) - if err != nil { - return nil, err - } - } - for _, r := range dns.Extra { - _, off, err = packRR(r, msg, off, compression, compress) - if err != nil { - return nil, err - } - } - return msg[:off], nil -} - -func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { - // If we are at the end of the message we should return *just* the - // header. This can still be useful to the caller. 9.9.9.9 sends these - // when responding with REFUSED for instance. - if off == len(msg) { - // reset sections before returning - dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil - return nil - } - - // Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are - // attacker controlled. This means we can't use them to pre-allocate - // slices. - dns.Question = nil - for i := 0; i < int(dh.Qdcount); i++ { - off1 := off - var q Question - q, off, err = unpackQuestion(msg, off) - if err != nil { - return err - } - if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! - dh.Qdcount = uint16(i) - break - } - dns.Question = append(dns.Question, q) - } - - dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off) - // The header counts might have been wrong so we need to update it - dh.Ancount = uint16(len(dns.Answer)) - if err == nil { - dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off) - } - // The header counts might have been wrong so we need to update it - dh.Nscount = uint16(len(dns.Ns)) - if err == nil { - dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off) - } - // The header counts might have been wrong so we need to update it - dh.Arcount = uint16(len(dns.Extra)) - - // Set extended Rcode - if opt := dns.IsEdns0(); opt != nil { - dns.Rcode |= opt.ExtendedRcode() - } - - if off != len(msg) { - // TODO(miek) make this an error? - // use PackOpt to let people tell how detailed the error reporting should be? - // println("dns: extra bytes in dns packet", off, "<", len(msg)) - } - return err - -} - -// Unpack unpacks a binary message to a Msg structure. -func (dns *Msg) Unpack(msg []byte) (err error) { - dh, off, err := unpackMsgHdr(msg, 0) - if err != nil { - return err - } - - dns.setHdr(dh) - return dns.unpack(dh, msg, off) -} - -// Convert a complete message to a string with dig-like output. -func (dns *Msg) String() string { - if dns == nil { - return " MsgHdr" - } - s := dns.MsgHdr.String() + " " - s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " - s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " - s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " - s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" - if len(dns.Question) > 0 { - s += "\n;; QUESTION SECTION:\n" - for _, r := range dns.Question { - s += r.String() + "\n" - } - } - if len(dns.Answer) > 0 { - s += "\n;; ANSWER SECTION:\n" - for _, r := range dns.Answer { - if r != nil { - s += r.String() + "\n" - } - } - } - if len(dns.Ns) > 0 { - s += "\n;; AUTHORITY SECTION:\n" - for _, r := range dns.Ns { - if r != nil { - s += r.String() + "\n" - } - } - } - if len(dns.Extra) > 0 { - s += "\n;; ADDITIONAL SECTION:\n" - for _, r := range dns.Extra { - if r != nil { - s += r.String() + "\n" - } - } - } - return s -} - -// isCompressible returns whether the msg may be compressible. -func (dns *Msg) isCompressible() bool { - // If we only have one question, there is nothing we can ever compress. - return len(dns.Question) > 1 || len(dns.Answer) > 0 || - len(dns.Ns) > 0 || len(dns.Extra) > 0 -} - -// Len returns the message length when in (un)compressed wire format. -// If dns.Compress is true compression it is taken into account. Len() -// is provided to be a faster way to get the size of the resulting packet, -// than packing it, measuring the size and discarding the buffer. -func (dns *Msg) Len() int { - // If this message can't be compressed, avoid filling the - // compression map and creating garbage. - if dns.Compress && dns.isCompressible() { - compression := make(map[string]struct{}) - return msgLenWithCompressionMap(dns, compression) - } - - return msgLenWithCompressionMap(dns, nil) -} - -func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int { - l := headerSize - - for _, r := range dns.Question { - l += r.len(l, compression) - } - for _, r := range dns.Answer { - if r != nil { - l += r.len(l, compression) - } - } - for _, r := range dns.Ns { - if r != nil { - l += r.len(l, compression) - } - } - for _, r := range dns.Extra { - if r != nil { - l += r.len(l, compression) - } - } - - return l -} - -func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int { - if s == "" || s == "." { - return 1 - } - - escaped := strings.Contains(s, "\\") - - if compression != nil && (compress || off < maxCompressionOffset) { - // compressionLenSearch will insert the entry into the compression - // map if it doesn't contain it. - if l, ok := compressionLenSearch(compression, s, off); ok && compress { - if escaped { - return escapedNameLen(s[:l]) + 2 - } - - return l + 2 - } - } - - if escaped { - return escapedNameLen(s) + 1 - } - - return len(s) + 1 -} - -func escapedNameLen(s string) int { - nameLen := len(s) - for i := 0; i < len(s); i++ { - if s[i] != '\\' { - continue - } - - if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { - nameLen -= 3 - i += 3 - } else { - nameLen-- - i++ - } - } - - return nameLen -} - -func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) { - for off, end := 0, false; !end; off, end = NextLabel(s, off) { - if _, ok := c[s[off:]]; ok { - return off, true - } - - if msgOff+off < maxCompressionOffset { - c[s[off:]] = struct{}{} - } - } - - return 0, false -} - -// Copy returns a new RR which is a deep-copy of r. -func Copy(r RR) RR { return r.copy() } - -// Len returns the length (in octets) of the uncompressed RR in wire format. -func Len(r RR) int { return r.len(0, nil) } - -// Copy returns a new *Msg which is a deep-copy of dns. -func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) } - -// CopyTo copies the contents to the provided message using a deep-copy and returns the copy. -func (dns *Msg) CopyTo(r1 *Msg) *Msg { - r1.MsgHdr = dns.MsgHdr - r1.Compress = dns.Compress - - if len(dns.Question) > 0 { - r1.Question = make([]Question, len(dns.Question)) - copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy - } - - rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) - r1.Answer, rrArr = rrArr[:0:len(dns.Answer)], rrArr[len(dns.Answer):] - r1.Ns, rrArr = rrArr[:0:len(dns.Ns)], rrArr[len(dns.Ns):] - r1.Extra = rrArr[:0:len(dns.Extra)] - - for _, r := range dns.Answer { - r1.Answer = append(r1.Answer, r.copy()) - } - - for _, r := range dns.Ns { - r1.Ns = append(r1.Ns, r.copy()) - } - - for _, r := range dns.Extra { - r1.Extra = append(r1.Extra, r.copy()) - } - - return r1 -} - -func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { - off, err := packDomainName(q.Name, msg, off, compression, compress) - if err != nil { - return off, err - } - off, err = packUint16(q.Qtype, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(q.Qclass, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func unpackQuestion(msg []byte, off int) (Question, int, error) { - var ( - q Question - err error - ) - q.Name, off, err = UnpackDomainName(msg, off) - if err != nil { - return q, off, err - } - if off == len(msg) { - return q, off, nil - } - q.Qtype, off, err = unpackUint16(msg, off) - if err != nil { - return q, off, err - } - if off == len(msg) { - return q, off, nil - } - q.Qclass, off, err = unpackUint16(msg, off) - if off == len(msg) { - return q, off, nil - } - return q, off, err -} - -func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { - off, err := packUint16(dh.Id, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(dh.Bits, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(dh.Qdcount, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(dh.Ancount, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(dh.Nscount, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(dh.Arcount, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func unpackMsgHdr(msg []byte, off int) (Header, int, error) { - var ( - dh Header - err error - ) - dh.Id, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - dh.Bits, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - dh.Qdcount, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - dh.Ancount, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - dh.Nscount, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - dh.Arcount, off, err = unpackUint16(msg, off) - if err != nil { - return dh, off, err - } - return dh, off, nil -} - -// setHdr set the header in the dns using the binary data in dh. -func (dns *Msg) setHdr(dh Header) { - dns.Id = dh.Id - dns.Response = dh.Bits&_QR != 0 - dns.Opcode = int(dh.Bits>>11) & 0xF - dns.Authoritative = dh.Bits&_AA != 0 - dns.Truncated = dh.Bits&_TC != 0 - dns.RecursionDesired = dh.Bits&_RD != 0 - dns.RecursionAvailable = dh.Bits&_RA != 0 - dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite. - dns.AuthenticatedData = dh.Bits&_AD != 0 - dns.CheckingDisabled = dh.Bits&_CD != 0 - dns.Rcode = int(dh.Bits & 0xF) -} diff --git a/vendor/github.com/miekg/dns/msg_helpers.go b/vendor/github.com/miekg/dns/msg_helpers.go deleted file mode 100644 index ecd9280f4..000000000 --- a/vendor/github.com/miekg/dns/msg_helpers.go +++ /dev/null @@ -1,648 +0,0 @@ -package dns - -import ( - "encoding/base32" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "net" - "strings" -) - -// helper functions called from the generated zmsg.go - -// These function are named after the tag to help pack/unpack, if there is no tag it is the name -// of the type they pack/unpack (string, int, etc). We prefix all with unpackData or packData, so packDataA or -// packDataDomainName. - -func unpackDataA(msg []byte, off int) (net.IP, int, error) { - if off+net.IPv4len > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking a"} - } - a := append(make(net.IP, 0, net.IPv4len), msg[off:off+net.IPv4len]...) - off += net.IPv4len - return a, off, nil -} - -func packDataA(a net.IP, msg []byte, off int) (int, error) { - switch len(a) { - case net.IPv4len, net.IPv6len: - // It must be a slice of 4, even if it is 16, we encode only the first 4 - if off+net.IPv4len > len(msg) { - return len(msg), &Error{err: "overflow packing a"} - } - - copy(msg[off:], a.To4()) - off += net.IPv4len - case 0: - // Allowed, for dynamic updates. - default: - return len(msg), &Error{err: "overflow packing a"} - } - return off, nil -} - -func unpackDataAAAA(msg []byte, off int) (net.IP, int, error) { - if off+net.IPv6len > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking aaaa"} - } - aaaa := append(make(net.IP, 0, net.IPv6len), msg[off:off+net.IPv6len]...) - off += net.IPv6len - return aaaa, off, nil -} - -func packDataAAAA(aaaa net.IP, msg []byte, off int) (int, error) { - switch len(aaaa) { - case net.IPv6len: - if off+net.IPv6len > len(msg) { - return len(msg), &Error{err: "overflow packing aaaa"} - } - - copy(msg[off:], aaaa) - off += net.IPv6len - case 0: - // Allowed, dynamic updates. - default: - return len(msg), &Error{err: "overflow packing aaaa"} - } - return off, nil -} - -// unpackHeader unpacks an RR header, returning the offset to the end of the header and a -// re-sliced msg according to the expected length of the RR. -func unpackHeader(msg []byte, off int) (rr RR_Header, off1 int, truncmsg []byte, err error) { - hdr := RR_Header{} - if off == len(msg) { - return hdr, off, msg, nil - } - - hdr.Name, off, err = UnpackDomainName(msg, off) - if err != nil { - return hdr, len(msg), msg, err - } - hdr.Rrtype, off, err = unpackUint16(msg, off) - if err != nil { - return hdr, len(msg), msg, err - } - hdr.Class, off, err = unpackUint16(msg, off) - if err != nil { - return hdr, len(msg), msg, err - } - hdr.Ttl, off, err = unpackUint32(msg, off) - if err != nil { - return hdr, len(msg), msg, err - } - hdr.Rdlength, off, err = unpackUint16(msg, off) - if err != nil { - return hdr, len(msg), msg, err - } - msg, err = truncateMsgFromRdlength(msg, off, hdr.Rdlength) - return hdr, off, msg, err -} - -// packHeader packs an RR header, returning the offset to the end of the header. -// See PackDomainName for documentation about the compression. -func (hdr RR_Header) packHeader(msg []byte, off int, compression compressionMap, compress bool) (int, error) { - if off == len(msg) { - return off, nil - } - - off, err := packDomainName(hdr.Name, msg, off, compression, compress) - if err != nil { - return len(msg), err - } - off, err = packUint16(hdr.Rrtype, msg, off) - if err != nil { - return len(msg), err - } - off, err = packUint16(hdr.Class, msg, off) - if err != nil { - return len(msg), err - } - off, err = packUint32(hdr.Ttl, msg, off) - if err != nil { - return len(msg), err - } - off, err = packUint16(0, msg, off) // The RDLENGTH field will be set later in packRR. - if err != nil { - return len(msg), err - } - return off, nil -} - -// helper helper functions. - -// truncateMsgFromRdLength truncates msg to match the expected length of the RR. -// Returns an error if msg is smaller than the expected size. -func truncateMsgFromRdlength(msg []byte, off int, rdlength uint16) (truncmsg []byte, err error) { - lenrd := off + int(rdlength) - if lenrd > len(msg) { - return msg, &Error{err: "overflowing header size"} - } - return msg[:lenrd], nil -} - -var base32HexNoPadEncoding = base32.HexEncoding.WithPadding(base32.NoPadding) - -func fromBase32(s []byte) (buf []byte, err error) { - for i, b := range s { - if b >= 'a' && b <= 'z' { - s[i] = b - 32 - } - } - buflen := base32HexNoPadEncoding.DecodedLen(len(s)) - buf = make([]byte, buflen) - n, err := base32HexNoPadEncoding.Decode(buf, s) - buf = buf[:n] - return -} - -func toBase32(b []byte) string { - return base32HexNoPadEncoding.EncodeToString(b) -} - -func fromBase64(s []byte) (buf []byte, err error) { - buflen := base64.StdEncoding.DecodedLen(len(s)) - buf = make([]byte, buflen) - n, err := base64.StdEncoding.Decode(buf, s) - buf = buf[:n] - return -} - -func toBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) } - -// dynamicUpdate returns true if the Rdlength is zero. -func noRdata(h RR_Header) bool { return h.Rdlength == 0 } - -func unpackUint8(msg []byte, off int) (i uint8, off1 int, err error) { - if off+1 > len(msg) { - return 0, len(msg), &Error{err: "overflow unpacking uint8"} - } - return msg[off], off + 1, nil -} - -func packUint8(i uint8, msg []byte, off int) (off1 int, err error) { - if off+1 > len(msg) { - return len(msg), &Error{err: "overflow packing uint8"} - } - msg[off] = i - return off + 1, nil -} - -func unpackUint16(msg []byte, off int) (i uint16, off1 int, err error) { - if off+2 > len(msg) { - return 0, len(msg), &Error{err: "overflow unpacking uint16"} - } - return binary.BigEndian.Uint16(msg[off:]), off + 2, nil -} - -func packUint16(i uint16, msg []byte, off int) (off1 int, err error) { - if off+2 > len(msg) { - return len(msg), &Error{err: "overflow packing uint16"} - } - binary.BigEndian.PutUint16(msg[off:], i) - return off + 2, nil -} - -func unpackUint32(msg []byte, off int) (i uint32, off1 int, err error) { - if off+4 > len(msg) { - return 0, len(msg), &Error{err: "overflow unpacking uint32"} - } - return binary.BigEndian.Uint32(msg[off:]), off + 4, nil -} - -func packUint32(i uint32, msg []byte, off int) (off1 int, err error) { - if off+4 > len(msg) { - return len(msg), &Error{err: "overflow packing uint32"} - } - binary.BigEndian.PutUint32(msg[off:], i) - return off + 4, nil -} - -func unpackUint48(msg []byte, off int) (i uint64, off1 int, err error) { - if off+6 > len(msg) { - return 0, len(msg), &Error{err: "overflow unpacking uint64 as uint48"} - } - // Used in TSIG where the last 48 bits are occupied, so for now, assume a uint48 (6 bytes) - i = uint64(msg[off])<<40 | uint64(msg[off+1])<<32 | uint64(msg[off+2])<<24 | uint64(msg[off+3])<<16 | - uint64(msg[off+4])<<8 | uint64(msg[off+5]) - off += 6 - return i, off, nil -} - -func packUint48(i uint64, msg []byte, off int) (off1 int, err error) { - if off+6 > len(msg) { - return len(msg), &Error{err: "overflow packing uint64 as uint48"} - } - msg[off] = byte(i >> 40) - msg[off+1] = byte(i >> 32) - msg[off+2] = byte(i >> 24) - msg[off+3] = byte(i >> 16) - msg[off+4] = byte(i >> 8) - msg[off+5] = byte(i) - off += 6 - return off, nil -} - -func unpackUint64(msg []byte, off int) (i uint64, off1 int, err error) { - if off+8 > len(msg) { - return 0, len(msg), &Error{err: "overflow unpacking uint64"} - } - return binary.BigEndian.Uint64(msg[off:]), off + 8, nil -} - -func packUint64(i uint64, msg []byte, off int) (off1 int, err error) { - if off+8 > len(msg) { - return len(msg), &Error{err: "overflow packing uint64"} - } - binary.BigEndian.PutUint64(msg[off:], i) - off += 8 - return off, nil -} - -func unpackString(msg []byte, off int) (string, int, error) { - if off+1 > len(msg) { - return "", off, &Error{err: "overflow unpacking txt"} - } - l := int(msg[off]) - if off+l+1 > len(msg) { - return "", off, &Error{err: "overflow unpacking txt"} - } - var s strings.Builder - s.Grow(l) - for _, b := range msg[off+1 : off+1+l] { - switch { - case b == '"' || b == '\\': - s.WriteByte('\\') - s.WriteByte(b) - case b < ' ' || b > '~': // unprintable - s.WriteString(escapeByte(b)) - default: - s.WriteByte(b) - } - } - off += 1 + l - return s.String(), off, nil -} - -func packString(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packTxtString(s, msg, off, txtTmp) - if err != nil { - return len(msg), err - } - return off, nil -} - -func unpackStringBase32(msg []byte, off, end int) (string, int, error) { - if end > len(msg) { - return "", len(msg), &Error{err: "overflow unpacking base32"} - } - s := toBase32(msg[off:end]) - return s, end, nil -} - -func packStringBase32(s string, msg []byte, off int) (int, error) { - b32, err := fromBase32([]byte(s)) - if err != nil { - return len(msg), err - } - if off+len(b32) > len(msg) { - return len(msg), &Error{err: "overflow packing base32"} - } - copy(msg[off:off+len(b32)], b32) - off += len(b32) - return off, nil -} - -func unpackStringBase64(msg []byte, off, end int) (string, int, error) { - // Rest of the RR is base64 encoded value, so we don't need an explicit length - // to be set. Thus far all RR's that have base64 encoded fields have those as their - // last one. What we do need is the end of the RR! - if end > len(msg) { - return "", len(msg), &Error{err: "overflow unpacking base64"} - } - s := toBase64(msg[off:end]) - return s, end, nil -} - -func packStringBase64(s string, msg []byte, off int) (int, error) { - b64, err := fromBase64([]byte(s)) - if err != nil { - return len(msg), err - } - if off+len(b64) > len(msg) { - return len(msg), &Error{err: "overflow packing base64"} - } - copy(msg[off:off+len(b64)], b64) - off += len(b64) - return off, nil -} - -func unpackStringHex(msg []byte, off, end int) (string, int, error) { - // Rest of the RR is hex encoded value, so we don't need an explicit length - // to be set. NSEC and TSIG have hex fields with a length field. - // What we do need is the end of the RR! - if end > len(msg) { - return "", len(msg), &Error{err: "overflow unpacking hex"} - } - - s := hex.EncodeToString(msg[off:end]) - return s, end, nil -} - -func packStringHex(s string, msg []byte, off int) (int, error) { - h, err := hex.DecodeString(s) - if err != nil { - return len(msg), err - } - if off+len(h) > len(msg) { - return len(msg), &Error{err: "overflow packing hex"} - } - copy(msg[off:off+len(h)], h) - off += len(h) - return off, nil -} - -func unpackStringAny(msg []byte, off, end int) (string, int, error) { - if end > len(msg) { - return "", len(msg), &Error{err: "overflow unpacking anything"} - } - return string(msg[off:end]), end, nil -} - -func packStringAny(s string, msg []byte, off int) (int, error) { - if off+len(s) > len(msg) { - return len(msg), &Error{err: "overflow packing anything"} - } - copy(msg[off:off+len(s)], s) - off += len(s) - return off, nil -} - -func unpackStringTxt(msg []byte, off int) ([]string, int, error) { - txt, off, err := unpackTxt(msg, off) - if err != nil { - return nil, len(msg), err - } - return txt, off, nil -} - -func packStringTxt(s []string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) // If the whole string consists out of \DDD we need this many. - off, err := packTxt(s, msg, off, txtTmp) - if err != nil { - return len(msg), err - } - return off, nil -} - -func unpackDataOpt(msg []byte, off int) ([]EDNS0, int, error) { - var edns []EDNS0 -Option: - var code uint16 - if off+4 > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - code = binary.BigEndian.Uint16(msg[off:]) - off += 2 - optlen := binary.BigEndian.Uint16(msg[off:]) - off += 2 - if off+int(optlen) > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking opt"} - } - switch code { - case EDNS0NSID: - e := new(EDNS0_NSID) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0SUBNET: - e := new(EDNS0_SUBNET) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0COOKIE: - e := new(EDNS0_COOKIE) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0UL: - e := new(EDNS0_UL) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0LLQ: - e := new(EDNS0_LLQ) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0DAU: - e := new(EDNS0_DAU) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0DHU: - e := new(EDNS0_DHU) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0N3U: - e := new(EDNS0_N3U) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - case EDNS0PADDING: - e := new(EDNS0_PADDING) - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - default: - e := new(EDNS0_LOCAL) - e.Code = code - if err := e.unpack(msg[off : off+int(optlen)]); err != nil { - return nil, len(msg), err - } - edns = append(edns, e) - off += int(optlen) - } - - if off < len(msg) { - goto Option - } - - return edns, off, nil -} - -func packDataOpt(options []EDNS0, msg []byte, off int) (int, error) { - for _, el := range options { - b, err := el.pack() - if err != nil || off+3 > len(msg) { - return len(msg), &Error{err: "overflow packing opt"} - } - binary.BigEndian.PutUint16(msg[off:], el.Option()) // Option code - binary.BigEndian.PutUint16(msg[off+2:], uint16(len(b))) // Length - off += 4 - if off+len(b) > len(msg) { - copy(msg[off:], b) - off = len(msg) - continue - } - // Actual data - copy(msg[off:off+len(b)], b) - off += len(b) - } - return off, nil -} - -func unpackStringOctet(msg []byte, off int) (string, int, error) { - s := string(msg[off:]) - return s, len(msg), nil -} - -func packStringOctet(s string, msg []byte, off int) (int, error) { - txtTmp := make([]byte, 256*4+1) - off, err := packOctetString(s, msg, off, txtTmp) - if err != nil { - return len(msg), err - } - return off, nil -} - -func unpackDataNsec(msg []byte, off int) ([]uint16, int, error) { - var nsec []uint16 - length, window, lastwindow := 0, 0, -1 - for off < len(msg) { - if off+2 > len(msg) { - return nsec, len(msg), &Error{err: "overflow unpacking nsecx"} - } - window = int(msg[off]) - length = int(msg[off+1]) - off += 2 - if window <= lastwindow { - // RFC 4034: Blocks are present in the NSEC RR RDATA in - // increasing numerical order. - return nsec, len(msg), &Error{err: "out of order NSEC block"} - } - if length == 0 { - // RFC 4034: Blocks with no types present MUST NOT be included. - return nsec, len(msg), &Error{err: "empty NSEC block"} - } - if length > 32 { - return nsec, len(msg), &Error{err: "NSEC block too long"} - } - if off+length > len(msg) { - return nsec, len(msg), &Error{err: "overflowing NSEC block"} - } - - // Walk the bytes in the window and extract the type bits - for j, b := range msg[off : off+length] { - // Check the bits one by one, and set the type - if b&0x80 == 0x80 { - nsec = append(nsec, uint16(window*256+j*8+0)) - } - if b&0x40 == 0x40 { - nsec = append(nsec, uint16(window*256+j*8+1)) - } - if b&0x20 == 0x20 { - nsec = append(nsec, uint16(window*256+j*8+2)) - } - if b&0x10 == 0x10 { - nsec = append(nsec, uint16(window*256+j*8+3)) - } - if b&0x8 == 0x8 { - nsec = append(nsec, uint16(window*256+j*8+4)) - } - if b&0x4 == 0x4 { - nsec = append(nsec, uint16(window*256+j*8+5)) - } - if b&0x2 == 0x2 { - nsec = append(nsec, uint16(window*256+j*8+6)) - } - if b&0x1 == 0x1 { - nsec = append(nsec, uint16(window*256+j*8+7)) - } - } - off += length - lastwindow = window - } - return nsec, off, nil -} - -func packDataNsec(bitmap []uint16, msg []byte, off int) (int, error) { - if len(bitmap) == 0 { - return off, nil - } - var lastwindow, lastlength uint16 - for _, t := range bitmap { - window := t / 256 - length := (t-window*256)/8 + 1 - if window > lastwindow && lastlength != 0 { // New window, jump to the new offset - off += int(lastlength) + 2 - lastlength = 0 - } - if window < lastwindow || length < lastlength { - return len(msg), &Error{err: "nsec bits out of order"} - } - if off+2+int(length) > len(msg) { - return len(msg), &Error{err: "overflow packing nsec"} - } - // Setting the window # - msg[off] = byte(window) - // Setting the octets length - msg[off+1] = byte(length) - // Setting the bit value for the type in the right octet - msg[off+1+int(length)] |= byte(1 << (7 - t%8)) - lastwindow, lastlength = window, length - } - off += int(lastlength) + 2 - return off, nil -} - -func unpackDataDomainNames(msg []byte, off, end int) ([]string, int, error) { - var ( - servers []string - s string - err error - ) - if end > len(msg) { - return nil, len(msg), &Error{err: "overflow unpacking domain names"} - } - for off < end { - s, off, err = UnpackDomainName(msg, off) - if err != nil { - return servers, len(msg), err - } - servers = append(servers, s) - } - return servers, off, nil -} - -func packDataDomainNames(names []string, msg []byte, off int, compression compressionMap, compress bool) (int, error) { - var err error - for _, name := range names { - off, err = packDomainName(name, msg, off, compression, compress) - if err != nil { - return len(msg), err - } - } - return off, nil -} diff --git a/vendor/github.com/miekg/dns/msg_truncate.go b/vendor/github.com/miekg/dns/msg_truncate.go deleted file mode 100644 index 4763fc610..000000000 --- a/vendor/github.com/miekg/dns/msg_truncate.go +++ /dev/null @@ -1,106 +0,0 @@ -package dns - -// Truncate ensures the reply message will fit into the requested buffer -// size by removing records that exceed the requested size. -// -// It will first check if the reply fits without compression and then with -// compression. If it won't fit with compression, Scrub then walks the -// record adding as many records as possible without exceeding the -// requested buffer size. -// -// The TC bit will be set if any answer records were excluded from the -// message. This indicates to that the client should retry over TCP. -// -// The appropriate buffer size can be retrieved from the requests OPT -// record, if present, and is transport specific otherwise. dns.MinMsgSize -// should be used for UDP requests without an OPT record, and -// dns.MaxMsgSize for TCP requests without an OPT record. -func (dns *Msg) Truncate(size int) { - if dns.IsTsig() != nil { - // To simplify this implementation, we don't perform - // truncation on responses with a TSIG record. - return - } - - // RFC 6891 mandates that the payload size in an OPT record - // less than 512 bytes must be treated as equal to 512 bytes. - // - // For ease of use, we impose that restriction here. - if size < 512 { - size = 512 - } - - l := msgLenWithCompressionMap(dns, nil) // uncompressed length - if l <= size { - // Don't waste effort compressing this message. - dns.Compress = false - return - } - - dns.Compress = true - - edns0 := dns.popEdns0() - if edns0 != nil { - // Account for the OPT record that gets added at the end, - // by subtracting that length from our budget. - // - // The EDNS(0) OPT record must have the root domain and - // it's length is thus unaffected by compression. - size -= Len(edns0) - } - - compression := make(map[string]struct{}) - - l = headerSize - for _, r := range dns.Question { - l += r.len(l, compression) - } - - var numAnswer int - if l < size { - l, numAnswer = truncateLoop(dns.Answer, size, l, compression) - } - - var numNS int - if l < size { - l, numNS = truncateLoop(dns.Ns, size, l, compression) - } - - var numExtra int - if l < size { - l, numExtra = truncateLoop(dns.Extra, size, l, compression) - } - - // According to RFC 2181, the TC bit should only be set if not all - // of the answer RRs can be included in the response. - dns.Truncated = len(dns.Answer) > numAnswer - - dns.Answer = dns.Answer[:numAnswer] - dns.Ns = dns.Ns[:numNS] - dns.Extra = dns.Extra[:numExtra] - - if edns0 != nil { - // Add the OPT record back onto the additional section. - dns.Extra = append(dns.Extra, edns0) - } -} - -func truncateLoop(rrs []RR, size, l int, compression map[string]struct{}) (int, int) { - for i, r := range rrs { - if r == nil { - continue - } - - l += r.len(l, compression) - if l > size { - // Return size, rather than l prior to this record, - // to prevent any further records being added. - return size, i - } - if l == size { - return l, i + 1 - } - } - - return l, len(rrs) -} diff --git a/vendor/github.com/miekg/dns/nsecx.go b/vendor/github.com/miekg/dns/nsecx.go deleted file mode 100644 index 8f071a473..000000000 --- a/vendor/github.com/miekg/dns/nsecx.go +++ /dev/null @@ -1,95 +0,0 @@ -package dns - -import ( - "crypto/sha1" - "encoding/hex" - "strings" -) - -// HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase. -func HashName(label string, ha uint8, iter uint16, salt string) string { - if ha != SHA1 { - return "" - } - - wireSalt := make([]byte, hex.DecodedLen(len(salt))) - n, err := packStringHex(salt, wireSalt, 0) - if err != nil { - return "" - } - wireSalt = wireSalt[:n] - - name := make([]byte, 255) - off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false) - if err != nil { - return "" - } - name = name[:off] - - s := sha1.New() - // k = 0 - s.Write(name) - s.Write(wireSalt) - nsec3 := s.Sum(nil) - - // k > 0 - for k := uint16(0); k < iter; k++ { - s.Reset() - s.Write(nsec3) - s.Write(wireSalt) - nsec3 = s.Sum(nsec3[:0]) - } - - return toBase32(nsec3) -} - -// Cover returns true if a name is covered by the NSEC3 record -func (rr *NSEC3) Cover(name string) bool { - nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) - owner := strings.ToUpper(rr.Hdr.Name) - labelIndices := Split(owner) - if len(labelIndices) < 2 { - return false - } - ownerHash := owner[:labelIndices[1]-1] - ownerZone := owner[labelIndices[1]:] - if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone - return false - } - - nextHash := rr.NextDomain - - // if empty interval found, try cover wildcard hashes so nameHash shouldn't match with ownerHash - if ownerHash == nextHash && nameHash != ownerHash { // empty interval - return true - } - if ownerHash > nextHash { // end of zone - if nameHash > ownerHash { // covered since there is nothing after ownerHash - return true - } - return nameHash < nextHash // if nameHash is before beginning of zone it is covered - } - if nameHash < ownerHash { // nameHash is before ownerHash, not covered - return false - } - return nameHash < nextHash // if nameHash is before nextHash is it covered (between ownerHash and nextHash) -} - -// Match returns true if a name matches the NSEC3 record -func (rr *NSEC3) Match(name string) bool { - nameHash := HashName(name, rr.Hash, rr.Iterations, rr.Salt) - owner := strings.ToUpper(rr.Hdr.Name) - labelIndices := Split(owner) - if len(labelIndices) < 2 { - return false - } - ownerHash := owner[:labelIndices[1]-1] - ownerZone := owner[labelIndices[1]:] - if !IsSubDomain(ownerZone, strings.ToUpper(name)) { // name is outside owner zone - return false - } - if ownerHash == nameHash { - return true - } - return false -} diff --git a/vendor/github.com/miekg/dns/privaterr.go b/vendor/github.com/miekg/dns/privaterr.go deleted file mode 100644 index d9c0d2677..000000000 --- a/vendor/github.com/miekg/dns/privaterr.go +++ /dev/null @@ -1,132 +0,0 @@ -package dns - -import ( - "fmt" - "strings" -) - -// PrivateRdata is an interface used for implementing "Private Use" RR types, see -// RFC 6895. This allows one to experiment with new RR types, without requesting an -// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove. -type PrivateRdata interface { - // String returns the text presentaton of the Rdata of the Private RR. - String() string - // Parse parses the Rdata of the private RR. - Parse([]string) error - // Pack is used when packing a private RR into a buffer. - Pack([]byte) (int, error) - // Unpack is used when unpacking a private RR from a buffer. - // TODO(miek): diff. signature than Pack, see edns0.go for instance. - Unpack([]byte) (int, error) - // Copy copies the Rdata. - Copy(PrivateRdata) error - // Len returns the length in octets of the Rdata. - Len() int -} - -// PrivateRR represents an RR that uses a PrivateRdata user-defined type. -// It mocks normal RRs and implements dns.RR interface. -type PrivateRR struct { - Hdr RR_Header - Data PrivateRdata -} - -func mkPrivateRR(rrtype uint16) *PrivateRR { - // Panics if RR is not an instance of PrivateRR. - rrfunc, ok := TypeToRR[rrtype] - if !ok { - panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype)) - } - - anyrr := rrfunc() - rr, ok := anyrr.(*PrivateRR) - if !ok { - panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr)) - } - - return rr -} - -// Header return the RR header of r. -func (r *PrivateRR) Header() *RR_Header { return &r.Hdr } - -func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() } - -// Private len and copy parts to satisfy RR interface. -func (r *PrivateRR) len(off int, compression map[string]struct{}) int { - l := r.Hdr.len(off, compression) - l += r.Data.Len() - return l -} - -func (r *PrivateRR) copy() RR { - // make new RR like this: - rr := mkPrivateRR(r.Hdr.Rrtype) - rr.Hdr = r.Hdr - - err := r.Data.Copy(rr.Data) - if err != nil { - panic("dns: got value that could not be used to copy Private rdata") - } - return rr -} - -func (r *PrivateRR) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { - n, err := r.Data.Pack(msg[off:]) - if err != nil { - return len(msg), err - } - off += n - return off, nil -} - -func (r *PrivateRR) unpack(msg []byte, off int) (int, error) { - off1, err := r.Data.Unpack(msg[off:]) - off += off1 - return off, err -} - -func (r *PrivateRR) parse(c *zlexer, origin, file string) *ParseError { - var l lex - text := make([]string, 0, 2) // could be 0..N elements, median is probably 1 -Fetch: - for { - // TODO(miek): we could also be returning _QUOTE, this might or might not - // be an issue (basically parsing TXT becomes hard) - switch l, _ = c.Next(); l.value { - case zNewline, zEOF: - break Fetch - case zString: - text = append(text, l.token) - } - } - - err := r.Data.Parse(text) - if err != nil { - return &ParseError{file, err.Error(), l} - } - - return nil -} - -func (r1 *PrivateRR) isDuplicate(r2 RR) bool { return false } - -// PrivateHandle registers a private resource record type. It requires -// string and numeric representation of private RR type and generator function as argument. -func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) { - rtypestr = strings.ToUpper(rtypestr) - - TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} } - TypeToString[rtype] = rtypestr - StringToType[rtypestr] = rtype -} - -// PrivateHandleRemove removes definitions required to support private RR type. -func PrivateHandleRemove(rtype uint16) { - rtypestr, ok := TypeToString[rtype] - if ok { - delete(TypeToRR, rtype) - delete(TypeToString, rtype) - delete(StringToType, rtypestr) - } -} diff --git a/vendor/github.com/miekg/dns/reverse.go b/vendor/github.com/miekg/dns/reverse.go deleted file mode 100644 index 28151af83..000000000 --- a/vendor/github.com/miekg/dns/reverse.go +++ /dev/null @@ -1,52 +0,0 @@ -package dns - -// StringToType is the reverse of TypeToString, needed for string parsing. -var StringToType = reverseInt16(TypeToString) - -// StringToClass is the reverse of ClassToString, needed for string parsing. -var StringToClass = reverseInt16(ClassToString) - -// StringToOpcode is a map of opcodes to strings. -var StringToOpcode = reverseInt(OpcodeToString) - -// StringToRcode is a map of rcodes to strings. -var StringToRcode = reverseInt(RcodeToString) - -func init() { - // Preserve previous NOTIMP typo, see github.com/miekg/dns/issues/733. - StringToRcode["NOTIMPL"] = RcodeNotImplemented -} - -// StringToAlgorithm is the reverse of AlgorithmToString. -var StringToAlgorithm = reverseInt8(AlgorithmToString) - -// StringToHash is a map of names to hash IDs. -var StringToHash = reverseInt8(HashToString) - -// StringToCertType is the reverseof CertTypeToString. -var StringToCertType = reverseInt16(CertTypeToString) - -// Reverse a map -func reverseInt8(m map[uint8]string) map[string]uint8 { - n := make(map[string]uint8, len(m)) - for u, s := range m { - n[s] = u - } - return n -} - -func reverseInt16(m map[uint16]string) map[string]uint16 { - n := make(map[string]uint16, len(m)) - for u, s := range m { - n[s] = u - } - return n -} - -func reverseInt(m map[int]string) map[string]int { - n := make(map[string]int, len(m)) - for u, s := range m { - n[s] = u - } - return n -} diff --git a/vendor/github.com/miekg/dns/sanitize.go b/vendor/github.com/miekg/dns/sanitize.go deleted file mode 100644 index a638e862e..000000000 --- a/vendor/github.com/miekg/dns/sanitize.go +++ /dev/null @@ -1,86 +0,0 @@ -package dns - -// Dedup removes identical RRs from rrs. It preserves the original ordering. -// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies -// rrs. -// m is used to store the RRs temporary. If it is nil a new map will be allocated. -func Dedup(rrs []RR, m map[string]RR) []RR { - - if m == nil { - m = make(map[string]RR) - } - // Save the keys, so we don't have to call normalizedString twice. - keys := make([]*string, 0, len(rrs)) - - for _, r := range rrs { - key := normalizedString(r) - keys = append(keys, &key) - if mr, ok := m[key]; ok { - // Shortest TTL wins. - rh, mrh := r.Header(), mr.Header() - if mrh.Ttl > rh.Ttl { - mrh.Ttl = rh.Ttl - } - continue - } - - m[key] = r - } - // If the length of the result map equals the amount of RRs we got, - // it means they were all different. We can then just return the original rrset. - if len(m) == len(rrs) { - return rrs - } - - j := 0 - for i, r := range rrs { - // If keys[i] lives in the map, we should copy and remove it. - if _, ok := m[*keys[i]]; ok { - delete(m, *keys[i]) - rrs[j] = r - j++ - } - - if len(m) == 0 { - break - } - } - - return rrs[:j] -} - -// normalizedString returns a normalized string from r. The TTL -// is removed and the domain name is lowercased. We go from this: -// DomainNameTTLCLASSTYPERDATA to: -// lowercasenameCLASSTYPE... -func normalizedString(r RR) string { - // A string Go DNS makes has: domainnameTTL... - b := []byte(r.String()) - - // find the first non-escaped tab, then another, so we capture where the TTL lives. - esc := false - ttlStart, ttlEnd := 0, 0 - for i := 0; i < len(b) && ttlEnd == 0; i++ { - switch { - case b[i] == '\\': - esc = !esc - case b[i] == '\t' && !esc: - if ttlStart == 0 { - ttlStart = i - continue - } - if ttlEnd == 0 { - ttlEnd = i - } - case b[i] >= 'A' && b[i] <= 'Z' && !esc: - b[i] += 32 - default: - esc = false - } - } - - // remove TTL. - copy(b[ttlStart:], b[ttlEnd:]) - cut := ttlEnd - ttlStart - return string(b[:len(b)-cut]) -} diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go deleted file mode 100644 index a8691bca7..000000000 --- a/vendor/github.com/miekg/dns/scan.go +++ /dev/null @@ -1,1337 +0,0 @@ -package dns - -import ( - "bufio" - "fmt" - "io" - "os" - "path/filepath" - "strconv" - "strings" -) - -const maxTok = 2048 // Largest token we can return. - -// The maximum depth of $INCLUDE directives supported by the -// ZoneParser API. -const maxIncludeDepth = 7 - -// Tokinize a RFC 1035 zone file. The tokenizer will normalize it: -// * Add ownernames if they are left blank; -// * Suppress sequences of spaces; -// * Make each RR fit on one line (_NEWLINE is send as last) -// * Handle comments: ; -// * Handle braces - anywhere. -const ( - // Zonefile - zEOF = iota - zString - zBlank - zQuote - zNewline - zRrtpe - zOwner - zClass - zDirOrigin // $ORIGIN - zDirTTL // $TTL - zDirInclude // $INCLUDE - zDirGenerate // $GENERATE - - // Privatekey file - zValue - zKey - - zExpectOwnerDir // Ownername - zExpectOwnerBl // Whitespace after the ownername - zExpectAny // Expect rrtype, ttl or class - zExpectAnyNoClass // Expect rrtype or ttl - zExpectAnyNoClassBl // The whitespace after _EXPECT_ANY_NOCLASS - zExpectAnyNoTTL // Expect rrtype or class - zExpectAnyNoTTLBl // Whitespace after _EXPECT_ANY_NOTTL - zExpectRrtype // Expect rrtype - zExpectRrtypeBl // Whitespace BEFORE rrtype - zExpectRdata // The first element of the rdata - zExpectDirTTLBl // Space after directive $TTL - zExpectDirTTL // Directive $TTL - zExpectDirOriginBl // Space after directive $ORIGIN - zExpectDirOrigin // Directive $ORIGIN - zExpectDirIncludeBl // Space after directive $INCLUDE - zExpectDirInclude // Directive $INCLUDE - zExpectDirGenerate // Directive $GENERATE - zExpectDirGenerateBl // Space after directive $GENERATE -) - -// ParseError is a parsing error. It contains the parse error and the location in the io.Reader -// where the error occurred. -type ParseError struct { - file string - err string - lex lex -} - -func (e *ParseError) Error() (s string) { - if e.file != "" { - s = e.file + ": " - } - s += "dns: " + e.err + ": " + strconv.QuoteToASCII(e.lex.token) + " at line: " + - strconv.Itoa(e.lex.line) + ":" + strconv.Itoa(e.lex.column) - return -} - -type lex struct { - token string // text of the token - err bool // when true, token text has lexer error - value uint8 // value: zString, _BLANK, etc. - torc uint16 // type or class as parsed in the lexer, we only need to look this up in the grammar - line int // line in the file - column int // column in the file -} - -// Token holds the token that are returned when a zone file is parsed. -type Token struct { - // The scanned resource record when error is not nil. - RR - // When an error occurred, this has the error specifics. - Error *ParseError - // A potential comment positioned after the RR and on the same line. - Comment string -} - -// ttlState describes the state necessary to fill in an omitted RR TTL -type ttlState struct { - ttl uint32 // ttl is the current default TTL - isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive -} - -// NewRR reads the RR contained in the string s. Only the first RR is -// returned. If s contains no records, NewRR will return nil with no -// error. -// -// The class defaults to IN and TTL defaults to 3600. The full zone -// file syntax like $TTL, $ORIGIN, etc. is supported. -// -// All fields of the returned RR are set, except RR.Header().Rdlength -// which is set to 0. -func NewRR(s string) (RR, error) { - if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline - return ReadRR(strings.NewReader(s+"\n"), "") - } - return ReadRR(strings.NewReader(s), "") -} - -// ReadRR reads the RR contained in r. -// -// The string file is used in error reporting and to resolve relative -// $INCLUDE directives. -// -// See NewRR for more documentation. -func ReadRR(r io.Reader, file string) (RR, error) { - zp := NewZoneParser(r, ".", file) - zp.SetDefaultTTL(defaultTtl) - zp.SetIncludeAllowed(true) - rr, _ := zp.Next() - return rr, zp.Err() -} - -// ParseZone reads a RFC 1035 style zonefile from r. It returns -// *Tokens on the returned channel, each consisting of either a -// parsed RR and optional comment or a nil RR and an error. The -// channel is closed by ParseZone when the end of r is reached. -// -// The string file is used in error reporting and to resolve relative -// $INCLUDE directives. The string origin is used as the initial -// origin, as if the file would start with an $ORIGIN directive. -// -// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all -// supported. -// -// Basic usage pattern when reading from a string (z) containing the -// zone data: -// -// for x := range dns.ParseZone(strings.NewReader(z), "", "") { -// if x.Error != nil { -// // log.Println(x.Error) -// } else { -// // Do something with x.RR -// } -// } -// -// Comments specified after an RR (and on the same line!) are -// returned too: -// -// foo. IN A 10.0.0.1 ; this is a comment -// -// The text "; this is comment" is returned in Token.Comment. -// Comments inside the RR are returned concatenated along with the -// RR. Comments on a line by themselves are discarded. -// -// To prevent memory leaks it is important to always fully drain the -// returned channel. If an error occurs, it will always be the last -// Token sent on the channel. -// -// Deprecated: New users should prefer the ZoneParser API. -func ParseZone(r io.Reader, origin, file string) chan *Token { - t := make(chan *Token, 10000) - go parseZone(r, origin, file, t) - return t -} - -func parseZone(r io.Reader, origin, file string, t chan *Token) { - defer close(t) - - zp := NewZoneParser(r, origin, file) - zp.SetIncludeAllowed(true) - - for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { - t <- &Token{RR: rr, Comment: zp.Comment()} - } - - if err := zp.Err(); err != nil { - pe, ok := err.(*ParseError) - if !ok { - pe = &ParseError{file: file, err: err.Error()} - } - - t <- &Token{Error: pe} - } -} - -// ZoneParser is a parser for an RFC 1035 style zonefile. -// -// Each parsed RR in the zone is returned sequentially from Next. An -// optional comment can be retrieved with Comment. -// -// The directives $INCLUDE, $ORIGIN, $TTL and $GENERATE are all -// supported. Although $INCLUDE is disabled by default. -// -// Basic usage pattern when reading from a string (z) containing the -// zone data: -// -// zp := NewZoneParser(strings.NewReader(z), "", "") -// -// for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { -// // Do something with rr -// } -// -// if err := zp.Err(); err != nil { -// // log.Println(err) -// } -// -// Comments specified after an RR (and on the same line!) are -// returned too: -// -// foo. IN A 10.0.0.1 ; this is a comment -// -// The text "; this is comment" is returned from Comment. Comments inside -// the RR are returned concatenated along with the RR. Comments on a line -// by themselves are discarded. -type ZoneParser struct { - c *zlexer - - parseErr *ParseError - - origin string - file string - - defttl *ttlState - - h RR_Header - - // sub is used to parse $INCLUDE files and $GENERATE directives. - // Next, by calling subNext, forwards the resulting RRs from this - // sub parser to the calling code. - sub *ZoneParser - osFile *os.File - - includeDepth uint8 - - includeAllowed bool -} - -// NewZoneParser returns an RFC 1035 style zonefile parser that reads -// from r. -// -// The string file is used in error reporting and to resolve relative -// $INCLUDE directives. The string origin is used as the initial -// origin, as if the file would start with an $ORIGIN directive. -func NewZoneParser(r io.Reader, origin, file string) *ZoneParser { - var pe *ParseError - if origin != "" { - origin = Fqdn(origin) - if _, ok := IsDomainName(origin); !ok { - pe = &ParseError{file, "bad initial origin name", lex{}} - } - } - - return &ZoneParser{ - c: newZLexer(r), - - parseErr: pe, - - origin: origin, - file: file, - } -} - -// SetDefaultTTL sets the parsers default TTL to ttl. -func (zp *ZoneParser) SetDefaultTTL(ttl uint32) { - zp.defttl = &ttlState{ttl, false} -} - -// SetIncludeAllowed controls whether $INCLUDE directives are -// allowed. $INCLUDE directives are not supported by default. -// -// The $INCLUDE directive will open and read from a user controlled -// file on the system. Even if the file is not a valid zonefile, the -// contents of the file may be revealed in error messages, such as: -// -// /etc/passwd: dns: not a TTL: "root:x:0:0:root:/root:/bin/bash" at line: 1:31 -// /etc/shadow: dns: not a TTL: "root:$6$::0:99999:7:::" at line: 1:125 -func (zp *ZoneParser) SetIncludeAllowed(v bool) { - zp.includeAllowed = v -} - -// Err returns the first non-EOF error that was encountered by the -// ZoneParser. -func (zp *ZoneParser) Err() error { - if zp.parseErr != nil { - return zp.parseErr - } - - if zp.sub != nil { - if err := zp.sub.Err(); err != nil { - return err - } - } - - return zp.c.Err() -} - -func (zp *ZoneParser) setParseError(err string, l lex) (RR, bool) { - zp.parseErr = &ParseError{zp.file, err, l} - return nil, false -} - -// Comment returns an optional text comment that occurred alongside -// the RR. -func (zp *ZoneParser) Comment() string { - if zp.parseErr != nil { - return "" - } - - if zp.sub != nil { - return zp.sub.Comment() - } - - return zp.c.Comment() -} - -func (zp *ZoneParser) subNext() (RR, bool) { - if rr, ok := zp.sub.Next(); ok { - return rr, true - } - - if zp.sub.osFile != nil { - zp.sub.osFile.Close() - zp.sub.osFile = nil - } - - if zp.sub.Err() != nil { - // We have errors to surface. - return nil, false - } - - zp.sub = nil - return zp.Next() -} - -// Next advances the parser to the next RR in the zonefile and -// returns the (RR, true). It will return (nil, false) when the -// parsing stops, either by reaching the end of the input or an -// error. After Next returns (nil, false), the Err method will return -// any error that occurred during parsing. -func (zp *ZoneParser) Next() (RR, bool) { - if zp.parseErr != nil { - return nil, false - } - if zp.sub != nil { - return zp.subNext() - } - - // 6 possible beginnings of a line (_ is a space): - // - // 0. zRRTYPE -> all omitted until the rrtype - // 1. zOwner _ zRrtype -> class/ttl omitted - // 2. zOwner _ zString _ zRrtype -> class omitted - // 3. zOwner _ zString _ zClass _ zRrtype -> ttl/class - // 4. zOwner _ zClass _ zRrtype -> ttl omitted - // 5. zOwner _ zClass _ zString _ zRrtype -> class/ttl (reversed) - // - // After detecting these, we know the zRrtype so we can jump to functions - // handling the rdata for each of these types. - - st := zExpectOwnerDir // initial state - h := &zp.h - - for l, ok := zp.c.Next(); ok; l, ok = zp.c.Next() { - // zlexer spotted an error already - if l.err { - return zp.setParseError(l.token, l) - } - - switch st { - case zExpectOwnerDir: - // We can also expect a directive, like $TTL or $ORIGIN - if zp.defttl != nil { - h.Ttl = zp.defttl.ttl - } - - h.Class = ClassINET - - switch l.value { - case zNewline: - st = zExpectOwnerDir - case zOwner: - name, ok := toAbsoluteName(l.token, zp.origin) - if !ok { - return zp.setParseError("bad owner name", l) - } - - h.Name = name - - st = zExpectOwnerBl - case zDirTTL: - st = zExpectDirTTLBl - case zDirOrigin: - st = zExpectDirOriginBl - case zDirInclude: - st = zExpectDirIncludeBl - case zDirGenerate: - st = zExpectDirGenerateBl - case zRrtpe: - h.Rrtype = l.torc - - st = zExpectRdata - case zClass: - h.Class = l.torc - - st = zExpectAnyNoClassBl - case zBlank: - // Discard, can happen when there is nothing on the - // line except the RR type - case zString: - ttl, ok := stringToTTL(l.token) - if !ok { - return zp.setParseError("not a TTL", l) - } - - h.Ttl = ttl - - if zp.defttl == nil || !zp.defttl.isByDirective { - zp.defttl = &ttlState{ttl, false} - } - - st = zExpectAnyNoTTLBl - default: - return zp.setParseError("syntax error at beginning", l) - } - case zExpectDirIncludeBl: - if l.value != zBlank { - return zp.setParseError("no blank after $INCLUDE-directive", l) - } - - st = zExpectDirInclude - case zExpectDirInclude: - if l.value != zString { - return zp.setParseError("expecting $INCLUDE value, not this...", l) - } - - neworigin := zp.origin // There may be optionally a new origin set after the filename, if not use current one - switch l, _ := zp.c.Next(); l.value { - case zBlank: - l, _ := zp.c.Next() - if l.value == zString { - name, ok := toAbsoluteName(l.token, zp.origin) - if !ok { - return zp.setParseError("bad origin name", l) - } - - neworigin = name - } - case zNewline, zEOF: - // Ok - default: - return zp.setParseError("garbage after $INCLUDE", l) - } - - if !zp.includeAllowed { - return zp.setParseError("$INCLUDE directive not allowed", l) - } - if zp.includeDepth >= maxIncludeDepth { - return zp.setParseError("too deeply nested $INCLUDE", l) - } - - // Start with the new file - includePath := l.token - if !filepath.IsAbs(includePath) { - includePath = filepath.Join(filepath.Dir(zp.file), includePath) - } - - r1, e1 := os.Open(includePath) - if e1 != nil { - var as string - if !filepath.IsAbs(l.token) { - as = fmt.Sprintf(" as `%s'", includePath) - } - - msg := fmt.Sprintf("failed to open `%s'%s: %v", l.token, as, e1) - return zp.setParseError(msg, l) - } - - zp.sub = NewZoneParser(r1, neworigin, includePath) - zp.sub.defttl, zp.sub.includeDepth, zp.sub.osFile = zp.defttl, zp.includeDepth+1, r1 - zp.sub.SetIncludeAllowed(true) - return zp.subNext() - case zExpectDirTTLBl: - if l.value != zBlank { - return zp.setParseError("no blank after $TTL-directive", l) - } - - st = zExpectDirTTL - case zExpectDirTTL: - if l.value != zString { - return zp.setParseError("expecting $TTL value, not this...", l) - } - - if e := slurpRemainder(zp.c, zp.file); e != nil { - zp.parseErr = e - return nil, false - } - - ttl, ok := stringToTTL(l.token) - if !ok { - return zp.setParseError("expecting $TTL value, not this...", l) - } - - zp.defttl = &ttlState{ttl, true} - - st = zExpectOwnerDir - case zExpectDirOriginBl: - if l.value != zBlank { - return zp.setParseError("no blank after $ORIGIN-directive", l) - } - - st = zExpectDirOrigin - case zExpectDirOrigin: - if l.value != zString { - return zp.setParseError("expecting $ORIGIN value, not this...", l) - } - - if e := slurpRemainder(zp.c, zp.file); e != nil { - zp.parseErr = e - return nil, false - } - - name, ok := toAbsoluteName(l.token, zp.origin) - if !ok { - return zp.setParseError("bad origin name", l) - } - - zp.origin = name - - st = zExpectOwnerDir - case zExpectDirGenerateBl: - if l.value != zBlank { - return zp.setParseError("no blank after $GENERATE-directive", l) - } - - st = zExpectDirGenerate - case zExpectDirGenerate: - if l.value != zString { - return zp.setParseError("expecting $GENERATE value, not this...", l) - } - - return zp.generate(l) - case zExpectOwnerBl: - if l.value != zBlank { - return zp.setParseError("no blank after owner", l) - } - - st = zExpectAny - case zExpectAny: - switch l.value { - case zRrtpe: - if zp.defttl == nil { - return zp.setParseError("missing TTL with no previous value", l) - } - - h.Rrtype = l.torc - - st = zExpectRdata - case zClass: - h.Class = l.torc - - st = zExpectAnyNoClassBl - case zString: - ttl, ok := stringToTTL(l.token) - if !ok { - return zp.setParseError("not a TTL", l) - } - - h.Ttl = ttl - - if zp.defttl == nil || !zp.defttl.isByDirective { - zp.defttl = &ttlState{ttl, false} - } - - st = zExpectAnyNoTTLBl - default: - return zp.setParseError("expecting RR type, TTL or class, not this...", l) - } - case zExpectAnyNoClassBl: - if l.value != zBlank { - return zp.setParseError("no blank before class", l) - } - - st = zExpectAnyNoClass - case zExpectAnyNoTTLBl: - if l.value != zBlank { - return zp.setParseError("no blank before TTL", l) - } - - st = zExpectAnyNoTTL - case zExpectAnyNoTTL: - switch l.value { - case zClass: - h.Class = l.torc - - st = zExpectRrtypeBl - case zRrtpe: - h.Rrtype = l.torc - - st = zExpectRdata - default: - return zp.setParseError("expecting RR type or class, not this...", l) - } - case zExpectAnyNoClass: - switch l.value { - case zString: - ttl, ok := stringToTTL(l.token) - if !ok { - return zp.setParseError("not a TTL", l) - } - - h.Ttl = ttl - - if zp.defttl == nil || !zp.defttl.isByDirective { - zp.defttl = &ttlState{ttl, false} - } - - st = zExpectRrtypeBl - case zRrtpe: - h.Rrtype = l.torc - - st = zExpectRdata - default: - return zp.setParseError("expecting RR type or TTL, not this...", l) - } - case zExpectRrtypeBl: - if l.value != zBlank { - return zp.setParseError("no blank before RR type", l) - } - - st = zExpectRrtype - case zExpectRrtype: - if l.value != zRrtpe { - return zp.setParseError("unknown RR type", l) - } - - h.Rrtype = l.torc - - st = zExpectRdata - case zExpectRdata: - r, e := setRR(*h, zp.c, zp.origin, zp.file) - if e != nil { - // If e.lex is nil than we have encounter a unknown RR type - // in that case we substitute our current lex token - if e.lex.token == "" && e.lex.value == 0 { - e.lex = l // Uh, dirty - } - - zp.parseErr = e - return nil, false - } - - return r, true - } - } - - // If we get here, we and the h.Rrtype is still zero, we haven't parsed anything, this - // is not an error, because an empty zone file is still a zone file. - return nil, false -} - -type zlexer struct { - br io.ByteReader - - readErr error - - line int - column int - - comBuf string - comment string - - l lex - - brace int - quote bool - space bool - commt bool - rrtype bool - owner bool - - nextL bool - - eol bool // end-of-line -} - -func newZLexer(r io.Reader) *zlexer { - br, ok := r.(io.ByteReader) - if !ok { - br = bufio.NewReaderSize(r, 1024) - } - - return &zlexer{ - br: br, - - line: 1, - - owner: true, - } -} - -func (zl *zlexer) Err() error { - if zl.readErr == io.EOF { - return nil - } - - return zl.readErr -} - -// readByte returns the next byte from the input -func (zl *zlexer) readByte() (byte, bool) { - if zl.readErr != nil { - return 0, false - } - - c, err := zl.br.ReadByte() - if err != nil { - zl.readErr = err - return 0, false - } - - // delay the newline handling until the next token is delivered, - // fixes off-by-one errors when reporting a parse error. - if zl.eol { - zl.line++ - zl.column = 0 - zl.eol = false - } - - if c == '\n' { - zl.eol = true - } else { - zl.column++ - } - - return c, true -} - -func (zl *zlexer) Next() (lex, bool) { - l := &zl.l - if zl.nextL { - zl.nextL = false - return *l, true - } - if l.err { - // Parsing errors should be sticky. - return lex{value: zEOF}, false - } - - var ( - str [maxTok]byte // Hold string text - com [maxTok]byte // Hold comment text - - stri int // Offset in str (0 means empty) - comi int // Offset in com (0 means empty) - - escape bool - ) - - if zl.comBuf != "" { - comi = copy(com[:], zl.comBuf) - zl.comBuf = "" - } - - zl.comment = "" - - for x, ok := zl.readByte(); ok; x, ok = zl.readByte() { - l.line, l.column = zl.line, zl.column - - if stri >= len(str) { - l.token = "token length insufficient for parsing" - l.err = true - return *l, true - } - if comi >= len(com) { - l.token = "comment length insufficient for parsing" - l.err = true - return *l, true - } - - switch x { - case ' ', '\t': - if escape || zl.quote { - // Inside quotes or escaped this is legal. - str[stri] = x - stri++ - - escape = false - break - } - - if zl.commt { - com[comi] = x - comi++ - break - } - - var retL lex - if stri == 0 { - // Space directly in the beginning, handled in the grammar - } else if zl.owner { - // If we have a string and its the first, make it an owner - l.value = zOwner - l.token = string(str[:stri]) - - // escape $... start with a \ not a $, so this will work - switch strings.ToUpper(l.token) { - case "$TTL": - l.value = zDirTTL - case "$ORIGIN": - l.value = zDirOrigin - case "$INCLUDE": - l.value = zDirInclude - case "$GENERATE": - l.value = zDirGenerate - } - - retL = *l - } else { - l.value = zString - l.token = string(str[:stri]) - - if !zl.rrtype { - tokenUpper := strings.ToUpper(l.token) - if t, ok := StringToType[tokenUpper]; ok { - l.value = zRrtpe - l.torc = t - - zl.rrtype = true - } else if strings.HasPrefix(tokenUpper, "TYPE") { - t, ok := typeToInt(l.token) - if !ok { - l.token = "unknown RR type" - l.err = true - return *l, true - } - - l.value = zRrtpe - l.torc = t - - zl.rrtype = true - } - - if t, ok := StringToClass[tokenUpper]; ok { - l.value = zClass - l.torc = t - } else if strings.HasPrefix(tokenUpper, "CLASS") { - t, ok := classToInt(l.token) - if !ok { - l.token = "unknown class" - l.err = true - return *l, true - } - - l.value = zClass - l.torc = t - } - } - - retL = *l - } - - zl.owner = false - - if !zl.space { - zl.space = true - - l.value = zBlank - l.token = " " - - if retL == (lex{}) { - return *l, true - } - - zl.nextL = true - } - - if retL != (lex{}) { - return retL, true - } - case ';': - if escape || zl.quote { - // Inside quotes or escaped this is legal. - str[stri] = x - stri++ - - escape = false - break - } - - zl.commt = true - zl.comBuf = "" - - if comi > 1 { - // A newline was previously seen inside a comment that - // was inside braces and we delayed adding it until now. - com[comi] = ' ' // convert newline to space - comi++ - } - - com[comi] = ';' - comi++ - - if stri > 0 { - zl.comBuf = string(com[:comi]) - - l.value = zString - l.token = string(str[:stri]) - return *l, true - } - case '\r': - escape = false - - if zl.quote { - str[stri] = x - stri++ - } - - // discard if outside of quotes - case '\n': - escape = false - - // Escaped newline - if zl.quote { - str[stri] = x - stri++ - break - } - - if zl.commt { - // Reset a comment - zl.commt = false - zl.rrtype = false - - // If not in a brace this ends the comment AND the RR - if zl.brace == 0 { - zl.owner = true - - l.value = zNewline - l.token = "\n" - zl.comment = string(com[:comi]) - return *l, true - } - - zl.comBuf = string(com[:comi]) - break - } - - if zl.brace == 0 { - // If there is previous text, we should output it here - var retL lex - if stri != 0 { - l.value = zString - l.token = string(str[:stri]) - - if !zl.rrtype { - tokenUpper := strings.ToUpper(l.token) - if t, ok := StringToType[tokenUpper]; ok { - zl.rrtype = true - - l.value = zRrtpe - l.torc = t - } - } - - retL = *l - } - - l.value = zNewline - l.token = "\n" - - zl.comment = zl.comBuf - zl.comBuf = "" - zl.rrtype = false - zl.owner = true - - if retL != (lex{}) { - zl.nextL = true - return retL, true - } - - return *l, true - } - case '\\': - // comments do not get escaped chars, everything is copied - if zl.commt { - com[comi] = x - comi++ - break - } - - // something already escaped must be in string - if escape { - str[stri] = x - stri++ - - escape = false - break - } - - // something escaped outside of string gets added to string - str[stri] = x - stri++ - - escape = true - case '"': - if zl.commt { - com[comi] = x - comi++ - break - } - - if escape { - str[stri] = x - stri++ - - escape = false - break - } - - zl.space = false - - // send previous gathered text and the quote - var retL lex - if stri != 0 { - l.value = zString - l.token = string(str[:stri]) - - retL = *l - } - - // send quote itself as separate token - l.value = zQuote - l.token = "\"" - - zl.quote = !zl.quote - - if retL != (lex{}) { - zl.nextL = true - return retL, true - } - - return *l, true - case '(', ')': - if zl.commt { - com[comi] = x - comi++ - break - } - - if escape || zl.quote { - // Inside quotes or escaped this is legal. - str[stri] = x - stri++ - - escape = false - break - } - - switch x { - case ')': - zl.brace-- - - if zl.brace < 0 { - l.token = "extra closing brace" - l.err = true - return *l, true - } - case '(': - zl.brace++ - } - default: - escape = false - - if zl.commt { - com[comi] = x - comi++ - break - } - - str[stri] = x - stri++ - - zl.space = false - } - } - - if zl.readErr != nil && zl.readErr != io.EOF { - // Don't return any tokens after a read error occurs. - return lex{value: zEOF}, false - } - - var retL lex - if stri > 0 { - // Send remainder of str - l.value = zString - l.token = string(str[:stri]) - retL = *l - - if comi <= 0 { - return retL, true - } - } - - if comi > 0 { - // Send remainder of com - l.value = zNewline - l.token = "\n" - zl.comment = string(com[:comi]) - - if retL != (lex{}) { - zl.nextL = true - return retL, true - } - - return *l, true - } - - if zl.brace != 0 { - l.token = "unbalanced brace" - l.err = true - return *l, true - } - - return lex{value: zEOF}, false -} - -func (zl *zlexer) Comment() string { - if zl.l.err { - return "" - } - - return zl.comment -} - -// Extract the class number from CLASSxx -func classToInt(token string) (uint16, bool) { - offset := 5 - if len(token) < offset+1 { - return 0, false - } - class, err := strconv.ParseUint(token[offset:], 10, 16) - if err != nil { - return 0, false - } - return uint16(class), true -} - -// Extract the rr number from TYPExxx -func typeToInt(token string) (uint16, bool) { - offset := 4 - if len(token) < offset+1 { - return 0, false - } - typ, err := strconv.ParseUint(token[offset:], 10, 16) - if err != nil { - return 0, false - } - return uint16(typ), true -} - -// stringToTTL parses things like 2w, 2m, etc, and returns the time in seconds. -func stringToTTL(token string) (uint32, bool) { - var s, i uint32 - for _, c := range token { - switch c { - case 's', 'S': - s += i - i = 0 - case 'm', 'M': - s += i * 60 - i = 0 - case 'h', 'H': - s += i * 60 * 60 - i = 0 - case 'd', 'D': - s += i * 60 * 60 * 24 - i = 0 - case 'w', 'W': - s += i * 60 * 60 * 24 * 7 - i = 0 - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - i *= 10 - i += uint32(c) - '0' - default: - return 0, false - } - } - return s + i, true -} - -// Parse LOC records' [.][mM] into a -// mantissa exponent format. Token should contain the entire -// string (i.e. no spaces allowed) -func stringToCm(token string) (e, m uint8, ok bool) { - if token[len(token)-1] == 'M' || token[len(token)-1] == 'm' { - token = token[0 : len(token)-1] - } - s := strings.SplitN(token, ".", 2) - var meters, cmeters, val int - var err error - switch len(s) { - case 2: - if cmeters, err = strconv.Atoi(s[1]); err != nil { - return - } - fallthrough - case 1: - if meters, err = strconv.Atoi(s[0]); err != nil { - return - } - case 0: - // huh? - return 0, 0, false - } - ok = true - if meters > 0 { - e = 2 - val = meters - } else { - e = 0 - val = cmeters - } - for val > 10 { - e++ - val /= 10 - } - if e > 9 { - ok = false - } - m = uint8(val) - return -} - -func toAbsoluteName(name, origin string) (absolute string, ok bool) { - // check for an explicit origin reference - if name == "@" { - // require a nonempty origin - if origin == "" { - return "", false - } - return origin, true - } - - // require a valid domain name - _, ok = IsDomainName(name) - if !ok || name == "" { - return "", false - } - - // check if name is already absolute - if IsFqdn(name) { - return name, true - } - - // require a nonempty origin - if origin == "" { - return "", false - } - return appendOrigin(name, origin), true -} - -func appendOrigin(name, origin string) string { - if origin == "." { - return name + origin - } - return name + "." + origin -} - -// LOC record helper function -func locCheckNorth(token string, latitude uint32) (uint32, bool) { - switch token { - case "n", "N": - return LOC_EQUATOR + latitude, true - case "s", "S": - return LOC_EQUATOR - latitude, true - } - return latitude, false -} - -// LOC record helper function -func locCheckEast(token string, longitude uint32) (uint32, bool) { - switch token { - case "e", "E": - return LOC_EQUATOR + longitude, true - case "w", "W": - return LOC_EQUATOR - longitude, true - } - return longitude, false -} - -// "Eat" the rest of the "line" -func slurpRemainder(c *zlexer, f string) *ParseError { - l, _ := c.Next() - switch l.value { - case zBlank: - l, _ = c.Next() - if l.value != zNewline && l.value != zEOF { - return &ParseError{f, "garbage after rdata", l} - } - case zNewline: - case zEOF: - default: - return &ParseError{f, "garbage after rdata", l} - } - return nil -} - -// Parse a 64 bit-like ipv6 address: "0014:4fff:ff20:ee64" -// Used for NID and L64 record. -func stringToNodeID(l lex) (uint64, *ParseError) { - if len(l.token) < 19 { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} - } - // There must be three colons at fixes postitions, if not its a parse error - if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} - } - s := l.token[0:4] + l.token[5:9] + l.token[10:14] + l.token[15:19] - u, err := strconv.ParseUint(s, 16, 64) - if err != nil { - return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l} - } - return u, nil -} diff --git a/vendor/github.com/miekg/dns/scan_rr.go b/vendor/github.com/miekg/dns/scan_rr.go deleted file mode 100644 index 6096f9b0c..000000000 --- a/vendor/github.com/miekg/dns/scan_rr.go +++ /dev/null @@ -1,1945 +0,0 @@ -package dns - -import ( - "encoding/base64" - "net" - "strconv" - "strings" -) - -// Parse the rdata of each rrtype. -// All data from the channel c is either zString or zBlank. -// After the rdata there may come a zBlank and then a zNewline -// or immediately a zNewline. If this is not the case we flag -// an *ParseError: garbage after rdata. -func setRR(h RR_Header, c *zlexer, o, f string) (RR, *ParseError) { - var rr RR - if newFn, ok := TypeToRR[h.Rrtype]; ok && canParseAsRR(h.Rrtype) { - rr = newFn() - *rr.Header() = h - } else { - rr = &RFC3597{Hdr: h} - } - - err := rr.parse(c, o, f) - if err != nil { - return nil, err - } - - return rr, nil -} - -// canParseAsRR returns true if the record type can be parsed as a -// concrete RR. It blacklists certain record types that must be parsed -// according to RFC 3597 because they lack a presentation format. -func canParseAsRR(rrtype uint16) bool { - switch rrtype { - case TypeANY, TypeNULL, TypeOPT, TypeTSIG: - return false - default: - return true - } -} - -// A remainder of the rdata with embedded spaces, return the parsed string (sans the spaces) -// or an error -func endingToString(c *zlexer, errstr, f string) (string, *ParseError) { - var s string - l, _ := c.Next() // zString - for l.value != zNewline && l.value != zEOF { - if l.err { - return s, &ParseError{f, errstr, l} - } - switch l.value { - case zString: - s += l.token - case zBlank: // Ok - default: - return "", &ParseError{f, errstr, l} - } - l, _ = c.Next() - } - - return s, nil -} - -// A remainder of the rdata with embedded spaces, split on unquoted whitespace -// and return the parsed string slice or an error -func endingToTxtSlice(c *zlexer, errstr, f string) ([]string, *ParseError) { - // Get the remaining data until we see a zNewline - l, _ := c.Next() - if l.err { - return nil, &ParseError{f, errstr, l} - } - - // Build the slice - s := make([]string, 0) - quote := false - empty := false - for l.value != zNewline && l.value != zEOF { - if l.err { - return nil, &ParseError{f, errstr, l} - } - switch l.value { - case zString: - empty = false - if len(l.token) > 255 { - // split up tokens that are larger than 255 into 255-chunks - sx := []string{} - p, i := 0, 255 - for { - if i <= len(l.token) { - sx = append(sx, l.token[p:i]) - } else { - sx = append(sx, l.token[p:]) - break - - } - p, i = p+255, i+255 - } - s = append(s, sx...) - break - } - - s = append(s, l.token) - case zBlank: - if quote { - // zBlank can only be seen in between txt parts. - return nil, &ParseError{f, errstr, l} - } - case zQuote: - if empty && quote { - s = append(s, "") - } - quote = !quote - empty = true - default: - return nil, &ParseError{f, errstr, l} - } - l, _ = c.Next() - } - - if quote { - return nil, &ParseError{f, errstr, l} - } - - return s, nil -} - -func (rr *A) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - rr.A = net.ParseIP(l.token) - // IPv4 addresses cannot include ":". - // We do this rather than use net.IP's To4() because - // To4() treats IPv4-mapped IPv6 addresses as being - // IPv4. - isIPv4 := !strings.Contains(l.token, ":") - if rr.A == nil || !isIPv4 || l.err { - return &ParseError{f, "bad A A", l} - } - return slurpRemainder(c, f) -} - -func (rr *AAAA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - rr.AAAA = net.ParseIP(l.token) - // IPv6 addresses must include ":", and IPv4 - // addresses cannot include ":". - isIPv6 := strings.Contains(l.token, ":") - if rr.AAAA == nil || !isIPv6 || l.err { - return &ParseError{f, "bad AAAA AAAA", l} - } - return slurpRemainder(c, f) -} - -func (rr *NS) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Ns = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad NS Ns", l} - } - rr.Ns = name - return slurpRemainder(c, f) -} - -func (rr *PTR) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Ptr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad PTR Ptr", l} - } - rr.Ptr = name - return slurpRemainder(c, f) -} - -func (rr *NSAPPTR) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Ptr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad NSAP-PTR Ptr", l} - } - rr.Ptr = name - return slurpRemainder(c, f) -} - -func (rr *RP) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Mbox = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - mbox, mboxOk := toAbsoluteName(l.token, o) - if l.err || !mboxOk { - return &ParseError{f, "bad RP Mbox", l} - } - rr.Mbox = mbox - - c.Next() // zBlank - l, _ = c.Next() - rr.Txt = l.token - - txt, txtOk := toAbsoluteName(l.token, o) - if l.err || !txtOk { - return &ParseError{f, "bad RP Txt", l} - } - rr.Txt = txt - - return slurpRemainder(c, f) -} - -func (rr *MR) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Mr = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MR Mr", l} - } - rr.Mr = name - return slurpRemainder(c, f) -} - -func (rr *MB) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Mb = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MB Mb", l} - } - rr.Mb = name - return slurpRemainder(c, f) -} - -func (rr *MG) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Mg = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MG Mg", l} - } - rr.Mg = name - return slurpRemainder(c, f) -} - -func (rr *HINFO) parse(c *zlexer, o, f string) *ParseError { - chunks, e := endingToTxtSlice(c, "bad HINFO Fields", f) - if e != nil { - return e - } - - if ln := len(chunks); ln == 0 { - return nil - } else if ln == 1 { - // Can we split it? - if out := strings.Fields(chunks[0]); len(out) > 1 { - chunks = out - } else { - chunks = append(chunks, "") - } - } - - rr.Cpu = chunks[0] - rr.Os = strings.Join(chunks[1:], " ") - - return nil -} - -func (rr *MINFO) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Rmail = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - rmail, rmailOk := toAbsoluteName(l.token, o) - if l.err || !rmailOk { - return &ParseError{f, "bad MINFO Rmail", l} - } - rr.Rmail = rmail - - c.Next() // zBlank - l, _ = c.Next() - rr.Email = l.token - - email, emailOk := toAbsoluteName(l.token, o) - if l.err || !emailOk { - return &ParseError{f, "bad MINFO Email", l} - } - rr.Email = email - - return slurpRemainder(c, f) -} - -func (rr *MF) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Mf = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MF Mf", l} - } - rr.Mf = name - return slurpRemainder(c, f) -} - -func (rr *MD) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Md = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MD Md", l} - } - rr.Md = name - return slurpRemainder(c, f) -} - -func (rr *MX) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad MX Pref", l} - } - rr.Preference = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Mx = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad MX Mx", l} - } - rr.Mx = name - - return slurpRemainder(c, f) -} - -func (rr *RT) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil { - return &ParseError{f, "bad RT Preference", l} - } - rr.Preference = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Host = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad RT Host", l} - } - rr.Host = name - - return slurpRemainder(c, f) -} - -func (rr *AFSDB) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad AFSDB Subtype", l} - } - rr.Subtype = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Hostname = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad AFSDB Hostname", l} - } - rr.Hostname = name - return slurpRemainder(c, f) -} - -func (rr *X25) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - if l.err { - return &ParseError{f, "bad X25 PSDNAddress", l} - } - rr.PSDNAddress = l.token - return slurpRemainder(c, f) -} - -func (rr *KX) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad KX Pref", l} - } - rr.Preference = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Exchanger = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad KX Exchanger", l} - } - rr.Exchanger = name - return slurpRemainder(c, f) -} - -func (rr *CNAME) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Target = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad CNAME Target", l} - } - rr.Target = name - return slurpRemainder(c, f) -} - -func (rr *DNAME) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Target = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad DNAME Target", l} - } - rr.Target = name - return slurpRemainder(c, f) -} - -func (rr *SOA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.Ns = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - ns, nsOk := toAbsoluteName(l.token, o) - if l.err || !nsOk { - return &ParseError{f, "bad SOA Ns", l} - } - rr.Ns = ns - - c.Next() // zBlank - l, _ = c.Next() - rr.Mbox = l.token - - mbox, mboxOk := toAbsoluteName(l.token, o) - if l.err || !mboxOk { - return &ParseError{f, "bad SOA Mbox", l} - } - rr.Mbox = mbox - - c.Next() // zBlank - - var ( - v uint32 - ok bool - ) - for i := 0; i < 5; i++ { - l, _ = c.Next() - if l.err { - return &ParseError{f, "bad SOA zone parameter", l} - } - if j, e := strconv.ParseUint(l.token, 10, 32); e != nil { - if i == 0 { - // Serial must be a number - return &ParseError{f, "bad SOA zone parameter", l} - } - // We allow other fields to be unitful duration strings - if v, ok = stringToTTL(l.token); !ok { - return &ParseError{f, "bad SOA zone parameter", l} - - } - } else { - v = uint32(j) - } - switch i { - case 0: - rr.Serial = v - c.Next() // zBlank - case 1: - rr.Refresh = v - c.Next() // zBlank - case 2: - rr.Retry = v - c.Next() // zBlank - case 3: - rr.Expire = v - c.Next() // zBlank - case 4: - rr.Minttl = v - } - } - return slurpRemainder(c, f) -} - -func (rr *SRV) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad SRV Priority", l} - } - rr.Priority = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad SRV Weight", l} - } - rr.Weight = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad SRV Port", l} - } - rr.Port = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Target = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad SRV Target", l} - } - rr.Target = name - return slurpRemainder(c, f) -} - -func (rr *NAPTR) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NAPTR Order", l} - } - rr.Order = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NAPTR Preference", l} - } - rr.Preference = uint16(i) - - // Flags - c.Next() // zBlank - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Flags", l} - } - l, _ = c.Next() // Either String or Quote - if l.value == zString { - rr.Flags = l.token - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Flags", l} - } - } else if l.value == zQuote { - rr.Flags = "" - } else { - return &ParseError{f, "bad NAPTR Flags", l} - } - - // Service - c.Next() // zBlank - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Service", l} - } - l, _ = c.Next() // Either String or Quote - if l.value == zString { - rr.Service = l.token - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Service", l} - } - } else if l.value == zQuote { - rr.Service = "" - } else { - return &ParseError{f, "bad NAPTR Service", l} - } - - // Regexp - c.Next() // zBlank - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Regexp", l} - } - l, _ = c.Next() // Either String or Quote - if l.value == zString { - rr.Regexp = l.token - l, _ = c.Next() // _QUOTE - if l.value != zQuote { - return &ParseError{f, "bad NAPTR Regexp", l} - } - } else if l.value == zQuote { - rr.Regexp = "" - } else { - return &ParseError{f, "bad NAPTR Regexp", l} - } - - // After quote no space?? - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Replacement = l.token - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad NAPTR Replacement", l} - } - rr.Replacement = name - return slurpRemainder(c, f) -} - -func (rr *TALINK) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.PreviousName = l.token - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - previousName, previousNameOk := toAbsoluteName(l.token, o) - if l.err || !previousNameOk { - return &ParseError{f, "bad TALINK PreviousName", l} - } - rr.PreviousName = previousName - - c.Next() // zBlank - l, _ = c.Next() - rr.NextName = l.token - - nextName, nextNameOk := toAbsoluteName(l.token, o) - if l.err || !nextNameOk { - return &ParseError{f, "bad TALINK NextName", l} - } - rr.NextName = nextName - - return slurpRemainder(c, f) -} - -func (rr *LOC) parse(c *zlexer, o, f string) *ParseError { - // Non zero defaults for LOC record, see RFC 1876, Section 3. - rr.HorizPre = 165 // 10000 - rr.VertPre = 162 // 10 - rr.Size = 18 // 1 - ok := false - - // North - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - i, e := strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { - return &ParseError{f, "bad LOC Latitude", l} - } - rr.Latitude = 1000 * 60 * 60 * uint32(i) - - c.Next() // zBlank - // Either number, 'N' or 'S' - l, _ = c.Next() - if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { - goto East - } - i, e = strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { - return &ParseError{f, "bad LOC Latitude minutes", l} - } - rr.Latitude += 1000 * 60 * uint32(i) - - c.Next() // zBlank - l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Latitude seconds", l} - } else { - rr.Latitude += uint32(1000 * i) - } - c.Next() // zBlank - // Either number, 'N' or 'S' - l, _ = c.Next() - if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok { - goto East - } - // If still alive, flag an error - return &ParseError{f, "bad LOC Latitude North/South", l} - -East: - // East - c.Next() // zBlank - l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude", l} - } else { - rr.Longitude = 1000 * 60 * 60 * uint32(i) - } - c.Next() // zBlank - // Either number, 'E' or 'W' - l, _ = c.Next() - if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { - goto Altitude - } - if i, e := strconv.ParseUint(l.token, 10, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude minutes", l} - } else { - rr.Longitude += 1000 * 60 * uint32(i) - } - c.Next() // zBlank - l, _ = c.Next() - if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err { - return &ParseError{f, "bad LOC Longitude seconds", l} - } else { - rr.Longitude += uint32(1000 * i) - } - c.Next() // zBlank - // Either number, 'E' or 'W' - l, _ = c.Next() - if rr.Longitude, ok = locCheckEast(l.token, rr.Longitude); ok { - goto Altitude - } - // If still alive, flag an error - return &ParseError{f, "bad LOC Longitude East/West", l} - -Altitude: - c.Next() // zBlank - l, _ = c.Next() - if len(l.token) == 0 || l.err { - return &ParseError{f, "bad LOC Altitude", l} - } - if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' { - l.token = l.token[0 : len(l.token)-1] - } - if i, e := strconv.ParseFloat(l.token, 32); e != nil { - return &ParseError{f, "bad LOC Altitude", l} - } else { - rr.Altitude = uint32(i*100.0 + 10000000.0 + 0.5) - } - - // And now optionally the other values - l, _ = c.Next() - count := 0 - for l.value != zNewline && l.value != zEOF { - switch l.value { - case zString: - switch count { - case 0: // Size - e, m, ok := stringToCm(l.token) - if !ok { - return &ParseError{f, "bad LOC Size", l} - } - rr.Size = e&0x0f | m<<4&0xf0 - case 1: // HorizPre - e, m, ok := stringToCm(l.token) - if !ok { - return &ParseError{f, "bad LOC HorizPre", l} - } - rr.HorizPre = e&0x0f | m<<4&0xf0 - case 2: // VertPre - e, m, ok := stringToCm(l.token) - if !ok { - return &ParseError{f, "bad LOC VertPre", l} - } - rr.VertPre = e&0x0f | m<<4&0xf0 - } - count++ - case zBlank: - // Ok - default: - return &ParseError{f, "bad LOC Size, HorizPre or VertPre", l} - } - l, _ = c.Next() - } - return nil -} - -func (rr *HIP) parse(c *zlexer, o, f string) *ParseError { - // HitLength is not represented - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad HIP PublicKeyAlgorithm", l} - } - rr.PublicKeyAlgorithm = uint8(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - if len(l.token) == 0 || l.err { - return &ParseError{f, "bad HIP Hit", l} - } - rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6. - rr.HitLength = uint8(len(rr.Hit)) / 2 - - c.Next() // zBlank - l, _ = c.Next() // zString - if len(l.token) == 0 || l.err { - return &ParseError{f, "bad HIP PublicKey", l} - } - rr.PublicKey = l.token // This cannot contain spaces - rr.PublicKeyLength = uint16(base64.StdEncoding.DecodedLen(len(rr.PublicKey))) - - // RendezvousServers (if any) - l, _ = c.Next() - var xs []string - for l.value != zNewline && l.value != zEOF { - switch l.value { - case zString: - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad HIP RendezvousServers", l} - } - xs = append(xs, name) - case zBlank: - // Ok - default: - return &ParseError{f, "bad HIP RendezvousServers", l} - } - l, _ = c.Next() - } - - rr.RendezvousServers = xs - return nil -} - -func (rr *CERT) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - if v, ok := StringToCertType[l.token]; ok { - rr.Type = v - } else if i, e := strconv.ParseUint(l.token, 10, 16); e != nil { - return &ParseError{f, "bad CERT Type", l} - } else { - rr.Type = uint16(i) - } - c.Next() // zBlank - l, _ = c.Next() // zString - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad CERT KeyTag", l} - } - rr.KeyTag = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - if v, ok := StringToAlgorithm[l.token]; ok { - rr.Algorithm = v - } else if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { - return &ParseError{f, "bad CERT Algorithm", l} - } else { - rr.Algorithm = uint8(i) - } - s, e1 := endingToString(c, "bad CERT Certificate", f) - if e1 != nil { - return e1 - } - rr.Certificate = s - return nil -} - -func (rr *OPENPGPKEY) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad OPENPGPKEY PublicKey", f) - if e != nil { - return e - } - rr.PublicKey = s - return nil -} - -func (rr *CSYNC) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - j, e := strconv.ParseUint(l.token, 10, 32) - if e != nil { - // Serial must be a number - return &ParseError{f, "bad CSYNC serial", l} - } - rr.Serial = uint32(j) - - c.Next() // zBlank - - l, _ = c.Next() - j, e = strconv.ParseUint(l.token, 10, 16) - if e != nil { - // Serial must be a number - return &ParseError{f, "bad CSYNC flags", l} - } - rr.Flags = uint16(j) - - rr.TypeBitMap = make([]uint16, 0) - var ( - k uint16 - ok bool - ) - l, _ = c.Next() - for l.value != zNewline && l.value != zEOF { - switch l.value { - case zBlank: - // Ok - case zString: - tokenUpper := strings.ToUpper(l.token) - if k, ok = StringToType[tokenUpper]; !ok { - if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad CSYNC TypeBitMap", l} - } - } - rr.TypeBitMap = append(rr.TypeBitMap, k) - default: - return &ParseError{f, "bad CSYNC TypeBitMap", l} - } - l, _ = c.Next() - } - return nil -} - -func (rr *SIG) parse(c *zlexer, o, f string) *ParseError { - return rr.RRSIG.parse(c, o, f) -} - -func (rr *RRSIG) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - tokenUpper := strings.ToUpper(l.token) - if t, ok := StringToType[tokenUpper]; !ok { - if strings.HasPrefix(tokenUpper, "TYPE") { - t, ok = typeToInt(l.token) - if !ok { - return &ParseError{f, "bad RRSIG Typecovered", l} - } - rr.TypeCovered = t - } else { - return &ParseError{f, "bad RRSIG Typecovered", l} - } - } else { - rr.TypeCovered = t - } - - c.Next() // zBlank - l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG Algorithm", l} - } - rr.Algorithm = uint8(i) - - c.Next() // zBlank - l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG Labels", l} - } - rr.Labels = uint8(i) - - c.Next() // zBlank - l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 32) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG OrigTtl", l} - } - rr.OrigTtl = uint32(i) - - c.Next() // zBlank - l, _ = c.Next() - if i, err := StringToTime(l.token); err != nil { - // Try to see if all numeric and use it as epoch - if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { - // TODO(miek): error out on > MAX_UINT32, same below - rr.Expiration = uint32(i) - } else { - return &ParseError{f, "bad RRSIG Expiration", l} - } - } else { - rr.Expiration = i - } - - c.Next() // zBlank - l, _ = c.Next() - if i, err := StringToTime(l.token); err != nil { - if i, err := strconv.ParseInt(l.token, 10, 64); err == nil { - rr.Inception = uint32(i) - } else { - return &ParseError{f, "bad RRSIG Inception", l} - } - } else { - rr.Inception = i - } - - c.Next() // zBlank - l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 16) - if err != nil || l.err { - return &ParseError{f, "bad RRSIG KeyTag", l} - } - rr.KeyTag = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() - rr.SignerName = l.token - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad RRSIG SignerName", l} - } - rr.SignerName = name - - s, e := endingToString(c, "bad RRSIG Signature", f) - if e != nil { - return e - } - rr.Signature = s - - return nil -} - -func (rr *NSEC) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - rr.NextDomain = l.token - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad NSEC NextDomain", l} - } - rr.NextDomain = name - - rr.TypeBitMap = make([]uint16, 0) - var ( - k uint16 - ok bool - ) - l, _ = c.Next() - for l.value != zNewline && l.value != zEOF { - switch l.value { - case zBlank: - // Ok - case zString: - tokenUpper := strings.ToUpper(l.token) - if k, ok = StringToType[tokenUpper]; !ok { - if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad NSEC TypeBitMap", l} - } - } - rr.TypeBitMap = append(rr.TypeBitMap, k) - default: - return &ParseError{f, "bad NSEC TypeBitMap", l} - } - l, _ = c.Next() - } - return nil -} - -func (rr *NSEC3) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Hash", l} - } - rr.Hash = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Flags", l} - } - rr.Flags = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3 Iterations", l} - } - rr.Iterations = uint16(i) - c.Next() - l, _ = c.Next() - if len(l.token) == 0 || l.err { - return &ParseError{f, "bad NSEC3 Salt", l} - } - if l.token != "-" { - rr.SaltLength = uint8(len(l.token)) / 2 - rr.Salt = l.token - } - - c.Next() - l, _ = c.Next() - if len(l.token) == 0 || l.err { - return &ParseError{f, "bad NSEC3 NextDomain", l} - } - rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits) - rr.NextDomain = l.token - - rr.TypeBitMap = make([]uint16, 0) - var ( - k uint16 - ok bool - ) - l, _ = c.Next() - for l.value != zNewline && l.value != zEOF { - switch l.value { - case zBlank: - // Ok - case zString: - tokenUpper := strings.ToUpper(l.token) - if k, ok = StringToType[tokenUpper]; !ok { - if k, ok = typeToInt(l.token); !ok { - return &ParseError{f, "bad NSEC3 TypeBitMap", l} - } - } - rr.TypeBitMap = append(rr.TypeBitMap, k) - default: - return &ParseError{f, "bad NSEC3 TypeBitMap", l} - } - l, _ = c.Next() - } - return nil -} - -func (rr *NSEC3PARAM) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Hash", l} - } - rr.Hash = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Flags", l} - } - rr.Flags = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NSEC3PARAM Iterations", l} - } - rr.Iterations = uint16(i) - c.Next() - l, _ = c.Next() - if l.token != "-" { - rr.SaltLength = uint8(len(l.token)) - rr.Salt = l.token - } - return slurpRemainder(c, f) -} - -func (rr *EUI48) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - if len(l.token) != 17 || l.err { - return &ParseError{f, "bad EUI48 Address", l} - } - addr := make([]byte, 12) - dash := 0 - for i := 0; i < 10; i += 2 { - addr[i] = l.token[i+dash] - addr[i+1] = l.token[i+1+dash] - dash++ - if l.token[i+1+dash] != '-' { - return &ParseError{f, "bad EUI48 Address", l} - } - } - addr[10] = l.token[15] - addr[11] = l.token[16] - - i, e := strconv.ParseUint(string(addr), 16, 48) - if e != nil { - return &ParseError{f, "bad EUI48 Address", l} - } - rr.Address = i - return slurpRemainder(c, f) -} - -func (rr *EUI64) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - if len(l.token) != 23 || l.err { - return &ParseError{f, "bad EUI64 Address", l} - } - addr := make([]byte, 16) - dash := 0 - for i := 0; i < 14; i += 2 { - addr[i] = l.token[i+dash] - addr[i+1] = l.token[i+1+dash] - dash++ - if l.token[i+1+dash] != '-' { - return &ParseError{f, "bad EUI64 Address", l} - } - } - addr[14] = l.token[21] - addr[15] = l.token[22] - - i, e := strconv.ParseUint(string(addr), 16, 64) - if e != nil { - return &ParseError{f, "bad EUI68 Address", l} - } - rr.Address = i - return slurpRemainder(c, f) -} - -func (rr *SSHFP) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SSHFP Algorithm", l} - } - rr.Algorithm = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SSHFP Type", l} - } - rr.Type = uint8(i) - c.Next() // zBlank - s, e1 := endingToString(c, "bad SSHFP Fingerprint", f) - if e1 != nil { - return e1 - } - rr.FingerPrint = s - return nil -} - -func (rr *DNSKEY) parseDNSKEY(c *zlexer, o, f, typ string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Flags", l} - } - rr.Flags = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Protocol", l} - } - rr.Protocol = uint8(i) - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " Algorithm", l} - } - rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" PublicKey", f) - if e1 != nil { - return e1 - } - rr.PublicKey = s - return nil -} - -func (rr *DNSKEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "DNSKEY") -} - -func (rr *KEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "KEY") -} - -func (rr *CDNSKEY) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDNSKEY(c, o, f, "CDNSKEY") -} - -func (rr *RKEY) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad RKEY Flags", l} - } - rr.Flags = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad RKEY Protocol", l} - } - rr.Protocol = uint8(i) - c.Next() // zBlank - l, _ = c.Next() // zString - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad RKEY Algorithm", l} - } - rr.Algorithm = uint8(i) - s, e1 := endingToString(c, "bad RKEY PublicKey", f) - if e1 != nil { - return e1 - } - rr.PublicKey = s - return nil -} - -func (rr *EID) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad EID Endpoint", f) - if e != nil { - return e - } - rr.Endpoint = s - return nil -} - -func (rr *NIMLOC) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToString(c, "bad NIMLOC Locator", f) - if e != nil { - return e - } - rr.Locator = s - return nil -} - -func (rr *GPOS) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - _, e := strconv.ParseFloat(l.token, 64) - if e != nil || l.err { - return &ParseError{f, "bad GPOS Longitude", l} - } - rr.Longitude = l.token - c.Next() // zBlank - l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { - return &ParseError{f, "bad GPOS Latitude", l} - } - rr.Latitude = l.token - c.Next() // zBlank - l, _ = c.Next() - _, e = strconv.ParseFloat(l.token, 64) - if e != nil || l.err { - return &ParseError{f, "bad GPOS Altitude", l} - } - rr.Altitude = l.token - return slurpRemainder(c, f) -} - -func (rr *DS) parseDS(c *zlexer, o, f, typ string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " KeyTag", l} - } - rr.KeyTag = uint16(i) - c.Next() // zBlank - l, _ = c.Next() - if i, e = strconv.ParseUint(l.token, 10, 8); e != nil { - tokenUpper := strings.ToUpper(l.token) - i, ok := StringToAlgorithm[tokenUpper] - if !ok || l.err { - return &ParseError{f, "bad " + typ + " Algorithm", l} - } - rr.Algorithm = i - } else { - rr.Algorithm = uint8(i) - } - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad " + typ + " DigestType", l} - } - rr.DigestType = uint8(i) - s, e1 := endingToString(c, "bad "+typ+" Digest", f) - if e1 != nil { - return e1 - } - rr.Digest = s - return nil -} - -func (rr *DS) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "DS") -} - -func (rr *DLV) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "DLV") -} - -func (rr *CDS) parse(c *zlexer, o, f string) *ParseError { - return rr.parseDS(c, o, f, "CDS") -} - -func (rr *TA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad TA KeyTag", l} - } - rr.KeyTag = uint16(i) - c.Next() // zBlank - l, _ = c.Next() - if i, e := strconv.ParseUint(l.token, 10, 8); e != nil { - tokenUpper := strings.ToUpper(l.token) - i, ok := StringToAlgorithm[tokenUpper] - if !ok || l.err { - return &ParseError{f, "bad TA Algorithm", l} - } - rr.Algorithm = i - } else { - rr.Algorithm = uint8(i) - } - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TA DigestType", l} - } - rr.DigestType = uint8(i) - s, err := endingToString(c, "bad TA Digest", f) - if err != nil { - return err - } - rr.Digest = s - return nil -} - -func (rr *TLSA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TLSA Usage", l} - } - rr.Usage = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TLSA Selector", l} - } - rr.Selector = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad TLSA MatchingType", l} - } - rr.MatchingType = uint8(i) - // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad TLSA Certificate", f) - if e2 != nil { - return e2 - } - rr.Certificate = s - return nil -} - -func (rr *SMIMEA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SMIMEA Usage", l} - } - rr.Usage = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SMIMEA Selector", l} - } - rr.Selector = uint8(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 8) - if e != nil || l.err { - return &ParseError{f, "bad SMIMEA MatchingType", l} - } - rr.MatchingType = uint8(i) - // So this needs be e2 (i.e. different than e), because...??t - s, e2 := endingToString(c, "bad SMIMEA Certificate", f) - if e2 != nil { - return e2 - } - rr.Certificate = s - return nil -} - -func (rr *RFC3597) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if l.token != "\\#" { - return &ParseError{f, "bad RFC3597 Rdata", l} - } - - c.Next() // zBlank - l, _ = c.Next() - rdlength, e := strconv.Atoi(l.token) - if e != nil || l.err { - return &ParseError{f, "bad RFC3597 Rdata ", l} - } - - s, e1 := endingToString(c, "bad RFC3597 Rdata", f) - if e1 != nil { - return e1 - } - if rdlength*2 != len(s) { - return &ParseError{f, "bad RFC3597 Rdata", l} - } - rr.Rdata = s - return nil -} - -func (rr *SPF) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad SPF Txt", f) - if e != nil { - return e - } - rr.Txt = s - return nil -} - -func (rr *AVC) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad AVC Txt", f) - if e != nil { - return e - } - rr.Txt = s - return nil -} - -func (rr *TXT) parse(c *zlexer, o, f string) *ParseError { - // no zBlank reading here, because all this rdata is TXT - s, e := endingToTxtSlice(c, "bad TXT Txt", f) - if e != nil { - return e - } - rr.Txt = s - return nil -} - -// identical to setTXT -func (rr *NINFO) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad NINFO ZSData", f) - if e != nil { - return e - } - rr.ZSData = s - return nil -} - -func (rr *URI) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad URI Priority", l} - } - rr.Priority = uint16(i) - c.Next() // zBlank - l, _ = c.Next() - i, e = strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad URI Weight", l} - } - rr.Weight = uint16(i) - - c.Next() // zBlank - s, err := endingToTxtSlice(c, "bad URI Target", f) - if err != nil { - return err - } - if len(s) != 1 { - return &ParseError{f, "bad URI Target", l} - } - rr.Target = s[0] - return nil -} - -func (rr *DHCID) parse(c *zlexer, o, f string) *ParseError { - // awesome record to parse! - s, e := endingToString(c, "bad DHCID Digest", f) - if e != nil { - return e - } - rr.Digest = s - return nil -} - -func (rr *NID) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad NID Preference", l} - } - rr.Preference = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err - } - rr.NodeID = u - return slurpRemainder(c, f) -} - -func (rr *L32) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad L32 Preference", l} - } - rr.Preference = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Locator32 = net.ParseIP(l.token) - if rr.Locator32 == nil || l.err { - return &ParseError{f, "bad L32 Locator", l} - } - return slurpRemainder(c, f) -} - -func (rr *LP) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad LP Preference", l} - } - rr.Preference = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Fqdn = l.token - name, nameOk := toAbsoluteName(l.token, o) - if l.err || !nameOk { - return &ParseError{f, "bad LP Fqdn", l} - } - rr.Fqdn = name - - return slurpRemainder(c, f) -} - -func (rr *L64) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad L64 Preference", l} - } - rr.Preference = uint16(i) - c.Next() // zBlank - l, _ = c.Next() // zString - u, err := stringToNodeID(l) - if err != nil || l.err { - return err - } - rr.Locator64 = u - return slurpRemainder(c, f) -} - -func (rr *UID) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { - return &ParseError{f, "bad UID Uid", l} - } - rr.Uid = uint32(i) - return slurpRemainder(c, f) -} - -func (rr *GID) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 32) - if e != nil || l.err { - return &ParseError{f, "bad GID Gid", l} - } - rr.Gid = uint32(i) - return slurpRemainder(c, f) -} - -func (rr *UINFO) parse(c *zlexer, o, f string) *ParseError { - s, e := endingToTxtSlice(c, "bad UINFO Uinfo", f) - if e != nil { - return e - } - if ln := len(s); ln == 0 { - return nil - } - rr.Uinfo = s[0] // silently discard anything after the first character-string - return nil -} - -func (rr *PX) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return slurpRemainder(c, f) - } - - i, e := strconv.ParseUint(l.token, 10, 16) - if e != nil || l.err { - return &ParseError{f, "bad PX Preference", l} - } - rr.Preference = uint16(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Map822 = l.token - map822, map822Ok := toAbsoluteName(l.token, o) - if l.err || !map822Ok { - return &ParseError{f, "bad PX Map822", l} - } - rr.Map822 = map822 - - c.Next() // zBlank - l, _ = c.Next() // zString - rr.Mapx400 = l.token - mapx400, mapx400Ok := toAbsoluteName(l.token, o) - if l.err || !mapx400Ok { - return &ParseError{f, "bad PX Mapx400", l} - } - rr.Mapx400 = mapx400 - - return slurpRemainder(c, f) -} - -func (rr *CAA) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - if len(l.token) == 0 { // dynamic update rr. - return nil - } - - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad CAA Flag", l} - } - rr.Flag = uint8(i) - - c.Next() // zBlank - l, _ = c.Next() // zString - if l.value != zString { - return &ParseError{f, "bad CAA Tag", l} - } - rr.Tag = l.token - - c.Next() // zBlank - s, e := endingToTxtSlice(c, "bad CAA Value", f) - if e != nil { - return e - } - if len(s) != 1 { - return &ParseError{f, "bad CAA Value", l} - } - rr.Value = s[0] - return nil -} - -func (rr *TKEY) parse(c *zlexer, o, f string) *ParseError { - l, _ := c.Next() - - // Algorithm - if l.value != zString { - return &ParseError{f, "bad TKEY algorithm", l} - } - rr.Algorithm = l.token - c.Next() // zBlank - - // Get the key length and key values - l, _ = c.Next() - i, err := strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad TKEY key length", l} - } - rr.KeySize = uint16(i) - c.Next() // zBlank - l, _ = c.Next() - if l.value != zString { - return &ParseError{f, "bad TKEY key", l} - } - rr.Key = l.token - c.Next() // zBlank - - // Get the otherdata length and string data - l, _ = c.Next() - i, err = strconv.ParseUint(l.token, 10, 8) - if err != nil || l.err { - return &ParseError{f, "bad TKEY otherdata length", l} - } - rr.OtherLen = uint16(i) - c.Next() // zBlank - l, _ = c.Next() - if l.value != zString { - return &ParseError{f, "bad TKEY otherday", l} - } - rr.OtherData = l.token - - return nil -} diff --git a/vendor/github.com/miekg/dns/serve_mux.go b/vendor/github.com/miekg/dns/serve_mux.go deleted file mode 100644 index ae304db53..000000000 --- a/vendor/github.com/miekg/dns/serve_mux.go +++ /dev/null @@ -1,147 +0,0 @@ -package dns - -import ( - "strings" - "sync" -) - -// ServeMux is an DNS request multiplexer. It matches the zone name of -// each incoming request against a list of registered patterns add calls -// the handler for the pattern that most closely matches the zone name. -// -// ServeMux is DNSSEC aware, meaning that queries for the DS record are -// redirected to the parent zone (if that is also registered), otherwise -// the child gets the query. -// -// ServeMux is also safe for concurrent access from multiple goroutines. -// -// The zero ServeMux is empty and ready for use. -type ServeMux struct { - z map[string]Handler - m sync.RWMutex -} - -// NewServeMux allocates and returns a new ServeMux. -func NewServeMux() *ServeMux { - return new(ServeMux) -} - -// DefaultServeMux is the default ServeMux used by Serve. -var DefaultServeMux = NewServeMux() - -func (mux *ServeMux) match(q string, t uint16) Handler { - mux.m.RLock() - defer mux.m.RUnlock() - if mux.z == nil { - return nil - } - - var handler Handler - - // TODO(tmthrgd): Once https://go-review.googlesource.com/c/go/+/137575 - // lands in a go release, replace the following with strings.ToLower. - var sb strings.Builder - for i := 0; i < len(q); i++ { - c := q[i] - if !(c >= 'A' && c <= 'Z') { - continue - } - - sb.Grow(len(q)) - sb.WriteString(q[:i]) - - for ; i < len(q); i++ { - c := q[i] - if c >= 'A' && c <= 'Z' { - c += 'a' - 'A' - } - - sb.WriteByte(c) - } - - q = sb.String() - break - } - - for off, end := 0, false; !end; off, end = NextLabel(q, off) { - if h, ok := mux.z[q[off:]]; ok { - if t != TypeDS { - return h - } - // Continue for DS to see if we have a parent too, if so delegate to the parent - handler = h - } - } - - // Wildcard match, if we have found nothing try the root zone as a last resort. - if h, ok := mux.z["."]; ok { - return h - } - - return handler -} - -// Handle adds a handler to the ServeMux for pattern. -func (mux *ServeMux) Handle(pattern string, handler Handler) { - if pattern == "" { - panic("dns: invalid pattern " + pattern) - } - mux.m.Lock() - if mux.z == nil { - mux.z = make(map[string]Handler) - } - mux.z[Fqdn(pattern)] = handler - mux.m.Unlock() -} - -// HandleFunc adds a handler function to the ServeMux for pattern. -func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { - mux.Handle(pattern, HandlerFunc(handler)) -} - -// HandleRemove deregisters the handler specific for pattern from the ServeMux. -func (mux *ServeMux) HandleRemove(pattern string) { - if pattern == "" { - panic("dns: invalid pattern " + pattern) - } - mux.m.Lock() - delete(mux.z, Fqdn(pattern)) - mux.m.Unlock() -} - -// ServeDNS dispatches the request to the handler whose pattern most -// closely matches the request message. -// -// ServeDNS is DNSSEC aware, meaning that queries for the DS record -// are redirected to the parent zone (if that is also registered), -// otherwise the child gets the query. -// -// If no handler is found, or there is no question, a standard SERVFAIL -// message is returned -func (mux *ServeMux) ServeDNS(w ResponseWriter, req *Msg) { - var h Handler - if len(req.Question) >= 1 { // allow more than one question - h = mux.match(req.Question[0].Name, req.Question[0].Qtype) - } - - if h != nil { - h.ServeDNS(w, req) - } else { - HandleFailed(w, req) - } -} - -// Handle registers the handler with the given pattern -// in the DefaultServeMux. The documentation for -// ServeMux explains how patterns are matched. -func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } - -// HandleRemove deregisters the handle with the given pattern -// in the DefaultServeMux. -func HandleRemove(pattern string) { DefaultServeMux.HandleRemove(pattern) } - -// HandleFunc registers the handler function with the given pattern -// in the DefaultServeMux. -func HandleFunc(pattern string, handler func(ResponseWriter, *Msg)) { - DefaultServeMux.HandleFunc(pattern, handler) -} diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go deleted file mode 100644 index 97cc87a71..000000000 --- a/vendor/github.com/miekg/dns/server.go +++ /dev/null @@ -1,757 +0,0 @@ -// DNS server implementation. - -package dns - -import ( - "context" - "crypto/tls" - "encoding/binary" - "errors" - "io" - "net" - "strings" - "sync" - "time" -) - -// Default maximum number of TCP queries before we close the socket. -const maxTCPQueries = 128 - -// aLongTimeAgo is a non-zero time, far in the past, used for -// immediate cancelation of network operations. -var aLongTimeAgo = time.Unix(1, 0) - -// Handler is implemented by any value that implements ServeDNS. -type Handler interface { - ServeDNS(w ResponseWriter, r *Msg) -} - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as DNS handlers. If f is a function -// with the appropriate signature, HandlerFunc(f) is a -// Handler object that calls f. -type HandlerFunc func(ResponseWriter, *Msg) - -// ServeDNS calls f(w, r). -func (f HandlerFunc) ServeDNS(w ResponseWriter, r *Msg) { - f(w, r) -} - -// A ResponseWriter interface is used by an DNS handler to -// construct an DNS response. -type ResponseWriter interface { - // LocalAddr returns the net.Addr of the server - LocalAddr() net.Addr - // RemoteAddr returns the net.Addr of the client that sent the current request. - RemoteAddr() net.Addr - // WriteMsg writes a reply back to the client. - WriteMsg(*Msg) error - // Write writes a raw buffer back to the client. - Write([]byte) (int, error) - // Close closes the connection. - Close() error - // TsigStatus returns the status of the Tsig. - TsigStatus() error - // TsigTimersOnly sets the tsig timers only boolean. - TsigTimersOnly(bool) - // Hijack lets the caller take over the connection. - // After a call to Hijack(), the DNS package will not do anything with the connection. - Hijack() -} - -// A ConnectionStater interface is used by a DNS Handler to access TLS connection state -// when available. -type ConnectionStater interface { - ConnectionState() *tls.ConnectionState -} - -type response struct { - closed bool // connection has been closed - hijacked bool // connection has been hijacked by handler - tsigTimersOnly bool - tsigStatus error - tsigRequestMAC string - tsigSecret map[string]string // the tsig secrets - udp *net.UDPConn // i/o connection if UDP was used - tcp net.Conn // i/o connection if TCP was used - udpSession *SessionUDP // oob data to get egress interface right - writer Writer // writer to output the raw DNS bits -} - -// HandleFailed returns a HandlerFunc that returns SERVFAIL for every request it gets. -func HandleFailed(w ResponseWriter, r *Msg) { - m := new(Msg) - m.SetRcode(r, RcodeServerFailure) - // does not matter if this write fails - w.WriteMsg(m) -} - -// ListenAndServe Starts a server on address and network specified Invoke handler -// for incoming queries. -func ListenAndServe(addr string, network string, handler Handler) error { - server := &Server{Addr: addr, Net: network, Handler: handler} - return server.ListenAndServe() -} - -// ListenAndServeTLS acts like http.ListenAndServeTLS, more information in -// http://golang.org/pkg/net/http/#ListenAndServeTLS -func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return err - } - - config := tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - server := &Server{ - Addr: addr, - Net: "tcp-tls", - TLSConfig: &config, - Handler: handler, - } - - return server.ListenAndServe() -} - -// ActivateAndServe activates a server with a listener from systemd, -// l and p should not both be non-nil. -// If both l and p are not nil only p will be used. -// Invoke handler for incoming queries. -func ActivateAndServe(l net.Listener, p net.PacketConn, handler Handler) error { - server := &Server{Listener: l, PacketConn: p, Handler: handler} - return server.ActivateAndServe() -} - -// Writer writes raw DNS messages; each call to Write should send an entire message. -type Writer interface { - io.Writer -} - -// Reader reads raw DNS messages; each call to ReadTCP or ReadUDP should return an entire message. -type Reader interface { - // ReadTCP reads a raw message from a TCP connection. Implementations may alter - // connection properties, for example the read-deadline. - ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) - // ReadUDP reads a raw message from a UDP connection. Implementations may alter - // connection properties, for example the read-deadline. - ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) -} - -// defaultReader is an adapter for the Server struct that implements the Reader interface -// using the readTCP and readUDP func of the embedded Server. -type defaultReader struct { - *Server -} - -func (dr defaultReader) ReadTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { - return dr.readTCP(conn, timeout) -} - -func (dr defaultReader) ReadUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { - return dr.readUDP(conn, timeout) -} - -// DecorateReader is a decorator hook for extending or supplanting the functionality of a Reader. -// Implementations should never return a nil Reader. -type DecorateReader func(Reader) Reader - -// DecorateWriter is a decorator hook for extending or supplanting the functionality of a Writer. -// Implementations should never return a nil Writer. -type DecorateWriter func(Writer) Writer - -// A Server defines parameters for running an DNS server. -type Server struct { - // Address to listen on, ":dns" if empty. - Addr string - // if "tcp" or "tcp-tls" (DNS over TLS) it will invoke a TCP listener, otherwise an UDP one - Net string - // TCP Listener to use, this is to aid in systemd's socket activation. - Listener net.Listener - // TLS connection configuration - TLSConfig *tls.Config - // UDP "Listener" to use, this is to aid in systemd's socket activation. - PacketConn net.PacketConn - // Handler to invoke, dns.DefaultServeMux if nil. - Handler Handler - // Default buffer size to use to read incoming UDP messages. If not set - // it defaults to MinMsgSize (512 B). - UDPSize int - // The net.Conn.SetReadTimeout value for new connections, defaults to 2 * time.Second. - ReadTimeout time.Duration - // The net.Conn.SetWriteTimeout value for new connections, defaults to 2 * time.Second. - WriteTimeout time.Duration - // TCP idle timeout for multiple queries, if nil, defaults to 8 * time.Second (RFC 5966). - IdleTimeout func() time.Duration - // Secret(s) for Tsig map[]. The zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2). - TsigSecret map[string]string - // If NotifyStartedFunc is set it is called once the server has started listening. - NotifyStartedFunc func() - // DecorateReader is optional, allows customization of the process that reads raw DNS messages. - DecorateReader DecorateReader - // DecorateWriter is optional, allows customization of the process that writes raw DNS messages. - DecorateWriter DecorateWriter - // Maximum number of TCP queries before we close the socket. Default is maxTCPQueries (unlimited if -1). - MaxTCPQueries int - // Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address. - // It is only supported on go1.11+ and when using ListenAndServe. - ReusePort bool - // AcceptMsgFunc will check the incoming message and will reject it early in the process. - // By default DefaultMsgAcceptFunc will be used. - MsgAcceptFunc MsgAcceptFunc - - // Shutdown handling - lock sync.RWMutex - started bool - shutdown chan struct{} - conns map[net.Conn]struct{} - - // A pool for UDP message buffers. - udpPool sync.Pool -} - -func (srv *Server) isStarted() bool { - srv.lock.RLock() - started := srv.started - srv.lock.RUnlock() - return started -} - -func makeUDPBuffer(size int) func() interface{} { - return func() interface{} { - return make([]byte, size) - } -} - -func (srv *Server) init() { - srv.shutdown = make(chan struct{}) - srv.conns = make(map[net.Conn]struct{}) - - if srv.UDPSize == 0 { - srv.UDPSize = MinMsgSize - } - if srv.MsgAcceptFunc == nil { - srv.MsgAcceptFunc = DefaultMsgAcceptFunc - } - if srv.Handler == nil { - srv.Handler = DefaultServeMux - } - - srv.udpPool.New = makeUDPBuffer(srv.UDPSize) -} - -func unlockOnce(l sync.Locker) func() { - var once sync.Once - return func() { once.Do(l.Unlock) } -} - -// ListenAndServe starts a nameserver on the configured address in *Server. -func (srv *Server) ListenAndServe() error { - unlock := unlockOnce(&srv.lock) - srv.lock.Lock() - defer unlock() - - if srv.started { - return &Error{err: "server already started"} - } - - addr := srv.Addr - if addr == "" { - addr = ":domain" - } - - srv.init() - - switch srv.Net { - case "tcp", "tcp4", "tcp6": - l, err := listenTCP(srv.Net, addr, srv.ReusePort) - if err != nil { - return err - } - srv.Listener = l - srv.started = true - unlock() - return srv.serveTCP(l) - case "tcp-tls", "tcp4-tls", "tcp6-tls": - if srv.TLSConfig == nil || (len(srv.TLSConfig.Certificates) == 0 && srv.TLSConfig.GetCertificate == nil) { - return errors.New("dns: neither Certificates nor GetCertificate set in Config") - } - network := strings.TrimSuffix(srv.Net, "-tls") - l, err := listenTCP(network, addr, srv.ReusePort) - if err != nil { - return err - } - l = tls.NewListener(l, srv.TLSConfig) - srv.Listener = l - srv.started = true - unlock() - return srv.serveTCP(l) - case "udp", "udp4", "udp6": - l, err := listenUDP(srv.Net, addr, srv.ReusePort) - if err != nil { - return err - } - u := l.(*net.UDPConn) - if e := setUDPSocketOptions(u); e != nil { - return e - } - srv.PacketConn = l - srv.started = true - unlock() - return srv.serveUDP(u) - } - return &Error{err: "bad network"} -} - -// ActivateAndServe starts a nameserver with the PacketConn or Listener -// configured in *Server. Its main use is to start a server from systemd. -func (srv *Server) ActivateAndServe() error { - unlock := unlockOnce(&srv.lock) - srv.lock.Lock() - defer unlock() - - if srv.started { - return &Error{err: "server already started"} - } - - srv.init() - - pConn := srv.PacketConn - l := srv.Listener - if pConn != nil { - // Check PacketConn interface's type is valid and value - // is not nil - if t, ok := pConn.(*net.UDPConn); ok && t != nil { - if e := setUDPSocketOptions(t); e != nil { - return e - } - srv.started = true - unlock() - return srv.serveUDP(t) - } - } - if l != nil { - srv.started = true - unlock() - return srv.serveTCP(l) - } - return &Error{err: "bad listeners"} -} - -// Shutdown shuts down a server. After a call to Shutdown, ListenAndServe and -// ActivateAndServe will return. -func (srv *Server) Shutdown() error { - return srv.ShutdownContext(context.Background()) -} - -// ShutdownContext shuts down a server. After a call to ShutdownContext, -// ListenAndServe and ActivateAndServe will return. -// -// A context.Context may be passed to limit how long to wait for connections -// to terminate. -func (srv *Server) ShutdownContext(ctx context.Context) error { - srv.lock.Lock() - if !srv.started { - srv.lock.Unlock() - return &Error{err: "server not started"} - } - - srv.started = false - - if srv.PacketConn != nil { - srv.PacketConn.SetReadDeadline(aLongTimeAgo) // Unblock reads - } - - if srv.Listener != nil { - srv.Listener.Close() - } - - for rw := range srv.conns { - rw.SetReadDeadline(aLongTimeAgo) // Unblock reads - } - - srv.lock.Unlock() - - if testShutdownNotify != nil { - testShutdownNotify.Broadcast() - } - - var ctxErr error - select { - case <-srv.shutdown: - case <-ctx.Done(): - ctxErr = ctx.Err() - } - - if srv.PacketConn != nil { - srv.PacketConn.Close() - } - - return ctxErr -} - -var testShutdownNotify *sync.Cond - -// getReadTimeout is a helper func to use system timeout if server did not intend to change it. -func (srv *Server) getReadTimeout() time.Duration { - if srv.ReadTimeout != 0 { - return srv.ReadTimeout - } - return dnsTimeout -} - -// serveTCP starts a TCP listener for the server. -func (srv *Server) serveTCP(l net.Listener) error { - defer l.Close() - - if srv.NotifyStartedFunc != nil { - srv.NotifyStartedFunc() - } - - var wg sync.WaitGroup - defer func() { - wg.Wait() - close(srv.shutdown) - }() - - for srv.isStarted() { - rw, err := l.Accept() - if err != nil { - if !srv.isStarted() { - return nil - } - if neterr, ok := err.(net.Error); ok && neterr.Temporary() { - continue - } - return err - } - srv.lock.Lock() - // Track the connection to allow unblocking reads on shutdown. - srv.conns[rw] = struct{}{} - srv.lock.Unlock() - wg.Add(1) - go srv.serveTCPConn(&wg, rw) - } - - return nil -} - -// serveUDP starts a UDP listener for the server. -func (srv *Server) serveUDP(l *net.UDPConn) error { - defer l.Close() - - if srv.NotifyStartedFunc != nil { - srv.NotifyStartedFunc() - } - - reader := Reader(defaultReader{srv}) - if srv.DecorateReader != nil { - reader = srv.DecorateReader(reader) - } - - var wg sync.WaitGroup - defer func() { - wg.Wait() - close(srv.shutdown) - }() - - rtimeout := srv.getReadTimeout() - // deadline is not used here - for srv.isStarted() { - m, s, err := reader.ReadUDP(l, rtimeout) - if err != nil { - if !srv.isStarted() { - return nil - } - if netErr, ok := err.(net.Error); ok && netErr.Temporary() { - continue - } - return err - } - if len(m) < headerSize { - if cap(m) == srv.UDPSize { - srv.udpPool.Put(m[:srv.UDPSize]) - } - continue - } - wg.Add(1) - go srv.serveUDPPacket(&wg, m, l, s) - } - - return nil -} - -// Serve a new TCP connection. -func (srv *Server) serveTCPConn(wg *sync.WaitGroup, rw net.Conn) { - w := &response{tsigSecret: srv.TsigSecret, tcp: rw} - if srv.DecorateWriter != nil { - w.writer = srv.DecorateWriter(w) - } else { - w.writer = w - } - - reader := Reader(defaultReader{srv}) - if srv.DecorateReader != nil { - reader = srv.DecorateReader(reader) - } - - idleTimeout := tcpIdleTimeout - if srv.IdleTimeout != nil { - idleTimeout = srv.IdleTimeout() - } - - timeout := srv.getReadTimeout() - - limit := srv.MaxTCPQueries - if limit == 0 { - limit = maxTCPQueries - } - - for q := 0; (q < limit || limit == -1) && srv.isStarted(); q++ { - m, err := reader.ReadTCP(w.tcp, timeout) - if err != nil { - // TODO(tmthrgd): handle error - break - } - srv.serveDNS(m, w) - if w.closed { - break // Close() was called - } - if w.hijacked { - break // client will call Close() themselves - } - // The first read uses the read timeout, the rest use the - // idle timeout. - timeout = idleTimeout - } - - if !w.hijacked { - w.Close() - } - - srv.lock.Lock() - delete(srv.conns, w.tcp) - srv.lock.Unlock() - - wg.Done() -} - -// Serve a new UDP request. -func (srv *Server) serveUDPPacket(wg *sync.WaitGroup, m []byte, u *net.UDPConn, s *SessionUDP) { - w := &response{tsigSecret: srv.TsigSecret, udp: u, udpSession: s} - if srv.DecorateWriter != nil { - w.writer = srv.DecorateWriter(w) - } else { - w.writer = w - } - - srv.serveDNS(m, w) - wg.Done() -} - -func (srv *Server) serveDNS(m []byte, w *response) { - dh, off, err := unpackMsgHdr(m, 0) - if err != nil { - // Let client hang, they are sending crap; any reply can be used to amplify. - return - } - - req := new(Msg) - req.setHdr(dh) - - switch srv.MsgAcceptFunc(dh) { - case MsgAccept: - if req.unpack(dh, m, off) == nil { - break - } - - fallthrough - case MsgReject: - req.SetRcodeFormatError(req) - // Are we allowed to delete any OPT records here? - req.Ns, req.Answer, req.Extra = nil, nil, nil - - w.WriteMsg(req) - fallthrough - case MsgIgnore: - if w.udp != nil && cap(m) == srv.UDPSize { - srv.udpPool.Put(m[:srv.UDPSize]) - } - - return - } - - w.tsigStatus = nil - if w.tsigSecret != nil { - if t := req.IsTsig(); t != nil { - if secret, ok := w.tsigSecret[t.Hdr.Name]; ok { - w.tsigStatus = TsigVerify(m, secret, "", false) - } else { - w.tsigStatus = ErrSecret - } - w.tsigTimersOnly = false - w.tsigRequestMAC = req.Extra[len(req.Extra)-1].(*TSIG).MAC - } - } - - if w.udp != nil && cap(m) == srv.UDPSize { - srv.udpPool.Put(m[:srv.UDPSize]) - } - - srv.Handler.ServeDNS(w, req) // Writes back to the client -} - -func (srv *Server) readTCP(conn net.Conn, timeout time.Duration) ([]byte, error) { - // If we race with ShutdownContext, the read deadline may - // have been set in the distant past to unblock the read - // below. We must not override it, otherwise we may block - // ShutdownContext. - srv.lock.RLock() - if srv.started { - conn.SetReadDeadline(time.Now().Add(timeout)) - } - srv.lock.RUnlock() - - var length uint16 - if err := binary.Read(conn, binary.BigEndian, &length); err != nil { - return nil, err - } - - m := make([]byte, length) - if _, err := io.ReadFull(conn, m); err != nil { - return nil, err - } - - return m, nil -} - -func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *SessionUDP, error) { - srv.lock.RLock() - if srv.started { - // See the comment in readTCP above. - conn.SetReadDeadline(time.Now().Add(timeout)) - } - srv.lock.RUnlock() - - m := srv.udpPool.Get().([]byte) - n, s, err := ReadFromSessionUDP(conn, m) - if err != nil { - srv.udpPool.Put(m) - return nil, nil, err - } - m = m[:n] - return m, s, nil -} - -// WriteMsg implements the ResponseWriter.WriteMsg method. -func (w *response) WriteMsg(m *Msg) (err error) { - if w.closed { - return &Error{err: "WriteMsg called after Close"} - } - - var data []byte - if w.tsigSecret != nil { // if no secrets, dont check for the tsig (which is a longer check) - if t := m.IsTsig(); t != nil { - data, w.tsigRequestMAC, err = TsigGenerate(m, w.tsigSecret[t.Hdr.Name], w.tsigRequestMAC, w.tsigTimersOnly) - if err != nil { - return err - } - _, err = w.writer.Write(data) - return err - } - } - data, err = m.Pack() - if err != nil { - return err - } - _, err = w.writer.Write(data) - return err -} - -// Write implements the ResponseWriter.Write method. -func (w *response) Write(m []byte) (int, error) { - if w.closed { - return 0, &Error{err: "Write called after Close"} - } - - switch { - case w.udp != nil: - return WriteToSessionUDP(w.udp, m, w.udpSession) - case w.tcp != nil: - if len(m) > MaxMsgSize { - return 0, &Error{err: "message too large"} - } - - l := make([]byte, 2) - binary.BigEndian.PutUint16(l, uint16(len(m))) - - n, err := (&net.Buffers{l, m}).WriteTo(w.tcp) - return int(n), err - default: - panic("dns: internal error: udp and tcp both nil") - } -} - -// LocalAddr implements the ResponseWriter.LocalAddr method. -func (w *response) LocalAddr() net.Addr { - switch { - case w.udp != nil: - return w.udp.LocalAddr() - case w.tcp != nil: - return w.tcp.LocalAddr() - default: - panic("dns: internal error: udp and tcp both nil") - } -} - -// RemoteAddr implements the ResponseWriter.RemoteAddr method. -func (w *response) RemoteAddr() net.Addr { - switch { - case w.udpSession != nil: - return w.udpSession.RemoteAddr() - case w.tcp != nil: - return w.tcp.RemoteAddr() - default: - panic("dns: internal error: udpSession and tcp both nil") - } -} - -// TsigStatus implements the ResponseWriter.TsigStatus method. -func (w *response) TsigStatus() error { return w.tsigStatus } - -// TsigTimersOnly implements the ResponseWriter.TsigTimersOnly method. -func (w *response) TsigTimersOnly(b bool) { w.tsigTimersOnly = b } - -// Hijack implements the ResponseWriter.Hijack method. -func (w *response) Hijack() { w.hijacked = true } - -// Close implements the ResponseWriter.Close method -func (w *response) Close() error { - if w.closed { - return &Error{err: "connection already closed"} - } - w.closed = true - - switch { - case w.udp != nil: - // Can't close the udp conn, as that is actually the listener. - return nil - case w.tcp != nil: - return w.tcp.Close() - default: - panic("dns: internal error: udp and tcp both nil") - } -} - -// ConnectionState() implements the ConnectionStater.ConnectionState() interface. -func (w *response) ConnectionState() *tls.ConnectionState { - type tlsConnectionStater interface { - ConnectionState() tls.ConnectionState - } - if v, ok := w.tcp.(tlsConnectionStater); ok { - t := v.ConnectionState() - return &t - } - return nil -} diff --git a/vendor/github.com/miekg/dns/sig0.go b/vendor/github.com/miekg/dns/sig0.go deleted file mode 100644 index 55cf1c386..000000000 --- a/vendor/github.com/miekg/dns/sig0.go +++ /dev/null @@ -1,209 +0,0 @@ -package dns - -import ( - "crypto" - "crypto/dsa" - "crypto/ecdsa" - "crypto/rsa" - "encoding/binary" - "math/big" - "strings" - "time" -) - -// Sign signs a dns.Msg. It fills the signature with the appropriate data. -// The SIG record should have the SignerName, KeyTag, Algorithm, Inception -// and Expiration set. -func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) { - if k == nil { - return nil, ErrPrivKey - } - if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { - return nil, ErrKey - } - - rr.Hdr = RR_Header{Name: ".", Rrtype: TypeSIG, Class: ClassANY, Ttl: 0} - rr.OrigTtl, rr.TypeCovered, rr.Labels = 0, 0, 0 - - buf := make([]byte, m.Len()+Len(rr)) - mbuf, err := m.PackBuffer(buf) - if err != nil { - return nil, err - } - if &buf[0] != &mbuf[0] { - return nil, ErrBuf - } - off, err := PackRR(rr, buf, len(mbuf), nil, false) - if err != nil { - return nil, err - } - buf = buf[:off:cap(buf)] - - hash, ok := AlgorithmToHash[rr.Algorithm] - if !ok { - return nil, ErrAlg - } - - hasher := hash.New() - // Write SIG rdata - hasher.Write(buf[len(mbuf)+1+2+2+4+2:]) - // Write message - hasher.Write(buf[:len(mbuf)]) - - signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm) - if err != nil { - return nil, err - } - - rr.Signature = toBase64(signature) - - buf = append(buf, signature...) - if len(buf) > int(^uint16(0)) { - return nil, ErrBuf - } - // Adjust sig data length - rdoff := len(mbuf) + 1 + 2 + 2 + 4 - rdlen := binary.BigEndian.Uint16(buf[rdoff:]) - rdlen += uint16(len(signature)) - binary.BigEndian.PutUint16(buf[rdoff:], rdlen) - // Adjust additional count - adc := binary.BigEndian.Uint16(buf[10:]) - adc++ - binary.BigEndian.PutUint16(buf[10:], adc) - return buf, nil -} - -// Verify validates the message buf using the key k. -// It's assumed that buf is a valid message from which rr was unpacked. -func (rr *SIG) Verify(k *KEY, buf []byte) error { - if k == nil { - return ErrKey - } - if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 { - return ErrKey - } - - var hash crypto.Hash - switch rr.Algorithm { - case DSA, RSASHA1: - hash = crypto.SHA1 - case RSASHA256, ECDSAP256SHA256: - hash = crypto.SHA256 - case ECDSAP384SHA384: - hash = crypto.SHA384 - case RSASHA512: - hash = crypto.SHA512 - default: - return ErrAlg - } - hasher := hash.New() - - buflen := len(buf) - qdc := binary.BigEndian.Uint16(buf[4:]) - anc := binary.BigEndian.Uint16(buf[6:]) - auc := binary.BigEndian.Uint16(buf[8:]) - adc := binary.BigEndian.Uint16(buf[10:]) - offset := headerSize - var err error - for i := uint16(0); i < qdc && offset < buflen; i++ { - _, offset, err = UnpackDomainName(buf, offset) - if err != nil { - return err - } - // Skip past Type and Class - offset += 2 + 2 - } - for i := uint16(1); i < anc+auc+adc && offset < buflen; i++ { - _, offset, err = UnpackDomainName(buf, offset) - if err != nil { - return err - } - // Skip past Type, Class and TTL - offset += 2 + 2 + 4 - if offset+1 >= buflen { - continue - } - rdlen := binary.BigEndian.Uint16(buf[offset:]) - offset += 2 - offset += int(rdlen) - } - if offset >= buflen { - return &Error{err: "overflowing unpacking signed message"} - } - - // offset should be just prior to SIG - bodyend := offset - // owner name SHOULD be root - _, offset, err = UnpackDomainName(buf, offset) - if err != nil { - return err - } - // Skip Type, Class, TTL, RDLen - offset += 2 + 2 + 4 + 2 - sigstart := offset - // Skip Type Covered, Algorithm, Labels, Original TTL - offset += 2 + 1 + 1 + 4 - if offset+4+4 >= buflen { - return &Error{err: "overflow unpacking signed message"} - } - expire := binary.BigEndian.Uint32(buf[offset:]) - offset += 4 - incept := binary.BigEndian.Uint32(buf[offset:]) - offset += 4 - now := uint32(time.Now().Unix()) - if now < incept || now > expire { - return ErrTime - } - // Skip key tag - offset += 2 - var signername string - signername, offset, err = UnpackDomainName(buf, offset) - if err != nil { - return err - } - // If key has come from the DNS name compression might - // have mangled the case of the name - if !strings.EqualFold(signername, k.Header().Name) { - return &Error{err: "signer name doesn't match key name"} - } - sigend := offset - hasher.Write(buf[sigstart:sigend]) - hasher.Write(buf[:10]) - hasher.Write([]byte{ - byte((adc - 1) << 8), - byte(adc - 1), - }) - hasher.Write(buf[12:bodyend]) - - hashed := hasher.Sum(nil) - sig := buf[sigend:] - switch k.Algorithm { - case DSA: - pk := k.publicKeyDSA() - sig = sig[1:] - r := new(big.Int).SetBytes(sig[:len(sig)/2]) - s := new(big.Int).SetBytes(sig[len(sig)/2:]) - if pk != nil { - if dsa.Verify(pk, hashed, r, s) { - return nil - } - return ErrSig - } - case RSASHA1, RSASHA256, RSASHA512: - pk := k.publicKeyRSA() - if pk != nil { - return rsa.VerifyPKCS1v15(pk, hash, hashed, sig) - } - case ECDSAP256SHA256, ECDSAP384SHA384: - pk := k.publicKeyECDSA() - r := new(big.Int).SetBytes(sig[:len(sig)/2]) - s := new(big.Int).SetBytes(sig[len(sig)/2:]) - if pk != nil { - if ecdsa.Verify(pk, hashed, r, s) { - return nil - } - return ErrSig - } - } - return ErrKeyAlg -} diff --git a/vendor/github.com/miekg/dns/singleinflight.go b/vendor/github.com/miekg/dns/singleinflight.go deleted file mode 100644 index febcc300f..000000000 --- a/vendor/github.com/miekg/dns/singleinflight.go +++ /dev/null @@ -1,61 +0,0 @@ -// 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. - -// Adapted for dns package usage by Miek Gieben. - -package dns - -import "sync" -import "time" - -// call is an in-flight or completed singleflight.Do call -type call struct { - wg sync.WaitGroup - val *Msg - rtt time.Duration - err error - dups int -} - -// singleflight represents a class of work and forms a namespace in -// which units of work can be executed with duplicate suppression. -type singleflight struct { - sync.Mutex // protects m - m map[string]*call // lazily initialized - - dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges -} - -// Do executes and returns the results of the given function, making -// sure that only one execution is in-flight for a given key at a -// time. If a duplicate comes in, the duplicate caller waits for the -// original to complete and receives the same results. -// The return value shared indicates whether v was given to multiple callers. -func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v *Msg, rtt time.Duration, err error, shared bool) { - g.Lock() - if g.m == nil { - g.m = make(map[string]*call) - } - if c, ok := g.m[key]; ok { - c.dups++ - g.Unlock() - c.wg.Wait() - return c.val, c.rtt, c.err, true - } - c := new(call) - c.wg.Add(1) - g.m[key] = c - g.Unlock() - - c.val, c.rtt, c.err = fn() - c.wg.Done() - - if !g.dontDeleteForTesting { - g.Lock() - delete(g.m, key) - g.Unlock() - } - - return c.val, c.rtt, c.err, c.dups > 0 -} diff --git a/vendor/github.com/miekg/dns/smimea.go b/vendor/github.com/miekg/dns/smimea.go deleted file mode 100644 index 89f09f0d1..000000000 --- a/vendor/github.com/miekg/dns/smimea.go +++ /dev/null @@ -1,44 +0,0 @@ -package dns - -import ( - "crypto/sha256" - "crypto/x509" - "encoding/hex" -) - -// Sign creates a SMIMEA record from an SSL certificate. -func (r *SMIMEA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) { - r.Hdr.Rrtype = TypeSMIMEA - r.Usage = uint8(usage) - r.Selector = uint8(selector) - r.MatchingType = uint8(matchingType) - - r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert) - return err -} - -// Verify verifies a SMIMEA record against an SSL certificate. If it is OK -// a nil error is returned. -func (r *SMIMEA) Verify(cert *x509.Certificate) error { - c, err := CertificateToDANE(r.Selector, r.MatchingType, cert) - if err != nil { - return err // Not also ErrSig? - } - if r.Certificate == c { - return nil - } - return ErrSig // ErrSig, really? -} - -// SMIMEAName returns the ownername of a SMIMEA resource record as per the -// format specified in RFC 'draft-ietf-dane-smime-12' Section 2 and 3 -func SMIMEAName(email, domain string) (string, error) { - hasher := sha256.New() - hasher.Write([]byte(email)) - - // RFC Section 3: "The local-part is hashed using the SHA2-256 - // algorithm with the hash truncated to 28 octets and - // represented in its hexadecimal representation to become the - // left-most label in the prepared domain name" - return hex.EncodeToString(hasher.Sum(nil)[:28]) + "." + "_smimecert." + domain, nil -} diff --git a/vendor/github.com/miekg/dns/tlsa.go b/vendor/github.com/miekg/dns/tlsa.go deleted file mode 100644 index 4e07983b9..000000000 --- a/vendor/github.com/miekg/dns/tlsa.go +++ /dev/null @@ -1,44 +0,0 @@ -package dns - -import ( - "crypto/x509" - "net" - "strconv" -) - -// Sign creates a TLSA record from an SSL certificate. -func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) (err error) { - r.Hdr.Rrtype = TypeTLSA - r.Usage = uint8(usage) - r.Selector = uint8(selector) - r.MatchingType = uint8(matchingType) - - r.Certificate, err = CertificateToDANE(r.Selector, r.MatchingType, cert) - return err -} - -// Verify verifies a TLSA record against an SSL certificate. If it is OK -// a nil error is returned. -func (r *TLSA) Verify(cert *x509.Certificate) error { - c, err := CertificateToDANE(r.Selector, r.MatchingType, cert) - if err != nil { - return err // Not also ErrSig? - } - if r.Certificate == c { - return nil - } - return ErrSig // ErrSig, really? -} - -// TLSAName returns the ownername of a TLSA resource record as per the -// rules specified in RFC 6698, Section 3. -func TLSAName(name, service, network string) (string, error) { - if !IsFqdn(name) { - return "", ErrFqdn - } - p, err := net.LookupPort(network, service) - if err != nil { - return "", err - } - return "_" + strconv.Itoa(p) + "._" + network + "." + name, nil -} diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go deleted file mode 100644 index afa462fa0..000000000 --- a/vendor/github.com/miekg/dns/tsig.go +++ /dev/null @@ -1,389 +0,0 @@ -package dns - -import ( - "crypto/hmac" - "crypto/md5" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "encoding/binary" - "encoding/hex" - "hash" - "strconv" - "strings" - "time" -) - -// HMAC hashing codes. These are transmitted as domain names. -const ( - HmacMD5 = "hmac-md5.sig-alg.reg.int." - HmacSHA1 = "hmac-sha1." - HmacSHA256 = "hmac-sha256." - HmacSHA512 = "hmac-sha512." -) - -// TSIG is the RR the holds the transaction signature of a message. -// See RFC 2845 and RFC 4635. -type TSIG struct { - Hdr RR_Header - Algorithm string `dns:"domain-name"` - TimeSigned uint64 `dns:"uint48"` - Fudge uint16 - MACSize uint16 - MAC string `dns:"size-hex:MACSize"` - OrigId uint16 - Error uint16 - OtherLen uint16 - OtherData string `dns:"size-hex:OtherLen"` -} - -// TSIG has no official presentation format, but this will suffice. - -func (rr *TSIG) String() string { - s := "\n;; TSIG PSEUDOSECTION:\n" - s += rr.Hdr.String() + - " " + rr.Algorithm + - " " + tsigTimeToString(rr.TimeSigned) + - " " + strconv.Itoa(int(rr.Fudge)) + - " " + strconv.Itoa(int(rr.MACSize)) + - " " + strings.ToUpper(rr.MAC) + - " " + strconv.Itoa(int(rr.OrigId)) + - " " + strconv.Itoa(int(rr.Error)) + // BIND prints NOERROR - " " + strconv.Itoa(int(rr.OtherLen)) + - " " + rr.OtherData - return s -} - -func (rr *TSIG) parse(c *zlexer, origin, file string) *ParseError { - panic("dns: internal error: parse should never be called on TSIG") -} - -// The following values must be put in wireformat, so that the MAC can be calculated. -// RFC 2845, section 3.4.2. TSIG Variables. -type tsigWireFmt struct { - // From RR_Header - Name string `dns:"domain-name"` - Class uint16 - Ttl uint32 - // Rdata of the TSIG - Algorithm string `dns:"domain-name"` - TimeSigned uint64 `dns:"uint48"` - Fudge uint16 - // MACSize, MAC and OrigId excluded - Error uint16 - OtherLen uint16 - OtherData string `dns:"size-hex:OtherLen"` -} - -// If we have the MAC use this type to convert it to wiredata. Section 3.4.3. Request MAC -type macWireFmt struct { - MACSize uint16 - MAC string `dns:"size-hex:MACSize"` -} - -// 3.3. Time values used in TSIG calculations -type timerWireFmt struct { - TimeSigned uint64 `dns:"uint48"` - Fudge uint16 -} - -// TsigGenerate fills out the TSIG record attached to the message. -// The message should contain -// a "stub" TSIG RR with the algorithm, key name (owner name of the RR), -// time fudge (defaults to 300 seconds) and the current time -// The TSIG MAC is saved in that Tsig RR. -// When TsigGenerate is called for the first time requestMAC is set to the empty string and -// timersOnly is false. -// If something goes wrong an error is returned, otherwise it is nil. -func TsigGenerate(m *Msg, secret, requestMAC string, timersOnly bool) ([]byte, string, error) { - if m.IsTsig() == nil { - panic("dns: TSIG not last RR in additional") - } - // If we barf here, the caller is to blame - rawsecret, err := fromBase64([]byte(secret)) - if err != nil { - return nil, "", err - } - - rr := m.Extra[len(m.Extra)-1].(*TSIG) - m.Extra = m.Extra[0 : len(m.Extra)-1] // kill the TSIG from the msg - mbuf, err := m.Pack() - if err != nil { - return nil, "", err - } - buf := tsigBuffer(mbuf, rr, requestMAC, timersOnly) - - t := new(TSIG) - var h hash.Hash - switch strings.ToLower(rr.Algorithm) { - case HmacMD5: - h = hmac.New(md5.New, rawsecret) - case HmacSHA1: - h = hmac.New(sha1.New, rawsecret) - case HmacSHA256: - h = hmac.New(sha256.New, rawsecret) - case HmacSHA512: - h = hmac.New(sha512.New, rawsecret) - default: - return nil, "", ErrKeyAlg - } - h.Write(buf) - t.MAC = hex.EncodeToString(h.Sum(nil)) - t.MACSize = uint16(len(t.MAC) / 2) // Size is half! - - t.Hdr = RR_Header{Name: rr.Hdr.Name, Rrtype: TypeTSIG, Class: ClassANY, Ttl: 0} - t.Fudge = rr.Fudge - t.TimeSigned = rr.TimeSigned - t.Algorithm = rr.Algorithm - t.OrigId = m.Id - - tbuf := make([]byte, Len(t)) - off, err := PackRR(t, tbuf, 0, nil, false) - if err != nil { - return nil, "", err - } - mbuf = append(mbuf, tbuf[:off]...) - // Update the ArCount directly in the buffer. - binary.BigEndian.PutUint16(mbuf[10:], uint16(len(m.Extra)+1)) - - return mbuf, t.MAC, nil -} - -// TsigVerify verifies the TSIG on a message. -// If the signature does not validate err contains the -// error, otherwise it is nil. -func TsigVerify(msg []byte, secret, requestMAC string, timersOnly bool) error { - rawsecret, err := fromBase64([]byte(secret)) - if err != nil { - return err - } - // Strip the TSIG from the incoming msg - stripped, tsig, err := stripTsig(msg) - if err != nil { - return err - } - - msgMAC, err := hex.DecodeString(tsig.MAC) - if err != nil { - return err - } - - buf := tsigBuffer(stripped, tsig, requestMAC, timersOnly) - - // Fudge factor works both ways. A message can arrive before it was signed because - // of clock skew. - now := uint64(time.Now().Unix()) - ti := now - tsig.TimeSigned - if now < tsig.TimeSigned { - ti = tsig.TimeSigned - now - } - if uint64(tsig.Fudge) < ti { - return ErrTime - } - - var h hash.Hash - switch strings.ToLower(tsig.Algorithm) { - case HmacMD5: - h = hmac.New(md5.New, rawsecret) - case HmacSHA1: - h = hmac.New(sha1.New, rawsecret) - case HmacSHA256: - h = hmac.New(sha256.New, rawsecret) - case HmacSHA512: - h = hmac.New(sha512.New, rawsecret) - default: - return ErrKeyAlg - } - h.Write(buf) - if !hmac.Equal(h.Sum(nil), msgMAC) { - return ErrSig - } - return nil -} - -// Create a wiredata buffer for the MAC calculation. -func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []byte { - var buf []byte - if rr.TimeSigned == 0 { - rr.TimeSigned = uint64(time.Now().Unix()) - } - if rr.Fudge == 0 { - rr.Fudge = 300 // Standard (RFC) default. - } - - // Replace message ID in header with original ID from TSIG - binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId) - - if requestMAC != "" { - m := new(macWireFmt) - m.MACSize = uint16(len(requestMAC) / 2) - m.MAC = requestMAC - buf = make([]byte, len(requestMAC)) // long enough - n, _ := packMacWire(m, buf) - buf = buf[:n] - } - - tsigvar := make([]byte, DefaultMsgSize) - if timersOnly { - tsig := new(timerWireFmt) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - n, _ := packTimerWire(tsig, tsigvar) - tsigvar = tsigvar[:n] - } else { - tsig := new(tsigWireFmt) - tsig.Name = strings.ToLower(rr.Hdr.Name) - tsig.Class = ClassANY - tsig.Ttl = rr.Hdr.Ttl - tsig.Algorithm = strings.ToLower(rr.Algorithm) - tsig.TimeSigned = rr.TimeSigned - tsig.Fudge = rr.Fudge - tsig.Error = rr.Error - tsig.OtherLen = rr.OtherLen - tsig.OtherData = rr.OtherData - n, _ := packTsigWire(tsig, tsigvar) - tsigvar = tsigvar[:n] - } - - if requestMAC != "" { - x := append(buf, msgbuf...) - buf = append(x, tsigvar...) - } else { - buf = append(msgbuf, tsigvar...) - } - return buf -} - -// Strip the TSIG from the raw message. -func stripTsig(msg []byte) ([]byte, *TSIG, error) { - // Copied from msg.go's Unpack() Header, but modified. - var ( - dh Header - err error - ) - off, tsigoff := 0, 0 - - if dh, off, err = unpackMsgHdr(msg, off); err != nil { - return nil, nil, err - } - if dh.Arcount == 0 { - return nil, nil, ErrNoSig - } - - // Rcode, see msg.go Unpack() - if int(dh.Bits&0xF) == RcodeNotAuth { - return nil, nil, ErrAuth - } - - for i := 0; i < int(dh.Qdcount); i++ { - _, off, err = unpackQuestion(msg, off) - if err != nil { - return nil, nil, err - } - } - - _, off, err = unpackRRslice(int(dh.Ancount), msg, off) - if err != nil { - return nil, nil, err - } - _, off, err = unpackRRslice(int(dh.Nscount), msg, off) - if err != nil { - return nil, nil, err - } - - rr := new(TSIG) - var extra RR - for i := 0; i < int(dh.Arcount); i++ { - tsigoff = off - extra, off, err = UnpackRR(msg, off) - if err != nil { - return nil, nil, err - } - if extra.Header().Rrtype == TypeTSIG { - rr = extra.(*TSIG) - // Adjust Arcount. - arcount := binary.BigEndian.Uint16(msg[10:]) - binary.BigEndian.PutUint16(msg[10:], arcount-1) - break - } - } - if rr == nil { - return nil, nil, ErrNoSig - } - return msg[:tsigoff], rr, nil -} - -// Translate the TSIG time signed into a date. There is no -// need for RFC1982 calculations as this date is 48 bits. -func tsigTimeToString(t uint64) string { - ti := time.Unix(int64(t), 0).UTC() - return ti.Format("20060102150405") -} - -func packTsigWire(tw *tsigWireFmt, msg []byte) (int, error) { - // copied from zmsg.go TSIG packing - // RR_Header - off, err := PackDomainName(tw.Name, msg, 0, nil, false) - if err != nil { - return off, err - } - off, err = packUint16(tw.Class, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(tw.Ttl, msg, off) - if err != nil { - return off, err - } - - off, err = PackDomainName(tw.Algorithm, msg, off, nil, false) - if err != nil { - return off, err - } - off, err = packUint48(tw.TimeSigned, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(tw.Fudge, msg, off) - if err != nil { - return off, err - } - - off, err = packUint16(tw.Error, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(tw.OtherLen, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(tw.OtherData, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func packMacWire(mw *macWireFmt, msg []byte) (int, error) { - off, err := packUint16(mw.MACSize, msg, 0) - if err != nil { - return off, err - } - off, err = packStringHex(mw.MAC, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func packTimerWire(tw *timerWireFmt, msg []byte) (int, error) { - off, err := packUint48(tw.TimeSigned, msg, 0) - if err != nil { - return off, err - } - off, err = packUint16(tw.Fudge, msg, off) - if err != nil { - return off, err - } - return off, nil -} diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go deleted file mode 100644 index 835f2fe7c..000000000 --- a/vendor/github.com/miekg/dns/types.go +++ /dev/null @@ -1,1434 +0,0 @@ -package dns - -import ( - "fmt" - "net" - "strconv" - "strings" - "time" -) - -type ( - // Type is a DNS type. - Type uint16 - // Class is a DNS class. - Class uint16 - // Name is a DNS domain name. - Name string -) - -// Packet formats - -// Wire constants and supported types. -const ( - // valid RR_Header.Rrtype and Question.qtype - - TypeNone uint16 = 0 - TypeA uint16 = 1 - TypeNS uint16 = 2 - TypeMD uint16 = 3 - TypeMF uint16 = 4 - TypeCNAME uint16 = 5 - TypeSOA uint16 = 6 - TypeMB uint16 = 7 - TypeMG uint16 = 8 - TypeMR uint16 = 9 - TypeNULL uint16 = 10 - TypePTR uint16 = 12 - TypeHINFO uint16 = 13 - TypeMINFO uint16 = 14 - TypeMX uint16 = 15 - TypeTXT uint16 = 16 - TypeRP uint16 = 17 - TypeAFSDB uint16 = 18 - TypeX25 uint16 = 19 - TypeISDN uint16 = 20 - TypeRT uint16 = 21 - TypeNSAPPTR uint16 = 23 - TypeSIG uint16 = 24 - TypeKEY uint16 = 25 - TypePX uint16 = 26 - TypeGPOS uint16 = 27 - TypeAAAA uint16 = 28 - TypeLOC uint16 = 29 - TypeNXT uint16 = 30 - TypeEID uint16 = 31 - TypeNIMLOC uint16 = 32 - TypeSRV uint16 = 33 - TypeATMA uint16 = 34 - TypeNAPTR uint16 = 35 - TypeKX uint16 = 36 - TypeCERT uint16 = 37 - TypeDNAME uint16 = 39 - TypeOPT uint16 = 41 // EDNS - TypeDS uint16 = 43 - TypeSSHFP uint16 = 44 - TypeRRSIG uint16 = 46 - TypeNSEC uint16 = 47 - TypeDNSKEY uint16 = 48 - TypeDHCID uint16 = 49 - TypeNSEC3 uint16 = 50 - TypeNSEC3PARAM uint16 = 51 - TypeTLSA uint16 = 52 - TypeSMIMEA uint16 = 53 - TypeHIP uint16 = 55 - TypeNINFO uint16 = 56 - TypeRKEY uint16 = 57 - TypeTALINK uint16 = 58 - TypeCDS uint16 = 59 - TypeCDNSKEY uint16 = 60 - TypeOPENPGPKEY uint16 = 61 - TypeCSYNC uint16 = 62 - TypeSPF uint16 = 99 - TypeUINFO uint16 = 100 - TypeUID uint16 = 101 - TypeGID uint16 = 102 - TypeUNSPEC uint16 = 103 - TypeNID uint16 = 104 - TypeL32 uint16 = 105 - TypeL64 uint16 = 106 - TypeLP uint16 = 107 - TypeEUI48 uint16 = 108 - TypeEUI64 uint16 = 109 - TypeURI uint16 = 256 - TypeCAA uint16 = 257 - TypeAVC uint16 = 258 - - TypeTKEY uint16 = 249 - TypeTSIG uint16 = 250 - - // valid Question.Qtype only - TypeIXFR uint16 = 251 - TypeAXFR uint16 = 252 - TypeMAILB uint16 = 253 - TypeMAILA uint16 = 254 - TypeANY uint16 = 255 - - TypeTA uint16 = 32768 - TypeDLV uint16 = 32769 - TypeReserved uint16 = 65535 - - // valid Question.Qclass - ClassINET = 1 - ClassCSNET = 2 - ClassCHAOS = 3 - ClassHESIOD = 4 - ClassNONE = 254 - ClassANY = 255 - - // Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml - RcodeSuccess = 0 // NoError - No Error [DNS] - RcodeFormatError = 1 // FormErr - Format Error [DNS] - RcodeServerFailure = 2 // ServFail - Server Failure [DNS] - RcodeNameError = 3 // NXDomain - Non-Existent Domain [DNS] - RcodeNotImplemented = 4 // NotImp - Not Implemented [DNS] - RcodeRefused = 5 // Refused - Query Refused [DNS] - RcodeYXDomain = 6 // YXDomain - Name Exists when it should not [DNS Update] - RcodeYXRrset = 7 // YXRRSet - RR Set Exists when it should not [DNS Update] - RcodeNXRrset = 8 // NXRRSet - RR Set that should exist does not [DNS Update] - RcodeNotAuth = 9 // NotAuth - Server Not Authoritative for zone [DNS Update] - RcodeNotZone = 10 // NotZone - Name not contained in zone [DNS Update/TSIG] - RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG] - RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0] - RcodeBadKey = 17 // BADKEY - Key not recognized [TSIG] - RcodeBadTime = 18 // BADTIME - Signature out of time window [TSIG] - RcodeBadMode = 19 // BADMODE - Bad TKEY Mode [TKEY] - RcodeBadName = 20 // BADNAME - Duplicate key name [TKEY] - RcodeBadAlg = 21 // BADALG - Algorithm not supported [TKEY] - RcodeBadTrunc = 22 // BADTRUNC - Bad Truncation [TSIG] - RcodeBadCookie = 23 // BADCOOKIE - Bad/missing Server Cookie [DNS Cookies] - - // Message Opcodes. There is no 3. - OpcodeQuery = 0 - OpcodeIQuery = 1 - OpcodeStatus = 2 - OpcodeNotify = 4 - OpcodeUpdate = 5 -) - -// Header is the wire format for the DNS packet header. -type Header struct { - Id uint16 - Bits uint16 - Qdcount, Ancount, Nscount, Arcount uint16 -} - -const ( - headerSize = 12 - - // Header.Bits - _QR = 1 << 15 // query/response (response=1) - _AA = 1 << 10 // authoritative - _TC = 1 << 9 // truncated - _RD = 1 << 8 // recursion desired - _RA = 1 << 7 // recursion available - _Z = 1 << 6 // Z - _AD = 1 << 5 // authticated data - _CD = 1 << 4 // checking disabled -) - -// Various constants used in the LOC RR, See RFC 1887. -const ( - LOC_EQUATOR = 1 << 31 // RFC 1876, Section 2. - LOC_PRIMEMERIDIAN = 1 << 31 // RFC 1876, Section 2. - LOC_HOURS = 60 * 1000 - LOC_DEGREES = 60 * LOC_HOURS - LOC_ALTITUDEBASE = 100000 -) - -// Different Certificate Types, see RFC 4398, Section 2.1 -const ( - CertPKIX = 1 + iota - CertSPKI - CertPGP - CertIPIX - CertISPKI - CertIPGP - CertACPKIX - CertIACPKIX - CertURI = 253 - CertOID = 254 -) - -// CertTypeToString converts the Cert Type to its string representation. -// See RFC 4398 and RFC 6944. -var CertTypeToString = map[uint16]string{ - CertPKIX: "PKIX", - CertSPKI: "SPKI", - CertPGP: "PGP", - CertIPIX: "IPIX", - CertISPKI: "ISPKI", - CertIPGP: "IPGP", - CertACPKIX: "ACPKIX", - CertIACPKIX: "IACPKIX", - CertURI: "URI", - CertOID: "OID", -} - -//go:generate go run types_generate.go - -// Question holds a DNS question. There can be multiple questions in the -// question section of a message. Usually there is just one. -type Question struct { - Name string `dns:"cdomain-name"` // "cdomain-name" specifies encoding (and may be compressed) - Qtype uint16 - Qclass uint16 -} - -func (q *Question) len(off int, compression map[string]struct{}) int { - l := domainNameLen(q.Name, off, compression, true) - l += 2 + 2 - return l -} - -func (q *Question) String() (s string) { - // prefix with ; (as in dig) - s = ";" + sprintName(q.Name) + "\t" - s += Class(q.Qclass).String() + "\t" - s += " " + Type(q.Qtype).String() - return s -} - -// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY -// is named "*" there. -type ANY struct { - Hdr RR_Header - // Does not have any rdata -} - -func (rr *ANY) String() string { return rr.Hdr.String() } - -func (rr *ANY) parse(c *zlexer, origin, file string) *ParseError { - panic("dns: internal error: parse should never be called on ANY") -} - -// NULL RR. See RFC 1035. -type NULL struct { - Hdr RR_Header - Data string `dns:"any"` -} - -func (rr *NULL) String() string { - // There is no presentation format; prefix string with a comment. - return ";" + rr.Hdr.String() + rr.Data -} - -func (rr *NULL) parse(c *zlexer, origin, file string) *ParseError { - panic("dns: internal error: parse should never be called on NULL") -} - -// CNAME RR. See RFC 1034. -type CNAME struct { - Hdr RR_Header - Target string `dns:"cdomain-name"` -} - -func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) } - -// HINFO RR. See RFC 1034. -type HINFO struct { - Hdr RR_Header - Cpu string - Os string -} - -func (rr *HINFO) String() string { - return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os}) -} - -// MB RR. See RFC 1035. -type MB struct { - Hdr RR_Header - Mb string `dns:"cdomain-name"` -} - -func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) } - -// MG RR. See RFC 1035. -type MG struct { - Hdr RR_Header - Mg string `dns:"cdomain-name"` -} - -func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) } - -// MINFO RR. See RFC 1035. -type MINFO struct { - Hdr RR_Header - Rmail string `dns:"cdomain-name"` - Email string `dns:"cdomain-name"` -} - -func (rr *MINFO) String() string { - return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email) -} - -// MR RR. See RFC 1035. -type MR struct { - Hdr RR_Header - Mr string `dns:"cdomain-name"` -} - -func (rr *MR) String() string { - return rr.Hdr.String() + sprintName(rr.Mr) -} - -// MF RR. See RFC 1035. -type MF struct { - Hdr RR_Header - Mf string `dns:"cdomain-name"` -} - -func (rr *MF) String() string { - return rr.Hdr.String() + sprintName(rr.Mf) -} - -// MD RR. See RFC 1035. -type MD struct { - Hdr RR_Header - Md string `dns:"cdomain-name"` -} - -func (rr *MD) String() string { - return rr.Hdr.String() + sprintName(rr.Md) -} - -// MX RR. See RFC 1035. -type MX struct { - Hdr RR_Header - Preference uint16 - Mx string `dns:"cdomain-name"` -} - -func (rr *MX) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx) -} - -// AFSDB RR. See RFC 1183. -type AFSDB struct { - Hdr RR_Header - Subtype uint16 - Hostname string `dns:"domain-name"` -} - -func (rr *AFSDB) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname) -} - -// X25 RR. See RFC 1183, Section 3.1. -type X25 struct { - Hdr RR_Header - PSDNAddress string -} - -func (rr *X25) String() string { - return rr.Hdr.String() + rr.PSDNAddress -} - -// RT RR. See RFC 1183, Section 3.3. -type RT struct { - Hdr RR_Header - Preference uint16 - Host string `dns:"domain-name"` // RFC 3597 prohibits compressing records not defined in RFC 1035. -} - -func (rr *RT) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host) -} - -// NS RR. See RFC 1035. -type NS struct { - Hdr RR_Header - Ns string `dns:"cdomain-name"` -} - -func (rr *NS) String() string { - return rr.Hdr.String() + sprintName(rr.Ns) -} - -// PTR RR. See RFC 1035. -type PTR struct { - Hdr RR_Header - Ptr string `dns:"cdomain-name"` -} - -func (rr *PTR) String() string { - return rr.Hdr.String() + sprintName(rr.Ptr) -} - -// RP RR. See RFC 1138, Section 2.2. -type RP struct { - Hdr RR_Header - Mbox string `dns:"domain-name"` - Txt string `dns:"domain-name"` -} - -func (rr *RP) String() string { - return rr.Hdr.String() + sprintName(rr.Mbox) + " " + sprintName(rr.Txt) -} - -// SOA RR. See RFC 1035. -type SOA struct { - Hdr RR_Header - Ns string `dns:"cdomain-name"` - Mbox string `dns:"cdomain-name"` - Serial uint32 - Refresh uint32 - Retry uint32 - Expire uint32 - Minttl uint32 -} - -func (rr *SOA) String() string { - return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) + - " " + strconv.FormatInt(int64(rr.Serial), 10) + - " " + strconv.FormatInt(int64(rr.Refresh), 10) + - " " + strconv.FormatInt(int64(rr.Retry), 10) + - " " + strconv.FormatInt(int64(rr.Expire), 10) + - " " + strconv.FormatInt(int64(rr.Minttl), 10) -} - -// TXT RR. See RFC 1035. -type TXT struct { - Hdr RR_Header - Txt []string `dns:"txt"` -} - -func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } - -func sprintName(s string) string { - var dst strings.Builder - dst.Grow(len(s)) - for i := 0; i < len(s); { - if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { - dst.WriteString(s[i : i+2]) - i += 2 - continue - } - - b, n := nextByte(s, i) - switch { - case n == 0: - i++ // dangling back slash - case b == '.': - dst.WriteByte('.') - default: - writeDomainNameByte(&dst, b) - } - i += n - } - return dst.String() -} - -func sprintTxtOctet(s string) string { - var dst strings.Builder - dst.Grow(2 + len(s)) - dst.WriteByte('"') - for i := 0; i < len(s); { - if i+1 < len(s) && s[i] == '\\' && s[i+1] == '.' { - dst.WriteString(s[i : i+2]) - i += 2 - continue - } - - b, n := nextByte(s, i) - switch { - case n == 0: - i++ // dangling back slash - case b == '.': - dst.WriteByte('.') - case b < ' ' || b > '~': - dst.WriteString(escapeByte(b)) - default: - dst.WriteByte(b) - } - i += n - } - dst.WriteByte('"') - return dst.String() -} - -func sprintTxt(txt []string) string { - var out strings.Builder - for i, s := range txt { - out.Grow(3 + len(s)) - if i > 0 { - out.WriteString(` "`) - } else { - out.WriteByte('"') - } - for j := 0; j < len(s); { - b, n := nextByte(s, j) - if n == 0 { - break - } - writeTXTStringByte(&out, b) - j += n - } - out.WriteByte('"') - } - return out.String() -} - -func writeDomainNameByte(s *strings.Builder, b byte) { - switch b { - case '.', ' ', '\'', '@', ';', '(', ')': // additional chars to escape - s.WriteByte('\\') - s.WriteByte(b) - default: - writeTXTStringByte(s, b) - } -} - -func writeTXTStringByte(s *strings.Builder, b byte) { - switch { - case b == '"' || b == '\\': - s.WriteByte('\\') - s.WriteByte(b) - case b < ' ' || b > '~': - s.WriteString(escapeByte(b)) - default: - s.WriteByte(b) - } -} - -const ( - escapedByteSmall = "" + - `\000\001\002\003\004\005\006\007\008\009` + - `\010\011\012\013\014\015\016\017\018\019` + - `\020\021\022\023\024\025\026\027\028\029` + - `\030\031` - escapedByteLarge = `\127\128\129` + - `\130\131\132\133\134\135\136\137\138\139` + - `\140\141\142\143\144\145\146\147\148\149` + - `\150\151\152\153\154\155\156\157\158\159` + - `\160\161\162\163\164\165\166\167\168\169` + - `\170\171\172\173\174\175\176\177\178\179` + - `\180\181\182\183\184\185\186\187\188\189` + - `\190\191\192\193\194\195\196\197\198\199` + - `\200\201\202\203\204\205\206\207\208\209` + - `\210\211\212\213\214\215\216\217\218\219` + - `\220\221\222\223\224\225\226\227\228\229` + - `\230\231\232\233\234\235\236\237\238\239` + - `\240\241\242\243\244\245\246\247\248\249` + - `\250\251\252\253\254\255` -) - -// escapeByte returns the \DDD escaping of b which must -// satisfy b < ' ' || b > '~'. -func escapeByte(b byte) string { - if b < ' ' { - return escapedByteSmall[b*4 : b*4+4] - } - - b -= '~' + 1 - // The cast here is needed as b*4 may overflow byte. - return escapedByteLarge[int(b)*4 : int(b)*4+4] -} - -func nextByte(s string, offset int) (byte, int) { - if offset >= len(s) { - return 0, 0 - } - if s[offset] != '\\' { - // not an escape sequence - return s[offset], 1 - } - switch len(s) - offset { - case 1: // dangling escape - return 0, 0 - case 2, 3: // too short to be \ddd - default: // maybe \ddd - if isDigit(s[offset+1]) && isDigit(s[offset+2]) && isDigit(s[offset+3]) { - return dddStringToByte(s[offset+1:]), 4 - } - } - // not \ddd, just an RFC 1035 "quoted" character - return s[offset+1], 2 -} - -// SPF RR. See RFC 4408, Section 3.1.1. -type SPF struct { - Hdr RR_Header - Txt []string `dns:"txt"` -} - -func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } - -// AVC RR. See https://www.iana.org/assignments/dns-parameters/AVC/avc-completed-template. -type AVC struct { - Hdr RR_Header - Txt []string `dns:"txt"` -} - -func (rr *AVC) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) } - -// SRV RR. See RFC 2782. -type SRV struct { - Hdr RR_Header - Priority uint16 - Weight uint16 - Port uint16 - Target string `dns:"domain-name"` -} - -func (rr *SRV) String() string { - return rr.Hdr.String() + - strconv.Itoa(int(rr.Priority)) + " " + - strconv.Itoa(int(rr.Weight)) + " " + - strconv.Itoa(int(rr.Port)) + " " + sprintName(rr.Target) -} - -// NAPTR RR. See RFC 2915. -type NAPTR struct { - Hdr RR_Header - Order uint16 - Preference uint16 - Flags string - Service string - Regexp string - Replacement string `dns:"domain-name"` -} - -func (rr *NAPTR) String() string { - return rr.Hdr.String() + - strconv.Itoa(int(rr.Order)) + " " + - strconv.Itoa(int(rr.Preference)) + " " + - "\"" + rr.Flags + "\" " + - "\"" + rr.Service + "\" " + - "\"" + rr.Regexp + "\" " + - rr.Replacement -} - -// CERT RR. See RFC 4398. -type CERT struct { - Hdr RR_Header - Type uint16 - KeyTag uint16 - Algorithm uint8 - Certificate string `dns:"base64"` -} - -func (rr *CERT) String() string { - var ( - ok bool - certtype, algorithm string - ) - if certtype, ok = CertTypeToString[rr.Type]; !ok { - certtype = strconv.Itoa(int(rr.Type)) - } - if algorithm, ok = AlgorithmToString[rr.Algorithm]; !ok { - algorithm = strconv.Itoa(int(rr.Algorithm)) - } - return rr.Hdr.String() + certtype + - " " + strconv.Itoa(int(rr.KeyTag)) + - " " + algorithm + - " " + rr.Certificate -} - -// DNAME RR. See RFC 2672. -type DNAME struct { - Hdr RR_Header - Target string `dns:"domain-name"` -} - -func (rr *DNAME) String() string { - return rr.Hdr.String() + sprintName(rr.Target) -} - -// A RR. See RFC 1035. -type A struct { - Hdr RR_Header - A net.IP `dns:"a"` -} - -func (rr *A) String() string { - if rr.A == nil { - return rr.Hdr.String() - } - return rr.Hdr.String() + rr.A.String() -} - -// AAAA RR. See RFC 3596. -type AAAA struct { - Hdr RR_Header - AAAA net.IP `dns:"aaaa"` -} - -func (rr *AAAA) String() string { - if rr.AAAA == nil { - return rr.Hdr.String() - } - return rr.Hdr.String() + rr.AAAA.String() -} - -// PX RR. See RFC 2163. -type PX struct { - Hdr RR_Header - Preference uint16 - Map822 string `dns:"domain-name"` - Mapx400 string `dns:"domain-name"` -} - -func (rr *PX) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400) -} - -// GPOS RR. See RFC 1712. -type GPOS struct { - Hdr RR_Header - Longitude string - Latitude string - Altitude string -} - -func (rr *GPOS) String() string { - return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude -} - -// LOC RR. See RFC RFC 1876. -type LOC struct { - Hdr RR_Header - Version uint8 - Size uint8 - HorizPre uint8 - VertPre uint8 - Latitude uint32 - Longitude uint32 - Altitude uint32 -} - -// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent -// format and returns a string in m (two decimals for the cm) -func cmToM(m, e uint8) string { - if e < 2 { - if e == 1 { - m *= 10 - } - - return fmt.Sprintf("0.%02d", m) - } - - s := fmt.Sprintf("%d", m) - for e > 2 { - s += "0" - e-- - } - return s -} - -func (rr *LOC) String() string { - s := rr.Hdr.String() - - lat := rr.Latitude - ns := "N" - if lat > LOC_EQUATOR { - lat = lat - LOC_EQUATOR - } else { - ns = "S" - lat = LOC_EQUATOR - lat - } - h := lat / LOC_DEGREES - lat = lat % LOC_DEGREES - m := lat / LOC_HOURS - lat = lat % LOC_HOURS - s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lat)/1000, ns) - - lon := rr.Longitude - ew := "E" - if lon > LOC_PRIMEMERIDIAN { - lon = lon - LOC_PRIMEMERIDIAN - } else { - ew = "W" - lon = LOC_PRIMEMERIDIAN - lon - } - h = lon / LOC_DEGREES - lon = lon % LOC_DEGREES - m = lon / LOC_HOURS - lon = lon % LOC_HOURS - s += fmt.Sprintf("%02d %02d %0.3f %s ", h, m, float64(lon)/1000, ew) - - var alt = float64(rr.Altitude) / 100 - alt -= LOC_ALTITUDEBASE - if rr.Altitude%100 != 0 { - s += fmt.Sprintf("%.2fm ", alt) - } else { - s += fmt.Sprintf("%.0fm ", alt) - } - - s += cmToM(rr.Size&0xf0>>4, rr.Size&0x0f) + "m " - s += cmToM(rr.HorizPre&0xf0>>4, rr.HorizPre&0x0f) + "m " - s += cmToM(rr.VertPre&0xf0>>4, rr.VertPre&0x0f) + "m" - - return s -} - -// SIG RR. See RFC 2535. The SIG RR is identical to RRSIG and nowadays only used for SIG(0), See RFC 2931. -type SIG struct { - RRSIG -} - -// RRSIG RR. See RFC 4034 and RFC 3755. -type RRSIG struct { - Hdr RR_Header - TypeCovered uint16 - Algorithm uint8 - Labels uint8 - OrigTtl uint32 - Expiration uint32 - Inception uint32 - KeyTag uint16 - SignerName string `dns:"domain-name"` - Signature string `dns:"base64"` -} - -func (rr *RRSIG) String() string { - s := rr.Hdr.String() - s += Type(rr.TypeCovered).String() - s += " " + strconv.Itoa(int(rr.Algorithm)) + - " " + strconv.Itoa(int(rr.Labels)) + - " " + strconv.FormatInt(int64(rr.OrigTtl), 10) + - " " + TimeToString(rr.Expiration) + - " " + TimeToString(rr.Inception) + - " " + strconv.Itoa(int(rr.KeyTag)) + - " " + sprintName(rr.SignerName) + - " " + rr.Signature - return s -} - -// NSEC RR. See RFC 4034 and RFC 3755. -type NSEC struct { - Hdr RR_Header - NextDomain string `dns:"domain-name"` - TypeBitMap []uint16 `dns:"nsec"` -} - -func (rr *NSEC) String() string { - s := rr.Hdr.String() + sprintName(rr.NextDomain) - for _, t := range rr.TypeBitMap { - s += " " + Type(t).String() - } - return s -} - -func (rr *NSEC) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.NextDomain, off+l, compression, false) - lastwindow := uint32(2 ^ 32 + 1) - for _, t := range rr.TypeBitMap { - window := t / 256 - if uint32(window) != lastwindow { - l += 1 + 32 - } - lastwindow = uint32(window) - } - return l -} - -// DLV RR. See RFC 4431. -type DLV struct{ DS } - -// CDS RR. See RFC 7344. -type CDS struct{ DS } - -// DS RR. See RFC 4034 and RFC 3658. -type DS struct { - Hdr RR_Header - KeyTag uint16 - Algorithm uint8 - DigestType uint8 - Digest string `dns:"hex"` -} - -func (rr *DS) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + strconv.Itoa(int(rr.DigestType)) + - " " + strings.ToUpper(rr.Digest) -} - -// KX RR. See RFC 2230. -type KX struct { - Hdr RR_Header - Preference uint16 - Exchanger string `dns:"domain-name"` -} - -func (rr *KX) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + - " " + sprintName(rr.Exchanger) -} - -// TA RR. See http://www.watson.org/~weiler/INI1999-19.pdf. -type TA struct { - Hdr RR_Header - KeyTag uint16 - Algorithm uint8 - DigestType uint8 - Digest string `dns:"hex"` -} - -func (rr *TA) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + strconv.Itoa(int(rr.DigestType)) + - " " + strings.ToUpper(rr.Digest) -} - -// TALINK RR. See https://www.iana.org/assignments/dns-parameters/TALINK/talink-completed-template. -type TALINK struct { - Hdr RR_Header - PreviousName string `dns:"domain-name"` - NextName string `dns:"domain-name"` -} - -func (rr *TALINK) String() string { - return rr.Hdr.String() + - sprintName(rr.PreviousName) + " " + sprintName(rr.NextName) -} - -// SSHFP RR. See RFC RFC 4255. -type SSHFP struct { - Hdr RR_Header - Algorithm uint8 - Type uint8 - FingerPrint string `dns:"hex"` -} - -func (rr *SSHFP) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) + - " " + strconv.Itoa(int(rr.Type)) + - " " + strings.ToUpper(rr.FingerPrint) -} - -// KEY RR. See RFC RFC 2535. -type KEY struct { - DNSKEY -} - -// CDNSKEY RR. See RFC 7344. -type CDNSKEY struct { - DNSKEY -} - -// DNSKEY RR. See RFC 4034 and RFC 3755. -type DNSKEY struct { - Hdr RR_Header - Flags uint16 - Protocol uint8 - Algorithm uint8 - PublicKey string `dns:"base64"` -} - -func (rr *DNSKEY) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + - " " + strconv.Itoa(int(rr.Protocol)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + rr.PublicKey -} - -// RKEY RR. See https://www.iana.org/assignments/dns-parameters/RKEY/rkey-completed-template. -type RKEY struct { - Hdr RR_Header - Flags uint16 - Protocol uint8 - Algorithm uint8 - PublicKey string `dns:"base64"` -} - -func (rr *RKEY) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) + - " " + strconv.Itoa(int(rr.Protocol)) + - " " + strconv.Itoa(int(rr.Algorithm)) + - " " + rr.PublicKey -} - -// NSAPPTR RR. See RFC 1348. -type NSAPPTR struct { - Hdr RR_Header - Ptr string `dns:"domain-name"` -} - -func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) } - -// NSEC3 RR. See RFC 5155. -type NSEC3 struct { - Hdr RR_Header - Hash uint8 - Flags uint8 - Iterations uint16 - SaltLength uint8 - Salt string `dns:"size-hex:SaltLength"` - HashLength uint8 - NextDomain string `dns:"size-base32:HashLength"` - TypeBitMap []uint16 `dns:"nsec"` -} - -func (rr *NSEC3) String() string { - s := rr.Hdr.String() - s += strconv.Itoa(int(rr.Hash)) + - " " + strconv.Itoa(int(rr.Flags)) + - " " + strconv.Itoa(int(rr.Iterations)) + - " " + saltToString(rr.Salt) + - " " + rr.NextDomain - for _, t := range rr.TypeBitMap { - s += " " + Type(t).String() - } - return s -} - -func (rr *NSEC3) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 6 + len(rr.Salt)/2 + 1 + len(rr.NextDomain) + 1 - lastwindow := uint32(2 ^ 32 + 1) - for _, t := range rr.TypeBitMap { - window := t / 256 - if uint32(window) != lastwindow { - l += 1 + 32 - } - lastwindow = uint32(window) - } - return l -} - -// NSEC3PARAM RR. See RFC 5155. -type NSEC3PARAM struct { - Hdr RR_Header - Hash uint8 - Flags uint8 - Iterations uint16 - SaltLength uint8 - Salt string `dns:"size-hex:SaltLength"` -} - -func (rr *NSEC3PARAM) String() string { - s := rr.Hdr.String() - s += strconv.Itoa(int(rr.Hash)) + - " " + strconv.Itoa(int(rr.Flags)) + - " " + strconv.Itoa(int(rr.Iterations)) + - " " + saltToString(rr.Salt) - return s -} - -// TKEY RR. See RFC 2930. -type TKEY struct { - Hdr RR_Header - Algorithm string `dns:"domain-name"` - Inception uint32 - Expiration uint32 - Mode uint16 - Error uint16 - KeySize uint16 - Key string `dns:"size-hex:KeySize"` - OtherLen uint16 - OtherData string `dns:"size-hex:OtherLen"` -} - -// TKEY has no official presentation format, but this will suffice. -func (rr *TKEY) String() string { - s := ";" + rr.Hdr.String() + - " " + rr.Algorithm + - " " + TimeToString(rr.Inception) + - " " + TimeToString(rr.Expiration) + - " " + strconv.Itoa(int(rr.Mode)) + - " " + strconv.Itoa(int(rr.Error)) + - " " + strconv.Itoa(int(rr.KeySize)) + - " " + rr.Key + - " " + strconv.Itoa(int(rr.OtherLen)) + - " " + rr.OtherData - return s -} - -// RFC3597 represents an unknown/generic RR. See RFC 3597. -type RFC3597 struct { - Hdr RR_Header - Rdata string `dns:"hex"` -} - -func (rr *RFC3597) String() string { - // Let's call it a hack - s := rfc3597Header(rr.Hdr) - - s += "\\# " + strconv.Itoa(len(rr.Rdata)/2) + " " + rr.Rdata - return s -} - -func rfc3597Header(h RR_Header) string { - var s string - - s += sprintName(h.Name) + "\t" - s += strconv.FormatInt(int64(h.Ttl), 10) + "\t" - s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t" - s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t" - return s -} - -// URI RR. See RFC 7553. -type URI struct { - Hdr RR_Header - Priority uint16 - Weight uint16 - Target string `dns:"octet"` -} - -func (rr *URI) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) + - " " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target) -} - -// DHCID RR. See RFC 4701. -type DHCID struct { - Hdr RR_Header - Digest string `dns:"base64"` -} - -func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest } - -// TLSA RR. See RFC 6698. -type TLSA struct { - Hdr RR_Header - Usage uint8 - Selector uint8 - MatchingType uint8 - Certificate string `dns:"hex"` -} - -func (rr *TLSA) String() string { - return rr.Hdr.String() + - strconv.Itoa(int(rr.Usage)) + - " " + strconv.Itoa(int(rr.Selector)) + - " " + strconv.Itoa(int(rr.MatchingType)) + - " " + rr.Certificate -} - -// SMIMEA RR. See RFC 8162. -type SMIMEA struct { - Hdr RR_Header - Usage uint8 - Selector uint8 - MatchingType uint8 - Certificate string `dns:"hex"` -} - -func (rr *SMIMEA) String() string { - s := rr.Hdr.String() + - strconv.Itoa(int(rr.Usage)) + - " " + strconv.Itoa(int(rr.Selector)) + - " " + strconv.Itoa(int(rr.MatchingType)) - - // Every Nth char needs a space on this output. If we output - // this as one giant line, we can't read it can in because in some cases - // the cert length overflows scan.maxTok (2048). - sx := splitN(rr.Certificate, 1024) // conservative value here - s += " " + strings.Join(sx, " ") - return s -} - -// HIP RR. See RFC 8005. -type HIP struct { - Hdr RR_Header - HitLength uint8 - PublicKeyAlgorithm uint8 - PublicKeyLength uint16 - Hit string `dns:"size-hex:HitLength"` - PublicKey string `dns:"size-base64:PublicKeyLength"` - RendezvousServers []string `dns:"domain-name"` -} - -func (rr *HIP) String() string { - s := rr.Hdr.String() + - strconv.Itoa(int(rr.PublicKeyAlgorithm)) + - " " + rr.Hit + - " " + rr.PublicKey - for _, d := range rr.RendezvousServers { - s += " " + sprintName(d) - } - return s -} - -// NINFO RR. See https://www.iana.org/assignments/dns-parameters/NINFO/ninfo-completed-template. -type NINFO struct { - Hdr RR_Header - ZSData []string `dns:"txt"` -} - -func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) } - -// NID RR. See RFC RFC 6742. -type NID struct { - Hdr RR_Header - Preference uint16 - NodeID uint64 -} - -func (rr *NID) String() string { - s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) - node := fmt.Sprintf("%0.16x", rr.NodeID) - s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] - return s -} - -// L32 RR, See RFC 6742. -type L32 struct { - Hdr RR_Header - Preference uint16 - Locator32 net.IP `dns:"a"` -} - -func (rr *L32) String() string { - if rr.Locator32 == nil { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) - } - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + - " " + rr.Locator32.String() -} - -// L64 RR, See RFC 6742. -type L64 struct { - Hdr RR_Header - Preference uint16 - Locator64 uint64 -} - -func (rr *L64) String() string { - s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) - node := fmt.Sprintf("%0.16X", rr.Locator64) - s += " " + node[0:4] + ":" + node[4:8] + ":" + node[8:12] + ":" + node[12:16] - return s -} - -// LP RR. See RFC 6742. -type LP struct { - Hdr RR_Header - Preference uint16 - Fqdn string `dns:"domain-name"` -} - -func (rr *LP) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn) -} - -// EUI48 RR. See RFC 7043. -type EUI48 struct { - Hdr RR_Header - Address uint64 `dns:"uint48"` -} - -func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) } - -// EUI64 RR. See RFC 7043. -type EUI64 struct { - Hdr RR_Header - Address uint64 -} - -func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) } - -// CAA RR. See RFC 6844. -type CAA struct { - Hdr RR_Header - Flag uint8 - Tag string - Value string `dns:"octet"` -} - -func (rr *CAA) String() string { - return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value) -} - -// UID RR. Deprecated, IANA-Reserved. -type UID struct { - Hdr RR_Header - Uid uint32 -} - -func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) } - -// GID RR. Deprecated, IANA-Reserved. -type GID struct { - Hdr RR_Header - Gid uint32 -} - -func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) } - -// UINFO RR. Deprecated, IANA-Reserved. -type UINFO struct { - Hdr RR_Header - Uinfo string -} - -func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) } - -// EID RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. -type EID struct { - Hdr RR_Header - Endpoint string `dns:"hex"` -} - -func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) } - -// NIMLOC RR. See http://ana-3.lcs.mit.edu/~jnc/nimrod/dns.txt. -type NIMLOC struct { - Hdr RR_Header - Locator string `dns:"hex"` -} - -func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) } - -// OPENPGPKEY RR. See RFC 7929. -type OPENPGPKEY struct { - Hdr RR_Header - PublicKey string `dns:"base64"` -} - -func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey } - -// CSYNC RR. See RFC 7477. -type CSYNC struct { - Hdr RR_Header - Serial uint32 - Flags uint16 - TypeBitMap []uint16 `dns:"nsec"` -} - -func (rr *CSYNC) String() string { - s := rr.Hdr.String() + strconv.FormatInt(int64(rr.Serial), 10) + " " + strconv.Itoa(int(rr.Flags)) - - for _, t := range rr.TypeBitMap { - s += " " + Type(t).String() - } - return s -} - -func (rr *CSYNC) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 4 + 2 - lastwindow := uint32(2 ^ 32 + 1) - for _, t := range rr.TypeBitMap { - window := t / 256 - if uint32(window) != lastwindow { - l += 1 + 32 - } - lastwindow = uint32(window) - } - return l -} - -// TimeToString translates the RRSIG's incep. and expir. times to the -// string representation used when printing the record. -// It takes serial arithmetic (RFC 1982) into account. -func TimeToString(t uint32) string { - mod := (int64(t)-time.Now().Unix())/year68 - 1 - if mod < 0 { - mod = 0 - } - ti := time.Unix(int64(t)-mod*year68, 0).UTC() - return ti.Format("20060102150405") -} - -// StringToTime translates the RRSIG's incep. and expir. times from -// string values like "20110403154150" to an 32 bit integer. -// It takes serial arithmetic (RFC 1982) into account. -func StringToTime(s string) (uint32, error) { - t, err := time.Parse("20060102150405", s) - if err != nil { - return 0, err - } - mod := t.Unix()/year68 - 1 - if mod < 0 { - mod = 0 - } - return uint32(t.Unix() - mod*year68), nil -} - -// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty. -func saltToString(s string) string { - if len(s) == 0 { - return "-" - } - return strings.ToUpper(s) -} - -func euiToString(eui uint64, bits int) (hex string) { - switch bits { - case 64: - hex = fmt.Sprintf("%16.16x", eui) - hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + - "-" + hex[8:10] + "-" + hex[10:12] + "-" + hex[12:14] + "-" + hex[14:16] - case 48: - hex = fmt.Sprintf("%12.12x", eui) - hex = hex[0:2] + "-" + hex[2:4] + "-" + hex[4:6] + "-" + hex[6:8] + - "-" + hex[8:10] + "-" + hex[10:12] - } - return -} - -// copyIP returns a copy of ip. -func copyIP(ip net.IP) net.IP { - p := make(net.IP, len(ip)) - copy(p, ip) - return p -} - -// SplitN splits a string into N sized string chunks. -// This might become an exported function once. -func splitN(s string, n int) []string { - if len(s) < n { - return []string{s} - } - sx := []string{} - p, i := 0, n - for { - if i <= len(s) { - sx = append(sx, s[p:i]) - } else { - sx = append(sx, s[p:]) - break - - } - p, i = p+n, i+n - } - - return sx -} diff --git a/vendor/github.com/miekg/dns/udp.go b/vendor/github.com/miekg/dns/udp.go deleted file mode 100644 index a4826ee2f..000000000 --- a/vendor/github.com/miekg/dns/udp.go +++ /dev/null @@ -1,102 +0,0 @@ -// +build !windows - -package dns - -import ( - "net" - - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -// This is the required size of the OOB buffer to pass to ReadMsgUDP. -var udpOOBSize = func() int { - // We can't know whether we'll get an IPv4 control message or an - // IPv6 control message ahead of time. To get around this, we size - // the buffer equal to the largest of the two. - - oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface) - oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface) - - if len(oob4) > len(oob6) { - return len(oob4) - } - - return len(oob6) -}() - -// SessionUDP holds the remote address and the associated -// out-of-band data. -type SessionUDP struct { - raddr *net.UDPAddr - context []byte -} - -// RemoteAddr returns the remote network address. -func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } - -// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a -// net.UDPAddr. -func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { - oob := make([]byte, udpOOBSize) - n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) - if err != nil { - return n, nil, err - } - return n, &SessionUDP{raddr, oob[:oobn]}, err -} - -// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. -func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { - oob := correctSource(session.context) - n, _, err := conn.WriteMsgUDP(b, oob, session.raddr) - return n, err -} - -func setUDPSocketOptions(conn *net.UDPConn) error { - // Try setting the flags for both families and ignore the errors unless they - // both error. - err6 := ipv6.NewPacketConn(conn).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true) - err4 := ipv4.NewPacketConn(conn).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true) - if err6 != nil && err4 != nil { - return err4 - } - return nil -} - -// parseDstFromOOB takes oob data and returns the destination IP. -func parseDstFromOOB(oob []byte) net.IP { - // Start with IPv6 and then fallback to IPv4 - // TODO(fastest963): Figure out a way to prefer one or the other. Looking at - // the lvl of the header for a 0 or 41 isn't cross-platform. - cm6 := new(ipv6.ControlMessage) - if cm6.Parse(oob) == nil && cm6.Dst != nil { - return cm6.Dst - } - cm4 := new(ipv4.ControlMessage) - if cm4.Parse(oob) == nil && cm4.Dst != nil { - return cm4.Dst - } - return nil -} - -// correctSource takes oob data and returns new oob data with the Src equal to the Dst -func correctSource(oob []byte) []byte { - dst := parseDstFromOOB(oob) - if dst == nil { - return nil - } - // If the dst is definitely an IPv6, then use ipv6's ControlMessage to - // respond otherwise use ipv4's because ipv6's marshal ignores ipv4 - // addresses. - if dst.To4() == nil { - cm := new(ipv6.ControlMessage) - cm.Src = dst - oob = cm.Marshal() - } else { - cm := new(ipv4.ControlMessage) - cm.Src = dst - oob = cm.Marshal() - } - return oob -} diff --git a/vendor/github.com/miekg/dns/udp_windows.go b/vendor/github.com/miekg/dns/udp_windows.go deleted file mode 100644 index e7dd8ca31..000000000 --- a/vendor/github.com/miekg/dns/udp_windows.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build windows - -package dns - -import "net" - -// SessionUDP holds the remote address -type SessionUDP struct { - raddr *net.UDPAddr -} - -// RemoteAddr returns the remote network address. -func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } - -// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a -// net.UDPAddr. -// TODO(fastest963): Once go1.10 is released, use ReadMsgUDP. -func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { - n, raddr, err := conn.ReadFrom(b) - if err != nil { - return n, nil, err - } - return n, &SessionUDP{raddr.(*net.UDPAddr)}, err -} - -// WriteToSessionUDP acts just like net.UDPConn.WriteTo(), but uses a *SessionUDP instead of a net.Addr. -// TODO(fastest963): Once go1.10 is released, use WriteMsgUDP. -func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { - return conn.WriteTo(b, session.raddr) -} - -// TODO(fastest963): Once go1.10 is released and we can use *MsgUDP methods -// use the standard method in udp.go for these. -func setUDPSocketOptions(*net.UDPConn) error { return nil } -func parseDstFromOOB([]byte, net.IP) net.IP { return nil } diff --git a/vendor/github.com/miekg/dns/update.go b/vendor/github.com/miekg/dns/update.go deleted file mode 100644 index 69dd38652..000000000 --- a/vendor/github.com/miekg/dns/update.go +++ /dev/null @@ -1,110 +0,0 @@ -package dns - -// NameUsed sets the RRs in the prereq section to -// "Name is in use" RRs. RFC 2136 section 2.4.4. -func (u *Msg) NameUsed(rr []RR) { - if u.Answer == nil { - u.Answer = make([]RR, 0, len(rr)) - } - for _, r := range rr { - u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) - } -} - -// NameNotUsed sets the RRs in the prereq section to -// "Name is in not use" RRs. RFC 2136 section 2.4.5. -func (u *Msg) NameNotUsed(rr []RR) { - if u.Answer == nil { - u.Answer = make([]RR, 0, len(rr)) - } - for _, r := range rr { - u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassNONE}}) - } -} - -// Used sets the RRs in the prereq section to -// "RRset exists (value dependent -- with rdata)" RRs. RFC 2136 section 2.4.2. -func (u *Msg) Used(rr []RR) { - if len(u.Question) == 0 { - panic("dns: empty question section") - } - if u.Answer == nil { - u.Answer = make([]RR, 0, len(rr)) - } - for _, r := range rr { - r.Header().Class = u.Question[0].Qclass - u.Answer = append(u.Answer, r) - } -} - -// RRsetUsed sets the RRs in the prereq section to -// "RRset exists (value independent -- no rdata)" RRs. RFC 2136 section 2.4.1. -func (u *Msg) RRsetUsed(rr []RR) { - if u.Answer == nil { - u.Answer = make([]RR, 0, len(rr)) - } - for _, r := range rr { - h := r.Header() - u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}}) - } -} - -// RRsetNotUsed sets the RRs in the prereq section to -// "RRset does not exist" RRs. RFC 2136 section 2.4.3. -func (u *Msg) RRsetNotUsed(rr []RR) { - if u.Answer == nil { - u.Answer = make([]RR, 0, len(rr)) - } - for _, r := range rr { - h := r.Header() - u.Answer = append(u.Answer, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassNONE}}) - } -} - -// Insert creates a dynamic update packet that adds an complete RRset, see RFC 2136 section 2.5.1. -func (u *Msg) Insert(rr []RR) { - if len(u.Question) == 0 { - panic("dns: empty question section") - } - if u.Ns == nil { - u.Ns = make([]RR, 0, len(rr)) - } - for _, r := range rr { - r.Header().Class = u.Question[0].Qclass - u.Ns = append(u.Ns, r) - } -} - -// RemoveRRset creates a dynamic update packet that deletes an RRset, see RFC 2136 section 2.5.2. -func (u *Msg) RemoveRRset(rr []RR) { - if u.Ns == nil { - u.Ns = make([]RR, 0, len(rr)) - } - for _, r := range rr { - h := r.Header() - u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: h.Name, Ttl: 0, Rrtype: h.Rrtype, Class: ClassANY}}) - } -} - -// RemoveName creates a dynamic update packet that deletes all RRsets of a name, see RFC 2136 section 2.5.3 -func (u *Msg) RemoveName(rr []RR) { - if u.Ns == nil { - u.Ns = make([]RR, 0, len(rr)) - } - for _, r := range rr { - u.Ns = append(u.Ns, &ANY{Hdr: RR_Header{Name: r.Header().Name, Ttl: 0, Rrtype: TypeANY, Class: ClassANY}}) - } -} - -// Remove creates a dynamic update packet deletes RR from a RRSset, see RFC 2136 section 2.5.4 -func (u *Msg) Remove(rr []RR) { - if u.Ns == nil { - u.Ns = make([]RR, 0, len(rr)) - } - for _, r := range rr { - h := r.Header() - h.Class = ClassNONE - h.Ttl = 0 - u.Ns = append(u.Ns, r) - } -} diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go deleted file mode 100644 index a25162fc4..000000000 --- a/vendor/github.com/miekg/dns/version.go +++ /dev/null @@ -1,15 +0,0 @@ -package dns - -import "fmt" - -// Version is current version of this library. -var Version = V{1, 1, 9} - -// V holds the version of this library. -type V struct { - Major, Minor, Patch int -} - -func (v V) String() string { - return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch) -} diff --git a/vendor/github.com/miekg/dns/xfr.go b/vendor/github.com/miekg/dns/xfr.go deleted file mode 100644 index 82afc52ea..000000000 --- a/vendor/github.com/miekg/dns/xfr.go +++ /dev/null @@ -1,260 +0,0 @@ -package dns - -import ( - "fmt" - "time" -) - -// Envelope is used when doing a zone transfer with a remote server. -type Envelope struct { - RR []RR // The set of RRs in the answer section of the xfr reply message. - Error error // If something went wrong, this contains the error. -} - -// A Transfer defines parameters that are used during a zone transfer. -type Transfer struct { - *Conn - DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - TsigSecret map[string]string // Secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) - tsigTimersOnly bool -} - -// Think we need to away to stop the transfer - -// In performs an incoming transfer with the server in a. -// If you would like to set the source IP, or some other attribute -// of a Dialer for a Transfer, you can do so by specifying the attributes -// in the Transfer.Conn: -// -// d := net.Dialer{LocalAddr: transfer_source} -// con, err := d.Dial("tcp", master) -// dnscon := &dns.Conn{Conn:con} -// transfer = &dns.Transfer{Conn: dnscon} -// channel, err := transfer.In(message, master) -// -func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) { - switch q.Question[0].Qtype { - case TypeAXFR, TypeIXFR: - default: - return nil, &Error{"unsupported question type"} - } - - timeout := dnsTimeout - if t.DialTimeout != 0 { - timeout = t.DialTimeout - } - - if t.Conn == nil { - t.Conn, err = DialTimeout("tcp", a, timeout) - if err != nil { - return nil, err - } - } - - if err := t.WriteMsg(q); err != nil { - return nil, err - } - - env = make(chan *Envelope) - switch q.Question[0].Qtype { - case TypeAXFR: - go t.inAxfr(q, env) - case TypeIXFR: - go t.inIxfr(q, env) - } - - return env, nil -} - -func (t *Transfer) inAxfr(q *Msg, c chan *Envelope) { - first := true - defer t.Close() - defer close(c) - timeout := dnsTimeout - if t.ReadTimeout != 0 { - timeout = t.ReadTimeout - } - for { - t.Conn.SetReadDeadline(time.Now().Add(timeout)) - in, err := t.ReadMsg() - if err != nil { - c <- &Envelope{nil, err} - return - } - if q.Id != in.Id { - c <- &Envelope{in.Answer, ErrId} - return - } - if first { - if in.Rcode != RcodeSuccess { - c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} - return - } - if !isSOAFirst(in) { - c <- &Envelope{in.Answer, ErrSoa} - return - } - first = !first - // only one answer that is SOA, receive more - if len(in.Answer) == 1 { - t.tsigTimersOnly = true - c <- &Envelope{in.Answer, nil} - continue - } - } - - if !first { - t.tsigTimersOnly = true // Subsequent envelopes use this. - if isSOALast(in) { - c <- &Envelope{in.Answer, nil} - return - } - c <- &Envelope{in.Answer, nil} - } - } -} - -func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) { - var serial uint32 // The first serial seen is the current server serial - axfr := true - n := 0 - qser := q.Ns[0].(*SOA).Serial - defer t.Close() - defer close(c) - timeout := dnsTimeout - if t.ReadTimeout != 0 { - timeout = t.ReadTimeout - } - for { - t.SetReadDeadline(time.Now().Add(timeout)) - in, err := t.ReadMsg() - if err != nil { - c <- &Envelope{nil, err} - return - } - if q.Id != in.Id { - c <- &Envelope{in.Answer, ErrId} - return - } - if in.Rcode != RcodeSuccess { - c <- &Envelope{in.Answer, &Error{err: fmt.Sprintf(errXFR, in.Rcode)}} - return - } - if n == 0 { - // Check if the returned answer is ok - if !isSOAFirst(in) { - c <- &Envelope{in.Answer, ErrSoa} - return - } - // This serial is important - serial = in.Answer[0].(*SOA).Serial - // Check if there are no changes in zone - if qser >= serial { - c <- &Envelope{in.Answer, nil} - return - } - } - // Now we need to check each message for SOA records, to see what we need to do - t.tsigTimersOnly = true - for _, rr := range in.Answer { - if v, ok := rr.(*SOA); ok { - if v.Serial == serial { - n++ - // quit if it's a full axfr or the the servers' SOA is repeated the third time - if axfr && n == 2 || n == 3 { - c <- &Envelope{in.Answer, nil} - return - } - } else if axfr { - // it's an ixfr - axfr = false - } - } - } - c <- &Envelope{in.Answer, nil} - } -} - -// Out performs an outgoing transfer with the client connecting in w. -// Basic use pattern: -// -// ch := make(chan *dns.Envelope) -// tr := new(dns.Transfer) -// go tr.Out(w, r, ch) -// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} -// close(ch) -// w.Hijack() -// // w.Close() // Client closes connection -// -// The server is responsible for sending the correct sequence of RRs through the -// channel ch. -func (t *Transfer) Out(w ResponseWriter, q *Msg, ch chan *Envelope) error { - for x := range ch { - r := new(Msg) - // Compress? - r.SetReply(q) - r.Authoritative = true - // assume it fits TODO(miek): fix - r.Answer = append(r.Answer, x.RR...) - if err := w.WriteMsg(r); err != nil { - return err - } - } - w.TsigTimersOnly(true) - return nil -} - -// ReadMsg reads a message from the transfer connection t. -func (t *Transfer) ReadMsg() (*Msg, error) { - m := new(Msg) - p := make([]byte, MaxMsgSize) - n, err := t.Read(p) - if err != nil && n == 0 { - return nil, err - } - p = p[:n] - if err := m.Unpack(p); err != nil { - return nil, err - } - if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil { - if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { - return m, ErrSecret - } - // Need to work on the original message p, as that was used to calculate the tsig. - err = TsigVerify(p, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly) - t.tsigRequestMAC = ts.MAC - } - return m, err -} - -// WriteMsg writes a message through the transfer connection t. -func (t *Transfer) WriteMsg(m *Msg) (err error) { - var out []byte - if ts := m.IsTsig(); ts != nil && t.TsigSecret != nil { - if _, ok := t.TsigSecret[ts.Hdr.Name]; !ok { - return ErrSecret - } - out, t.tsigRequestMAC, err = TsigGenerate(m, t.TsigSecret[ts.Hdr.Name], t.tsigRequestMAC, t.tsigTimersOnly) - } else { - out, err = m.Pack() - } - if err != nil { - return err - } - _, err = t.Write(out) - return err -} - -func isSOAFirst(in *Msg) bool { - return len(in.Answer) > 0 && - in.Answer[0].Header().Rrtype == TypeSOA -} - -func isSOALast(in *Msg) bool { - return len(in.Answer) > 0 && - in.Answer[len(in.Answer)-1].Header().Rrtype == TypeSOA -} - -const errXFR = "bad xfr rcode: %d" diff --git a/vendor/github.com/miekg/dns/zduplicate.go b/vendor/github.com/miekg/dns/zduplicate.go deleted file mode 100644 index 74389162f..000000000 --- a/vendor/github.com/miekg/dns/zduplicate.go +++ /dev/null @@ -1,1140 +0,0 @@ -// Code generated by "go run duplicate_generate.go"; DO NOT EDIT. - -package dns - -// isDuplicate() functions - -func (r1 *A) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*A) - if !ok { - return false - } - _ = r2 - if !r1.A.Equal(r2.A) { - return false - } - return true -} - -func (r1 *AAAA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*AAAA) - if !ok { - return false - } - _ = r2 - if !r1.AAAA.Equal(r2.AAAA) { - return false - } - return true -} - -func (r1 *AFSDB) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*AFSDB) - if !ok { - return false - } - _ = r2 - if r1.Subtype != r2.Subtype { - return false - } - if !isDuplicateName(r1.Hostname, r2.Hostname) { - return false - } - return true -} - -func (r1 *ANY) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*ANY) - if !ok { - return false - } - _ = r2 - return true -} - -func (r1 *AVC) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*AVC) - if !ok { - return false - } - _ = r2 - if len(r1.Txt) != len(r2.Txt) { - return false - } - for i := 0; i < len(r1.Txt); i++ { - if r1.Txt[i] != r2.Txt[i] { - return false - } - } - return true -} - -func (r1 *CAA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*CAA) - if !ok { - return false - } - _ = r2 - if r1.Flag != r2.Flag { - return false - } - if r1.Tag != r2.Tag { - return false - } - if r1.Value != r2.Value { - return false - } - return true -} - -func (r1 *CERT) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*CERT) - if !ok { - return false - } - _ = r2 - if r1.Type != r2.Type { - return false - } - if r1.KeyTag != r2.KeyTag { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.Certificate != r2.Certificate { - return false - } - return true -} - -func (r1 *CNAME) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*CNAME) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Target, r2.Target) { - return false - } - return true -} - -func (r1 *CSYNC) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*CSYNC) - if !ok { - return false - } - _ = r2 - if r1.Serial != r2.Serial { - return false - } - if r1.Flags != r2.Flags { - return false - } - if len(r1.TypeBitMap) != len(r2.TypeBitMap) { - return false - } - for i := 0; i < len(r1.TypeBitMap); i++ { - if r1.TypeBitMap[i] != r2.TypeBitMap[i] { - return false - } - } - return true -} - -func (r1 *DHCID) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*DHCID) - if !ok { - return false - } - _ = r2 - if r1.Digest != r2.Digest { - return false - } - return true -} - -func (r1 *DNAME) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*DNAME) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Target, r2.Target) { - return false - } - return true -} - -func (r1 *DNSKEY) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*DNSKEY) - if !ok { - return false - } - _ = r2 - if r1.Flags != r2.Flags { - return false - } - if r1.Protocol != r2.Protocol { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.PublicKey != r2.PublicKey { - return false - } - return true -} - -func (r1 *DS) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*DS) - if !ok { - return false - } - _ = r2 - if r1.KeyTag != r2.KeyTag { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.DigestType != r2.DigestType { - return false - } - if r1.Digest != r2.Digest { - return false - } - return true -} - -func (r1 *EID) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*EID) - if !ok { - return false - } - _ = r2 - if r1.Endpoint != r2.Endpoint { - return false - } - return true -} - -func (r1 *EUI48) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*EUI48) - if !ok { - return false - } - _ = r2 - if r1.Address != r2.Address { - return false - } - return true -} - -func (r1 *EUI64) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*EUI64) - if !ok { - return false - } - _ = r2 - if r1.Address != r2.Address { - return false - } - return true -} - -func (r1 *GID) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*GID) - if !ok { - return false - } - _ = r2 - if r1.Gid != r2.Gid { - return false - } - return true -} - -func (r1 *GPOS) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*GPOS) - if !ok { - return false - } - _ = r2 - if r1.Longitude != r2.Longitude { - return false - } - if r1.Latitude != r2.Latitude { - return false - } - if r1.Altitude != r2.Altitude { - return false - } - return true -} - -func (r1 *HINFO) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*HINFO) - if !ok { - return false - } - _ = r2 - if r1.Cpu != r2.Cpu { - return false - } - if r1.Os != r2.Os { - return false - } - return true -} - -func (r1 *HIP) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*HIP) - if !ok { - return false - } - _ = r2 - if r1.HitLength != r2.HitLength { - return false - } - if r1.PublicKeyAlgorithm != r2.PublicKeyAlgorithm { - return false - } - if r1.PublicKeyLength != r2.PublicKeyLength { - return false - } - if r1.Hit != r2.Hit { - return false - } - if r1.PublicKey != r2.PublicKey { - return false - } - if len(r1.RendezvousServers) != len(r2.RendezvousServers) { - return false - } - for i := 0; i < len(r1.RendezvousServers); i++ { - if !isDuplicateName(r1.RendezvousServers[i], r2.RendezvousServers[i]) { - return false - } - } - return true -} - -func (r1 *KX) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*KX) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !isDuplicateName(r1.Exchanger, r2.Exchanger) { - return false - } - return true -} - -func (r1 *L32) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*L32) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !r1.Locator32.Equal(r2.Locator32) { - return false - } - return true -} - -func (r1 *L64) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*L64) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if r1.Locator64 != r2.Locator64 { - return false - } - return true -} - -func (r1 *LOC) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*LOC) - if !ok { - return false - } - _ = r2 - if r1.Version != r2.Version { - return false - } - if r1.Size != r2.Size { - return false - } - if r1.HorizPre != r2.HorizPre { - return false - } - if r1.VertPre != r2.VertPre { - return false - } - if r1.Latitude != r2.Latitude { - return false - } - if r1.Longitude != r2.Longitude { - return false - } - if r1.Altitude != r2.Altitude { - return false - } - return true -} - -func (r1 *LP) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*LP) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !isDuplicateName(r1.Fqdn, r2.Fqdn) { - return false - } - return true -} - -func (r1 *MB) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MB) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Mb, r2.Mb) { - return false - } - return true -} - -func (r1 *MD) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MD) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Md, r2.Md) { - return false - } - return true -} - -func (r1 *MF) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MF) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Mf, r2.Mf) { - return false - } - return true -} - -func (r1 *MG) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MG) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Mg, r2.Mg) { - return false - } - return true -} - -func (r1 *MINFO) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MINFO) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Rmail, r2.Rmail) { - return false - } - if !isDuplicateName(r1.Email, r2.Email) { - return false - } - return true -} - -func (r1 *MR) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MR) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Mr, r2.Mr) { - return false - } - return true -} - -func (r1 *MX) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*MX) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !isDuplicateName(r1.Mx, r2.Mx) { - return false - } - return true -} - -func (r1 *NAPTR) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NAPTR) - if !ok { - return false - } - _ = r2 - if r1.Order != r2.Order { - return false - } - if r1.Preference != r2.Preference { - return false - } - if r1.Flags != r2.Flags { - return false - } - if r1.Service != r2.Service { - return false - } - if r1.Regexp != r2.Regexp { - return false - } - if !isDuplicateName(r1.Replacement, r2.Replacement) { - return false - } - return true -} - -func (r1 *NID) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NID) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if r1.NodeID != r2.NodeID { - return false - } - return true -} - -func (r1 *NIMLOC) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NIMLOC) - if !ok { - return false - } - _ = r2 - if r1.Locator != r2.Locator { - return false - } - return true -} - -func (r1 *NINFO) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NINFO) - if !ok { - return false - } - _ = r2 - if len(r1.ZSData) != len(r2.ZSData) { - return false - } - for i := 0; i < len(r1.ZSData); i++ { - if r1.ZSData[i] != r2.ZSData[i] { - return false - } - } - return true -} - -func (r1 *NS) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NS) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Ns, r2.Ns) { - return false - } - return true -} - -func (r1 *NSAPPTR) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NSAPPTR) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Ptr, r2.Ptr) { - return false - } - return true -} - -func (r1 *NSEC) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NSEC) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.NextDomain, r2.NextDomain) { - return false - } - if len(r1.TypeBitMap) != len(r2.TypeBitMap) { - return false - } - for i := 0; i < len(r1.TypeBitMap); i++ { - if r1.TypeBitMap[i] != r2.TypeBitMap[i] { - return false - } - } - return true -} - -func (r1 *NSEC3) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NSEC3) - if !ok { - return false - } - _ = r2 - if r1.Hash != r2.Hash { - return false - } - if r1.Flags != r2.Flags { - return false - } - if r1.Iterations != r2.Iterations { - return false - } - if r1.SaltLength != r2.SaltLength { - return false - } - if r1.Salt != r2.Salt { - return false - } - if r1.HashLength != r2.HashLength { - return false - } - if r1.NextDomain != r2.NextDomain { - return false - } - if len(r1.TypeBitMap) != len(r2.TypeBitMap) { - return false - } - for i := 0; i < len(r1.TypeBitMap); i++ { - if r1.TypeBitMap[i] != r2.TypeBitMap[i] { - return false - } - } - return true -} - -func (r1 *NSEC3PARAM) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NSEC3PARAM) - if !ok { - return false - } - _ = r2 - if r1.Hash != r2.Hash { - return false - } - if r1.Flags != r2.Flags { - return false - } - if r1.Iterations != r2.Iterations { - return false - } - if r1.SaltLength != r2.SaltLength { - return false - } - if r1.Salt != r2.Salt { - return false - } - return true -} - -func (r1 *NULL) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*NULL) - if !ok { - return false - } - _ = r2 - if r1.Data != r2.Data { - return false - } - return true -} - -func (r1 *OPENPGPKEY) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*OPENPGPKEY) - if !ok { - return false - } - _ = r2 - if r1.PublicKey != r2.PublicKey { - return false - } - return true -} - -func (r1 *PTR) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*PTR) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Ptr, r2.Ptr) { - return false - } - return true -} - -func (r1 *PX) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*PX) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !isDuplicateName(r1.Map822, r2.Map822) { - return false - } - if !isDuplicateName(r1.Mapx400, r2.Mapx400) { - return false - } - return true -} - -func (r1 *RFC3597) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RFC3597) - if !ok { - return false - } - _ = r2 - if r1.Rdata != r2.Rdata { - return false - } - return true -} - -func (r1 *RKEY) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RKEY) - if !ok { - return false - } - _ = r2 - if r1.Flags != r2.Flags { - return false - } - if r1.Protocol != r2.Protocol { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.PublicKey != r2.PublicKey { - return false - } - return true -} - -func (r1 *RP) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RP) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Mbox, r2.Mbox) { - return false - } - if !isDuplicateName(r1.Txt, r2.Txt) { - return false - } - return true -} - -func (r1 *RRSIG) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RRSIG) - if !ok { - return false - } - _ = r2 - if r1.TypeCovered != r2.TypeCovered { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.Labels != r2.Labels { - return false - } - if r1.OrigTtl != r2.OrigTtl { - return false - } - if r1.Expiration != r2.Expiration { - return false - } - if r1.Inception != r2.Inception { - return false - } - if r1.KeyTag != r2.KeyTag { - return false - } - if !isDuplicateName(r1.SignerName, r2.SignerName) { - return false - } - if r1.Signature != r2.Signature { - return false - } - return true -} - -func (r1 *RT) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*RT) - if !ok { - return false - } - _ = r2 - if r1.Preference != r2.Preference { - return false - } - if !isDuplicateName(r1.Host, r2.Host) { - return false - } - return true -} - -func (r1 *SMIMEA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*SMIMEA) - if !ok { - return false - } - _ = r2 - if r1.Usage != r2.Usage { - return false - } - if r1.Selector != r2.Selector { - return false - } - if r1.MatchingType != r2.MatchingType { - return false - } - if r1.Certificate != r2.Certificate { - return false - } - return true -} - -func (r1 *SOA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*SOA) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Ns, r2.Ns) { - return false - } - if !isDuplicateName(r1.Mbox, r2.Mbox) { - return false - } - if r1.Serial != r2.Serial { - return false - } - if r1.Refresh != r2.Refresh { - return false - } - if r1.Retry != r2.Retry { - return false - } - if r1.Expire != r2.Expire { - return false - } - if r1.Minttl != r2.Minttl { - return false - } - return true -} - -func (r1 *SPF) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*SPF) - if !ok { - return false - } - _ = r2 - if len(r1.Txt) != len(r2.Txt) { - return false - } - for i := 0; i < len(r1.Txt); i++ { - if r1.Txt[i] != r2.Txt[i] { - return false - } - } - return true -} - -func (r1 *SRV) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*SRV) - if !ok { - return false - } - _ = r2 - if r1.Priority != r2.Priority { - return false - } - if r1.Weight != r2.Weight { - return false - } - if r1.Port != r2.Port { - return false - } - if !isDuplicateName(r1.Target, r2.Target) { - return false - } - return true -} - -func (r1 *SSHFP) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*SSHFP) - if !ok { - return false - } - _ = r2 - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.Type != r2.Type { - return false - } - if r1.FingerPrint != r2.FingerPrint { - return false - } - return true -} - -func (r1 *TA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TA) - if !ok { - return false - } - _ = r2 - if r1.KeyTag != r2.KeyTag { - return false - } - if r1.Algorithm != r2.Algorithm { - return false - } - if r1.DigestType != r2.DigestType { - return false - } - if r1.Digest != r2.Digest { - return false - } - return true -} - -func (r1 *TALINK) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TALINK) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.PreviousName, r2.PreviousName) { - return false - } - if !isDuplicateName(r1.NextName, r2.NextName) { - return false - } - return true -} - -func (r1 *TKEY) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TKEY) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Algorithm, r2.Algorithm) { - return false - } - if r1.Inception != r2.Inception { - return false - } - if r1.Expiration != r2.Expiration { - return false - } - if r1.Mode != r2.Mode { - return false - } - if r1.Error != r2.Error { - return false - } - if r1.KeySize != r2.KeySize { - return false - } - if r1.Key != r2.Key { - return false - } - if r1.OtherLen != r2.OtherLen { - return false - } - if r1.OtherData != r2.OtherData { - return false - } - return true -} - -func (r1 *TLSA) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TLSA) - if !ok { - return false - } - _ = r2 - if r1.Usage != r2.Usage { - return false - } - if r1.Selector != r2.Selector { - return false - } - if r1.MatchingType != r2.MatchingType { - return false - } - if r1.Certificate != r2.Certificate { - return false - } - return true -} - -func (r1 *TSIG) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TSIG) - if !ok { - return false - } - _ = r2 - if !isDuplicateName(r1.Algorithm, r2.Algorithm) { - return false - } - if r1.TimeSigned != r2.TimeSigned { - return false - } - if r1.Fudge != r2.Fudge { - return false - } - if r1.MACSize != r2.MACSize { - return false - } - if r1.MAC != r2.MAC { - return false - } - if r1.OrigId != r2.OrigId { - return false - } - if r1.Error != r2.Error { - return false - } - if r1.OtherLen != r2.OtherLen { - return false - } - if r1.OtherData != r2.OtherData { - return false - } - return true -} - -func (r1 *TXT) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*TXT) - if !ok { - return false - } - _ = r2 - if len(r1.Txt) != len(r2.Txt) { - return false - } - for i := 0; i < len(r1.Txt); i++ { - if r1.Txt[i] != r2.Txt[i] { - return false - } - } - return true -} - -func (r1 *UID) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*UID) - if !ok { - return false - } - _ = r2 - if r1.Uid != r2.Uid { - return false - } - return true -} - -func (r1 *UINFO) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*UINFO) - if !ok { - return false - } - _ = r2 - if r1.Uinfo != r2.Uinfo { - return false - } - return true -} - -func (r1 *URI) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*URI) - if !ok { - return false - } - _ = r2 - if r1.Priority != r2.Priority { - return false - } - if r1.Weight != r2.Weight { - return false - } - if r1.Target != r2.Target { - return false - } - return true -} - -func (r1 *X25) isDuplicate(_r2 RR) bool { - r2, ok := _r2.(*X25) - if !ok { - return false - } - _ = r2 - if r1.PSDNAddress != r2.PSDNAddress { - return false - } - return true -} diff --git a/vendor/github.com/miekg/dns/zmsg.go b/vendor/github.com/miekg/dns/zmsg.go deleted file mode 100644 index c4cf4757e..000000000 --- a/vendor/github.com/miekg/dns/zmsg.go +++ /dev/null @@ -1,2722 +0,0 @@ -// Code generated by "go run msg_generate.go"; DO NOT EDIT. - -package dns - -// pack*() functions - -func (rr *A) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDataA(rr.A, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *AAAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDataAAAA(rr.AAAA, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *AFSDB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Subtype, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Hostname, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - return off, nil -} - -func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringTxt(rr.Txt, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CAA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Flag, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Tag, msg, off) - if err != nil { - return off, err - } - off, err = packStringOctet(rr.Value, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CDNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Protocol, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CDS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.DigestType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Digest, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CERT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Type, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.Certificate, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Target, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CSYNC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint32(rr.Serial, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packDataNsec(rr.TypeBitMap, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DHCID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringBase64(rr.Digest, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DLV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.DigestType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Digest, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DNAME) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Target, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DNSKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Protocol, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.DigestType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Digest, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringHex(rr.Endpoint, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EUI48) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint48(rr.Address, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EUI64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint64(rr.Address, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *GID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint32(rr.Gid, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *GPOS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packString(rr.Longitude, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Latitude, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Altitude, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *HINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packString(rr.Cpu, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Os, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *HIP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.HitLength, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.PublicKeyAlgorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.PublicKeyLength, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Hit, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - off, err = packDataDomainNames(rr.RendezvousServers, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *KEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Protocol, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *KX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Exchanger, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *L32) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDataA(rr.Locator32, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *L64) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packUint64(rr.Locator64, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *LOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Version, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Size, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.HorizPre, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.VertPre, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Latitude, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Longitude, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Altitude, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *LP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Fqdn, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MB) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Mb, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Md, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Mf, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Mg, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Rmail, msg, off, compression, compress) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Email, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Mr, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Mx, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NAPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Order, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Service, msg, off) - if err != nil { - return off, err - } - off, err = packString(rr.Regexp, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Replacement, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packUint64(rr.NodeID, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NIMLOC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringHex(rr.Locator, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringTxt(rr.ZSData, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NS) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Ns, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSAPPTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Ptr, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.NextDomain, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packDataNsec(rr.TypeBitMap, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC3) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Hash, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Iterations, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.SaltLength, msg, off) - if err != nil { - return off, err - } - // Only pack salt if value is not "-", i.e. empty - if rr.Salt != "-" { - off, err = packStringHex(rr.Salt, msg, off) - if err != nil { - return off, err - } - } - off, err = packUint8(rr.HashLength, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase32(rr.NextDomain, msg, off) - if err != nil { - return off, err - } - off, err = packDataNsec(rr.TypeBitMap, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC3PARAM) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Hash, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Iterations, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.SaltLength, msg, off) - if err != nil { - return off, err - } - // Only pack salt if value is not "-", i.e. empty - if rr.Salt != "-" { - off, err = packStringHex(rr.Salt, msg, off) - if err != nil { - return off, err - } - } - return off, nil -} - -func (rr *NULL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringAny(rr.Data, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *OPENPGPKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *OPT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDataOpt(rr.Option, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *PTR) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Ptr, msg, off, compression, compress) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *PX) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Map822, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Mapx400, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RFC3597) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringHex(rr.Rdata, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Flags, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Protocol, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.PublicKey, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Mbox, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Txt, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RRSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.TypeCovered, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Labels, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.OrigTtl, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Expiration, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Inception, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.SignerName, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.Signature, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Preference, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Host, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.TypeCovered, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Labels, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.OrigTtl, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Expiration, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Inception, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.SignerName, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packStringBase64(rr.Signature, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SMIMEA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Usage, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Selector, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.MatchingType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Certificate, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SOA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Ns, msg, off, compression, compress) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Mbox, msg, off, compression, compress) - if err != nil { - return off, err - } - off, err = packUint32(rr.Serial, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Refresh, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Retry, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Expire, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Minttl, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SPF) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringTxt(rr.Txt, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SRV) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Priority, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Weight, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Port, msg, off) - if err != nil { - return off, err - } - off, err = packDomainName(rr.Target, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SSHFP) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Type, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.FingerPrint, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.KeyTag, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Algorithm, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.DigestType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Digest, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TALINK) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.PreviousName, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packDomainName(rr.NextName, msg, off, compression, false) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TKEY) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Algorithm, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packUint32(rr.Inception, msg, off) - if err != nil { - return off, err - } - off, err = packUint32(rr.Expiration, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Mode, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Error, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.KeySize, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Key, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.OtherLen, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.OtherData, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TLSA) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint8(rr.Usage, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.Selector, msg, off) - if err != nil { - return off, err - } - off, err = packUint8(rr.MatchingType, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.Certificate, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TSIG) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packDomainName(rr.Algorithm, msg, off, compression, false) - if err != nil { - return off, err - } - off, err = packUint48(rr.TimeSigned, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Fudge, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.MACSize, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.MAC, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.OrigId, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Error, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.OtherLen, msg, off) - if err != nil { - return off, err - } - off, err = packStringHex(rr.OtherData, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TXT) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packStringTxt(rr.Txt, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *UID) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint32(rr.Uid, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *UINFO) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packString(rr.Uinfo, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *URI) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packUint16(rr.Priority, msg, off) - if err != nil { - return off, err - } - off, err = packUint16(rr.Weight, msg, off) - if err != nil { - return off, err - } - off, err = packStringOctet(rr.Target, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { - off, err = packString(rr.PSDNAddress, msg, off) - if err != nil { - return off, err - } - return off, nil -} - -// unpack*() functions - -func (rr *A) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.A, off, err = unpackDataA(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *AAAA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.AAAA, off, err = unpackDataAAAA(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *AFSDB) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Subtype, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Hostname, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - return off, nil -} - -func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Txt, off, err = unpackStringTxt(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CAA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Flag, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Tag, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Value, off, err = unpackStringOctet(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CDNSKEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Flags, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Protocol, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CDS) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.DigestType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CERT) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Type, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Certificate, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CNAME) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Target, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *CSYNC) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Serial, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Flags, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.TypeBitMap, off, err = unpackDataNsec(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DHCID) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Digest, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DLV) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.DigestType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DNAME) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Target, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DNSKEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Flags, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Protocol, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *DS) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.DigestType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EID) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Endpoint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EUI48) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Address, off, err = unpackUint48(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *EUI64) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Address, off, err = unpackUint64(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *GID) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Gid, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *GPOS) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Longitude, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Latitude, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Altitude, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *HINFO) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Cpu, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Os, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *HIP) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.HitLength, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKeyAlgorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKeyLength, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Hit, off, err = unpackStringHex(msg, off, off+int(rr.HitLength)) - if err != nil { - return off, err - } - rr.PublicKey, off, err = unpackStringBase64(msg, off, off+int(rr.PublicKeyLength)) - if err != nil { - return off, err - } - rr.RendezvousServers, off, err = unpackDataDomainNames(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *KEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Flags, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Protocol, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *KX) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Exchanger, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *L32) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Locator32, off, err = unpackDataA(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *L64) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Locator64, off, err = unpackUint64(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *LOC) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Version, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Size, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.HorizPre, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.VertPre, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Latitude, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Longitude, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Altitude, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *LP) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Fqdn, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MB) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Mb, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MD) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Md, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MF) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Mf, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MG) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Mg, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MINFO) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Rmail, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Email, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MR) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Mr, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *MX) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Mx, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NAPTR) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Order, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Flags, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Service, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Regexp, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Replacement, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NID) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.NodeID, off, err = unpackUint64(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NIMLOC) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Locator, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NINFO) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.ZSData, off, err = unpackStringTxt(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NS) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Ns, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSAPPTR) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Ptr, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.NextDomain, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.TypeBitMap, off, err = unpackDataNsec(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC3) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Hash, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Flags, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Iterations, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.SaltLength, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength)) - if err != nil { - return off, err - } - rr.HashLength, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.NextDomain, off, err = unpackStringBase32(msg, off, off+int(rr.HashLength)) - if err != nil { - return off, err - } - rr.TypeBitMap, off, err = unpackDataNsec(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NSEC3PARAM) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Hash, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Flags, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Iterations, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.SaltLength, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Salt, off, err = unpackStringHex(msg, off, off+int(rr.SaltLength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *NULL) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Data, off, err = unpackStringAny(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *OPENPGPKEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *OPT) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Option, off, err = unpackDataOpt(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *PTR) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Ptr, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *PX) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Map822, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Mapx400, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RFC3597) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Rdata, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RKEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Flags, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Protocol, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.PublicKey, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RP) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Mbox, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Txt, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RRSIG) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.TypeCovered, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Labels, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.OrigTtl, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Expiration, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Inception, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.SignerName, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *RT) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Preference, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Host, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SIG) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.TypeCovered, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Labels, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.OrigTtl, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Expiration, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Inception, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.SignerName, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Signature, off, err = unpackStringBase64(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SMIMEA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Usage, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Selector, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.MatchingType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SOA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Ns, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Mbox, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Serial, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Refresh, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Retry, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Expire, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Minttl, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SPF) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Txt, off, err = unpackStringTxt(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SRV) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Priority, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Weight, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Port, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Target, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *SSHFP) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Type, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.FingerPrint, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.KeyTag, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Algorithm, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.DigestType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TALINK) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.PreviousName, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.NextName, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TKEY) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Algorithm, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Inception, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Expiration, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Mode, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Error, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.KeySize, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Key, off, err = unpackStringHex(msg, off, off+int(rr.KeySize)) - if err != nil { - return off, err - } - rr.OtherLen, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TLSA) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Usage, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Selector, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.MatchingType, off, err = unpackUint8(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Certificate, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TSIG) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Algorithm, off, err = UnpackDomainName(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.TimeSigned, off, err = unpackUint48(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Fudge, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.MACSize, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.MAC, off, err = unpackStringHex(msg, off, off+int(rr.MACSize)) - if err != nil { - return off, err - } - rr.OrigId, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Error, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.OtherLen, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.OtherData, off, err = unpackStringHex(msg, off, off+int(rr.OtherLen)) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *TXT) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Txt, off, err = unpackStringTxt(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *UID) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Uid, off, err = unpackUint32(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *UINFO) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Uinfo, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *URI) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.Priority, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Weight, off, err = unpackUint16(msg, off) - if err != nil { - return off, err - } - if off == len(msg) { - return off, nil - } - rr.Target, off, err = unpackStringOctet(msg, off) - if err != nil { - return off, err - } - return off, nil -} - -func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) { - rdStart := off - _ = rdStart - - rr.PSDNAddress, off, err = unpackString(msg, off) - if err != nil { - return off, err - } - return off, nil -} diff --git a/vendor/github.com/miekg/dns/ztypes.go b/vendor/github.com/miekg/dns/ztypes.go deleted file mode 100644 index 495a83e30..000000000 --- a/vendor/github.com/miekg/dns/ztypes.go +++ /dev/null @@ -1,881 +0,0 @@ -// Code generated by "go run types_generate.go"; DO NOT EDIT. - -package dns - -import ( - "encoding/base64" - "net" -) - -// TypeToRR is a map of constructors for each RR type. -var TypeToRR = map[uint16]func() RR{ - TypeA: func() RR { return new(A) }, - TypeAAAA: func() RR { return new(AAAA) }, - TypeAFSDB: func() RR { return new(AFSDB) }, - TypeANY: func() RR { return new(ANY) }, - TypeAVC: func() RR { return new(AVC) }, - TypeCAA: func() RR { return new(CAA) }, - TypeCDNSKEY: func() RR { return new(CDNSKEY) }, - TypeCDS: func() RR { return new(CDS) }, - TypeCERT: func() RR { return new(CERT) }, - TypeCNAME: func() RR { return new(CNAME) }, - TypeCSYNC: func() RR { return new(CSYNC) }, - TypeDHCID: func() RR { return new(DHCID) }, - TypeDLV: func() RR { return new(DLV) }, - TypeDNAME: func() RR { return new(DNAME) }, - TypeDNSKEY: func() RR { return new(DNSKEY) }, - TypeDS: func() RR { return new(DS) }, - TypeEID: func() RR { return new(EID) }, - TypeEUI48: func() RR { return new(EUI48) }, - TypeEUI64: func() RR { return new(EUI64) }, - TypeGID: func() RR { return new(GID) }, - TypeGPOS: func() RR { return new(GPOS) }, - TypeHINFO: func() RR { return new(HINFO) }, - TypeHIP: func() RR { return new(HIP) }, - TypeKEY: func() RR { return new(KEY) }, - TypeKX: func() RR { return new(KX) }, - TypeL32: func() RR { return new(L32) }, - TypeL64: func() RR { return new(L64) }, - TypeLOC: func() RR { return new(LOC) }, - TypeLP: func() RR { return new(LP) }, - TypeMB: func() RR { return new(MB) }, - TypeMD: func() RR { return new(MD) }, - TypeMF: func() RR { return new(MF) }, - TypeMG: func() RR { return new(MG) }, - TypeMINFO: func() RR { return new(MINFO) }, - TypeMR: func() RR { return new(MR) }, - TypeMX: func() RR { return new(MX) }, - TypeNAPTR: func() RR { return new(NAPTR) }, - TypeNID: func() RR { return new(NID) }, - TypeNIMLOC: func() RR { return new(NIMLOC) }, - TypeNINFO: func() RR { return new(NINFO) }, - TypeNS: func() RR { return new(NS) }, - TypeNSAPPTR: func() RR { return new(NSAPPTR) }, - TypeNSEC: func() RR { return new(NSEC) }, - TypeNSEC3: func() RR { return new(NSEC3) }, - TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) }, - TypeNULL: func() RR { return new(NULL) }, - TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) }, - TypeOPT: func() RR { return new(OPT) }, - TypePTR: func() RR { return new(PTR) }, - TypePX: func() RR { return new(PX) }, - TypeRKEY: func() RR { return new(RKEY) }, - TypeRP: func() RR { return new(RP) }, - TypeRRSIG: func() RR { return new(RRSIG) }, - TypeRT: func() RR { return new(RT) }, - TypeSIG: func() RR { return new(SIG) }, - TypeSMIMEA: func() RR { return new(SMIMEA) }, - TypeSOA: func() RR { return new(SOA) }, - TypeSPF: func() RR { return new(SPF) }, - TypeSRV: func() RR { return new(SRV) }, - TypeSSHFP: func() RR { return new(SSHFP) }, - TypeTA: func() RR { return new(TA) }, - TypeTALINK: func() RR { return new(TALINK) }, - TypeTKEY: func() RR { return new(TKEY) }, - TypeTLSA: func() RR { return new(TLSA) }, - TypeTSIG: func() RR { return new(TSIG) }, - TypeTXT: func() RR { return new(TXT) }, - TypeUID: func() RR { return new(UID) }, - TypeUINFO: func() RR { return new(UINFO) }, - TypeURI: func() RR { return new(URI) }, - TypeX25: func() RR { return new(X25) }, -} - -// TypeToString is a map of strings for each RR type. -var TypeToString = map[uint16]string{ - TypeA: "A", - TypeAAAA: "AAAA", - TypeAFSDB: "AFSDB", - TypeANY: "ANY", - TypeATMA: "ATMA", - TypeAVC: "AVC", - TypeAXFR: "AXFR", - TypeCAA: "CAA", - TypeCDNSKEY: "CDNSKEY", - TypeCDS: "CDS", - TypeCERT: "CERT", - TypeCNAME: "CNAME", - TypeCSYNC: "CSYNC", - TypeDHCID: "DHCID", - TypeDLV: "DLV", - TypeDNAME: "DNAME", - TypeDNSKEY: "DNSKEY", - TypeDS: "DS", - TypeEID: "EID", - TypeEUI48: "EUI48", - TypeEUI64: "EUI64", - TypeGID: "GID", - TypeGPOS: "GPOS", - TypeHINFO: "HINFO", - TypeHIP: "HIP", - TypeISDN: "ISDN", - TypeIXFR: "IXFR", - TypeKEY: "KEY", - TypeKX: "KX", - TypeL32: "L32", - TypeL64: "L64", - TypeLOC: "LOC", - TypeLP: "LP", - TypeMAILA: "MAILA", - TypeMAILB: "MAILB", - TypeMB: "MB", - TypeMD: "MD", - TypeMF: "MF", - TypeMG: "MG", - TypeMINFO: "MINFO", - TypeMR: "MR", - TypeMX: "MX", - TypeNAPTR: "NAPTR", - TypeNID: "NID", - TypeNIMLOC: "NIMLOC", - TypeNINFO: "NINFO", - TypeNS: "NS", - TypeNSEC: "NSEC", - TypeNSEC3: "NSEC3", - TypeNSEC3PARAM: "NSEC3PARAM", - TypeNULL: "NULL", - TypeNXT: "NXT", - TypeNone: "None", - TypeOPENPGPKEY: "OPENPGPKEY", - TypeOPT: "OPT", - TypePTR: "PTR", - TypePX: "PX", - TypeRKEY: "RKEY", - TypeRP: "RP", - TypeRRSIG: "RRSIG", - TypeRT: "RT", - TypeReserved: "Reserved", - TypeSIG: "SIG", - TypeSMIMEA: "SMIMEA", - TypeSOA: "SOA", - TypeSPF: "SPF", - TypeSRV: "SRV", - TypeSSHFP: "SSHFP", - TypeTA: "TA", - TypeTALINK: "TALINK", - TypeTKEY: "TKEY", - TypeTLSA: "TLSA", - TypeTSIG: "TSIG", - TypeTXT: "TXT", - TypeUID: "UID", - TypeUINFO: "UINFO", - TypeUNSPEC: "UNSPEC", - TypeURI: "URI", - TypeX25: "X25", - TypeNSAPPTR: "NSAP-PTR", -} - -func (rr *A) Header() *RR_Header { return &rr.Hdr } -func (rr *AAAA) Header() *RR_Header { return &rr.Hdr } -func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr } -func (rr *ANY) Header() *RR_Header { return &rr.Hdr } -func (rr *AVC) Header() *RR_Header { return &rr.Hdr } -func (rr *CAA) Header() *RR_Header { return &rr.Hdr } -func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *CDS) Header() *RR_Header { return &rr.Hdr } -func (rr *CERT) Header() *RR_Header { return &rr.Hdr } -func (rr *CNAME) Header() *RR_Header { return &rr.Hdr } -func (rr *CSYNC) Header() *RR_Header { return &rr.Hdr } -func (rr *DHCID) Header() *RR_Header { return &rr.Hdr } -func (rr *DLV) Header() *RR_Header { return &rr.Hdr } -func (rr *DNAME) Header() *RR_Header { return &rr.Hdr } -func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *DS) Header() *RR_Header { return &rr.Hdr } -func (rr *EID) Header() *RR_Header { return &rr.Hdr } -func (rr *EUI48) Header() *RR_Header { return &rr.Hdr } -func (rr *EUI64) Header() *RR_Header { return &rr.Hdr } -func (rr *GID) Header() *RR_Header { return &rr.Hdr } -func (rr *GPOS) Header() *RR_Header { return &rr.Hdr } -func (rr *HINFO) Header() *RR_Header { return &rr.Hdr } -func (rr *HIP) Header() *RR_Header { return &rr.Hdr } -func (rr *KEY) Header() *RR_Header { return &rr.Hdr } -func (rr *KX) Header() *RR_Header { return &rr.Hdr } -func (rr *L32) Header() *RR_Header { return &rr.Hdr } -func (rr *L64) Header() *RR_Header { return &rr.Hdr } -func (rr *LOC) Header() *RR_Header { return &rr.Hdr } -func (rr *LP) Header() *RR_Header { return &rr.Hdr } -func (rr *MB) Header() *RR_Header { return &rr.Hdr } -func (rr *MD) Header() *RR_Header { return &rr.Hdr } -func (rr *MF) Header() *RR_Header { return &rr.Hdr } -func (rr *MG) Header() *RR_Header { return &rr.Hdr } -func (rr *MINFO) Header() *RR_Header { return &rr.Hdr } -func (rr *MR) Header() *RR_Header { return &rr.Hdr } -func (rr *MX) Header() *RR_Header { return &rr.Hdr } -func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr } -func (rr *NID) Header() *RR_Header { return &rr.Hdr } -func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr } -func (rr *NINFO) Header() *RR_Header { return &rr.Hdr } -func (rr *NS) Header() *RR_Header { return &rr.Hdr } -func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr } -func (rr *NSEC) Header() *RR_Header { return &rr.Hdr } -func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr } -func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr } -func (rr *NULL) Header() *RR_Header { return &rr.Hdr } -func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *OPT) Header() *RR_Header { return &rr.Hdr } -func (rr *PTR) Header() *RR_Header { return &rr.Hdr } -func (rr *PX) Header() *RR_Header { return &rr.Hdr } -func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr } -func (rr *RKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *RP) Header() *RR_Header { return &rr.Hdr } -func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr } -func (rr *RT) Header() *RR_Header { return &rr.Hdr } -func (rr *SIG) Header() *RR_Header { return &rr.Hdr } -func (rr *SMIMEA) Header() *RR_Header { return &rr.Hdr } -func (rr *SOA) Header() *RR_Header { return &rr.Hdr } -func (rr *SPF) Header() *RR_Header { return &rr.Hdr } -func (rr *SRV) Header() *RR_Header { return &rr.Hdr } -func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr } -func (rr *TA) Header() *RR_Header { return &rr.Hdr } -func (rr *TALINK) Header() *RR_Header { return &rr.Hdr } -func (rr *TKEY) Header() *RR_Header { return &rr.Hdr } -func (rr *TLSA) Header() *RR_Header { return &rr.Hdr } -func (rr *TSIG) Header() *RR_Header { return &rr.Hdr } -func (rr *TXT) Header() *RR_Header { return &rr.Hdr } -func (rr *UID) Header() *RR_Header { return &rr.Hdr } -func (rr *UINFO) Header() *RR_Header { return &rr.Hdr } -func (rr *URI) Header() *RR_Header { return &rr.Hdr } -func (rr *X25) Header() *RR_Header { return &rr.Hdr } - -// len() functions -func (rr *A) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - if len(rr.A) != 0 { - l += net.IPv4len - } - return l -} -func (rr *AAAA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - if len(rr.AAAA) != 0 { - l += net.IPv6len - } - return l -} -func (rr *AFSDB) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Subtype - l += domainNameLen(rr.Hostname, off+l, compression, false) - return l -} -func (rr *ANY) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - return l -} -func (rr *AVC) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - for _, x := range rr.Txt { - l += len(x) + 1 - } - return l -} -func (rr *CAA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Flag - l += len(rr.Tag) + 1 - l += len(rr.Value) - return l -} -func (rr *CERT) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Type - l += 2 // KeyTag - l++ // Algorithm - l += base64.StdEncoding.DecodedLen(len(rr.Certificate)) - return l -} -func (rr *CNAME) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Target, off+l, compression, true) - return l -} -func (rr *DHCID) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += base64.StdEncoding.DecodedLen(len(rr.Digest)) - return l -} -func (rr *DNAME) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Target, off+l, compression, false) - return l -} -func (rr *DNSKEY) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Flags - l++ // Protocol - l++ // Algorithm - l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) - return l -} -func (rr *DS) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // KeyTag - l++ // Algorithm - l++ // DigestType - l += len(rr.Digest)/2 + 1 - return l -} -func (rr *EID) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Endpoint)/2 + 1 - return l -} -func (rr *EUI48) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 6 // Address - return l -} -func (rr *EUI64) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 8 // Address - return l -} -func (rr *GID) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 4 // Gid - return l -} -func (rr *GPOS) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Longitude) + 1 - l += len(rr.Latitude) + 1 - l += len(rr.Altitude) + 1 - return l -} -func (rr *HINFO) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Cpu) + 1 - l += len(rr.Os) + 1 - return l -} -func (rr *HIP) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // HitLength - l++ // PublicKeyAlgorithm - l += 2 // PublicKeyLength - l += len(rr.Hit) / 2 - l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) - for _, x := range rr.RendezvousServers { - l += domainNameLen(x, off+l, compression, false) - } - return l -} -func (rr *KX) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += domainNameLen(rr.Exchanger, off+l, compression, false) - return l -} -func (rr *L32) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - if len(rr.Locator32) != 0 { - l += net.IPv4len - } - return l -} -func (rr *L64) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += 8 // Locator64 - return l -} -func (rr *LOC) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Version - l++ // Size - l++ // HorizPre - l++ // VertPre - l += 4 // Latitude - l += 4 // Longitude - l += 4 // Altitude - return l -} -func (rr *LP) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += domainNameLen(rr.Fqdn, off+l, compression, false) - return l -} -func (rr *MB) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Mb, off+l, compression, true) - return l -} -func (rr *MD) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Md, off+l, compression, true) - return l -} -func (rr *MF) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Mf, off+l, compression, true) - return l -} -func (rr *MG) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Mg, off+l, compression, true) - return l -} -func (rr *MINFO) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Rmail, off+l, compression, true) - l += domainNameLen(rr.Email, off+l, compression, true) - return l -} -func (rr *MR) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Mr, off+l, compression, true) - return l -} -func (rr *MX) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += domainNameLen(rr.Mx, off+l, compression, true) - return l -} -func (rr *NAPTR) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Order - l += 2 // Preference - l += len(rr.Flags) + 1 - l += len(rr.Service) + 1 - l += len(rr.Regexp) + 1 - l += domainNameLen(rr.Replacement, off+l, compression, false) - return l -} -func (rr *NID) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += 8 // NodeID - return l -} -func (rr *NIMLOC) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Locator)/2 + 1 - return l -} -func (rr *NINFO) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - for _, x := range rr.ZSData { - l += len(x) + 1 - } - return l -} -func (rr *NS) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Ns, off+l, compression, true) - return l -} -func (rr *NSAPPTR) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Ptr, off+l, compression, false) - return l -} -func (rr *NSEC3PARAM) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Hash - l++ // Flags - l += 2 // Iterations - l++ // SaltLength - l += len(rr.Salt) / 2 - return l -} -func (rr *NULL) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Data) - return l -} -func (rr *OPENPGPKEY) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) - return l -} -func (rr *PTR) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Ptr, off+l, compression, true) - return l -} -func (rr *PX) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += domainNameLen(rr.Map822, off+l, compression, false) - l += domainNameLen(rr.Mapx400, off+l, compression, false) - return l -} -func (rr *RFC3597) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Rdata)/2 + 1 - return l -} -func (rr *RKEY) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Flags - l++ // Protocol - l++ // Algorithm - l += base64.StdEncoding.DecodedLen(len(rr.PublicKey)) - return l -} -func (rr *RP) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Mbox, off+l, compression, false) - l += domainNameLen(rr.Txt, off+l, compression, false) - return l -} -func (rr *RRSIG) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // TypeCovered - l++ // Algorithm - l++ // Labels - l += 4 // OrigTtl - l += 4 // Expiration - l += 4 // Inception - l += 2 // KeyTag - l += domainNameLen(rr.SignerName, off+l, compression, false) - l += base64.StdEncoding.DecodedLen(len(rr.Signature)) - return l -} -func (rr *RT) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Preference - l += domainNameLen(rr.Host, off+l, compression, false) - return l -} -func (rr *SMIMEA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Usage - l++ // Selector - l++ // MatchingType - l += len(rr.Certificate)/2 + 1 - return l -} -func (rr *SOA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Ns, off+l, compression, true) - l += domainNameLen(rr.Mbox, off+l, compression, true) - l += 4 // Serial - l += 4 // Refresh - l += 4 // Retry - l += 4 // Expire - l += 4 // Minttl - return l -} -func (rr *SPF) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - for _, x := range rr.Txt { - l += len(x) + 1 - } - return l -} -func (rr *SRV) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Priority - l += 2 // Weight - l += 2 // Port - l += domainNameLen(rr.Target, off+l, compression, false) - return l -} -func (rr *SSHFP) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Algorithm - l++ // Type - l += len(rr.FingerPrint)/2 + 1 - return l -} -func (rr *TA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // KeyTag - l++ // Algorithm - l++ // DigestType - l += len(rr.Digest)/2 + 1 - return l -} -func (rr *TALINK) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.PreviousName, off+l, compression, false) - l += domainNameLen(rr.NextName, off+l, compression, false) - return l -} -func (rr *TKEY) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Algorithm, off+l, compression, false) - l += 4 // Inception - l += 4 // Expiration - l += 2 // Mode - l += 2 // Error - l += 2 // KeySize - l += len(rr.Key) / 2 - l += 2 // OtherLen - l += len(rr.OtherData) / 2 - return l -} -func (rr *TLSA) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l++ // Usage - l++ // Selector - l++ // MatchingType - l += len(rr.Certificate)/2 + 1 - return l -} -func (rr *TSIG) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += domainNameLen(rr.Algorithm, off+l, compression, false) - l += 6 // TimeSigned - l += 2 // Fudge - l += 2 // MACSize - l += len(rr.MAC) / 2 - l += 2 // OrigId - l += 2 // Error - l += 2 // OtherLen - l += len(rr.OtherData) / 2 - return l -} -func (rr *TXT) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - for _, x := range rr.Txt { - l += len(x) + 1 - } - return l -} -func (rr *UID) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 4 // Uid - return l -} -func (rr *UINFO) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.Uinfo) + 1 - return l -} -func (rr *URI) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += 2 // Priority - l += 2 // Weight - l += len(rr.Target) - return l -} -func (rr *X25) len(off int, compression map[string]struct{}) int { - l := rr.Hdr.len(off, compression) - l += len(rr.PSDNAddress) + 1 - return l -} - -// copy() functions -func (rr *A) copy() RR { - return &A{rr.Hdr, copyIP(rr.A)} -} -func (rr *AAAA) copy() RR { - return &AAAA{rr.Hdr, copyIP(rr.AAAA)} -} -func (rr *AFSDB) copy() RR { - return &AFSDB{rr.Hdr, rr.Subtype, rr.Hostname} -} -func (rr *ANY) copy() RR { - return &ANY{rr.Hdr} -} -func (rr *AVC) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &AVC{rr.Hdr, Txt} -} -func (rr *CAA) copy() RR { - return &CAA{rr.Hdr, rr.Flag, rr.Tag, rr.Value} -} -func (rr *CERT) copy() RR { - return &CERT{rr.Hdr, rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate} -} -func (rr *CNAME) copy() RR { - return &CNAME{rr.Hdr, rr.Target} -} -func (rr *CSYNC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &CSYNC{rr.Hdr, rr.Serial, rr.Flags, TypeBitMap} -} -func (rr *DHCID) copy() RR { - return &DHCID{rr.Hdr, rr.Digest} -} -func (rr *DNAME) copy() RR { - return &DNAME{rr.Hdr, rr.Target} -} -func (rr *DNSKEY) copy() RR { - return &DNSKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} -} -func (rr *DS) copy() RR { - return &DS{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} -} -func (rr *EID) copy() RR { - return &EID{rr.Hdr, rr.Endpoint} -} -func (rr *EUI48) copy() RR { - return &EUI48{rr.Hdr, rr.Address} -} -func (rr *EUI64) copy() RR { - return &EUI64{rr.Hdr, rr.Address} -} -func (rr *GID) copy() RR { - return &GID{rr.Hdr, rr.Gid} -} -func (rr *GPOS) copy() RR { - return &GPOS{rr.Hdr, rr.Longitude, rr.Latitude, rr.Altitude} -} -func (rr *HINFO) copy() RR { - return &HINFO{rr.Hdr, rr.Cpu, rr.Os} -} -func (rr *HIP) copy() RR { - RendezvousServers := make([]string, len(rr.RendezvousServers)) - copy(RendezvousServers, rr.RendezvousServers) - return &HIP{rr.Hdr, rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers} -} -func (rr *KX) copy() RR { - return &KX{rr.Hdr, rr.Preference, rr.Exchanger} -} -func (rr *L32) copy() RR { - return &L32{rr.Hdr, rr.Preference, copyIP(rr.Locator32)} -} -func (rr *L64) copy() RR { - return &L64{rr.Hdr, rr.Preference, rr.Locator64} -} -func (rr *LOC) copy() RR { - return &LOC{rr.Hdr, rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude} -} -func (rr *LP) copy() RR { - return &LP{rr.Hdr, rr.Preference, rr.Fqdn} -} -func (rr *MB) copy() RR { - return &MB{rr.Hdr, rr.Mb} -} -func (rr *MD) copy() RR { - return &MD{rr.Hdr, rr.Md} -} -func (rr *MF) copy() RR { - return &MF{rr.Hdr, rr.Mf} -} -func (rr *MG) copy() RR { - return &MG{rr.Hdr, rr.Mg} -} -func (rr *MINFO) copy() RR { - return &MINFO{rr.Hdr, rr.Rmail, rr.Email} -} -func (rr *MR) copy() RR { - return &MR{rr.Hdr, rr.Mr} -} -func (rr *MX) copy() RR { - return &MX{rr.Hdr, rr.Preference, rr.Mx} -} -func (rr *NAPTR) copy() RR { - return &NAPTR{rr.Hdr, rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement} -} -func (rr *NID) copy() RR { - return &NID{rr.Hdr, rr.Preference, rr.NodeID} -} -func (rr *NIMLOC) copy() RR { - return &NIMLOC{rr.Hdr, rr.Locator} -} -func (rr *NINFO) copy() RR { - ZSData := make([]string, len(rr.ZSData)) - copy(ZSData, rr.ZSData) - return &NINFO{rr.Hdr, ZSData} -} -func (rr *NS) copy() RR { - return &NS{rr.Hdr, rr.Ns} -} -func (rr *NSAPPTR) copy() RR { - return &NSAPPTR{rr.Hdr, rr.Ptr} -} -func (rr *NSEC) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC{rr.Hdr, rr.NextDomain, TypeBitMap} -} -func (rr *NSEC3) copy() RR { - TypeBitMap := make([]uint16, len(rr.TypeBitMap)) - copy(TypeBitMap, rr.TypeBitMap) - return &NSEC3{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap} -} -func (rr *NSEC3PARAM) copy() RR { - return &NSEC3PARAM{rr.Hdr, rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt} -} -func (rr *NULL) copy() RR { - return &NULL{rr.Hdr, rr.Data} -} -func (rr *OPENPGPKEY) copy() RR { - return &OPENPGPKEY{rr.Hdr, rr.PublicKey} -} -func (rr *OPT) copy() RR { - Option := make([]EDNS0, len(rr.Option)) - for i, e := range rr.Option { - Option[i] = e.copy() - } - return &OPT{rr.Hdr, Option} -} -func (rr *PTR) copy() RR { - return &PTR{rr.Hdr, rr.Ptr} -} -func (rr *PX) copy() RR { - return &PX{rr.Hdr, rr.Preference, rr.Map822, rr.Mapx400} -} -func (rr *RFC3597) copy() RR { - return &RFC3597{rr.Hdr, rr.Rdata} -} -func (rr *RKEY) copy() RR { - return &RKEY{rr.Hdr, rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey} -} -func (rr *RP) copy() RR { - return &RP{rr.Hdr, rr.Mbox, rr.Txt} -} -func (rr *RRSIG) copy() RR { - return &RRSIG{rr.Hdr, rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature} -} -func (rr *RT) copy() RR { - return &RT{rr.Hdr, rr.Preference, rr.Host} -} -func (rr *SMIMEA) copy() RR { - return &SMIMEA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} -} -func (rr *SOA) copy() RR { - return &SOA{rr.Hdr, rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl} -} -func (rr *SPF) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &SPF{rr.Hdr, Txt} -} -func (rr *SRV) copy() RR { - return &SRV{rr.Hdr, rr.Priority, rr.Weight, rr.Port, rr.Target} -} -func (rr *SSHFP) copy() RR { - return &SSHFP{rr.Hdr, rr.Algorithm, rr.Type, rr.FingerPrint} -} -func (rr *TA) copy() RR { - return &TA{rr.Hdr, rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest} -} -func (rr *TALINK) copy() RR { - return &TALINK{rr.Hdr, rr.PreviousName, rr.NextName} -} -func (rr *TKEY) copy() RR { - return &TKEY{rr.Hdr, rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData} -} -func (rr *TLSA) copy() RR { - return &TLSA{rr.Hdr, rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate} -} -func (rr *TSIG) copy() RR { - return &TSIG{rr.Hdr, rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData} -} -func (rr *TXT) copy() RR { - Txt := make([]string, len(rr.Txt)) - copy(Txt, rr.Txt) - return &TXT{rr.Hdr, Txt} -} -func (rr *UID) copy() RR { - return &UID{rr.Hdr, rr.Uid} -} -func (rr *UINFO) copy() RR { - return &UINFO{rr.Hdr, rr.Uinfo} -} -func (rr *URI) copy() RR { - return &URI{rr.Hdr, rr.Priority, rr.Weight, rr.Target} -} -func (rr *X25) copy() RR { - return &X25{rr.Hdr, rr.PSDNAddress} -} diff --git a/vendor/github.com/mxk/go-flowrate/LICENSE b/vendor/github.com/mxk/go-flowrate/LICENSE new file mode 100644 index 000000000..e9f9f628b --- /dev/null +++ b/vendor/github.com/mxk/go-flowrate/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2014 The Go-FlowRate 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 the go-flowrate project 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/github.com/mxk/go-flowrate/flowrate/flowrate.go b/vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go new file mode 100644 index 000000000..1b727721e --- /dev/null +++ b/vendor/github.com/mxk/go-flowrate/flowrate/flowrate.go @@ -0,0 +1,267 @@ +// +// Written by Maxim Khitrov (November 2012) +// + +// Package flowrate provides the tools for monitoring and limiting the flow rate +// of an arbitrary data stream. +package flowrate + +import ( + "math" + "sync" + "time" +) + +// Monitor monitors and limits the transfer rate of a data stream. +type Monitor struct { + mu sync.Mutex // Mutex guarding access to all internal fields + active bool // Flag indicating an active transfer + start time.Duration // Transfer start time (clock() value) + bytes int64 // Total number of bytes transferred + samples int64 // Total number of samples taken + + rSample float64 // Most recent transfer rate sample (bytes per second) + rEMA float64 // Exponential moving average of rSample + rPeak float64 // Peak transfer rate (max of all rSamples) + rWindow float64 // rEMA window (seconds) + + sBytes int64 // Number of bytes transferred since sLast + sLast time.Duration // Most recent sample time (stop time when inactive) + sRate time.Duration // Sampling rate + + tBytes int64 // Number of bytes expected in the current transfer + tLast time.Duration // Time of the most recent transfer of at least 1 byte +} + +// New creates a new flow control monitor. Instantaneous transfer rate is +// measured and updated for each sampleRate interval. windowSize determines the +// weight of each sample in the exponential moving average (EMA) calculation. +// The exact formulas are: +// +// sampleTime = currentTime - prevSampleTime +// sampleRate = byteCount / sampleTime +// weight = 1 - exp(-sampleTime/windowSize) +// newRate = weight*sampleRate + (1-weight)*oldRate +// +// The default values for sampleRate and windowSize (if <= 0) are 100ms and 1s, +// respectively. +func New(sampleRate, windowSize time.Duration) *Monitor { + if sampleRate = clockRound(sampleRate); sampleRate <= 0 { + sampleRate = 5 * clockRate + } + if windowSize <= 0 { + windowSize = 1 * time.Second + } + now := clock() + return &Monitor{ + active: true, + start: now, + rWindow: windowSize.Seconds(), + sLast: now, + sRate: sampleRate, + tLast: now, + } +} + +// Update records the transfer of n bytes and returns n. It should be called +// after each Read/Write operation, even if n is 0. +func (m *Monitor) Update(n int) int { + m.mu.Lock() + m.update(n) + m.mu.Unlock() + return n +} + +// IO is a convenience method intended to wrap io.Reader and io.Writer method +// execution. It calls m.Update(n) and then returns (n, err) unmodified. +func (m *Monitor) IO(n int, err error) (int, error) { + return m.Update(n), err +} + +// Done marks the transfer as finished and prevents any further updates or +// limiting. Instantaneous and current transfer rates drop to 0. Update, IO, and +// Limit methods become NOOPs. It returns the total number of bytes transferred. +func (m *Monitor) Done() int64 { + m.mu.Lock() + if now := m.update(0); m.sBytes > 0 { + m.reset(now) + } + m.active = false + m.tLast = 0 + n := m.bytes + m.mu.Unlock() + return n +} + +// timeRemLimit is the maximum Status.TimeRem value. +const timeRemLimit = 999*time.Hour + 59*time.Minute + 59*time.Second + +// Status represents the current Monitor status. All transfer rates are in bytes +// per second rounded to the nearest byte. +type Status struct { + Active bool // Flag indicating an active transfer + Start time.Time // Transfer start time + Duration time.Duration // Time period covered by the statistics + Idle time.Duration // Time since the last transfer of at least 1 byte + Bytes int64 // Total number of bytes transferred + Samples int64 // Total number of samples taken + InstRate int64 // Instantaneous transfer rate + CurRate int64 // Current transfer rate (EMA of InstRate) + AvgRate int64 // Average transfer rate (Bytes / Duration) + PeakRate int64 // Maximum instantaneous transfer rate + BytesRem int64 // Number of bytes remaining in the transfer + TimeRem time.Duration // Estimated time to completion + Progress Percent // Overall transfer progress +} + +// Status returns current transfer status information. The returned value +// becomes static after a call to Done. +func (m *Monitor) Status() Status { + m.mu.Lock() + now := m.update(0) + s := Status{ + Active: m.active, + Start: clockToTime(m.start), + Duration: m.sLast - m.start, + Idle: now - m.tLast, + Bytes: m.bytes, + Samples: m.samples, + PeakRate: round(m.rPeak), + BytesRem: m.tBytes - m.bytes, + Progress: percentOf(float64(m.bytes), float64(m.tBytes)), + } + if s.BytesRem < 0 { + s.BytesRem = 0 + } + if s.Duration > 0 { + rAvg := float64(s.Bytes) / s.Duration.Seconds() + s.AvgRate = round(rAvg) + if s.Active { + s.InstRate = round(m.rSample) + s.CurRate = round(m.rEMA) + if s.BytesRem > 0 { + if tRate := 0.8*m.rEMA + 0.2*rAvg; tRate > 0 { + ns := float64(s.BytesRem) / tRate * 1e9 + if ns > float64(timeRemLimit) { + ns = float64(timeRemLimit) + } + s.TimeRem = clockRound(time.Duration(ns)) + } + } + } + } + m.mu.Unlock() + return s +} + +// Limit restricts the instantaneous (per-sample) data flow to rate bytes per +// second. It returns the maximum number of bytes (0 <= n <= want) that may be +// transferred immediately without exceeding the limit. If block == true, the +// call blocks until n > 0. want is returned unmodified if want < 1, rate < 1, +// or the transfer is inactive (after a call to Done). +// +// At least one byte is always allowed to be transferred in any given sampling +// period. Thus, if the sampling rate is 100ms, the lowest achievable flow rate +// is 10 bytes per second. +// +// For usage examples, see the implementation of Reader and Writer in io.go. +func (m *Monitor) Limit(want int, rate int64, block bool) (n int) { + if want < 1 || rate < 1 { + return want + } + m.mu.Lock() + + // Determine the maximum number of bytes that can be sent in one sample + limit := round(float64(rate) * m.sRate.Seconds()) + if limit <= 0 { + limit = 1 + } + + // If block == true, wait until m.sBytes < limit + if now := m.update(0); block { + for m.sBytes >= limit && m.active { + now = m.waitNextSample(now) + } + } + + // Make limit <= want (unlimited if the transfer is no longer active) + if limit -= m.sBytes; limit > int64(want) || !m.active { + limit = int64(want) + } + m.mu.Unlock() + + if limit < 0 { + limit = 0 + } + return int(limit) +} + +// SetTransferSize specifies the total size of the data transfer, which allows +// the Monitor to calculate the overall progress and time to completion. +func (m *Monitor) SetTransferSize(bytes int64) { + if bytes < 0 { + bytes = 0 + } + m.mu.Lock() + m.tBytes = bytes + m.mu.Unlock() +} + +// update accumulates the transferred byte count for the current sample until +// clock() - m.sLast >= m.sRate. The monitor status is updated once the current +// sample is done. +func (m *Monitor) update(n int) (now time.Duration) { + if !m.active { + return + } + if now = clock(); n > 0 { + m.tLast = now + } + m.sBytes += int64(n) + if sTime := now - m.sLast; sTime >= m.sRate { + t := sTime.Seconds() + if m.rSample = float64(m.sBytes) / t; m.rSample > m.rPeak { + m.rPeak = m.rSample + } + + // Exponential moving average using a method similar to *nix load + // average calculation. Longer sampling periods carry greater weight. + if m.samples > 0 { + w := math.Exp(-t / m.rWindow) + m.rEMA = m.rSample + w*(m.rEMA-m.rSample) + } else { + m.rEMA = m.rSample + } + m.reset(now) + } + return +} + +// reset clears the current sample state in preparation for the next sample. +func (m *Monitor) reset(sampleTime time.Duration) { + m.bytes += m.sBytes + m.samples++ + m.sBytes = 0 + m.sLast = sampleTime +} + +// waitNextSample sleeps for the remainder of the current sample. The lock is +// released and reacquired during the actual sleep period, so it's possible for +// the transfer to be inactive when this method returns. +func (m *Monitor) waitNextSample(now time.Duration) time.Duration { + const minWait = 5 * time.Millisecond + current := m.sLast + + // sleep until the last sample time changes (ideally, just one iteration) + for m.sLast == current && m.active { + d := current + m.sRate - now + m.mu.Unlock() + if d < minWait { + d = minWait + } + time.Sleep(d) + m.mu.Lock() + now = m.update(0) + } + return now +} diff --git a/vendor/github.com/mxk/go-flowrate/flowrate/io.go b/vendor/github.com/mxk/go-flowrate/flowrate/io.go new file mode 100644 index 000000000..fbe090972 --- /dev/null +++ b/vendor/github.com/mxk/go-flowrate/flowrate/io.go @@ -0,0 +1,133 @@ +// +// Written by Maxim Khitrov (November 2012) +// + +package flowrate + +import ( + "errors" + "io" +) + +// ErrLimit is returned by the Writer when a non-blocking write is short due to +// the transfer rate limit. +var ErrLimit = errors.New("flowrate: flow rate limit exceeded") + +// Limiter is implemented by the Reader and Writer to provide a consistent +// interface for monitoring and controlling data transfer. +type Limiter interface { + Done() int64 + Status() Status + SetTransferSize(bytes int64) + SetLimit(new int64) (old int64) + SetBlocking(new bool) (old bool) +} + +// Reader implements io.ReadCloser with a restriction on the rate of data +// transfer. +type Reader struct { + io.Reader // Data source + *Monitor // Flow control monitor + + limit int64 // Rate limit in bytes per second (unlimited when <= 0) + block bool // What to do when no new bytes can be read due to the limit +} + +// NewReader restricts all Read operations on r to limit bytes per second. +func NewReader(r io.Reader, limit int64) *Reader { + return &Reader{r, New(0, 0), limit, true} +} + +// Read reads up to len(p) bytes into p without exceeding the current transfer +// rate limit. It returns (0, nil) immediately if r is non-blocking and no new +// bytes can be read at this time. +func (r *Reader) Read(p []byte) (n int, err error) { + p = p[:r.Limit(len(p), r.limit, r.block)] + if len(p) > 0 { + n, err = r.IO(r.Reader.Read(p)) + } + return +} + +// SetLimit changes the transfer rate limit to new bytes per second and returns +// the previous setting. +func (r *Reader) SetLimit(new int64) (old int64) { + old, r.limit = r.limit, new + return +} + +// SetBlocking changes the blocking behavior and returns the previous setting. A +// Read call on a non-blocking reader returns immediately if no additional bytes +// may be read at this time due to the rate limit. +func (r *Reader) SetBlocking(new bool) (old bool) { + old, r.block = r.block, new + return +} + +// Close closes the underlying reader if it implements the io.Closer interface. +func (r *Reader) Close() error { + defer r.Done() + if c, ok := r.Reader.(io.Closer); ok { + return c.Close() + } + return nil +} + +// Writer implements io.WriteCloser with a restriction on the rate of data +// transfer. +type Writer struct { + io.Writer // Data destination + *Monitor // Flow control monitor + + limit int64 // Rate limit in bytes per second (unlimited when <= 0) + block bool // What to do when no new bytes can be written due to the limit +} + +// NewWriter restricts all Write operations on w to limit bytes per second. The +// transfer rate and the default blocking behavior (true) can be changed +// directly on the returned *Writer. +func NewWriter(w io.Writer, limit int64) *Writer { + return &Writer{w, New(0, 0), limit, true} +} + +// Write writes len(p) bytes from p to the underlying data stream without +// exceeding the current transfer rate limit. It returns (n, ErrLimit) if w is +// non-blocking and no additional bytes can be written at this time. +func (w *Writer) Write(p []byte) (n int, err error) { + var c int + for len(p) > 0 && err == nil { + s := p[:w.Limit(len(p), w.limit, w.block)] + if len(s) > 0 { + c, err = w.IO(w.Writer.Write(s)) + } else { + return n, ErrLimit + } + p = p[c:] + n += c + } + return +} + +// SetLimit changes the transfer rate limit to new bytes per second and returns +// the previous setting. +func (w *Writer) SetLimit(new int64) (old int64) { + old, w.limit = w.limit, new + return +} + +// SetBlocking changes the blocking behavior and returns the previous setting. A +// Write call on a non-blocking writer returns as soon as no additional bytes +// may be written at this time due to the rate limit. +func (w *Writer) SetBlocking(new bool) (old bool) { + old, w.block = w.block, new + return +} + +// Close closes the underlying writer if it implements the io.Closer interface. +func (w *Writer) Close() error { + defer w.Done() + if c, ok := w.Writer.(io.Closer); ok { + return c.Close() + } + return nil +} diff --git a/vendor/github.com/mxk/go-flowrate/flowrate/util.go b/vendor/github.com/mxk/go-flowrate/flowrate/util.go new file mode 100644 index 000000000..4caac583f --- /dev/null +++ b/vendor/github.com/mxk/go-flowrate/flowrate/util.go @@ -0,0 +1,67 @@ +// +// Written by Maxim Khitrov (November 2012) +// + +package flowrate + +import ( + "math" + "strconv" + "time" +) + +// clockRate is the resolution and precision of clock(). +const clockRate = 20 * time.Millisecond + +// czero is the process start time rounded down to the nearest clockRate +// increment. +var czero = time.Duration(time.Now().UnixNano()) / clockRate * clockRate + +// clock returns a low resolution timestamp relative to the process start time. +func clock() time.Duration { + return time.Duration(time.Now().UnixNano())/clockRate*clockRate - czero +} + +// clockToTime converts a clock() timestamp to an absolute time.Time value. +func clockToTime(c time.Duration) time.Time { + return time.Unix(0, int64(czero+c)) +} + +// clockRound returns d rounded to the nearest clockRate increment. +func clockRound(d time.Duration) time.Duration { + return (d + clockRate>>1) / clockRate * clockRate +} + +// round returns x rounded to the nearest int64 (non-negative values only). +func round(x float64) int64 { + if _, frac := math.Modf(x); frac >= 0.5 { + return int64(math.Ceil(x)) + } + return int64(math.Floor(x)) +} + +// Percent represents a percentage in increments of 1/1000th of a percent. +type Percent uint32 + +// percentOf calculates what percent of the total is x. +func percentOf(x, total float64) Percent { + if x < 0 || total <= 0 { + return 0 + } else if p := round(x / total * 1e5); p <= math.MaxUint32 { + return Percent(p) + } + return Percent(math.MaxUint32) +} + +func (p Percent) Float() float64 { + return float64(p) * 1e-3 +} + +func (p Percent) String() string { + var buf [12]byte + b := strconv.AppendUint(buf[:0], uint64(p)/1000, 10) + n := len(b) + b = strconv.AppendUint(b, 1000+uint64(p)%1000, 10) + b[n] = '.' + return string(append(b, '%')) +} diff --git a/vendor/github.com/naoina/go-stringutil/.travis.yml b/vendor/github.com/naoina/go-stringutil/.travis.yml deleted file mode 100644 index 0489ad5ea..000000000 --- a/vendor/github.com/naoina/go-stringutil/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: go -go: - - 1.4 - - 1.5 - - tip -install: - - go get -v github.com/naoina/go-stringutil -script: - - go test -v -bench . -benchmem ./... diff --git a/vendor/github.com/naoina/go-stringutil/LICENSE b/vendor/github.com/naoina/go-stringutil/LICENSE deleted file mode 100644 index 0fff1c58b..000000000 --- a/vendor/github.com/naoina/go-stringutil/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 Naoya Inada - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/naoina/go-stringutil/README.md b/vendor/github.com/naoina/go-stringutil/README.md deleted file mode 100644 index ecf7a5fae..000000000 --- a/vendor/github.com/naoina/go-stringutil/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# stringutil [![Build Status](https://travis-ci.org/naoina/go-stringutil.svg?branch=master)](https://travis-ci.org/naoina/go-stringutil) - -## Installation - - go get -u github.com/naoina/go-stringutil - -## Documentation - -See https://godoc.org/github.com/naoina/go-stringutil - -## License - -MIT diff --git a/vendor/github.com/naoina/go-stringutil/da.go b/vendor/github.com/naoina/go-stringutil/da.go deleted file mode 100644 index 8fe651659..000000000 --- a/vendor/github.com/naoina/go-stringutil/da.go +++ /dev/null @@ -1,253 +0,0 @@ -package stringutil - -import ( - "fmt" - "sort" - "unicode/utf8" -) - -const ( - terminationCharacter = '#' -) - -func mustDoubleArray(da *doubleArray, err error) *doubleArray { - if err != nil { - panic(err) - } - return da -} - -func (da *doubleArray) Build(keys []string) error { - records := makeRecords(keys) - if err := da.build(records, 1, 0, make(map[int]struct{})); err != nil { - return err - } - return nil -} - -type doubleArray struct { - bc []baseCheck - node []int -} - -func newDoubleArray(keys []string) (*doubleArray, error) { - da := &doubleArray{ - bc: []baseCheck{0}, - node: []int{-1}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node. - } - if err := da.Build(keys); err != nil { - return nil, err - } - return da, nil -} - -// baseCheck contains BASE, CHECK and Extra flags. -// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK. -// -// BASE (22bit) | Extra flags (2bit) | CHECK (8bit) -// |----------------------|--|--------| -// 32 10 8 0 -type baseCheck uint32 - -func (bc baseCheck) Base() int { - return int(bc >> 10) -} - -func (bc *baseCheck) SetBase(base int) { - *bc |= baseCheck(base) << 10 -} - -func (bc baseCheck) Check() byte { - return byte(bc) -} - -func (bc *baseCheck) SetCheck(check byte) { - *bc |= baseCheck(check) -} - -func (bc baseCheck) IsEmpty() bool { - return bc&0xfffffcff == 0 -} - -func (da *doubleArray) Lookup(path string) (length int) { - idx := 1 - tmpIdx := idx - for i := 0; i < len(path); i++ { - c := path[i] - tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) - if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { - break - } - idx = tmpIdx - } - if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { - return da.node[da.bc[next].Base()] - } - return -1 -} - -func (da *doubleArray) LookupByBytes(path []byte) (length int) { - idx := 1 - tmpIdx := idx - for i := 0; i < len(path); i++ { - c := path[i] - tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c) - if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c { - break - } - idx = tmpIdx - } - if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter { - return da.node[da.bc[next].Base()] - } - return -1 -} - -func (da *doubleArray) build(srcs []record, idx, depth int, usedBase map[int]struct{}) error { - sort.Stable(recordSlice(srcs)) - base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase) - if err != nil { - return err - } - if leaf != nil { - da.bc[idx].SetBase(len(da.node)) - da.node = append(da.node, leaf.value) - } - for _, sib := range siblings { - da.setCheck(da.nextIndex(base, sib.c), sib.c) - } - for _, sib := range siblings { - if err := da.build(srcs[sib.start:sib.end], da.nextIndex(base, sib.c), depth+1, usedBase); err != nil { - return err - } - } - return nil -} - -func (da *doubleArray) setBase(i, base int) { - da.bc[i].SetBase(base) -} - -func (da *doubleArray) setCheck(i int, check byte) { - da.bc[i].SetCheck(check) -} - -func (da *doubleArray) findEmptyIndex(start int) int { - i := start - for ; i < len(da.bc); i++ { - if da.bc[i].IsEmpty() { - break - } - } - return i -} - -// findBase returns good BASE. -func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) { - for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) { - base = da.nextIndex(idx, firstChar) - if _, used := usedBase[base]; used { - continue - } - i := 0 - for ; i < len(siblings); i++ { - next := da.nextIndex(base, siblings[i].c) - if len(da.bc) <= next { - da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...) - } - if !da.bc[next].IsEmpty() { - break - } - } - if i == len(siblings) { - break - } - } - usedBase[base] = struct{}{} - return base -} - -func (da *doubleArray) arrange(records []record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) { - siblings, leaf, err = makeSiblings(records, depth) - if err != nil { - return -1, nil, nil, err - } - if len(siblings) < 1 { - return -1, nil, leaf, nil - } - base = da.findBase(siblings, idx, usedBase) - da.setBase(idx, base) - return base, siblings, leaf, err -} - -type sibling struct { - start int - end int - c byte -} - -func (da *doubleArray) nextIndex(base int, c byte) int { - return base ^ int(c) -} - -func makeSiblings(records []record, depth int) (sib []sibling, leaf *record, err error) { - var ( - pc byte - n int - ) - for i, r := range records { - if len(r.key) <= depth { - leaf = &r - continue - } - c := r.key[depth] - switch { - case pc < c: - sib = append(sib, sibling{start: i, c: c}) - case pc == c: - continue - default: - return nil, nil, fmt.Errorf("stringutil: BUG: records hasn't been sorted") - } - if n > 0 { - sib[n-1].end = i - } - pc = c - n++ - } - if n == 0 { - return nil, leaf, nil - } - sib[n-1].end = len(records) - return sib, leaf, nil -} - -type record struct { - key string - value int -} - -func makeRecords(srcs []string) (records []record) { - termChar := string(terminationCharacter) - for _, s := range srcs { - records = append(records, record{ - key: string(s + termChar), - value: utf8.RuneCountInString(s), - }) - } - return records -} - -type recordSlice []record - -func (rs recordSlice) Len() int { - return len(rs) -} - -func (rs recordSlice) Less(i, j int) bool { - return rs[i].key < rs[j].key -} - -func (rs recordSlice) Swap(i, j int) { - rs[i], rs[j] = rs[j], rs[i] -} diff --git a/vendor/github.com/naoina/go-stringutil/strings.go b/vendor/github.com/naoina/go-stringutil/strings.go deleted file mode 100644 index 881ca2c8f..000000000 --- a/vendor/github.com/naoina/go-stringutil/strings.go +++ /dev/null @@ -1,320 +0,0 @@ -package stringutil - -import ( - "sync" - "unicode" - "unicode/utf8" -) - -var ( - mu sync.Mutex - - // Based on https://github.com/golang/lint/blob/32a87160691b3c96046c0c678fe57c5bef761456/lint.go#L702 - commonInitialismMap = map[string]struct{}{ - "API": struct{}{}, - "ASCII": struct{}{}, - "CPU": struct{}{}, - "CSRF": struct{}{}, - "CSS": struct{}{}, - "DNS": struct{}{}, - "EOF": struct{}{}, - "GUID": struct{}{}, - "HTML": struct{}{}, - "HTTP": struct{}{}, - "HTTPS": struct{}{}, - "ID": struct{}{}, - "IP": struct{}{}, - "JSON": struct{}{}, - "LHS": struct{}{}, - "QPS": struct{}{}, - "RAM": struct{}{}, - "RHS": struct{}{}, - "RPC": struct{}{}, - "SLA": struct{}{}, - "SMTP": struct{}{}, - "SQL": struct{}{}, - "SSH": struct{}{}, - "TCP": struct{}{}, - "TLS": struct{}{}, - "TTL": struct{}{}, - "UDP": struct{}{}, - "UI": struct{}{}, - "UID": struct{}{}, - "UUID": struct{}{}, - "URI": struct{}{}, - "URL": struct{}{}, - "UTF8": struct{}{}, - "VM": struct{}{}, - "XML": struct{}{}, - "XSRF": struct{}{}, - "XSS": struct{}{}, - } - commonInitialisms = keys(commonInitialismMap) - commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) - longestLen = longestLength(commonInitialisms) - shortestLen = shortestLength(commonInitialisms, longestLen) -) - -// ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case. -// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'. -func ToUpperCamelCase(s string) string { - if s == "" { - return "" - } - upper := true - start := 0 - result := make([]byte, 0, len(s)) - var runeBuf [utf8.UTFMax]byte - var initialism []byte - for _, c := range s { - if c == '_' { - upper = true - candidate := string(result[start:]) - initialism = initialism[:0] - for _, r := range candidate { - if r < utf8.RuneSelf { - initialism = append(initialism, toUpperASCII(byte(r))) - } else { - n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) - initialism = append(initialism, runeBuf[:n]...) - } - } - if length := commonInitialism.LookupByBytes(initialism); length > 0 { - result = append(result[:start], initialism...) - } - start = len(result) - continue - } - if upper { - if c < utf8.RuneSelf { - result = append(result, toUpperASCII(byte(c))) - } else { - n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(c)) - result = append(result, runeBuf[:n]...) - } - upper = false - continue - } - if c < utf8.RuneSelf { - result = append(result, byte(c)) - } else { - n := utf8.EncodeRune(runeBuf[:], c) - result = append(result, runeBuf[:n]...) - } - } - candidate := string(result[start:]) - initialism = initialism[:0] - for _, r := range candidate { - if r < utf8.RuneSelf { - initialism = append(initialism, toUpperASCII(byte(r))) - } else { - n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r)) - initialism = append(initialism, runeBuf[:n]...) - } - } - if length := commonInitialism.LookupByBytes(initialism); length > 0 { - result = append(result[:start], initialism...) - } - return string(result) -} - -// ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for -// only the ASCII characters. -// ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if -// contains non-ASCII characters. -func ToUpperCamelCaseASCII(s string) string { - if s == "" { - return "" - } - upper := true - start := 0 - result := make([]byte, 0, len(s)) - var initialism []byte - for i := 0; i < len(s); i++ { - c := s[i] - if c == '_' { - upper = true - candidate := result[start:] - initialism = initialism[:0] - for _, b := range candidate { - initialism = append(initialism, toUpperASCII(b)) - } - if length := commonInitialism.LookupByBytes(initialism); length > 0 { - result = append(result[:start], initialism...) - } - start = len(result) - continue - } - if upper { - result = append(result, toUpperASCII(c)) - upper = false - continue - } - result = append(result, c) - } - candidate := result[start:] - initialism = initialism[:0] - for _, b := range candidate { - initialism = append(initialism, toUpperASCII(b)) - } - if length := commonInitialism.LookupByBytes(initialism); length > 0 { - result = append(result[:start], initialism...) - } - return string(result) -} - -// ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case. -// It will insert letter of '_' at position of previous letter of uppercase and all -// letters convert to lower case. -// ToSnakeCase does not insert '_' letter into a common initialism word like ID, URL and so on. -func ToSnakeCase(s string) string { - if s == "" { - return "" - } - result := make([]byte, 0, len(s)) - var runeBuf [utf8.UTFMax]byte - var j, skipCount int - for i, c := range s { - if i < skipCount { - continue - } - if unicode.IsUpper(c) { - if i != 0 { - result = append(result, '_') - } - next := nextIndex(j, len(s)) - if length := commonInitialism.Lookup(s[j:next]); length > 0 { - for _, r := range s[j : j+length] { - if r < utf8.RuneSelf { - result = append(result, toLowerASCII(byte(r))) - } else { - n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(r)) - result = append(result, runeBuf[:n]...) - } - } - j += length - 1 - skipCount = i + length - continue - } - } - if c < utf8.RuneSelf { - result = append(result, toLowerASCII(byte(c))) - } else { - n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(c)) - result = append(result, runeBuf[:n]...) - } - j++ - } - return string(result) -} - -// ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII -// characters. -// ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if -// contains non-ASCII characters. -func ToSnakeCaseASCII(s string) string { - if s == "" { - return "" - } - result := make([]byte, 0, len(s)) - for i := 0; i < len(s); i++ { - c := s[i] - if isUpperASCII(c) { - if i != 0 { - result = append(result, '_') - } - if k := i + shortestLen - 1; k < len(s) && isUpperASCII(s[k]) { - if length := commonInitialism.Lookup(s[i:nextIndex(i, len(s))]); length > 0 { - for j, buf := 0, s[i:i+length]; j < len(buf); j++ { - result = append(result, toLowerASCII(buf[j])) - } - i += length - 1 - continue - } - } - } - result = append(result, toLowerASCII(c)) - } - return string(result) -} - -// AddCommonInitialism adds ss to list of common initialisms. -func AddCommonInitialism(ss ...string) { - mu.Lock() - defer mu.Unlock() - for _, s := range ss { - commonInitialismMap[s] = struct{}{} - } - commonInitialisms = keys(commonInitialismMap) - commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) - longestLen = longestLength(commonInitialisms) - shortestLen = shortestLength(commonInitialisms, longestLen) -} - -// DelCommonInitialism deletes ss from list of common initialisms. -func DelCommonInitialism(ss ...string) { - mu.Lock() - defer mu.Unlock() - for _, s := range ss { - delete(commonInitialismMap, s) - } - commonInitialisms = keys(commonInitialismMap) - commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms)) - longestLen = longestLength(commonInitialisms) - shortestLen = shortestLength(commonInitialisms, longestLen) -} - -func isUpperASCII(c byte) bool { - return 'A' <= c && c <= 'Z' -} - -func isLowerASCII(c byte) bool { - return 'a' <= c && c <= 'z' -} - -func toUpperASCII(c byte) byte { - if isLowerASCII(c) { - return c - ('a' - 'A') - } - return c -} - -func toLowerASCII(c byte) byte { - if isUpperASCII(c) { - return c + 'a' - 'A' - } - return c -} - -func nextIndex(i, maxlen int) int { - if n := i + longestLen; n < maxlen { - return n - } - return maxlen -} - -func keys(m map[string]struct{}) []string { - result := make([]string, 0, len(m)) - for k := range m { - result = append(result, k) - } - return result -} - -func shortestLength(strs []string, shortest int) int { - for _, s := range strs { - if candidate := utf8.RuneCountInString(s); candidate < shortest { - shortest = candidate - } - } - return shortest -} - -func longestLength(strs []string) (longest int) { - for _, s := range strs { - if candidate := utf8.RuneCountInString(s); candidate > longest { - longest = candidate - } - } - return longest -} diff --git a/vendor/github.com/naoina/toml/.travis.yml b/vendor/github.com/naoina/toml/.travis.yml deleted file mode 100644 index 7b7551caa..000000000 --- a/vendor/github.com/naoina/toml/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: go - -go: - - 1.3 - - 1.x - -install: - - go get -t -v ./... - -script: - - go test ./... diff --git a/vendor/github.com/naoina/toml/LICENSE b/vendor/github.com/naoina/toml/LICENSE deleted file mode 100644 index e65039ad8..000000000 --- a/vendor/github.com/naoina/toml/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014 Naoya Inada - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/naoina/toml/README.md b/vendor/github.com/naoina/toml/README.md deleted file mode 100644 index 1c0143348..000000000 --- a/vendor/github.com/naoina/toml/README.md +++ /dev/null @@ -1,392 +0,0 @@ -# TOML parser and encoder library for Golang [![Build Status](https://travis-ci.org/naoina/toml.png?branch=master)](https://travis-ci.org/naoina/toml) - -[TOML](https://github.com/toml-lang/toml) parser and encoder library for [Golang](http://golang.org/). - -This library is compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md). - -## Installation - - go get -u github.com/naoina/toml - -## Usage - -The following TOML save as `example.toml`. - -```toml -# This is a TOML document. Boom. - -title = "TOML Example" - -[owner] -name = "Lance Uppercut" -dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not? - -[database] -server = "192.168.1.1" -ports = [ 8001, 8001, 8002 ] -connection_max = 5000 -enabled = true - -[servers] - - # You can indent as you please. Tabs or spaces. TOML don't care. - [servers.alpha] - ip = "10.0.0.1" - dc = "eqdc10" - - [servers.beta] - ip = "10.0.0.2" - dc = "eqdc10" - -[clients] -data = [ ["gamma", "delta"], [1, 2] ] - -# Line breaks are OK when inside arrays -hosts = [ - "alpha", - "omega" -] -``` - -Then above TOML will mapping to `tomlConfig` struct using `toml.Unmarshal`. - -```go -package main - -import ( - "io/ioutil" - "os" - "time" - - "github.com/naoina/toml" -) - -type tomlConfig struct { - Title string - Owner struct { - Name string - Dob time.Time - } - Database struct { - Server string - Ports []int - ConnectionMax uint - Enabled bool - } - Servers map[string]ServerInfo - Clients struct { - Data [][]interface{} - Hosts []string - } -} - -type ServerInfo struct { - IP net.IP - DC string -} - -func main() { - f, err := os.Open("example.toml") - if err != nil { - panic(err) - } - defer f.Close() - var config Config - if err := toml.NewDecoder(f).Decode(&config); err != nil { - panic(err) - } - - // then to use the unmarshaled config... - fmt.Println("IP of server 'alpha':", config.Servers["alpha"].IP) -} -``` - -## Mappings - -A key and value of TOML will map to the corresponding field. -The fields of struct for mapping must be exported. - -The rules of the mapping of key are following: - -#### Exact matching - -```toml -timeout_seconds = 256 -``` - -```go -type Config struct { - Timeout_seconds int -} -``` - -#### Camelcase matching - -```toml -server_name = "srv1" -``` - -```go -type Config struct { - ServerName string -} -``` - -#### Uppercase matching - -```toml -ip = "10.0.0.1" -``` - -```go -type Config struct { - IP string -} -``` - -See the following examples for the value mappings. - -### String - -```toml -val = "string" -``` - -```go -type Config struct { - Val string -} -``` - -### Integer - -```toml -val = 100 -``` - -```go -type Config struct { - Val int -} -``` - -All types that can be used are following: - -* int8 (from `-128` to `127`) -* int16 (from `-32768` to `32767`) -* int32 (from `-2147483648` to `2147483647`) -* int64 (from `-9223372036854775808` to `9223372036854775807`) -* int (same as `int32` on 32bit environment, or `int64` on 64bit environment) -* uint8 (from `0` to `255`) -* uint16 (from `0` to `65535`) -* uint32 (from `0` to `4294967295`) -* uint64 (from `0` to `18446744073709551615`) -* uint (same as `uint` on 32bit environment, or `uint64` on 64bit environment) - -### Float - -```toml -val = 3.1415 -``` - -```go -type Config struct { - Val float32 -} -``` - -All types that can be used are following: - -* float32 -* float64 - -### Boolean - -```toml -val = true -``` - -```go -type Config struct { - Val bool -} -``` - -### Datetime - -```toml -val = 2014-09-28T21:27:39Z -``` - -```go -type Config struct { - Val time.Time -} -``` - -### Array - -```toml -val = ["a", "b", "c"] -``` - -```go -type Config struct { - Val []string -} -``` - -Also following examples all can be mapped: - -```toml -val1 = [1, 2, 3] -val2 = [["a", "b"], ["c", "d"]] -val3 = [[1, 2, 3], ["a", "b", "c"]] -val4 = [[1, 2, 3], [["a", "b"], [true, false]]] -``` - -```go -type Config struct { - Val1 []int - Val2 [][]string - Val3 [][]interface{} - Val4 [][]interface{} -} -``` - -### Table - -```toml -[server] -type = "app" - - [server.development] - ip = "10.0.0.1" - - [server.production] - ip = "10.0.0.2" -``` - -```go -type Config struct { - Server map[string]Server -} - -type Server struct { - IP string -} -``` - -You can also use the following struct instead of map of struct. - -```go -type Config struct { - Server struct { - Development Server - Production Server - } -} - -type Server struct { - IP string -} -``` - -### Array of Tables - -```toml -[[fruit]] - name = "apple" - - [fruit.physical] - color = "red" - shape = "round" - - [[fruit.variety]] - name = "red delicious" - - [[fruit.variety]] - name = "granny smith" - -[[fruit]] - name = "banana" - - [[fruit.variety]] - name = "plantain" -``` - -```go -type Config struct { - Fruit []struct { - Name string - Physical struct { - Color string - Shape string - } - Variety []struct { - Name string - } - } -} -``` - -### Using the `encoding.TextUnmarshaler` interface - -Package toml supports `encoding.TextUnmarshaler` (and `encoding.TextMarshaler`). You can -use it to apply custom marshaling rules for certain types. The `UnmarshalText` method is -called with the value text found in the TOML input. TOML strings are passed unquoted. - -```toml -duration = "10s" -``` - -```go -import time - -type Duration time.Duration - -// UnmarshalText implements encoding.TextUnmarshaler -func (d *Duration) UnmarshalText(data []byte) error { - duration, err := time.ParseDuration(string(data)) - if err == nil { - *d = Duration(duration) - } - return err -} - -// MarshalText implements encoding.TextMarshaler -func (d Duration) MarshalText() ([]byte, error) { - return []byte(time.Duration(d).String()), nil -} - -type ConfigWithDuration struct { - Duration Duration -} -``` -### Using the `toml.UnmarshalerRec` interface - -You can also override marshaling rules specifically for TOML using the `UnmarshalerRec` -and `MarshalerRec` interfaces. These are useful if you want to control how structs or -arrays are handled. You can apply additional validation or set unexported struct fields. - -Note: `encoding.TextUnmarshaler` and `encoding.TextMarshaler` should be preferred for -simple (scalar) values because they're also compatible with other formats like JSON or -YAML. - -[See the UnmarshalerRec example](https://godoc.org/github.com/naoina/toml/#example_UnmarshalerRec). - -### Using the `toml.Unmarshaler` interface - -If you want to deal with raw TOML syntax, use the `Unmarshaler` and `Marshaler` -interfaces. Their input and output is raw TOML syntax. As such, these interfaces are -useful if you want to handle TOML at the syntax level. - -[See the Unmarshaler example](https://godoc.org/github.com/naoina/toml/#example_Unmarshaler). - -## API documentation - -See [Godoc](http://godoc.org/github.com/naoina/toml). - -## License - -MIT diff --git a/vendor/github.com/naoina/toml/ast/ast.go b/vendor/github.com/naoina/toml/ast/ast.go deleted file mode 100644 index 4868e2e1a..000000000 --- a/vendor/github.com/naoina/toml/ast/ast.go +++ /dev/null @@ -1,192 +0,0 @@ -package ast - -import ( - "strconv" - "strings" - "time" -) - -type Position struct { - Begin int - End int -} - -type Value interface { - Pos() int - End() int - Source() string -} - -type String struct { - Position Position - Value string - Data []rune -} - -func (s *String) Pos() int { - return s.Position.Begin -} - -func (s *String) End() int { - return s.Position.End -} - -func (s *String) Source() string { - return string(s.Data) -} - -type Integer struct { - Position Position - Value string - Data []rune -} - -func (i *Integer) Pos() int { - return i.Position.Begin -} - -func (i *Integer) End() int { - return i.Position.End -} - -func (i *Integer) Source() string { - return string(i.Data) -} - -func (i *Integer) Int() (int64, error) { - return strconv.ParseInt(i.Value, 10, 64) -} - -type Float struct { - Position Position - Value string - Data []rune -} - -func (f *Float) Pos() int { - return f.Position.Begin -} - -func (f *Float) End() int { - return f.Position.End -} - -func (f *Float) Source() string { - return string(f.Data) -} - -func (f *Float) Float() (float64, error) { - return strconv.ParseFloat(f.Value, 64) -} - -type Boolean struct { - Position Position - Value string - Data []rune -} - -func (b *Boolean) Pos() int { - return b.Position.Begin -} - -func (b *Boolean) End() int { - return b.Position.End -} - -func (b *Boolean) Source() string { - return string(b.Data) -} - -func (b *Boolean) Boolean() (bool, error) { - return strconv.ParseBool(b.Value) -} - -type Datetime struct { - Position Position - Value string - Data []rune -} - -func (d *Datetime) Pos() int { - return d.Position.Begin -} - -func (d *Datetime) End() int { - return d.Position.End -} - -func (d *Datetime) Source() string { - return string(d.Data) -} - -func (d *Datetime) Time() (time.Time, error) { - switch { - case !strings.Contains(d.Value, ":"): - return time.Parse("2006-01-02", d.Value) - case !strings.Contains(d.Value, "-"): - return time.Parse("15:04:05.999999999", d.Value) - default: - return time.Parse(time.RFC3339Nano, d.Value) - } -} - -type Array struct { - Position Position - Value []Value - Data []rune -} - -func (a *Array) Pos() int { - return a.Position.Begin -} - -func (a *Array) End() int { - return a.Position.End -} - -func (a *Array) Source() string { - return string(a.Data) -} - -type TableType uint8 - -const ( - TableTypeNormal TableType = iota - TableTypeArray -) - -var tableTypes = [...]string{ - "normal", - "array", -} - -func (t TableType) String() string { - return tableTypes[t] -} - -type Table struct { - Position Position - Line int - Name string - Fields map[string]interface{} - Type TableType - Data []rune -} - -func (t *Table) Pos() int { - return t.Position.Begin -} - -func (t *Table) End() int { - return t.Position.End -} - -func (t *Table) Source() string { - return string(t.Data) -} - -type KeyValue struct { - Key string - Value Value - Line int -} diff --git a/vendor/github.com/naoina/toml/config.go b/vendor/github.com/naoina/toml/config.go deleted file mode 100644 index 06bb9493b..000000000 --- a/vendor/github.com/naoina/toml/config.go +++ /dev/null @@ -1,86 +0,0 @@ -package toml - -import ( - "fmt" - "io" - "reflect" - "strings" - - stringutil "github.com/naoina/go-stringutil" - "github.com/naoina/toml/ast" -) - -// Config contains options for encoding and decoding. -type Config struct { - // NormFieldName is used to match TOML keys to struct fields. The function runs for - // both input keys and struct field names and should return a string that makes the - // two match. You must set this field to use the decoder. - // - // Example: The function in the default config removes _ and lowercases all keys. This - // allows a key called 'api_key' to match the struct field 'APIKey' because both are - // normalized to 'apikey'. - // - // Note that NormFieldName is not used for fields which define a TOML - // key through the struct tag. - NormFieldName func(typ reflect.Type, keyOrField string) string - - // FieldToKey determines the TOML key of a struct field when encoding. - // You must set this field to use the encoder. - // - // Note that FieldToKey is not used for fields which define a TOML - // key through the struct tag. - FieldToKey func(typ reflect.Type, field string) string - - // MissingField, if non-nil, is called when the decoder encounters a key for which no - // matching struct field exists. The default behavior is to return an error. - MissingField func(typ reflect.Type, key string) error -} - -// DefaultConfig contains the default options for encoding and decoding. -// Snake case (i.e. 'foo_bar') is used for key names. -var DefaultConfig = Config{ - NormFieldName: defaultNormFieldName, - FieldToKey: snakeCase, -} - -func defaultNormFieldName(typ reflect.Type, s string) string { - return strings.Replace(strings.ToLower(s), "_", "", -1) -} - -func snakeCase(typ reflect.Type, s string) string { - return stringutil.ToSnakeCase(s) -} - -func defaultMissingField(typ reflect.Type, key string) error { - return fmt.Errorf("field corresponding to `%s' is not defined in %v", key, typ) -} - -// NewEncoder returns a new Encoder that writes to w. -// It is shorthand for DefaultConfig.NewEncoder(w). -func NewEncoder(w io.Writer) *Encoder { - return DefaultConfig.NewEncoder(w) -} - -// Marshal returns the TOML encoding of v. -// It is shorthand for DefaultConfig.Marshal(v). -func Marshal(v interface{}) ([]byte, error) { - return DefaultConfig.Marshal(v) -} - -// Unmarshal parses the TOML data and stores the result in the value pointed to by v. -// It is shorthand for DefaultConfig.Unmarshal(data, v). -func Unmarshal(data []byte, v interface{}) error { - return DefaultConfig.Unmarshal(data, v) -} - -// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v. -// It is shorthand for DefaultConfig.UnmarshalTable(t, v). -func UnmarshalTable(t *ast.Table, v interface{}) error { - return DefaultConfig.UnmarshalTable(t, v) -} - -// NewDecoder returns a new Decoder that reads from r. -// It is shorthand for DefaultConfig.NewDecoder(r). -func NewDecoder(r io.Reader) *Decoder { - return DefaultConfig.NewDecoder(r) -} diff --git a/vendor/github.com/naoina/toml/decode.go b/vendor/github.com/naoina/toml/decode.go deleted file mode 100644 index b3c169eb1..000000000 --- a/vendor/github.com/naoina/toml/decode.go +++ /dev/null @@ -1,478 +0,0 @@ -// Package toml encodes and decodes the TOML configuration format using reflection. -// -// This library is compatible with TOML version v0.4.0. -package toml - -import ( - "encoding" - "fmt" - "io" - "io/ioutil" - "reflect" - "strconv" - "strings" - "time" - - "github.com/naoina/toml/ast" -) - -const ( - tableSeparator = '.' -) - -var ( - escapeReplacer = strings.NewReplacer( - "\b", "\\n", - "\f", "\\f", - "\n", "\\n", - "\r", "\\r", - "\t", "\\t", - ) - underscoreReplacer = strings.NewReplacer( - "_", "", - ) -) - -var timeType = reflect.TypeOf(time.Time{}) - -// Unmarshal parses the TOML data and stores the result in the value pointed to by v. -// -// Unmarshal will mapped to v that according to following rules: -// -// TOML strings to string -// TOML integers to any int type -// TOML floats to float32 or float64 -// TOML booleans to bool -// TOML datetimes to time.Time -// TOML arrays to any type of slice -// TOML tables to struct or map -// TOML array tables to slice of struct or map -func (cfg *Config) Unmarshal(data []byte, v interface{}) error { - table, err := Parse(data) - if err != nil { - return err - } - if err := cfg.UnmarshalTable(table, v); err != nil { - return err - } - return nil -} - -// A Decoder reads and decodes TOML from an input stream. -type Decoder struct { - r io.Reader - cfg *Config -} - -// NewDecoder returns a new Decoder that reads from r. -// Note that it reads all from r before parsing it. -func (cfg *Config) NewDecoder(r io.Reader) *Decoder { - return &Decoder{r, cfg} -} - -// Decode parses the TOML data from its input and stores it in the value pointed to by v. -// See the documentation for Unmarshal for details about the conversion of TOML into a Go value. -func (d *Decoder) Decode(v interface{}) error { - b, err := ioutil.ReadAll(d.r) - if err != nil { - return err - } - return d.cfg.Unmarshal(b, v) -} - -// UnmarshalerRec may be implemented by types to customize their behavior when being -// unmarshaled from TOML. You can use it to implement custom validation or to set -// unexported fields. -// -// UnmarshalTOML receives a function that can be called to unmarshal the original TOML -// value into a field or variable. It is safe to call the function more than once if -// necessary. -type UnmarshalerRec interface { - UnmarshalTOML(fn func(interface{}) error) error -} - -// Unmarshaler can be used to capture and process raw TOML source of a table or value. -// UnmarshalTOML must copy the input if it wishes to retain it after returning. -// -// Note: this interface is retained for backwards compatibility. You probably want -// to implement encoding.TextUnmarshaler or UnmarshalerRec instead. -type Unmarshaler interface { - UnmarshalTOML(input []byte) error -} - -// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v. -// -// UnmarshalTable will mapped to v that according to following rules: -// -// TOML strings to string -// TOML integers to any int type -// TOML floats to float32 or float64 -// TOML booleans to bool -// TOML datetimes to time.Time -// TOML arrays to any type of slice -// TOML tables to struct or map -// TOML array tables to slice of struct or map -func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error { - rv := reflect.ValueOf(v) - toplevelMap := rv.Kind() == reflect.Map - if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() { - return &invalidUnmarshalError{reflect.TypeOf(v)} - } - return unmarshalTable(cfg, rv, t, toplevelMap) -} - -// used for UnmarshalerRec. -func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error { - if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() { - return &invalidUnmarshalError{rv.Type()} - } - rv = indirect(rv) - - switch av.(type) { - case *ast.KeyValue, *ast.Table, []*ast.Table: - if err := unmarshalField(cfg, rv, av); err != nil { - return lineError(fieldLineNumber(av), err) - } - return nil - case ast.Value: - return setValue(cfg, rv, av.(ast.Value)) - default: - panic(fmt.Sprintf("BUG: unhandled AST node type %T", av)) - } -} - -// unmarshalTable unmarshals the fields of a table into a struct or map. -// -// toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this -// (special) case, the map is used as-is instead of creating a new map. -func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error { - rv = indirect(rv) - if err, ok := setUnmarshaler(cfg, rv, t); ok { - return lineError(t.Line, err) - } - switch { - case rv.Kind() == reflect.Struct: - fc := makeFieldCache(cfg, rv.Type()) - for key, fieldAst := range t.Fields { - fv, fieldName, err := fc.findField(cfg, rv, key) - if err != nil { - return lineError(fieldLineNumber(fieldAst), err) - } - if fv.IsValid() { - if err := unmarshalField(cfg, fv, fieldAst); err != nil { - return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err) - } - } - } - case rv.Kind() == reflect.Map || isEface(rv): - m := rv - if !toplevelMap { - if rv.Kind() == reflect.Interface { - m = reflect.ValueOf(make(map[string]interface{})) - } else { - m = reflect.MakeMap(rv.Type()) - } - } - elemtyp := m.Type().Elem() - for key, fieldAst := range t.Fields { - kv, err := unmarshalMapKey(m.Type().Key(), key) - if err != nil { - return lineError(fieldLineNumber(fieldAst), err) - } - fv := reflect.New(elemtyp).Elem() - if err := unmarshalField(cfg, fv, fieldAst); err != nil { - return lineError(fieldLineNumber(fieldAst), err) - } - m.SetMapIndex(kv, fv) - } - if !toplevelMap { - rv.Set(m) - } - default: - return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()}) - } - return nil -} - -func fieldLineNumber(fieldAst interface{}) int { - switch av := fieldAst.(type) { - case *ast.KeyValue: - return av.Line - case *ast.Table: - return av.Line - case []*ast.Table: - return av[0].Line - default: - panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst)) - } -} - -func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error { - switch av := fieldAst.(type) { - case *ast.KeyValue: - return setValue(cfg, rv, av.Value) - case *ast.Table: - return unmarshalTable(cfg, rv, av, false) - case []*ast.Table: - rv = indirect(rv) - if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok { - return err - } - var slice reflect.Value - switch { - case rv.Kind() == reflect.Slice: - slice = reflect.MakeSlice(rv.Type(), len(av), len(av)) - case isEface(rv): - slice = reflect.ValueOf(make([]interface{}, len(av))) - default: - return &unmarshalTypeError{"array table", "slice", rv.Type()} - } - for i, tbl := range av { - vv := reflect.New(slice.Type().Elem()).Elem() - if err := unmarshalTable(cfg, vv, tbl, false); err != nil { - return err - } - slice.Index(i).Set(vv) - } - rv.Set(slice) - default: - panic(fmt.Sprintf("BUG: unhandled AST node type %T", av)) - } - return nil -} - -func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) { - rv := reflect.New(typ).Elem() - if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok { - return rv, u.UnmarshalText([]byte(key)) - } - switch typ.Kind() { - case reflect.String: - rv.SetString(key) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - i, err := strconv.ParseInt(key, 10, int(typ.Size()*8)) - if err != nil { - return rv, convertNumError(typ.Kind(), err) - } - rv.SetInt(i) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - i, err := strconv.ParseUint(key, 10, int(typ.Size()*8)) - if err != nil { - return rv, convertNumError(typ.Kind(), err) - } - rv.SetUint(i) - default: - return rv, fmt.Errorf("invalid map key type %s", typ) - } - return rv, nil -} - -func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error { - lhs = indirect(lhs) - if err, ok := setUnmarshaler(cfg, lhs, val); ok { - return err - } - if err, ok := setTextUnmarshaler(lhs, val); ok { - return err - } - switch v := val.(type) { - case *ast.Integer: - return setInt(lhs, v) - case *ast.Float: - return setFloat(lhs, v) - case *ast.String: - return setString(lhs, v) - case *ast.Boolean: - return setBoolean(lhs, v) - case *ast.Datetime: - return setDatetime(lhs, v) - case *ast.Array: - return setArray(cfg, lhs, v) - default: - panic(fmt.Sprintf("BUG: unhandled node type %T", v)) - } -} - -func indirect(rv reflect.Value) reflect.Value { - for rv.Kind() == reflect.Ptr { - if rv.IsNil() { - rv.Set(reflect.New(rv.Type().Elem())) - } - rv = rv.Elem() - } - return rv -} - -func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) { - if lhs.CanAddr() { - if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok { - err := u.UnmarshalTOML(func(v interface{}) error { - return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av) - }) - return err, true - } - if u, ok := lhs.Addr().Interface().(Unmarshaler); ok { - return u.UnmarshalTOML(unmarshalerSource(av)), true - } - } - return nil, false -} - -func unmarshalerSource(av interface{}) []byte { - var source []byte - switch av := av.(type) { - case []*ast.Table: - for i, tab := range av { - source = append(source, tab.Source()...) - if i != len(av)-1 { - source = append(source, '\n') - } - } - case ast.Value: - source = []byte(av.Source()) - default: - panic(fmt.Sprintf("BUG: unhandled node type %T", av)) - } - return source -} - -func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) { - if !lhs.CanAddr() { - return nil, false - } - u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler) - if !ok || lhs.Type() == timeType { - return nil, false - } - var data string - switch val := val.(type) { - case *ast.Array: - return &unmarshalTypeError{"array", "", lhs.Type()}, true - case *ast.String: - data = val.Value - default: - data = val.Source() - } - return u.UnmarshalText([]byte(data)), true -} - -func setInt(fv reflect.Value, v *ast.Integer) error { - k := fv.Kind() - switch { - case k >= reflect.Int && k <= reflect.Int64: - i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8)) - if err != nil { - return convertNumError(fv.Kind(), err) - } - fv.SetInt(i) - case k >= reflect.Uint && k <= reflect.Uintptr: - i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8)) - if err != nil { - return convertNumError(fv.Kind(), err) - } - fv.SetUint(i) - case isEface(fv): - i, err := strconv.ParseInt(v.Value, 10, 64) - if err != nil { - return convertNumError(reflect.Int64, err) - } - fv.Set(reflect.ValueOf(i)) - default: - return &unmarshalTypeError{"integer", "", fv.Type()} - } - return nil -} - -func setFloat(fv reflect.Value, v *ast.Float) error { - f, err := v.Float() - if err != nil { - return err - } - switch { - case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64: - if fv.OverflowFloat(f) { - return &overflowError{fv.Kind(), v.Value} - } - fv.SetFloat(f) - case isEface(fv): - fv.Set(reflect.ValueOf(f)) - default: - return &unmarshalTypeError{"float", "", fv.Type()} - } - return nil -} - -func setString(fv reflect.Value, v *ast.String) error { - switch { - case fv.Kind() == reflect.String: - fv.SetString(v.Value) - case isEface(fv): - fv.Set(reflect.ValueOf(v.Value)) - default: - return &unmarshalTypeError{"string", "", fv.Type()} - } - return nil -} - -func setBoolean(fv reflect.Value, v *ast.Boolean) error { - b, _ := v.Boolean() - switch { - case fv.Kind() == reflect.Bool: - fv.SetBool(b) - case isEface(fv): - fv.Set(reflect.ValueOf(b)) - default: - return &unmarshalTypeError{"boolean", "", fv.Type()} - } - return nil -} - -func setDatetime(rv reflect.Value, v *ast.Datetime) error { - t, err := v.Time() - if err != nil { - return err - } - if !timeType.AssignableTo(rv.Type()) { - return &unmarshalTypeError{"datetime", "", rv.Type()} - } - rv.Set(reflect.ValueOf(t)) - return nil -} - -func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error { - var slicetyp reflect.Type - switch { - case rv.Kind() == reflect.Slice: - slicetyp = rv.Type() - case isEface(rv): - slicetyp = reflect.SliceOf(rv.Type()) - default: - return &unmarshalTypeError{"array", "slice", rv.Type()} - } - - if len(v.Value) == 0 { - // Ensure defined slices are always set to a non-nil value. - rv.Set(reflect.MakeSlice(slicetyp, 0, 0)) - return nil - } - - tomltyp := reflect.TypeOf(v.Value[0]) - slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value)) - typ := slicetyp.Elem() - for i, vv := range v.Value { - if i > 0 && tomltyp != reflect.TypeOf(vv) { - return errArrayMultiType - } - tmp := reflect.New(typ).Elem() - if err := setValue(cfg, tmp, vv); err != nil { - return err - } - slice.Index(i).Set(tmp) - } - rv.Set(slice) - return nil -} - -func isEface(rv reflect.Value) bool { - return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0 -} diff --git a/vendor/github.com/naoina/toml/encode.go b/vendor/github.com/naoina/toml/encode.go deleted file mode 100644 index ae6bfd575..000000000 --- a/vendor/github.com/naoina/toml/encode.go +++ /dev/null @@ -1,398 +0,0 @@ -package toml - -import ( - "bytes" - "encoding" - "fmt" - "io" - "reflect" - "sort" - "strconv" - "time" - - "github.com/naoina/toml/ast" -) - -const ( - tagOmitempty = "omitempty" - tagSkip = "-" -) - -// Marshal returns the TOML encoding of v. -// -// Struct values encode as TOML. Each exported struct field becomes a field of -// the TOML structure unless -// - the field's tag is "-", or -// - the field is empty and its tag specifies the "omitempty" option. -// -// The "toml" key in the struct field's tag value is the key name, followed by -// an optional comma and options. Examples: -// -// // Field is ignored by this package. -// Field int `toml:"-"` -// -// // Field appears in TOML as key "myName". -// Field int `toml:"myName"` -// -// // Field appears in TOML as key "myName" and the field is omitted from the -// // result of encoding if its value is empty. -// Field int `toml:"myName,omitempty"` -// -// // Field appears in TOML as key "field", but the field is skipped if -// // empty. Note the leading comma. -// Field int `toml:",omitempty"` -func (cfg *Config) Marshal(v interface{}) ([]byte, error) { - buf := new(bytes.Buffer) - err := cfg.NewEncoder(buf).Encode(v) - return buf.Bytes(), err -} - -// A Encoder writes TOML to an output stream. -type Encoder struct { - w io.Writer - cfg *Config -} - -// NewEncoder returns a new Encoder that writes to w. -func (cfg *Config) NewEncoder(w io.Writer) *Encoder { - return &Encoder{w, cfg} -} - -// Encode writes the TOML of v to the stream. -// See the documentation for Marshal for details about the conversion of Go values to TOML. -func (e *Encoder) Encode(v interface{}) error { - rv := reflect.ValueOf(v) - for rv.Kind() == reflect.Ptr { - if rv.IsNil() { - return &marshalNilError{rv.Type()} - } - rv = rv.Elem() - } - buf := &tableBuf{typ: ast.TableTypeNormal} - var err error - switch rv.Kind() { - case reflect.Struct: - err = buf.structFields(e.cfg, rv) - case reflect.Map: - err = buf.mapFields(e.cfg, rv) - default: - err = &marshalTableError{rv.Type()} - } - if err != nil { - return err - } - return buf.writeTo(e.w, "") -} - -// Marshaler can be implemented to override the encoding of TOML values. The returned text -// must be a simple TOML value (i.e. not a table) and is inserted into marshaler output. -// -// This interface exists for backwards-compatibility reasons. You probably want to -// implement encoding.TextMarshaler or MarshalerRec instead. -type Marshaler interface { - MarshalTOML() ([]byte, error) -} - -// MarshalerRec can be implemented to override the TOML encoding of a type. -// The returned value is marshaled in place of the receiver. -type MarshalerRec interface { - MarshalTOML() (interface{}, error) -} - -type tableBuf struct { - name string // already escaped / quoted - body []byte - children []*tableBuf - typ ast.TableType - arrayDepth int -} - -func (b *tableBuf) writeTo(w io.Writer, prefix string) error { - key := b.name // TODO: escape dots - if prefix != "" { - key = prefix + "." + key - } - - if b.name != "" { - head := "[" + key + "]" - if b.typ == ast.TableTypeArray { - head = "[" + head + "]" - } - head += "\n" - if _, err := io.WriteString(w, head); err != nil { - return err - } - } - if _, err := w.Write(b.body); err != nil { - return err - } - - for i, child := range b.children { - if len(b.body) > 0 || i > 0 { - if _, err := w.Write([]byte("\n")); err != nil { - return err - } - } - if err := child.writeTo(w, key); err != nil { - return err - } - } - return nil -} - -func (b *tableBuf) newChild(name string) *tableBuf { - child := &tableBuf{name: quoteName(name), typ: ast.TableTypeNormal} - if b.arrayDepth > 0 { - child.typ = ast.TableTypeArray - } - return child -} - -func (b *tableBuf) addChild(child *tableBuf) { - // Empty table elision: we can avoid writing a table that doesn't have any keys on its - // own. Array tables can't be elided because they define array elements (which would - // be missing if elided). - if len(child.body) == 0 && child.typ == ast.TableTypeNormal { - for _, gchild := range child.children { - gchild.name = child.name + "." + gchild.name - b.addChild(gchild) - } - return - } - b.children = append(b.children, child) -} - -func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error { - rt := rv.Type() - for i := 0; i < rv.NumField(); i++ { - ft := rt.Field(i) - if ft.PkgPath != "" && !ft.Anonymous { // not exported - continue - } - name, rest := extractTag(ft.Tag.Get(fieldTagName)) - if name == tagSkip { - continue - } - fv := rv.Field(i) - if rest == tagOmitempty && isEmptyValue(fv) { - continue - } - if name == "" { - name = cfg.FieldToKey(rt, ft.Name) - } - if err := b.field(cfg, name, fv); err != nil { - return err - } - } - return nil -} - -type mapKeyList []struct { - key string - value reflect.Value -} - -func (l mapKeyList) Len() int { return len(l) } -func (l mapKeyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } -func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key } - -func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error { - keys := rv.MapKeys() - keylist := make(mapKeyList, len(keys)) - for i, key := range keys { - var err error - keylist[i].key, err = encodeMapKey(key) - if err != nil { - return err - } - keylist[i].value = rv.MapIndex(key) - } - sort.Sort(keylist) - - for _, kv := range keylist { - if err := b.field(cfg, kv.key, kv.value); err != nil { - return err - } - } - return nil -} - -func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error { - off := len(b.body) - b.body = append(b.body, quoteName(name)...) - b.body = append(b.body, " = "...) - isTable, err := b.value(cfg, rv, name) - if isTable { - b.body = b.body[:off] // rub out "key =" - } else { - b.body = append(b.body, '\n') - } - return err -} - -func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) { - isMarshaler, isTable, err := b.marshaler(cfg, rv, name) - if isMarshaler { - return isTable, err - } - switch rv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - b.body = strconv.AppendInt(b.body, rv.Int(), 10) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - b.body = strconv.AppendUint(b.body, rv.Uint(), 10) - case reflect.Float32, reflect.Float64: - b.body = strconv.AppendFloat(b.body, rv.Float(), 'e', -1, 64) - case reflect.Bool: - b.body = strconv.AppendBool(b.body, rv.Bool()) - case reflect.String: - b.body = strconv.AppendQuote(b.body, rv.String()) - case reflect.Ptr, reflect.Interface: - if rv.IsNil() { - return false, &marshalNilError{rv.Type()} - } - return b.value(cfg, rv.Elem(), name) - case reflect.Slice, reflect.Array: - rvlen := rv.Len() - if rvlen == 0 { - b.body = append(b.body, '[', ']') - return false, nil - } - - b.arrayDepth++ - wroteElem := false - b.body = append(b.body, '[') - for i := 0; i < rvlen; i++ { - isTable, err := b.value(cfg, rv.Index(i), name) - if err != nil { - return isTable, err - } - wroteElem = wroteElem || !isTable - if wroteElem { - if i < rvlen-1 { - b.body = append(b.body, ',', ' ') - } else { - b.body = append(b.body, ']') - } - } - } - if !wroteElem { - b.body = b.body[:len(b.body)-1] // rub out '[' - } - b.arrayDepth-- - return !wroteElem, nil - case reflect.Struct: - child := b.newChild(name) - err := child.structFields(cfg, rv) - b.addChild(child) - return true, err - case reflect.Map: - child := b.newChild(name) - err := child.mapFields(cfg, rv) - b.addChild(child) - return true, err - default: - return false, fmt.Errorf("toml: marshal: unsupported type %v", rv.Kind()) - } - return false, nil -} - -func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) { - switch t := rv.Interface().(type) { - case encoding.TextMarshaler: - enc, err := t.MarshalText() - if err != nil { - return true, false, err - } - b.body = encodeTextMarshaler(b.body, string(enc)) - return true, false, nil - case MarshalerRec: - newval, err := t.MarshalTOML() - if err != nil { - return true, false, err - } - isTable, err = b.value(cfg, reflect.ValueOf(newval), name) - return true, isTable, err - case Marshaler: - enc, err := t.MarshalTOML() - if err != nil { - return true, false, err - } - b.body = append(b.body, enc...) - return true, false, nil - } - return false, false, nil -} - -func encodeTextMarshaler(buf []byte, v string) []byte { - // Emit the value without quotes if possible. - if v == "true" || v == "false" { - return append(buf, v...) - } else if _, err := time.Parse(time.RFC3339Nano, v); err == nil { - return append(buf, v...) - } else if _, err := strconv.ParseInt(v, 10, 64); err == nil { - return append(buf, v...) - } else if _, err := strconv.ParseUint(v, 10, 64); err == nil { - return append(buf, v...) - } else if _, err := strconv.ParseFloat(v, 64); err == nil { - return append(buf, v...) - } - return strconv.AppendQuote(buf, v) -} - -func encodeMapKey(rv reflect.Value) (string, error) { - if rv.Kind() == reflect.String { - return rv.String(), nil - } - if tm, ok := rv.Interface().(encoding.TextMarshaler); ok { - b, err := tm.MarshalText() - return string(b), err - } - switch rv.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return strconv.FormatInt(rv.Int(), 10), nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return strconv.FormatUint(rv.Uint(), 10), nil - } - return "", fmt.Errorf("toml: invalid map key type %v", rv.Type()) -} - -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array: - // encoding/json treats all arrays with non-zero length as non-empty. We check the - // array content here because zero-length arrays are almost never used. - len := v.Len() - for i := 0; i < len; i++ { - if !isEmptyValue(v.Index(i)) { - return false - } - } - return true - case reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - return false -} - -func quoteName(s string) string { - if len(s) == 0 { - return strconv.Quote(s) - } - for _, r := range s { - if r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '-' || r == '_' { - continue - } - return strconv.Quote(s) - } - return s -} diff --git a/vendor/github.com/naoina/toml/error.go b/vendor/github.com/naoina/toml/error.go deleted file mode 100644 index cb73b5e0a..000000000 --- a/vendor/github.com/naoina/toml/error.go +++ /dev/null @@ -1,107 +0,0 @@ -package toml - -import ( - "errors" - "fmt" - "reflect" - "strconv" -) - -var ( - errArrayMultiType = errors.New("array can't contain multiple types") -) - -// LineError is returned by Unmarshal, UnmarshalTable and Parse -// if the error is local to a line. -type LineError struct { - Line int - StructField string - Err error -} - -func (err *LineError) Error() string { - field := "" - if err.StructField != "" { - field = "(" + err.StructField + ") " - } - return fmt.Sprintf("line %d: %s%v", err.Line, field, err.Err) -} - -func lineError(line int, err error) error { - if err == nil { - return nil - } - if _, ok := err.(*LineError); ok { - return err - } - return &LineError{Line: line, Err: err} -} - -func lineErrorField(line int, field string, err error) error { - if lerr, ok := err.(*LineError); ok { - return lerr - } else if err != nil { - err = &LineError{Line: line, StructField: field, Err: err} - } - return err -} - -type overflowError struct { - kind reflect.Kind - v string -} - -func (err *overflowError) Error() string { - return fmt.Sprintf("value %s is out of range for %v", err.v, err.kind) -} - -func convertNumError(kind reflect.Kind, err error) error { - if numerr, ok := err.(*strconv.NumError); ok && numerr.Err == strconv.ErrRange { - return &overflowError{kind, numerr.Num} - } - return err -} - -type invalidUnmarshalError struct { - typ reflect.Type -} - -func (err *invalidUnmarshalError) Error() string { - if err.typ == nil { - return "toml: Unmarshal(nil)" - } - if err.typ.Kind() != reflect.Ptr { - return "toml: Unmarshal(non-pointer " + err.typ.String() + ")" - } - return "toml: Unmarshal(nil " + err.typ.String() + ")" -} - -type unmarshalTypeError struct { - what string - want string - typ reflect.Type -} - -func (err *unmarshalTypeError) Error() string { - msg := fmt.Sprintf("cannot unmarshal TOML %s into %s", err.what, err.typ) - if err.want != "" { - msg += " (need " + err.want + ")" - } - return msg -} - -type marshalNilError struct { - typ reflect.Type -} - -func (err *marshalNilError) Error() string { - return fmt.Sprintf("toml: cannot marshal nil %s", err.typ) -} - -type marshalTableError struct { - typ reflect.Type -} - -func (err *marshalTableError) Error() string { - return fmt.Sprintf("toml: cannot marshal %s as table, want struct or map type", err.typ) -} diff --git a/vendor/github.com/naoina/toml/parse.go b/vendor/github.com/naoina/toml/parse.go deleted file mode 100644 index de9108566..000000000 --- a/vendor/github.com/naoina/toml/parse.go +++ /dev/null @@ -1,362 +0,0 @@ -package toml - -import ( - "errors" - "fmt" - "strconv" - "strings" - - "github.com/naoina/toml/ast" -) - -// The parser is generated by github.com/pointlander/peg. To regenerate it, do: -// -// go get -u github.com/pointlander/peg -// go generate . - -//go:generate peg -switch -inline parse.peg - -var errParse = errors.New("invalid TOML syntax") - -// Parse returns an AST representation of TOML. -// The toplevel is represented by a table. -func Parse(data []byte) (*ast.Table, error) { - d := &parseState{p: &tomlParser{Buffer: string(data)}} - d.init() - - if err := d.parse(); err != nil { - return nil, err - } - - return d.p.toml.table, nil -} - -type parseState struct { - p *tomlParser -} - -func (d *parseState) init() { - d.p.Init() - d.p.toml.init(d.p.buffer) -} - -func (d *parseState) parse() error { - if err := d.p.Parse(); err != nil { - if err, ok := err.(*parseError); ok { - return lineError(err.Line(), errParse) - } - return err - } - return d.execute() -} - -func (d *parseState) execute() (err error) { - defer func() { - if e := recover(); e != nil { - lerr, ok := e.(*LineError) - if !ok { - panic(e) - } - err = lerr - } - }() - d.p.Execute() - return nil -} - -func (e *parseError) Line() int { - tokens := []token32{e.max} - positions, p := make([]int, 2*len(tokens)), 0 - for _, token := range tokens { - positions[p], p = int(token.begin), p+1 - positions[p], p = int(token.end), p+1 - } - for _, t := range translatePositions(e.p.buffer, positions) { - if e.p.line < t.line { - e.p.line = t.line - } - } - return e.p.line -} - -type stack struct { - key string - table *ast.Table -} - -type array struct { - parent *array - child *array - current *ast.Array - line int -} - -type toml struct { - table *ast.Table - line int - currentTable *ast.Table - s string - key string - tableKeys []string - val ast.Value - arr *array - stack []*stack - skip bool -} - -func (p *toml) init(data []rune) { - p.line = 1 - p.table = p.newTable(ast.TableTypeNormal, "") - p.table.Position.End = len(data) - 1 - p.table.Data = data[:len(data)-1] // truncate the end_symbol added by PEG parse generator. - p.currentTable = p.table -} - -func (p *toml) Error(err error) { - panic(lineError(p.line, err)) -} - -func (p *tomlParser) SetTime(begin, end int) { - p.val = &ast.Datetime{ - Position: ast.Position{Begin: begin, End: end}, - Data: p.buffer[begin:end], - Value: string(p.buffer[begin:end]), - } -} - -func (p *tomlParser) SetFloat64(begin, end int) { - p.val = &ast.Float{ - Position: ast.Position{Begin: begin, End: end}, - Data: p.buffer[begin:end], - Value: underscoreReplacer.Replace(string(p.buffer[begin:end])), - } -} - -func (p *tomlParser) SetInt64(begin, end int) { - p.val = &ast.Integer{ - Position: ast.Position{Begin: begin, End: end}, - Data: p.buffer[begin:end], - Value: underscoreReplacer.Replace(string(p.buffer[begin:end])), - } -} - -func (p *tomlParser) SetString(begin, end int) { - p.val = &ast.String{ - Position: ast.Position{Begin: begin, End: end}, - Data: p.buffer[begin:end], - Value: p.s, - } - p.s = "" -} - -func (p *tomlParser) SetBool(begin, end int) { - p.val = &ast.Boolean{ - Position: ast.Position{Begin: begin, End: end}, - Data: p.buffer[begin:end], - Value: string(p.buffer[begin:end]), - } -} - -func (p *tomlParser) StartArray() { - if p.arr == nil { - p.arr = &array{line: p.line, current: &ast.Array{}} - return - } - p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}} - p.arr = p.arr.child -} - -func (p *tomlParser) AddArrayVal() { - if p.arr.current == nil { - p.arr.current = &ast.Array{} - } - p.arr.current.Value = append(p.arr.current.Value, p.val) -} - -func (p *tomlParser) SetArray(begin, end int) { - p.arr.current.Position = ast.Position{Begin: begin, End: end} - p.arr.current.Data = p.buffer[begin:end] - p.val = p.arr.current - p.arr = p.arr.parent -} - -func (p *toml) SetTable(buf []rune, begin, end int) { - rawName := string(buf[begin:end]) - p.setTable(p.table, rawName, p.tableKeys) - p.tableKeys = nil -} - -func (p *toml) setTable(parent *ast.Table, name string, names []string) { - parent, err := p.lookupTable(parent, names[:len(names)-1]) - if err != nil { - p.Error(err) - } - last := names[len(names)-1] - tbl := p.newTable(ast.TableTypeNormal, last) - switch v := parent.Fields[last].(type) { - case nil: - parent.Fields[last] = tbl - case []*ast.Table: - p.Error(fmt.Errorf("table `%s' is in conflict with array table in line %d", name, v[0].Line)) - case *ast.Table: - if (v.Position == ast.Position{}) { - // This table was created as an implicit parent. - // Replace it with the real defined table. - tbl.Fields = v.Fields - parent.Fields[last] = tbl - } else { - p.Error(fmt.Errorf("table `%s' is in conflict with table in line %d", name, v.Line)) - } - case *ast.KeyValue: - p.Error(fmt.Errorf("table `%s' is in conflict with line %d", name, v.Line)) - default: - p.Error(fmt.Errorf("BUG: table `%s' is in conflict but it's unknown type `%T'", last, v)) - } - p.currentTable = tbl -} - -func (p *toml) newTable(typ ast.TableType, name string) *ast.Table { - return &ast.Table{ - Line: p.line, - Name: name, - Type: typ, - Fields: make(map[string]interface{}), - } -} - -func (p *tomlParser) SetTableString(begin, end int) { - p.currentTable.Data = p.buffer[begin:end] - p.currentTable.Position.Begin = begin - p.currentTable.Position.End = end -} - -func (p *toml) SetArrayTable(buf []rune, begin, end int) { - rawName := string(buf[begin:end]) - p.setArrayTable(p.table, rawName, p.tableKeys) - p.tableKeys = nil -} - -func (p *toml) setArrayTable(parent *ast.Table, name string, names []string) { - parent, err := p.lookupTable(parent, names[:len(names)-1]) - if err != nil { - p.Error(err) - } - last := names[len(names)-1] - tbl := p.newTable(ast.TableTypeArray, last) - switch v := parent.Fields[last].(type) { - case nil: - parent.Fields[last] = []*ast.Table{tbl} - case []*ast.Table: - parent.Fields[last] = append(v, tbl) - case *ast.Table: - p.Error(fmt.Errorf("array table `%s' is in conflict with table in line %d", name, v.Line)) - case *ast.KeyValue: - p.Error(fmt.Errorf("array table `%s' is in conflict with line %d", name, v.Line)) - default: - p.Error(fmt.Errorf("BUG: array table `%s' is in conflict but it's unknown type `%T'", name, v)) - } - p.currentTable = tbl -} - -func (p *toml) StartInlineTable() { - p.skip = false - p.stack = append(p.stack, &stack{p.key, p.currentTable}) - names := []string{p.key} - if p.arr == nil { - p.setTable(p.currentTable, names[0], names) - } else { - p.setArrayTable(p.currentTable, names[0], names) - } -} - -func (p *toml) EndInlineTable() { - st := p.stack[len(p.stack)-1] - p.key, p.currentTable = st.key, st.table - p.stack[len(p.stack)-1] = nil - p.stack = p.stack[:len(p.stack)-1] - p.skip = true -} - -func (p *toml) AddLineCount(i int) { - p.line += i -} - -func (p *toml) SetKey(buf []rune, begin, end int) { - p.key = string(buf[begin:end]) - if len(p.key) > 0 && p.key[0] == '"' { - p.key = p.unquote(p.key) - } -} - -func (p *toml) AddTableKey() { - p.tableKeys = append(p.tableKeys, p.key) -} - -func (p *toml) AddKeyValue() { - if p.skip { - p.skip = false - return - } - if val, exists := p.currentTable.Fields[p.key]; exists { - switch v := val.(type) { - case *ast.Table: - p.Error(fmt.Errorf("key `%s' is in conflict with table in line %d", p.key, v.Line)) - case *ast.KeyValue: - p.Error(fmt.Errorf("key `%s' is in conflict with line %xd", p.key, v.Line)) - default: - p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v)) - } - } - p.currentTable.Fields[p.key] = &ast.KeyValue{Key: p.key, Value: p.val, Line: p.line} -} - -func (p *toml) SetBasicString(buf []rune, begin, end int) { - p.s = p.unquote(string(buf[begin:end])) -} - -func (p *toml) SetMultilineString() { - p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`) -} - -func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) { - p.s += string(buf[begin:end]) -} - -func (p *toml) SetLiteralString(buf []rune, begin, end int) { - p.s = string(buf[begin:end]) -} - -func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) { - p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n") -} - -func (p *toml) unquote(s string) string { - s, err := strconv.Unquote(s) - if err != nil { - p.Error(err) - } - return s -} - -func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) { - for _, s := range keys { - val, exists := t.Fields[s] - if !exists { - tbl := p.newTable(ast.TableTypeNormal, s) - t.Fields[s] = tbl - t = tbl - continue - } - switch v := val.(type) { - case *ast.Table: - t = v - case []*ast.Table: - t = v[len(v)-1] - case *ast.KeyValue: - return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line) - default: - return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v) - } - } - return t, nil -} diff --git a/vendor/github.com/naoina/toml/parse.peg b/vendor/github.com/naoina/toml/parse.peg deleted file mode 100644 index dd9eb8880..000000000 --- a/vendor/github.com/naoina/toml/parse.peg +++ /dev/null @@ -1,147 +0,0 @@ -package toml - -type tomlParser Peg { - toml -} - -TOML <- Expression (newline Expression)* newline? !. { _ = buffer } - -Expression <- ( - { p.SetTableString(begin, end) } - / ws keyval ws comment? - / ws comment? - / ws -) - -newline <- <[\r\n]+> { p.AddLineCount(end - begin) } - -ws <- [ \t]* -wsnl <- ( - [ \t] - / <[\r\n]> { p.AddLineCount(end - begin) } -)* - -comment <- '#' <[\t -\0x10FFFF]*> - -keyval <- key ws '=' ws val { p.AddKeyValue() } - -key <- bareKey / quotedKey - -bareKey <- <[0-9A-Za-z\-_]+> { p.SetKey(p.buffer, begin, end) } - -quotedKey <- < '"' basicChar* '"' > { p.SetKey(p.buffer, begin, end) } - -val <- ( - { p.SetTime(begin, end) } - / { p.SetFloat64(begin, end) } - / { p.SetInt64(begin, end) } - / { p.SetString(begin, end) } - / { p.SetBool(begin, end) } - / { p.SetArray(begin, end) } - / inlineTable -) - -table <- stdTable / arrayTable - -stdTable <- '[' ws ws ']' { p.SetTable(p.buffer, begin, end) } - -arrayTable <- '[[' ws ws ']]' { p.SetArrayTable(p.buffer, begin, end) } - -inlineTable <- ( - '{' { p.StartInlineTable() } - ws inlineTableKeyValues ws - '}' { p.EndInlineTable() } -) - -inlineTableKeyValues <- (keyval inlineTableValSep?)* - -tableKey <- tableKeyComp (tableKeySep tableKeyComp)* - -tableKeyComp <- key { p.AddTableKey() } - -tableKeySep <- ws '.' ws - -inlineTableValSep <- ws ',' ws - -integer <- [\-+]? int -int <- [1-9] (digit / '_' digit)+ / digit - -float <- integer (frac exp? / frac? exp) -frac <- '.' digit (digit / '_' digit)* -exp <- [eE] [\-+]? digit (digit / '_' digit)* - -string <- ( - mlLiteralString - / literalString - / mlBasicString - / basicString -) - -basicString <- <'"' basicChar* '"'> { p.SetBasicString(p.buffer, begin, end) } - -basicChar <- basicUnescaped / escaped -escaped <- escape ([btnfr"/\\] / 'u' hexQuad / 'U' hexQuad hexQuad) - -basicUnescaped <- [ -!#-\[\]-\0x10FFFF] - -escape <- '\\' - -mlBasicString <- '"""' mlBasicBody '"""' { p.SetMultilineString() } - -mlBasicBody <- ( - { p.AddMultilineBasicBody(p.buffer, begin, end) } - / escape newline wsnl -)* - -literalString <- "'" "'" { p.SetLiteralString(p.buffer, begin, end) } - -literalChar <- [\t -&(-\0x10FFFF] - -mlLiteralString <- "'''" "'''" { p.SetMultilineLiteralString(p.buffer, begin, end) } - -mlLiteralBody <- (!"'''" (mlLiteralChar / newline))* - -mlLiteralChar <- [\t -\0x10FFFF] - -hexdigit <- [0-9A-Fa-f] -hexQuad <- hexdigit hexdigit hexdigit hexdigit - -boolean <- 'true' / 'false' - -dateFullYear <- digitQuad -dateMonth <- digitDual -dateMDay <- digitDual -timeHour <- digitDual -timeMinute <- digitDual -timeSecond <- digitDual -timeSecfrac <- '.' digit+ -timeNumoffset <- [\-+] timeHour ':' timeMinute -timeOffset <- 'Z' / timeNumoffset -partialTime <- timeHour ':' timeMinute ':' timeSecond timeSecfrac? -fullDate <- dateFullYear '-' dateMonth '-' dateMDay -fullTime <- partialTime timeOffset -datetime <- (fullDate ('T' fullTime)?) / partialTime - -digit <- [0-9] -digitDual <- digit digit -digitQuad <- digitDual digitDual - -array <- ( - '[' { p.StartArray() } - wsnl arrayValues? wsnl - ']' -) - -arrayValues <- ( - val { p.AddArrayVal() } - ( - wsnl comment? - wsnl arraySep - wsnl comment? - wsnl val { p.AddArrayVal() } - )* - wsnl arraySep? - wsnl comment? -) - -arraySep <- ',' diff --git a/vendor/github.com/naoina/toml/parse.peg.go b/vendor/github.com/naoina/toml/parse.peg.go deleted file mode 100644 index d768b00a8..000000000 --- a/vendor/github.com/naoina/toml/parse.peg.go +++ /dev/null @@ -1,2579 +0,0 @@ -package toml - -import ( - "fmt" - "math" - "sort" - "strconv" -) - -const endSymbol rune = 1114112 - -/* The rule types inferred from the grammar are below. */ -type pegRule uint8 - -const ( - ruleUnknown pegRule = iota - ruleTOML - ruleExpression - rulenewline - rulews - rulewsnl - rulecomment - rulekeyval - rulekey - rulebareKey - rulequotedKey - ruleval - ruletable - rulestdTable - rulearrayTable - ruleinlineTable - ruleinlineTableKeyValues - ruletableKey - ruletableKeyComp - ruletableKeySep - ruleinlineTableValSep - ruleinteger - ruleint - rulefloat - rulefrac - ruleexp - rulestring - rulebasicString - rulebasicChar - ruleescaped - rulebasicUnescaped - ruleescape - rulemlBasicString - rulemlBasicBody - ruleliteralString - ruleliteralChar - rulemlLiteralString - rulemlLiteralBody - rulemlLiteralChar - rulehexdigit - rulehexQuad - ruleboolean - ruledateFullYear - ruledateMonth - ruledateMDay - ruletimeHour - ruletimeMinute - ruletimeSecond - ruletimeSecfrac - ruletimeNumoffset - ruletimeOffset - rulepartialTime - rulefullDate - rulefullTime - ruledatetime - ruledigit - ruledigitDual - ruledigitQuad - rulearray - rulearrayValues - rulearraySep - ruleAction0 - rulePegText - ruleAction1 - ruleAction2 - ruleAction3 - ruleAction4 - ruleAction5 - ruleAction6 - ruleAction7 - ruleAction8 - ruleAction9 - ruleAction10 - ruleAction11 - ruleAction12 - ruleAction13 - ruleAction14 - ruleAction15 - ruleAction16 - ruleAction17 - ruleAction18 - ruleAction19 - ruleAction20 - ruleAction21 - ruleAction22 - ruleAction23 - ruleAction24 - ruleAction25 -) - -var rul3s = [...]string{ - "Unknown", - "TOML", - "Expression", - "newline", - "ws", - "wsnl", - "comment", - "keyval", - "key", - "bareKey", - "quotedKey", - "val", - "table", - "stdTable", - "arrayTable", - "inlineTable", - "inlineTableKeyValues", - "tableKey", - "tableKeyComp", - "tableKeySep", - "inlineTableValSep", - "integer", - "int", - "float", - "frac", - "exp", - "string", - "basicString", - "basicChar", - "escaped", - "basicUnescaped", - "escape", - "mlBasicString", - "mlBasicBody", - "literalString", - "literalChar", - "mlLiteralString", - "mlLiteralBody", - "mlLiteralChar", - "hexdigit", - "hexQuad", - "boolean", - "dateFullYear", - "dateMonth", - "dateMDay", - "timeHour", - "timeMinute", - "timeSecond", - "timeSecfrac", - "timeNumoffset", - "timeOffset", - "partialTime", - "fullDate", - "fullTime", - "datetime", - "digit", - "digitDual", - "digitQuad", - "array", - "arrayValues", - "arraySep", - "Action0", - "PegText", - "Action1", - "Action2", - "Action3", - "Action4", - "Action5", - "Action6", - "Action7", - "Action8", - "Action9", - "Action10", - "Action11", - "Action12", - "Action13", - "Action14", - "Action15", - "Action16", - "Action17", - "Action18", - "Action19", - "Action20", - "Action21", - "Action22", - "Action23", - "Action24", - "Action25", -} - -type token32 struct { - pegRule - begin, end uint32 -} - -func (t *token32) String() string { - return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end) -} - -type node32 struct { - token32 - up, next *node32 -} - -func (node *node32) print(pretty bool, buffer string) { - var print func(node *node32, depth int) - print = func(node *node32, depth int) { - for node != nil { - for c := 0; c < depth; c++ { - fmt.Printf(" ") - } - rule := rul3s[node.pegRule] - quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end]))) - if !pretty { - fmt.Printf("%v %v\n", rule, quote) - } else { - fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote) - } - if node.up != nil { - print(node.up, depth+1) - } - node = node.next - } - } - print(node, 0) -} - -func (node *node32) Print(buffer string) { - node.print(false, buffer) -} - -func (node *node32) PrettyPrint(buffer string) { - node.print(true, buffer) -} - -type tokens32 struct { - tree []token32 -} - -func (t *tokens32) Trim(length uint32) { - t.tree = t.tree[:length] -} - -func (t *tokens32) Print() { - for _, token := range t.tree { - fmt.Println(token.String()) - } -} - -func (t *tokens32) AST() *node32 { - type element struct { - node *node32 - down *element - } - tokens := t.Tokens() - var stack *element - for _, token := range tokens { - if token.begin == token.end { - continue - } - node := &node32{token32: token} - for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end { - stack.node.next = node.up - node.up = stack.node - stack = stack.down - } - stack = &element{node: node, down: stack} - } - if stack != nil { - return stack.node - } - return nil -} - -func (t *tokens32) PrintSyntaxTree(buffer string) { - t.AST().Print(buffer) -} - -func (t *tokens32) PrettyPrintSyntaxTree(buffer string) { - t.AST().PrettyPrint(buffer) -} - -func (t *tokens32) Add(rule pegRule, begin, end, index uint32) { - if tree := t.tree; int(index) >= len(tree) { - expanded := make([]token32, 2*len(tree)) - copy(expanded, tree) - t.tree = expanded - } - t.tree[index] = token32{ - pegRule: rule, - begin: begin, - end: end, - } -} - -func (t *tokens32) Tokens() []token32 { - return t.tree -} - -type tomlParser struct { - toml - - Buffer string - buffer []rune - rules [88]func() bool - parse func(rule ...int) error - reset func() - Pretty bool - tokens32 -} - -func (p *tomlParser) Parse(rule ...int) error { - return p.parse(rule...) -} - -func (p *tomlParser) Reset() { - p.reset() -} - -type textPosition struct { - line, symbol int -} - -type textPositionMap map[int]textPosition - -func translatePositions(buffer []rune, positions []int) textPositionMap { - length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0 - sort.Ints(positions) - -search: - for i, c := range buffer { - if c == '\n' { - line, symbol = line+1, 0 - } else { - symbol++ - } - if i == positions[j] { - translations[positions[j]] = textPosition{line, symbol} - for j++; j < length; j++ { - if i != positions[j] { - continue search - } - } - break search - } - } - - return translations -} - -type parseError struct { - p *tomlParser - max token32 -} - -func (e *parseError) Error() string { - tokens, error := []token32{e.max}, "\n" - positions, p := make([]int, 2*len(tokens)), 0 - for _, token := range tokens { - positions[p], p = int(token.begin), p+1 - positions[p], p = int(token.end), p+1 - } - translations := translatePositions(e.p.buffer, positions) - format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n" - if e.p.Pretty { - format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n" - } - for _, token := range tokens { - begin, end := int(token.begin), int(token.end) - error += fmt.Sprintf(format, - rul3s[token.pegRule], - translations[begin].line, translations[begin].symbol, - translations[end].line, translations[end].symbol, - strconv.Quote(string(e.p.buffer[begin:end]))) - } - - return error -} - -func (p *tomlParser) PrintSyntaxTree() { - if p.Pretty { - p.tokens32.PrettyPrintSyntaxTree(p.Buffer) - } else { - p.tokens32.PrintSyntaxTree(p.Buffer) - } -} - -func (p *tomlParser) Execute() { - buffer, _buffer, text, begin, end := p.Buffer, p.buffer, "", 0, 0 - for _, token := range p.Tokens() { - switch token.pegRule { - - case rulePegText: - begin, end = int(token.begin), int(token.end) - text = string(_buffer[begin:end]) - - case ruleAction0: - _ = buffer - case ruleAction1: - p.SetTableString(begin, end) - case ruleAction2: - p.AddLineCount(end - begin) - case ruleAction3: - p.AddLineCount(end - begin) - case ruleAction4: - p.AddKeyValue() - case ruleAction5: - p.SetKey(p.buffer, begin, end) - case ruleAction6: - p.SetKey(p.buffer, begin, end) - case ruleAction7: - p.SetTime(begin, end) - case ruleAction8: - p.SetFloat64(begin, end) - case ruleAction9: - p.SetInt64(begin, end) - case ruleAction10: - p.SetString(begin, end) - case ruleAction11: - p.SetBool(begin, end) - case ruleAction12: - p.SetArray(begin, end) - case ruleAction13: - p.SetTable(p.buffer, begin, end) - case ruleAction14: - p.SetArrayTable(p.buffer, begin, end) - case ruleAction15: - p.StartInlineTable() - case ruleAction16: - p.EndInlineTable() - case ruleAction17: - p.AddTableKey() - case ruleAction18: - p.SetBasicString(p.buffer, begin, end) - case ruleAction19: - p.SetMultilineString() - case ruleAction20: - p.AddMultilineBasicBody(p.buffer, begin, end) - case ruleAction21: - p.SetLiteralString(p.buffer, begin, end) - case ruleAction22: - p.SetMultilineLiteralString(p.buffer, begin, end) - case ruleAction23: - p.StartArray() - case ruleAction24: - p.AddArrayVal() - case ruleAction25: - p.AddArrayVal() - - } - } - _, _, _, _, _ = buffer, _buffer, text, begin, end -} - -func (p *tomlParser) Init() { - var ( - max token32 - position, tokenIndex uint32 - buffer []rune - ) - p.reset = func() { - max = token32{} - position, tokenIndex = 0, 0 - - p.buffer = []rune(p.Buffer) - if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol { - p.buffer = append(p.buffer, endSymbol) - } - buffer = p.buffer - } - p.reset() - - _rules := p.rules - tree := tokens32{tree: make([]token32, math.MaxInt16)} - p.parse = func(rule ...int) error { - r := 1 - if len(rule) > 0 { - r = rule[0] - } - matches := p.rules[r]() - p.tokens32 = tree - if matches { - p.Trim(tokenIndex) - return nil - } - return &parseError{p, max} - } - - add := func(rule pegRule, begin uint32) { - tree.Add(rule, begin, position, tokenIndex) - tokenIndex++ - if begin != position && position > max.end { - max = token32{rule, begin, position} - } - } - - matchDot := func() bool { - if buffer[position] != endSymbol { - position++ - return true - } - return false - } - - /*matchChar := func(c byte) bool { - if buffer[position] == c { - position++ - return true - } - return false - }*/ - - /*matchRange := func(lower byte, upper byte) bool { - if c := buffer[position]; c >= lower && c <= upper { - position++ - return true - } - return false - }*/ - - _rules = [...]func() bool{ - nil, - /* 0 TOML <- <(Expression (newline Expression)* newline? !. Action0)> */ - func() bool { - position0, tokenIndex0 := position, tokenIndex - { - position1 := position - if !_rules[ruleExpression]() { - goto l0 - } - l2: - { - position3, tokenIndex3 := position, tokenIndex - if !_rules[rulenewline]() { - goto l3 - } - if !_rules[ruleExpression]() { - goto l3 - } - goto l2 - l3: - position, tokenIndex = position3, tokenIndex3 - } - { - position4, tokenIndex4 := position, tokenIndex - if !_rules[rulenewline]() { - goto l4 - } - goto l5 - l4: - position, tokenIndex = position4, tokenIndex4 - } - l5: - { - position6, tokenIndex6 := position, tokenIndex - if !matchDot() { - goto l6 - } - goto l0 - l6: - position, tokenIndex = position6, tokenIndex6 - } - { - add(ruleAction0, position) - } - add(ruleTOML, position1) - } - return true - l0: - position, tokenIndex = position0, tokenIndex0 - return false - }, - /* 1 Expression <- <((<(ws table ws comment? (wsnl keyval ws comment?)*)> Action1) / (ws keyval ws comment?) / (ws comment?) / ws)> */ - func() bool { - position8, tokenIndex8 := position, tokenIndex - { - position9 := position - { - position10, tokenIndex10 := position, tokenIndex - { - position12 := position - if !_rules[rulews]() { - goto l11 - } - { - position13 := position - { - position14, tokenIndex14 := position, tokenIndex - { - position16 := position - if buffer[position] != rune('[') { - goto l15 - } - position++ - if !_rules[rulews]() { - goto l15 - } - { - position17 := position - if !_rules[ruletableKey]() { - goto l15 - } - add(rulePegText, position17) - } - if !_rules[rulews]() { - goto l15 - } - if buffer[position] != rune(']') { - goto l15 - } - position++ - { - add(ruleAction13, position) - } - add(rulestdTable, position16) - } - goto l14 - l15: - position, tokenIndex = position14, tokenIndex14 - { - position19 := position - if buffer[position] != rune('[') { - goto l11 - } - position++ - if buffer[position] != rune('[') { - goto l11 - } - position++ - if !_rules[rulews]() { - goto l11 - } - { - position20 := position - if !_rules[ruletableKey]() { - goto l11 - } - add(rulePegText, position20) - } - if !_rules[rulews]() { - goto l11 - } - if buffer[position] != rune(']') { - goto l11 - } - position++ - if buffer[position] != rune(']') { - goto l11 - } - position++ - { - add(ruleAction14, position) - } - add(rulearrayTable, position19) - } - } - l14: - add(ruletable, position13) - } - if !_rules[rulews]() { - goto l11 - } - { - position22, tokenIndex22 := position, tokenIndex - if !_rules[rulecomment]() { - goto l22 - } - goto l23 - l22: - position, tokenIndex = position22, tokenIndex22 - } - l23: - l24: - { - position25, tokenIndex25 := position, tokenIndex - if !_rules[rulewsnl]() { - goto l25 - } - if !_rules[rulekeyval]() { - goto l25 - } - if !_rules[rulews]() { - goto l25 - } - { - position26, tokenIndex26 := position, tokenIndex - if !_rules[rulecomment]() { - goto l26 - } - goto l27 - l26: - position, tokenIndex = position26, tokenIndex26 - } - l27: - goto l24 - l25: - position, tokenIndex = position25, tokenIndex25 - } - add(rulePegText, position12) - } - { - add(ruleAction1, position) - } - goto l10 - l11: - position, tokenIndex = position10, tokenIndex10 - if !_rules[rulews]() { - goto l29 - } - if !_rules[rulekeyval]() { - goto l29 - } - if !_rules[rulews]() { - goto l29 - } - { - position30, tokenIndex30 := position, tokenIndex - if !_rules[rulecomment]() { - goto l30 - } - goto l31 - l30: - position, tokenIndex = position30, tokenIndex30 - } - l31: - goto l10 - l29: - position, tokenIndex = position10, tokenIndex10 - if !_rules[rulews]() { - goto l32 - } - { - position33, tokenIndex33 := position, tokenIndex - if !_rules[rulecomment]() { - goto l33 - } - goto l34 - l33: - position, tokenIndex = position33, tokenIndex33 - } - l34: - goto l10 - l32: - position, tokenIndex = position10, tokenIndex10 - if !_rules[rulews]() { - goto l8 - } - } - l10: - add(ruleExpression, position9) - } - return true - l8: - position, tokenIndex = position8, tokenIndex8 - return false - }, - /* 2 newline <- <(<('\r' / '\n')+> Action2)> */ - func() bool { - position35, tokenIndex35 := position, tokenIndex - { - position36 := position - { - position37 := position - { - position40, tokenIndex40 := position, tokenIndex - if buffer[position] != rune('\r') { - goto l41 - } - position++ - goto l40 - l41: - position, tokenIndex = position40, tokenIndex40 - if buffer[position] != rune('\n') { - goto l35 - } - position++ - } - l40: - l38: - { - position39, tokenIndex39 := position, tokenIndex - { - position42, tokenIndex42 := position, tokenIndex - if buffer[position] != rune('\r') { - goto l43 - } - position++ - goto l42 - l43: - position, tokenIndex = position42, tokenIndex42 - if buffer[position] != rune('\n') { - goto l39 - } - position++ - } - l42: - goto l38 - l39: - position, tokenIndex = position39, tokenIndex39 - } - add(rulePegText, position37) - } - { - add(ruleAction2, position) - } - add(rulenewline, position36) - } - return true - l35: - position, tokenIndex = position35, tokenIndex35 - return false - }, - /* 3 ws <- <(' ' / '\t')*> */ - func() bool { - { - position46 := position - l47: - { - position48, tokenIndex48 := position, tokenIndex - { - position49, tokenIndex49 := position, tokenIndex - if buffer[position] != rune(' ') { - goto l50 - } - position++ - goto l49 - l50: - position, tokenIndex = position49, tokenIndex49 - if buffer[position] != rune('\t') { - goto l48 - } - position++ - } - l49: - goto l47 - l48: - position, tokenIndex = position48, tokenIndex48 - } - add(rulews, position46) - } - return true - }, - /* 4 wsnl <- <((&('\t') '\t') | (&(' ') ' ') | (&('\n' | '\r') (<('\r' / '\n')> Action3)))*> */ - func() bool { - { - position52 := position - l53: - { - position54, tokenIndex54 := position, tokenIndex - { - switch buffer[position] { - case '\t': - if buffer[position] != rune('\t') { - goto l54 - } - position++ - break - case ' ': - if buffer[position] != rune(' ') { - goto l54 - } - position++ - break - default: - { - position56 := position - { - position57, tokenIndex57 := position, tokenIndex - if buffer[position] != rune('\r') { - goto l58 - } - position++ - goto l57 - l58: - position, tokenIndex = position57, tokenIndex57 - if buffer[position] != rune('\n') { - goto l54 - } - position++ - } - l57: - add(rulePegText, position56) - } - { - add(ruleAction3, position) - } - break - } - } - - goto l53 - l54: - position, tokenIndex = position54, tokenIndex54 - } - add(rulewsnl, position52) - } - return true - }, - /* 5 comment <- <('#' <('\t' / [ -\U0010ffff])*>)> */ - func() bool { - position60, tokenIndex60 := position, tokenIndex - { - position61 := position - if buffer[position] != rune('#') { - goto l60 - } - position++ - { - position62 := position - l63: - { - position64, tokenIndex64 := position, tokenIndex - { - position65, tokenIndex65 := position, tokenIndex - if buffer[position] != rune('\t') { - goto l66 - } - position++ - goto l65 - l66: - position, tokenIndex = position65, tokenIndex65 - if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') { - goto l64 - } - position++ - } - l65: - goto l63 - l64: - position, tokenIndex = position64, tokenIndex64 - } - add(rulePegText, position62) - } - add(rulecomment, position61) - } - return true - l60: - position, tokenIndex = position60, tokenIndex60 - return false - }, - /* 6 keyval <- <(key ws '=' ws val Action4)> */ - func() bool { - position67, tokenIndex67 := position, tokenIndex - { - position68 := position - if !_rules[rulekey]() { - goto l67 - } - if !_rules[rulews]() { - goto l67 - } - if buffer[position] != rune('=') { - goto l67 - } - position++ - if !_rules[rulews]() { - goto l67 - } - if !_rules[ruleval]() { - goto l67 - } - { - add(ruleAction4, position) - } - add(rulekeyval, position68) - } - return true - l67: - position, tokenIndex = position67, tokenIndex67 - return false - }, - /* 7 key <- <(bareKey / quotedKey)> */ - func() bool { - position70, tokenIndex70 := position, tokenIndex - { - position71 := position - { - position72, tokenIndex72 := position, tokenIndex - { - position74 := position - { - position75 := position - { - switch buffer[position] { - case '_': - if buffer[position] != rune('_') { - goto l73 - } - position++ - break - case '-': - if buffer[position] != rune('-') { - goto l73 - } - position++ - break - case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': - if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l73 - } - position++ - break - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l73 - } - position++ - break - default: - if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l73 - } - position++ - break - } - } - - l76: - { - position77, tokenIndex77 := position, tokenIndex - { - switch buffer[position] { - case '_': - if buffer[position] != rune('_') { - goto l77 - } - position++ - break - case '-': - if buffer[position] != rune('-') { - goto l77 - } - position++ - break - case 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z': - if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l77 - } - position++ - break - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l77 - } - position++ - break - default: - if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l77 - } - position++ - break - } - } - - goto l76 - l77: - position, tokenIndex = position77, tokenIndex77 - } - add(rulePegText, position75) - } - { - add(ruleAction5, position) - } - add(rulebareKey, position74) - } - goto l72 - l73: - position, tokenIndex = position72, tokenIndex72 - { - position81 := position - { - position82 := position - if buffer[position] != rune('"') { - goto l70 - } - position++ - l83: - { - position84, tokenIndex84 := position, tokenIndex - if !_rules[rulebasicChar]() { - goto l84 - } - goto l83 - l84: - position, tokenIndex = position84, tokenIndex84 - } - if buffer[position] != rune('"') { - goto l70 - } - position++ - add(rulePegText, position82) - } - { - add(ruleAction6, position) - } - add(rulequotedKey, position81) - } - } - l72: - add(rulekey, position71) - } - return true - l70: - position, tokenIndex = position70, tokenIndex70 - return false - }, - /* 8 bareKey <- <(<((&('_') '_') | (&('-') '-') | (&('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z') [a-z]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z') [A-Z]))+> Action5)> */ - nil, - /* 9 quotedKey <- <(<('"' basicChar* '"')> Action6)> */ - nil, - /* 10 val <- <(( Action7) / ( Action8) / ((&('{') inlineTable) | (&('[') ( Action12)) | (&('f' | 't') ( Action11)) | (&('"' | '\'') ( Action10)) | (&('+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') ( Action9))))> */ - func() bool { - position88, tokenIndex88 := position, tokenIndex - { - position89 := position - { - position90, tokenIndex90 := position, tokenIndex - { - position92 := position - { - position93 := position - { - position94, tokenIndex94 := position, tokenIndex - { - position96 := position - { - position97 := position - { - position98 := position - if !_rules[ruledigitDual]() { - goto l95 - } - if !_rules[ruledigitDual]() { - goto l95 - } - add(ruledigitQuad, position98) - } - add(ruledateFullYear, position97) - } - if buffer[position] != rune('-') { - goto l95 - } - position++ - { - position99 := position - if !_rules[ruledigitDual]() { - goto l95 - } - add(ruledateMonth, position99) - } - if buffer[position] != rune('-') { - goto l95 - } - position++ - { - position100 := position - if !_rules[ruledigitDual]() { - goto l95 - } - add(ruledateMDay, position100) - } - add(rulefullDate, position96) - } - { - position101, tokenIndex101 := position, tokenIndex - if buffer[position] != rune('T') { - goto l101 - } - position++ - { - position103 := position - if !_rules[rulepartialTime]() { - goto l101 - } - { - position104 := position - { - position105, tokenIndex105 := position, tokenIndex - if buffer[position] != rune('Z') { - goto l106 - } - position++ - goto l105 - l106: - position, tokenIndex = position105, tokenIndex105 - { - position107 := position - { - position108, tokenIndex108 := position, tokenIndex - if buffer[position] != rune('-') { - goto l109 - } - position++ - goto l108 - l109: - position, tokenIndex = position108, tokenIndex108 - if buffer[position] != rune('+') { - goto l101 - } - position++ - } - l108: - if !_rules[ruletimeHour]() { - goto l101 - } - if buffer[position] != rune(':') { - goto l101 - } - position++ - if !_rules[ruletimeMinute]() { - goto l101 - } - add(ruletimeNumoffset, position107) - } - } - l105: - add(ruletimeOffset, position104) - } - add(rulefullTime, position103) - } - goto l102 - l101: - position, tokenIndex = position101, tokenIndex101 - } - l102: - goto l94 - l95: - position, tokenIndex = position94, tokenIndex94 - if !_rules[rulepartialTime]() { - goto l91 - } - } - l94: - add(ruledatetime, position93) - } - add(rulePegText, position92) - } - { - add(ruleAction7, position) - } - goto l90 - l91: - position, tokenIndex = position90, tokenIndex90 - { - position112 := position - { - position113 := position - if !_rules[ruleinteger]() { - goto l111 - } - { - position114, tokenIndex114 := position, tokenIndex - if !_rules[rulefrac]() { - goto l115 - } - { - position116, tokenIndex116 := position, tokenIndex - if !_rules[ruleexp]() { - goto l116 - } - goto l117 - l116: - position, tokenIndex = position116, tokenIndex116 - } - l117: - goto l114 - l115: - position, tokenIndex = position114, tokenIndex114 - { - position118, tokenIndex118 := position, tokenIndex - if !_rules[rulefrac]() { - goto l118 - } - goto l119 - l118: - position, tokenIndex = position118, tokenIndex118 - } - l119: - if !_rules[ruleexp]() { - goto l111 - } - } - l114: - add(rulefloat, position113) - } - add(rulePegText, position112) - } - { - add(ruleAction8, position) - } - goto l90 - l111: - position, tokenIndex = position90, tokenIndex90 - { - switch buffer[position] { - case '{': - { - position122 := position - if buffer[position] != rune('{') { - goto l88 - } - position++ - { - add(ruleAction15, position) - } - if !_rules[rulews]() { - goto l88 - } - { - position124 := position - l125: - { - position126, tokenIndex126 := position, tokenIndex - if !_rules[rulekeyval]() { - goto l126 - } - { - position127, tokenIndex127 := position, tokenIndex - { - position129 := position - if !_rules[rulews]() { - goto l127 - } - if buffer[position] != rune(',') { - goto l127 - } - position++ - if !_rules[rulews]() { - goto l127 - } - add(ruleinlineTableValSep, position129) - } - goto l128 - l127: - position, tokenIndex = position127, tokenIndex127 - } - l128: - goto l125 - l126: - position, tokenIndex = position126, tokenIndex126 - } - add(ruleinlineTableKeyValues, position124) - } - if !_rules[rulews]() { - goto l88 - } - if buffer[position] != rune('}') { - goto l88 - } - position++ - { - add(ruleAction16, position) - } - add(ruleinlineTable, position122) - } - break - case '[': - { - position131 := position - { - position132 := position - if buffer[position] != rune('[') { - goto l88 - } - position++ - { - add(ruleAction23, position) - } - if !_rules[rulewsnl]() { - goto l88 - } - { - position134, tokenIndex134 := position, tokenIndex - { - position136 := position - if !_rules[ruleval]() { - goto l134 - } - { - add(ruleAction24, position) - } - l138: - { - position139, tokenIndex139 := position, tokenIndex - if !_rules[rulewsnl]() { - goto l139 - } - { - position140, tokenIndex140 := position, tokenIndex - if !_rules[rulecomment]() { - goto l140 - } - goto l141 - l140: - position, tokenIndex = position140, tokenIndex140 - } - l141: - if !_rules[rulewsnl]() { - goto l139 - } - if !_rules[rulearraySep]() { - goto l139 - } - if !_rules[rulewsnl]() { - goto l139 - } - { - position142, tokenIndex142 := position, tokenIndex - if !_rules[rulecomment]() { - goto l142 - } - goto l143 - l142: - position, tokenIndex = position142, tokenIndex142 - } - l143: - if !_rules[rulewsnl]() { - goto l139 - } - if !_rules[ruleval]() { - goto l139 - } - { - add(ruleAction25, position) - } - goto l138 - l139: - position, tokenIndex = position139, tokenIndex139 - } - if !_rules[rulewsnl]() { - goto l134 - } - { - position145, tokenIndex145 := position, tokenIndex - if !_rules[rulearraySep]() { - goto l145 - } - goto l146 - l145: - position, tokenIndex = position145, tokenIndex145 - } - l146: - if !_rules[rulewsnl]() { - goto l134 - } - { - position147, tokenIndex147 := position, tokenIndex - if !_rules[rulecomment]() { - goto l147 - } - goto l148 - l147: - position, tokenIndex = position147, tokenIndex147 - } - l148: - add(rulearrayValues, position136) - } - goto l135 - l134: - position, tokenIndex = position134, tokenIndex134 - } - l135: - if !_rules[rulewsnl]() { - goto l88 - } - if buffer[position] != rune(']') { - goto l88 - } - position++ - add(rulearray, position132) - } - add(rulePegText, position131) - } - { - add(ruleAction12, position) - } - break - case 'f', 't': - { - position150 := position - { - position151 := position - { - position152, tokenIndex152 := position, tokenIndex - if buffer[position] != rune('t') { - goto l153 - } - position++ - if buffer[position] != rune('r') { - goto l153 - } - position++ - if buffer[position] != rune('u') { - goto l153 - } - position++ - if buffer[position] != rune('e') { - goto l153 - } - position++ - goto l152 - l153: - position, tokenIndex = position152, tokenIndex152 - if buffer[position] != rune('f') { - goto l88 - } - position++ - if buffer[position] != rune('a') { - goto l88 - } - position++ - if buffer[position] != rune('l') { - goto l88 - } - position++ - if buffer[position] != rune('s') { - goto l88 - } - position++ - if buffer[position] != rune('e') { - goto l88 - } - position++ - } - l152: - add(ruleboolean, position151) - } - add(rulePegText, position150) - } - { - add(ruleAction11, position) - } - break - case '"', '\'': - { - position155 := position - { - position156 := position - { - position157, tokenIndex157 := position, tokenIndex - { - position159 := position - if buffer[position] != rune('\'') { - goto l158 - } - position++ - if buffer[position] != rune('\'') { - goto l158 - } - position++ - if buffer[position] != rune('\'') { - goto l158 - } - position++ - { - position160 := position - { - position161 := position - l162: - { - position163, tokenIndex163 := position, tokenIndex - { - position164, tokenIndex164 := position, tokenIndex - if buffer[position] != rune('\'') { - goto l164 - } - position++ - if buffer[position] != rune('\'') { - goto l164 - } - position++ - if buffer[position] != rune('\'') { - goto l164 - } - position++ - goto l163 - l164: - position, tokenIndex = position164, tokenIndex164 - } - { - position165, tokenIndex165 := position, tokenIndex - { - position167 := position - { - position168, tokenIndex168 := position, tokenIndex - if buffer[position] != rune('\t') { - goto l169 - } - position++ - goto l168 - l169: - position, tokenIndex = position168, tokenIndex168 - if c := buffer[position]; c < rune(' ') || c > rune('\U0010ffff') { - goto l166 - } - position++ - } - l168: - add(rulemlLiteralChar, position167) - } - goto l165 - l166: - position, tokenIndex = position165, tokenIndex165 - if !_rules[rulenewline]() { - goto l163 - } - } - l165: - goto l162 - l163: - position, tokenIndex = position163, tokenIndex163 - } - add(rulemlLiteralBody, position161) - } - add(rulePegText, position160) - } - if buffer[position] != rune('\'') { - goto l158 - } - position++ - if buffer[position] != rune('\'') { - goto l158 - } - position++ - if buffer[position] != rune('\'') { - goto l158 - } - position++ - { - add(ruleAction22, position) - } - add(rulemlLiteralString, position159) - } - goto l157 - l158: - position, tokenIndex = position157, tokenIndex157 - { - position172 := position - if buffer[position] != rune('\'') { - goto l171 - } - position++ - { - position173 := position - l174: - { - position175, tokenIndex175 := position, tokenIndex - { - position176 := position - { - switch buffer[position] { - case '\t': - if buffer[position] != rune('\t') { - goto l175 - } - position++ - break - case ' ', '!', '"', '#', '$', '%', '&': - if c := buffer[position]; c < rune(' ') || c > rune('&') { - goto l175 - } - position++ - break - default: - if c := buffer[position]; c < rune('(') || c > rune('\U0010ffff') { - goto l175 - } - position++ - break - } - } - - add(ruleliteralChar, position176) - } - goto l174 - l175: - position, tokenIndex = position175, tokenIndex175 - } - add(rulePegText, position173) - } - if buffer[position] != rune('\'') { - goto l171 - } - position++ - { - add(ruleAction21, position) - } - add(ruleliteralString, position172) - } - goto l157 - l171: - position, tokenIndex = position157, tokenIndex157 - { - position180 := position - if buffer[position] != rune('"') { - goto l179 - } - position++ - if buffer[position] != rune('"') { - goto l179 - } - position++ - if buffer[position] != rune('"') { - goto l179 - } - position++ - { - position181 := position - l182: - { - position183, tokenIndex183 := position, tokenIndex - { - position184, tokenIndex184 := position, tokenIndex - { - position186 := position - { - position187, tokenIndex187 := position, tokenIndex - if !_rules[rulebasicChar]() { - goto l188 - } - goto l187 - l188: - position, tokenIndex = position187, tokenIndex187 - if !_rules[rulenewline]() { - goto l185 - } - } - l187: - add(rulePegText, position186) - } - { - add(ruleAction20, position) - } - goto l184 - l185: - position, tokenIndex = position184, tokenIndex184 - if !_rules[ruleescape]() { - goto l183 - } - if !_rules[rulenewline]() { - goto l183 - } - if !_rules[rulewsnl]() { - goto l183 - } - } - l184: - goto l182 - l183: - position, tokenIndex = position183, tokenIndex183 - } - add(rulemlBasicBody, position181) - } - if buffer[position] != rune('"') { - goto l179 - } - position++ - if buffer[position] != rune('"') { - goto l179 - } - position++ - if buffer[position] != rune('"') { - goto l179 - } - position++ - { - add(ruleAction19, position) - } - add(rulemlBasicString, position180) - } - goto l157 - l179: - position, tokenIndex = position157, tokenIndex157 - { - position191 := position - { - position192 := position - if buffer[position] != rune('"') { - goto l88 - } - position++ - l193: - { - position194, tokenIndex194 := position, tokenIndex - if !_rules[rulebasicChar]() { - goto l194 - } - goto l193 - l194: - position, tokenIndex = position194, tokenIndex194 - } - if buffer[position] != rune('"') { - goto l88 - } - position++ - add(rulePegText, position192) - } - { - add(ruleAction18, position) - } - add(rulebasicString, position191) - } - } - l157: - add(rulestring, position156) - } - add(rulePegText, position155) - } - { - add(ruleAction10, position) - } - break - default: - { - position197 := position - if !_rules[ruleinteger]() { - goto l88 - } - add(rulePegText, position197) - } - { - add(ruleAction9, position) - } - break - } - } - - } - l90: - add(ruleval, position89) - } - return true - l88: - position, tokenIndex = position88, tokenIndex88 - return false - }, - /* 11 table <- <(stdTable / arrayTable)> */ - nil, - /* 12 stdTable <- <('[' ws ws ']' Action13)> */ - nil, - /* 13 arrayTable <- <('[' '[' ws ws (']' ']') Action14)> */ - nil, - /* 14 inlineTable <- <('{' Action15 ws inlineTableKeyValues ws '}' Action16)> */ - nil, - /* 15 inlineTableKeyValues <- <(keyval inlineTableValSep?)*> */ - nil, - /* 16 tableKey <- <(tableKeyComp (tableKeySep tableKeyComp)*)> */ - func() bool { - position204, tokenIndex204 := position, tokenIndex - { - position205 := position - if !_rules[ruletableKeyComp]() { - goto l204 - } - l206: - { - position207, tokenIndex207 := position, tokenIndex - { - position208 := position - if !_rules[rulews]() { - goto l207 - } - if buffer[position] != rune('.') { - goto l207 - } - position++ - if !_rules[rulews]() { - goto l207 - } - add(ruletableKeySep, position208) - } - if !_rules[ruletableKeyComp]() { - goto l207 - } - goto l206 - l207: - position, tokenIndex = position207, tokenIndex207 - } - add(ruletableKey, position205) - } - return true - l204: - position, tokenIndex = position204, tokenIndex204 - return false - }, - /* 17 tableKeyComp <- <(key Action17)> */ - func() bool { - position209, tokenIndex209 := position, tokenIndex - { - position210 := position - if !_rules[rulekey]() { - goto l209 - } - { - add(ruleAction17, position) - } - add(ruletableKeyComp, position210) - } - return true - l209: - position, tokenIndex = position209, tokenIndex209 - return false - }, - /* 18 tableKeySep <- <(ws '.' ws)> */ - nil, - /* 19 inlineTableValSep <- <(ws ',' ws)> */ - nil, - /* 20 integer <- <(('-' / '+')? int)> */ - func() bool { - position214, tokenIndex214 := position, tokenIndex - { - position215 := position - { - position216, tokenIndex216 := position, tokenIndex - { - position218, tokenIndex218 := position, tokenIndex - if buffer[position] != rune('-') { - goto l219 - } - position++ - goto l218 - l219: - position, tokenIndex = position218, tokenIndex218 - if buffer[position] != rune('+') { - goto l216 - } - position++ - } - l218: - goto l217 - l216: - position, tokenIndex = position216, tokenIndex216 - } - l217: - { - position220 := position - { - position221, tokenIndex221 := position, tokenIndex - if c := buffer[position]; c < rune('1') || c > rune('9') { - goto l222 - } - position++ - { - position225, tokenIndex225 := position, tokenIndex - if !_rules[ruledigit]() { - goto l226 - } - goto l225 - l226: - position, tokenIndex = position225, tokenIndex225 - if buffer[position] != rune('_') { - goto l222 - } - position++ - if !_rules[ruledigit]() { - goto l222 - } - } - l225: - l223: - { - position224, tokenIndex224 := position, tokenIndex - { - position227, tokenIndex227 := position, tokenIndex - if !_rules[ruledigit]() { - goto l228 - } - goto l227 - l228: - position, tokenIndex = position227, tokenIndex227 - if buffer[position] != rune('_') { - goto l224 - } - position++ - if !_rules[ruledigit]() { - goto l224 - } - } - l227: - goto l223 - l224: - position, tokenIndex = position224, tokenIndex224 - } - goto l221 - l222: - position, tokenIndex = position221, tokenIndex221 - if !_rules[ruledigit]() { - goto l214 - } - } - l221: - add(ruleint, position220) - } - add(ruleinteger, position215) - } - return true - l214: - position, tokenIndex = position214, tokenIndex214 - return false - }, - /* 21 int <- <(([1-9] (digit / ('_' digit))+) / digit)> */ - nil, - /* 22 float <- <(integer ((frac exp?) / (frac? exp)))> */ - nil, - /* 23 frac <- <('.' digit (digit / ('_' digit))*)> */ - func() bool { - position231, tokenIndex231 := position, tokenIndex - { - position232 := position - if buffer[position] != rune('.') { - goto l231 - } - position++ - if !_rules[ruledigit]() { - goto l231 - } - l233: - { - position234, tokenIndex234 := position, tokenIndex - { - position235, tokenIndex235 := position, tokenIndex - if !_rules[ruledigit]() { - goto l236 - } - goto l235 - l236: - position, tokenIndex = position235, tokenIndex235 - if buffer[position] != rune('_') { - goto l234 - } - position++ - if !_rules[ruledigit]() { - goto l234 - } - } - l235: - goto l233 - l234: - position, tokenIndex = position234, tokenIndex234 - } - add(rulefrac, position232) - } - return true - l231: - position, tokenIndex = position231, tokenIndex231 - return false - }, - /* 24 exp <- <(('e' / 'E') ('-' / '+')? digit (digit / ('_' digit))*)> */ - func() bool { - position237, tokenIndex237 := position, tokenIndex - { - position238 := position - { - position239, tokenIndex239 := position, tokenIndex - if buffer[position] != rune('e') { - goto l240 - } - position++ - goto l239 - l240: - position, tokenIndex = position239, tokenIndex239 - if buffer[position] != rune('E') { - goto l237 - } - position++ - } - l239: - { - position241, tokenIndex241 := position, tokenIndex - { - position243, tokenIndex243 := position, tokenIndex - if buffer[position] != rune('-') { - goto l244 - } - position++ - goto l243 - l244: - position, tokenIndex = position243, tokenIndex243 - if buffer[position] != rune('+') { - goto l241 - } - position++ - } - l243: - goto l242 - l241: - position, tokenIndex = position241, tokenIndex241 - } - l242: - if !_rules[ruledigit]() { - goto l237 - } - l245: - { - position246, tokenIndex246 := position, tokenIndex - { - position247, tokenIndex247 := position, tokenIndex - if !_rules[ruledigit]() { - goto l248 - } - goto l247 - l248: - position, tokenIndex = position247, tokenIndex247 - if buffer[position] != rune('_') { - goto l246 - } - position++ - if !_rules[ruledigit]() { - goto l246 - } - } - l247: - goto l245 - l246: - position, tokenIndex = position246, tokenIndex246 - } - add(ruleexp, position238) - } - return true - l237: - position, tokenIndex = position237, tokenIndex237 - return false - }, - /* 25 string <- <(mlLiteralString / literalString / mlBasicString / basicString)> */ - nil, - /* 26 basicString <- <(<('"' basicChar* '"')> Action18)> */ - nil, - /* 27 basicChar <- <(basicUnescaped / escaped)> */ - func() bool { - position251, tokenIndex251 := position, tokenIndex - { - position252 := position - { - position253, tokenIndex253 := position, tokenIndex - { - position255 := position - { - switch buffer[position] { - case ' ', '!': - if c := buffer[position]; c < rune(' ') || c > rune('!') { - goto l254 - } - position++ - break - case '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[': - if c := buffer[position]; c < rune('#') || c > rune('[') { - goto l254 - } - position++ - break - default: - if c := buffer[position]; c < rune(']') || c > rune('\U0010ffff') { - goto l254 - } - position++ - break - } - } - - add(rulebasicUnescaped, position255) - } - goto l253 - l254: - position, tokenIndex = position253, tokenIndex253 - { - position257 := position - if !_rules[ruleescape]() { - goto l251 - } - { - switch buffer[position] { - case 'U': - if buffer[position] != rune('U') { - goto l251 - } - position++ - if !_rules[rulehexQuad]() { - goto l251 - } - if !_rules[rulehexQuad]() { - goto l251 - } - break - case 'u': - if buffer[position] != rune('u') { - goto l251 - } - position++ - if !_rules[rulehexQuad]() { - goto l251 - } - break - case '\\': - if buffer[position] != rune('\\') { - goto l251 - } - position++ - break - case '/': - if buffer[position] != rune('/') { - goto l251 - } - position++ - break - case '"': - if buffer[position] != rune('"') { - goto l251 - } - position++ - break - case 'r': - if buffer[position] != rune('r') { - goto l251 - } - position++ - break - case 'f': - if buffer[position] != rune('f') { - goto l251 - } - position++ - break - case 'n': - if buffer[position] != rune('n') { - goto l251 - } - position++ - break - case 't': - if buffer[position] != rune('t') { - goto l251 - } - position++ - break - default: - if buffer[position] != rune('b') { - goto l251 - } - position++ - break - } - } - - add(ruleescaped, position257) - } - } - l253: - add(rulebasicChar, position252) - } - return true - l251: - position, tokenIndex = position251, tokenIndex251 - return false - }, - /* 28 escaped <- <(escape ((&('U') ('U' hexQuad hexQuad)) | (&('u') ('u' hexQuad)) | (&('\\') '\\') | (&('/') '/') | (&('"') '"') | (&('r') 'r') | (&('f') 'f') | (&('n') 'n') | (&('t') 't') | (&('b') 'b')))> */ - nil, - /* 29 basicUnescaped <- <((&(' ' | '!') [ -!]) | (&('#' | '$' | '%' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[') [#-[]) | (&(']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | 'Â¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Ã' | 'Â' | 'Ã' | 'Ä' | 'Ã…' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'ÃŒ' | 'Ã' | 'ÃŽ' | 'Ã' | 'Ã' | 'Ñ' | 'Ã’' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ã' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'Ã¥' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') []-\U0010ffff]))> */ - nil, - /* 30 escape <- <'\\'> */ - func() bool { - position261, tokenIndex261 := position, tokenIndex - { - position262 := position - if buffer[position] != rune('\\') { - goto l261 - } - position++ - add(ruleescape, position262) - } - return true - l261: - position, tokenIndex = position261, tokenIndex261 - return false - }, - /* 31 mlBasicString <- <('"' '"' '"' mlBasicBody ('"' '"' '"') Action19)> */ - nil, - /* 32 mlBasicBody <- <((<(basicChar / newline)> Action20) / (escape newline wsnl))*> */ - nil, - /* 33 literalString <- <('\'' '\'' Action21)> */ - nil, - /* 34 literalChar <- <((&('\t') '\t') | (&(' ' | '!' | '"' | '#' | '$' | '%' | '&') [ -&]) | (&('(' | ')' | '*' | '+' | ',' | '-' | '.' | '/' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | ':' | ';' | '<' | '=' | '>' | '?' | '@' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '[' | '\\' | ']' | '^' | '_' | '`' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '{' | '|' | '}' | '~' | '\u007f' | '\u0080' | '\u0081' | '\u0082' | '\u0083' | '\u0084' | '\u0085' | '\u0086' | '\u0087' | '\u0088' | '\u0089' | '\u008a' | '\u008b' | '\u008c' | '\u008d' | '\u008e' | '\u008f' | '\u0090' | '\u0091' | '\u0092' | '\u0093' | '\u0094' | '\u0095' | '\u0096' | '\u0097' | '\u0098' | '\u0099' | '\u009a' | '\u009b' | '\u009c' | '\u009d' | '\u009e' | '\u009f' | '\u00a0' | '¡' | '¢' | '£' | '¤' | 'Â¥' | '¦' | '§' | '¨' | '©' | 'ª' | '«' | '¬' | '\u00ad' | '®' | '¯' | '°' | '±' | '²' | '³' | '´' | 'µ' | '¶' | '·' | '¸' | '¹' | 'º' | '»' | '¼' | '½' | '¾' | '¿' | 'À' | 'Ã' | 'Â' | 'Ã' | 'Ä' | 'Ã…' | 'Æ' | 'Ç' | 'È' | 'É' | 'Ê' | 'Ë' | 'ÃŒ' | 'Ã' | 'ÃŽ' | 'Ã' | 'Ã' | 'Ñ' | 'Ã’' | 'Ó' | 'Ô' | 'Õ' | 'Ö' | '×' | 'Ø' | 'Ù' | 'Ú' | 'Û' | 'Ü' | 'Ã' | 'Þ' | 'ß' | 'à' | 'á' | 'â' | 'ã' | 'ä' | 'Ã¥' | 'æ' | 'ç' | 'è' | 'é' | 'ê' | 'ë' | 'ì' | 'í' | 'î' | 'ï' | 'ð' | 'ñ' | 'ò' | 'ó' | 'ô' | 'õ' | 'ö' | '÷' | 'ø' | 'ù' | 'ú' | 'û' | 'ü' | 'ý' | 'þ' | 'ÿ') [(-\U0010ffff]))> */ - nil, - /* 35 mlLiteralString <- <('\'' '\'' '\'' ('\'' '\'' '\'') Action22)> */ - nil, - /* 36 mlLiteralBody <- <(!('\'' '\'' '\'') (mlLiteralChar / newline))*> */ - nil, - /* 37 mlLiteralChar <- <('\t' / [ -\U0010ffff])> */ - nil, - /* 38 hexdigit <- <((&('a' | 'b' | 'c' | 'd' | 'e' | 'f') [a-f]) | (&('A' | 'B' | 'C' | 'D' | 'E' | 'F') [A-F]) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') [0-9]))> */ - func() bool { - position270, tokenIndex270 := position, tokenIndex - { - position271 := position - { - switch buffer[position] { - case 'a', 'b', 'c', 'd', 'e', 'f': - if c := buffer[position]; c < rune('a') || c > rune('f') { - goto l270 - } - position++ - break - case 'A', 'B', 'C', 'D', 'E', 'F': - if c := buffer[position]; c < rune('A') || c > rune('F') { - goto l270 - } - position++ - break - default: - if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l270 - } - position++ - break - } - } - - add(rulehexdigit, position271) - } - return true - l270: - position, tokenIndex = position270, tokenIndex270 - return false - }, - /* 39 hexQuad <- <(hexdigit hexdigit hexdigit hexdigit)> */ - func() bool { - position273, tokenIndex273 := position, tokenIndex - { - position274 := position - if !_rules[rulehexdigit]() { - goto l273 - } - if !_rules[rulehexdigit]() { - goto l273 - } - if !_rules[rulehexdigit]() { - goto l273 - } - if !_rules[rulehexdigit]() { - goto l273 - } - add(rulehexQuad, position274) - } - return true - l273: - position, tokenIndex = position273, tokenIndex273 - return false - }, - /* 40 boolean <- <(('t' 'r' 'u' 'e') / ('f' 'a' 'l' 's' 'e'))> */ - nil, - /* 41 dateFullYear <- */ - nil, - /* 42 dateMonth <- */ - nil, - /* 43 dateMDay <- */ - nil, - /* 44 timeHour <- */ - func() bool { - position279, tokenIndex279 := position, tokenIndex - { - position280 := position - if !_rules[ruledigitDual]() { - goto l279 - } - add(ruletimeHour, position280) - } - return true - l279: - position, tokenIndex = position279, tokenIndex279 - return false - }, - /* 45 timeMinute <- */ - func() bool { - position281, tokenIndex281 := position, tokenIndex - { - position282 := position - if !_rules[ruledigitDual]() { - goto l281 - } - add(ruletimeMinute, position282) - } - return true - l281: - position, tokenIndex = position281, tokenIndex281 - return false - }, - /* 46 timeSecond <- */ - nil, - /* 47 timeSecfrac <- <('.' digit+)> */ - nil, - /* 48 timeNumoffset <- <(('-' / '+') timeHour ':' timeMinute)> */ - nil, - /* 49 timeOffset <- <('Z' / timeNumoffset)> */ - nil, - /* 50 partialTime <- <(timeHour ':' timeMinute ':' timeSecond timeSecfrac?)> */ - func() bool { - position287, tokenIndex287 := position, tokenIndex - { - position288 := position - if !_rules[ruletimeHour]() { - goto l287 - } - if buffer[position] != rune(':') { - goto l287 - } - position++ - if !_rules[ruletimeMinute]() { - goto l287 - } - if buffer[position] != rune(':') { - goto l287 - } - position++ - { - position289 := position - if !_rules[ruledigitDual]() { - goto l287 - } - add(ruletimeSecond, position289) - } - { - position290, tokenIndex290 := position, tokenIndex - { - position292 := position - if buffer[position] != rune('.') { - goto l290 - } - position++ - if !_rules[ruledigit]() { - goto l290 - } - l293: - { - position294, tokenIndex294 := position, tokenIndex - if !_rules[ruledigit]() { - goto l294 - } - goto l293 - l294: - position, tokenIndex = position294, tokenIndex294 - } - add(ruletimeSecfrac, position292) - } - goto l291 - l290: - position, tokenIndex = position290, tokenIndex290 - } - l291: - add(rulepartialTime, position288) - } - return true - l287: - position, tokenIndex = position287, tokenIndex287 - return false - }, - /* 51 fullDate <- <(dateFullYear '-' dateMonth '-' dateMDay)> */ - nil, - /* 52 fullTime <- <(partialTime timeOffset)> */ - nil, - /* 53 datetime <- <((fullDate ('T' fullTime)?) / partialTime)> */ - nil, - /* 54 digit <- <[0-9]> */ - func() bool { - position298, tokenIndex298 := position, tokenIndex - { - position299 := position - if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l298 - } - position++ - add(ruledigit, position299) - } - return true - l298: - position, tokenIndex = position298, tokenIndex298 - return false - }, - /* 55 digitDual <- <(digit digit)> */ - func() bool { - position300, tokenIndex300 := position, tokenIndex - { - position301 := position - if !_rules[ruledigit]() { - goto l300 - } - if !_rules[ruledigit]() { - goto l300 - } - add(ruledigitDual, position301) - } - return true - l300: - position, tokenIndex = position300, tokenIndex300 - return false - }, - /* 56 digitQuad <- <(digitDual digitDual)> */ - nil, - /* 57 array <- <('[' Action23 wsnl arrayValues? wsnl ']')> */ - nil, - /* 58 arrayValues <- <(val Action24 (wsnl comment? wsnl arraySep wsnl comment? wsnl val Action25)* wsnl arraySep? wsnl comment?)> */ - nil, - /* 59 arraySep <- <','> */ - func() bool { - position305, tokenIndex305 := position, tokenIndex - { - position306 := position - if buffer[position] != rune(',') { - goto l305 - } - position++ - add(rulearraySep, position306) - } - return true - l305: - position, tokenIndex = position305, tokenIndex305 - return false - }, - /* 61 Action0 <- <{ _ = buffer }> */ - nil, - nil, - /* 63 Action1 <- <{ p.SetTableString(begin, end) }> */ - nil, - /* 64 Action2 <- <{ p.AddLineCount(end - begin) }> */ - nil, - /* 65 Action3 <- <{ p.AddLineCount(end - begin) }> */ - nil, - /* 66 Action4 <- <{ p.AddKeyValue() }> */ - nil, - /* 67 Action5 <- <{ p.SetKey(p.buffer, begin, end) }> */ - nil, - /* 68 Action6 <- <{ p.SetKey(p.buffer, begin, end) }> */ - nil, - /* 69 Action7 <- <{ p.SetTime(begin, end) }> */ - nil, - /* 70 Action8 <- <{ p.SetFloat64(begin, end) }> */ - nil, - /* 71 Action9 <- <{ p.SetInt64(begin, end) }> */ - nil, - /* 72 Action10 <- <{ p.SetString(begin, end) }> */ - nil, - /* 73 Action11 <- <{ p.SetBool(begin, end) }> */ - nil, - /* 74 Action12 <- <{ p.SetArray(begin, end) }> */ - nil, - /* 75 Action13 <- <{ p.SetTable(p.buffer, begin, end) }> */ - nil, - /* 76 Action14 <- <{ p.SetArrayTable(p.buffer, begin, end) }> */ - nil, - /* 77 Action15 <- <{ p.StartInlineTable() }> */ - nil, - /* 78 Action16 <- <{ p.EndInlineTable() }> */ - nil, - /* 79 Action17 <- <{ p.AddTableKey() }> */ - nil, - /* 80 Action18 <- <{ p.SetBasicString(p.buffer, begin, end) }> */ - nil, - /* 81 Action19 <- <{ p.SetMultilineString() }> */ - nil, - /* 82 Action20 <- <{ p.AddMultilineBasicBody(p.buffer, begin, end) }> */ - nil, - /* 83 Action21 <- <{ p.SetLiteralString(p.buffer, begin, end) }> */ - nil, - /* 84 Action22 <- <{ p.SetMultilineLiteralString(p.buffer, begin, end) }> */ - nil, - /* 85 Action23 <- <{ p.StartArray() }> */ - nil, - /* 86 Action24 <- <{ p.AddArrayVal() }> */ - nil, - /* 87 Action25 <- <{ p.AddArrayVal() }> */ - nil, - } - p.rules = _rules -} diff --git a/vendor/github.com/naoina/toml/util.go b/vendor/github.com/naoina/toml/util.go deleted file mode 100644 index f882f4e5f..000000000 --- a/vendor/github.com/naoina/toml/util.go +++ /dev/null @@ -1,65 +0,0 @@ -package toml - -import ( - "fmt" - "reflect" - "strings" -) - -const fieldTagName = "toml" - -// fieldCache maps normalized field names to their position in a struct. -type fieldCache struct { - named map[string]fieldInfo // fields with an explicit name in tag - auto map[string]fieldInfo // fields with auto-assigned normalized names -} - -type fieldInfo struct { - index []int - name string - ignored bool -} - -func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache { - named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo) - for i := 0; i < rt.NumField(); i++ { - ft := rt.Field(i) - // skip unexported fields - if ft.PkgPath != "" && !ft.Anonymous { - continue - } - col, _ := extractTag(ft.Tag.Get(fieldTagName)) - info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"} - if col == "" || col == "-" { - auto[cfg.NormFieldName(rt, ft.Name)] = info - } else { - named[col] = info - } - } - return fieldCache{named, auto} -} - -func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) { - info, found := fc.named[name] - if !found { - info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)] - } - if !found { - if cfg.MissingField == nil { - return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type()) - } else { - return reflect.Value{}, "", cfg.MissingField(rv.Type(), name) - } - } else if info.ignored { - return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type()) - } - return rv.FieldByIndex(info.index), info.name, nil -} - -func extractTag(tag string) (col, rest string) { - tags := strings.SplitN(tag, ",", 2) - if len(tags) == 2 { - return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1]) - } - return strings.TrimSpace(tags[0]), "" -} diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table.go b/vendor/github.com/onsi/ginkgo/extensions/table/table.go deleted file mode 100644 index ae8ab7d24..000000000 --- a/vendor/github.com/onsi/ginkgo/extensions/table/table.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - -Table provides a simple DSL for Ginkgo-native Table-Driven Tests - -The godoc documentation describes Table's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo#table-driven-tests - -*/ - -package table - -import ( - "fmt" - "reflect" - - "github.com/onsi/ginkgo" -) - -/* -DescribeTable describes a table-driven test. - -For example: - - DescribeTable("a simple table", - func(x int, y int, expected bool) { - Ω(x > y).Should(Equal(expected)) - }, - Entry("x > y", 1, 0, true), - Entry("x == y", 0, 0, false), - Entry("x < y", 0, 1, false), - ) - -The first argument to `DescribeTable` is a string description. -The second argument is a function that will be run for each table entry. Your assertions go here - the function is equivalent to a Ginkgo It. -The subsequent arguments must be of type `TableEntry`. We recommend using the `Entry` convenience constructors. - -The `Entry` constructor takes a string description followed by an arbitrary set of parameters. These parameters are passed into your function. - -Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each `Entry` is turned into an `It` within the `Describe`. - -It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run). - -Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable. -*/ -func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, false) - return true -} - -/* -You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`. -*/ -func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, true) - return true -} - -/* -You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`. -*/ -func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) - return true -} - -/* -You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`. -*/ -func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) - return true -} - -func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) { - itBodyValue := reflect.ValueOf(itBody) - if itBodyValue.Kind() != reflect.Func { - panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody)) - } - - if pending { - ginkgo.PDescribe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } else if focused { - ginkgo.FDescribe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } else { - ginkgo.Describe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } -} diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go deleted file mode 100644 index 5fa645bce..000000000 --- a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go +++ /dev/null @@ -1,81 +0,0 @@ -package table - -import ( - "reflect" - - "github.com/onsi/ginkgo" -) - -/* -TableEntry represents an entry in a table test. You generally use the `Entry` constructor. -*/ -type TableEntry struct { - Description string - Parameters []interface{} - Pending bool - Focused bool -} - -func (t TableEntry) generateIt(itBody reflect.Value) { - if t.Pending { - ginkgo.PIt(t.Description) - return - } - - values := []reflect.Value{} - for i, param := range t.Parameters { - var value reflect.Value - - if param == nil { - inType := itBody.Type().In(i) - value = reflect.Zero(inType) - } else { - value = reflect.ValueOf(param) - } - - values = append(values, value) - } - - body := func() { - itBody.Call(values) - } - - if t.Focused { - ginkgo.FIt(t.Description, body) - } else { - ginkgo.It(t.Description, body) - } -} - -/* -Entry constructs a TableEntry. - -The first argument is a required description (this becomes the content of the generated Ginkgo `It`). -Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`. - -Each Entry ends up generating an individual Ginkgo It. -*/ -func Entry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, false} -} - -/* -You can focus a particular entry with FEntry. This is equivalent to FIt. -*/ -func FEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, true} -} - -/* -You can mark a particular entry as pending with PEntry. This is equivalent to PIt. -*/ -func PEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} -} - -/* -You can mark a particular entry as pending with XEntry. This is equivalent to XIt. -*/ -func XEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} -} diff --git a/vendor/github.com/onsi/gomega/gstruct/elements.go b/vendor/github.com/onsi/gomega/gstruct/elements.go deleted file mode 100644 index 13bf5b895..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/elements.go +++ /dev/null @@ -1,159 +0,0 @@ -package gstruct - -import ( - "errors" - "fmt" - "reflect" - "runtime/debug" - - "github.com/onsi/gomega/format" - errorsutil "github.com/onsi/gomega/gstruct/errors" - "github.com/onsi/gomega/types" -) - -//MatchAllElements succeeds if every element of a slice matches the element matcher it maps to -//through the id function, and every element matcher is matched. -// idFn := func(element interface{}) string { -// return fmt.Sprintf("%v", element) -// } -// -// Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{ -// "a": Equal("a"), -// "b": Equal("b"), -// })) -func MatchAllElements(identifier Identifier, elements Elements) types.GomegaMatcher { - return &ElementsMatcher{ - Identifier: identifier, - Elements: elements, - } -} - -//MatchElements succeeds if each element of a slice matches the element matcher it maps to -//through the id function. It can ignore extra elements and/or missing elements. -// idFn := func(element interface{}) string { -// return fmt.Sprintf("%v", element) -// } -// -// Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{ -// "a": Equal("a"), -// "b": Equal("b"), -// })) -// Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{ -// "a": Equal("a"), -// "b": Equal("b"), -// "c": Equal("c"), -// "d": Equal("d"), -// })) -func MatchElements(identifier Identifier, options Options, elements Elements) types.GomegaMatcher { - return &ElementsMatcher{ - Identifier: identifier, - Elements: elements, - IgnoreExtras: options&IgnoreExtras != 0, - IgnoreMissing: options&IgnoreMissing != 0, - AllowDuplicates: options&AllowDuplicates != 0, - } -} - -// ElementsMatcher is a NestingMatcher that applies custom matchers to each element of a slice mapped -// by the Identifier function. -// TODO: Extend this to work with arrays & maps (map the key) as well. -type ElementsMatcher struct { - // Matchers for each element. - Elements Elements - // Function mapping an element to the string key identifying its matcher. - Identifier Identifier - - // Whether to ignore extra elements or consider it an error. - IgnoreExtras bool - // Whether to ignore missing elements or consider it an error. - IgnoreMissing bool - // Whether to key duplicates when matching IDs. - AllowDuplicates bool - - // State. - failures []error -} - -// Element ID to matcher. -type Elements map[string]types.GomegaMatcher - -// Function for identifying (mapping) elements. -type Identifier func(element interface{}) string - -func (m *ElementsMatcher) Match(actual interface{}) (success bool, err error) { - if reflect.TypeOf(actual).Kind() != reflect.Slice { - return false, fmt.Errorf("%v is type %T, expected slice", actual, actual) - } - - m.failures = m.matchElements(actual) - if len(m.failures) > 0 { - return false, nil - } - return true, nil -} - -func (m *ElementsMatcher) matchElements(actual interface{}) (errs []error) { - // Provide more useful error messages in the case of a panic. - defer func() { - if err := recover(); err != nil { - errs = append(errs, fmt.Errorf("panic checking %+v: %v\n%s", actual, err, debug.Stack())) - } - }() - - val := reflect.ValueOf(actual) - elements := map[string]bool{} - for i := 0; i < val.Len(); i++ { - element := val.Index(i).Interface() - id := m.Identifier(element) - if elements[id] { - if !m.AllowDuplicates { - errs = append(errs, fmt.Errorf("found duplicate element ID %s", id)) - continue - } - } - elements[id] = true - - matcher, expected := m.Elements[id] - if !expected { - if !m.IgnoreExtras { - errs = append(errs, fmt.Errorf("unexpected element %s", id)) - } - continue - } - - match, err := matcher.Match(element) - if match { - continue - } - - if err == nil { - if nesting, ok := matcher.(errorsutil.NestingMatcher); ok { - err = errorsutil.AggregateError(nesting.Failures()) - } else { - err = errors.New(matcher.FailureMessage(element)) - } - } - errs = append(errs, errorsutil.Nest(fmt.Sprintf("[%s]", id), err)) - } - - for id := range m.Elements { - if !elements[id] && !m.IgnoreMissing { - errs = append(errs, fmt.Errorf("missing expected element %s", id)) - } - } - - return errs -} - -func (m *ElementsMatcher) FailureMessage(actual interface{}) (message string) { - failure := errorsutil.AggregateError(m.failures) - return format.Message(actual, fmt.Sprintf("to match elements: %v", failure)) -} - -func (m *ElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to match elements") -} - -func (m *ElementsMatcher) Failures() []error { - return m.failures -} diff --git a/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go b/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go deleted file mode 100644 index 188492b21..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/errors/nested_types.go +++ /dev/null @@ -1,72 +0,0 @@ -package errors - -import ( - "fmt" - "strings" - - "github.com/onsi/gomega/types" -) - -// A stateful matcher that nests other matchers within it and preserves the error types of the -// nested matcher failures. -type NestingMatcher interface { - types.GomegaMatcher - - // Returns the failures of nested matchers. - Failures() []error -} - -// An error type for labeling errors on deeply nested matchers. -type NestedError struct { - Path string - Err error -} - -func (e *NestedError) Error() string { - // Indent Errors. - indented := strings.Replace(e.Err.Error(), "\n", "\n\t", -1) - return fmt.Sprintf("%s:\n\t%v", e.Path, indented) -} - -// Create a NestedError with the given path. -// If err is a NestedError, prepend the path to it. -// If err is an AggregateError, recursively Nest each error. -func Nest(path string, err error) error { - if ag, ok := err.(AggregateError); ok { - var errs AggregateError - for _, e := range ag { - errs = append(errs, Nest(path, e)) - } - return errs - } - if ne, ok := err.(*NestedError); ok { - return &NestedError{ - Path: path + ne.Path, - Err: ne.Err, - } - } - return &NestedError{ - Path: path, - Err: err, - } -} - -// An error type for treating multiple errors as a single error. -type AggregateError []error - -// Error is part of the error interface. -func (err AggregateError) Error() string { - if len(err) == 0 { - // This should never happen, really. - return "" - } - if len(err) == 1 { - return err[0].Error() - } - result := fmt.Sprintf("[%s", err[0].Error()) - for i := 1; i < len(err); i++ { - result += fmt.Sprintf(", %s", err[i].Error()) - } - result += "]" - return result -} diff --git a/vendor/github.com/onsi/gomega/gstruct/fields.go b/vendor/github.com/onsi/gomega/gstruct/fields.go deleted file mode 100644 index 154c1d480..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/fields.go +++ /dev/null @@ -1,163 +0,0 @@ -package gstruct - -import ( - "errors" - "fmt" - "reflect" - "runtime/debug" - "strings" - - "github.com/onsi/gomega/format" - errorsutil "github.com/onsi/gomega/gstruct/errors" - "github.com/onsi/gomega/types" -) - -//MatchAllFields succeeds if every field of a struct matches the field matcher associated with -//it, and every element matcher is matched. -// actual := struct{ -// A int -// B []bool -// C string -// }{ -// A: 5, -// B: []bool{true, false}, -// C: "foo", -// } -// -// Expect(actual).To(MatchAllFields(Fields{ -// "A": Equal(5), -// "B": ConsistOf(true, false), -// "C": Equal("foo"), -// })) -func MatchAllFields(fields Fields) types.GomegaMatcher { - return &FieldsMatcher{ - Fields: fields, - } -} - -//MatchFields succeeds if each element of a struct matches the field matcher associated with -//it. It can ignore extra fields and/or missing fields. -// actual := struct{ -// A int -// B []bool -// C string -// }{ -// A: 5, -// B: []bool{true, false}, -// C: "foo", -// } -// -// Expect(actual).To(MatchFields(IgnoreExtras, Fields{ -// "A": Equal(5), -// "B": ConsistOf(true, false), -// })) -// Expect(actual).To(MatchFields(IgnoreMissing, Fields{ -// "A": Equal(5), -// "B": ConsistOf(true, false), -// "C": Equal("foo"), -// "D": Equal("extra"), -// })) -func MatchFields(options Options, fields Fields) types.GomegaMatcher { - return &FieldsMatcher{ - Fields: fields, - IgnoreExtras: options&IgnoreExtras != 0, - IgnoreMissing: options&IgnoreMissing != 0, - } -} - -type FieldsMatcher struct { - // Matchers for each field. - Fields Fields - - // Whether to ignore extra elements or consider it an error. - IgnoreExtras bool - // Whether to ignore missing elements or consider it an error. - IgnoreMissing bool - - // State. - failures []error -} - -// Field name to matcher. -type Fields map[string]types.GomegaMatcher - -func (m *FieldsMatcher) Match(actual interface{}) (success bool, err error) { - if reflect.TypeOf(actual).Kind() != reflect.Struct { - return false, fmt.Errorf("%v is type %T, expected struct", actual, actual) - } - - m.failures = m.matchFields(actual) - if len(m.failures) > 0 { - return false, nil - } - return true, nil -} - -func (m *FieldsMatcher) matchFields(actual interface{}) (errs []error) { - val := reflect.ValueOf(actual) - typ := val.Type() - fields := map[string]bool{} - for i := 0; i < val.NumField(); i++ { - fieldName := typ.Field(i).Name - fields[fieldName] = true - - err := func() (err error) { - // This test relies heavily on reflect, which tends to panic. - // Recover here to provide more useful error messages in that case. - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack()) - } - }() - - matcher, expected := m.Fields[fieldName] - if !expected { - if !m.IgnoreExtras { - return fmt.Errorf("unexpected field %s: %+v", fieldName, actual) - } - return nil - } - - field := val.Field(i).Interface() - - match, err := matcher.Match(field) - if err != nil { - return err - } else if !match { - if nesting, ok := matcher.(errorsutil.NestingMatcher); ok { - return errorsutil.AggregateError(nesting.Failures()) - } - return errors.New(matcher.FailureMessage(field)) - } - return nil - }() - if err != nil { - errs = append(errs, errorsutil.Nest("."+fieldName, err)) - } - } - - for field := range m.Fields { - if !fields[field] && !m.IgnoreMissing { - errs = append(errs, fmt.Errorf("missing expected field %s", field)) - } - } - - return errs -} - -func (m *FieldsMatcher) FailureMessage(actual interface{}) (message string) { - failures := make([]string, len(m.failures)) - for i := range m.failures { - failures[i] = m.failures[i].Error() - } - return format.Message(reflect.TypeOf(actual).Name(), - fmt.Sprintf("to match fields: {\n%v\n}\n", strings.Join(failures, "\n"))) -} - -func (m *FieldsMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to match fields") -} - -func (m *FieldsMatcher) Failures() []error { - return m.failures -} diff --git a/vendor/github.com/onsi/gomega/gstruct/ignore.go b/vendor/github.com/onsi/gomega/gstruct/ignore.go deleted file mode 100644 index 0365f32ad..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/ignore.go +++ /dev/null @@ -1,37 +0,0 @@ -package gstruct - -import ( - "github.com/onsi/gomega/types" -) - -//Ignore ignores the actual value and always succeeds. -// Expect(nil).To(Ignore()) -// Expect(true).To(Ignore()) -func Ignore() types.GomegaMatcher { - return &IgnoreMatcher{true} -} - -//Reject ignores the actual value and always fails. It can be used in conjunction with IgnoreMissing -//to catch problematic elements, or to verify tests are running. -// Expect(nil).NotTo(Reject()) -// Expect(true).NotTo(Reject()) -func Reject() types.GomegaMatcher { - return &IgnoreMatcher{false} -} - -// A matcher that either always succeeds or always fails. -type IgnoreMatcher struct { - Succeed bool -} - -func (m *IgnoreMatcher) Match(actual interface{}) (bool, error) { - return m.Succeed, nil -} - -func (m *IgnoreMatcher) FailureMessage(_ interface{}) (message string) { - return "Unconditional failure" -} - -func (m *IgnoreMatcher) NegatedFailureMessage(_ interface{}) (message string) { - return "Unconditional success" -} diff --git a/vendor/github.com/onsi/gomega/gstruct/keys.go b/vendor/github.com/onsi/gomega/gstruct/keys.go deleted file mode 100644 index d5549ff55..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/keys.go +++ /dev/null @@ -1,124 +0,0 @@ -package gstruct - -import ( - "errors" - "fmt" - "reflect" - "runtime/debug" - "strings" - - "github.com/onsi/gomega/format" - errorsutil "github.com/onsi/gomega/gstruct/errors" - "github.com/onsi/gomega/types" -) - -func MatchAllKeys(keys Keys) types.GomegaMatcher { - return &KeysMatcher{ - Keys: keys, - } -} - -func MatchKeys(options Options, keys Keys) types.GomegaMatcher { - return &KeysMatcher{ - Keys: keys, - IgnoreExtras: options&IgnoreExtras != 0, - IgnoreMissing: options&IgnoreMissing != 0, - } -} - -type KeysMatcher struct { - // Matchers for each key. - Keys Keys - - // Whether to ignore extra keys or consider it an error. - IgnoreExtras bool - // Whether to ignore missing keys or consider it an error. - IgnoreMissing bool - - // State. - failures []error -} - -type Keys map[interface{}]types.GomegaMatcher - -func (m *KeysMatcher) Match(actual interface{}) (success bool, err error) { - if reflect.TypeOf(actual).Kind() != reflect.Map { - return false, fmt.Errorf("%v is type %T, expected map", actual, actual) - } - - m.failures = m.matchKeys(actual) - if len(m.failures) > 0 { - return false, nil - } - return true, nil -} - -func (m *KeysMatcher) matchKeys(actual interface{}) (errs []error) { - actualValue := reflect.ValueOf(actual) - keys := map[interface{}]bool{} - for _, keyValue := range actualValue.MapKeys() { - key := keyValue.Interface() - keys[key] = true - - err := func() (err error) { - // This test relies heavily on reflect, which tends to panic. - // Recover here to provide more useful error messages in that case. - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic checking %+v: %v\n%s", actual, r, debug.Stack()) - } - }() - - matcher, ok := m.Keys[key] - if !ok { - if !m.IgnoreExtras { - return fmt.Errorf("unexpected key %s: %+v", key, actual) - } - return nil - } - - valInterface := actualValue.MapIndex(keyValue).Interface() - - match, err := matcher.Match(valInterface) - if err != nil { - return err - } - - if !match { - if nesting, ok := matcher.(errorsutil.NestingMatcher); ok { - return errorsutil.AggregateError(nesting.Failures()) - } - return errors.New(matcher.FailureMessage(valInterface)) - } - return nil - }() - if err != nil { - errs = append(errs, errorsutil.Nest(fmt.Sprintf(".%#v", key), err)) - } - } - - for key := range m.Keys { - if !keys[key] && !m.IgnoreMissing { - errs = append(errs, fmt.Errorf("missing expected key %s", key)) - } - } - - return errs -} - -func (m *KeysMatcher) FailureMessage(actual interface{}) (message string) { - failures := make([]string, len(m.failures)) - for i := range m.failures { - failures[i] = m.failures[i].Error() - } - return format.Message(reflect.TypeOf(actual).Name(), - fmt.Sprintf("to match keys: {\n%v\n}\n", strings.Join(failures, "\n"))) -} - -func (m *KeysMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to match keys") -} - -func (m *KeysMatcher) Failures() []error { - return m.failures -} diff --git a/vendor/github.com/onsi/gomega/gstruct/pointer.go b/vendor/github.com/onsi/gomega/gstruct/pointer.go deleted file mode 100644 index 0a2f35de3..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/pointer.go +++ /dev/null @@ -1,56 +0,0 @@ -package gstruct - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/format" - "github.com/onsi/gomega/types" -) - -//PointTo applies the given matcher to the value pointed to by actual. It fails if the pointer is -//nil. -// actual := 5 -// Expect(&actual).To(PointTo(Equal(5))) -func PointTo(matcher types.GomegaMatcher) types.GomegaMatcher { - return &PointerMatcher{ - Matcher: matcher, - } -} - -type PointerMatcher struct { - Matcher types.GomegaMatcher - - // Failure message. - failure string -} - -func (m *PointerMatcher) Match(actual interface{}) (bool, error) { - val := reflect.ValueOf(actual) - - // return error if actual type is not a pointer - if val.Kind() != reflect.Ptr { - return false, fmt.Errorf("PointerMatcher expects a pointer but we have '%s'", val.Kind()) - } - - if !val.IsValid() || val.IsNil() { - m.failure = format.Message(actual, "not to be ") - return false, nil - } - - // Forward the value. - elem := val.Elem().Interface() - match, err := m.Matcher.Match(elem) - if !match { - m.failure = m.Matcher.FailureMessage(elem) - } - return match, err -} - -func (m *PointerMatcher) FailureMessage(_ interface{}) (message string) { - return m.failure -} - -func (m *PointerMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return m.Matcher.NegatedFailureMessage(actual) -} diff --git a/vendor/github.com/onsi/gomega/gstruct/types.go b/vendor/github.com/onsi/gomega/gstruct/types.go deleted file mode 100644 index 48cbbe8f6..000000000 --- a/vendor/github.com/onsi/gomega/gstruct/types.go +++ /dev/null @@ -1,15 +0,0 @@ -package gstruct - -//Options is the type for options passed to some matchers. -type Options int - -const ( - //IgnoreExtras tells the matcher to ignore extra elements or fields, rather than triggering a failure. - IgnoreExtras Options = 1 << iota - //IgnoreMissing tells the matcher to ignore missing elements or fields, rather than triggering a failure. - IgnoreMissing - //AllowDuplicates tells the matcher to permit multiple members of the slice to produce the same ID when - //considered by the indentifier function. All members that map to a given key must still match successfully - //with the matcher that is provided for that key. - AllowDuplicates -) diff --git a/vendor/github.com/open-policy-agent/opa/LICENSE b/vendor/github.com/open-policy-agent/opa/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/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/github.com/open-policy-agent/opa/ast/builtins.go b/vendor/github.com/open-policy-agent/opa/ast/builtins.go new file mode 100644 index 000000000..1ab7dc0a9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/builtins.go @@ -0,0 +1,1902 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" + + "github.com/open-policy-agent/opa/types" +) + +// Builtins is the registry of built-in functions supported by OPA. +// Call RegisterBuiltin to add a new built-in. +var Builtins []*Builtin + +// RegisterBuiltin adds a new built-in function to the registry. +func RegisterBuiltin(b *Builtin) { + Builtins = append(Builtins, b) + BuiltinMap[b.Name] = b + if len(b.Infix) > 0 { + BuiltinMap[b.Infix] = b + } +} + +// DefaultBuiltins is the registry of built-in functions supported in OPA +// by default. When adding a new built-in function to OPA, update this +// list. +var DefaultBuiltins = [...]*Builtin{ + // Unification/equality ("=") + Equality, + + // Assignment (":=") + Assign, + + // Comparisons + GreaterThan, + GreaterThanEq, + LessThan, + LessThanEq, + NotEqual, + Equal, + + // Arithmetic + Plus, + Minus, + Multiply, + Divide, + Round, + Abs, + Rem, + + // Bitwise Arithmetic + BitsOr, + BitsAnd, + BitsNegate, + BitsXOr, + BitsShiftLeft, + BitsShiftRight, + + // Binary + And, + Or, + + // Aggregates + Count, + Sum, + Product, + Max, + Min, + Any, + All, + + // Arrays + ArrayConcat, + ArraySlice, + + // Conversions + ToNumber, + + // Casts (DEPRECATED) + CastObject, + CastNull, + CastBoolean, + CastString, + CastSet, + CastArray, + + // Regular Expressions + RegexMatch, + RegexSplit, + GlobsMatch, + RegexTemplateMatch, + RegexFind, + RegexFindAllStringSubmatch, + + // Sets + SetDiff, + Intersection, + Union, + + // Strings + Concat, + FormatInt, + IndexOf, + Substring, + Lower, + Upper, + Contains, + StartsWith, + EndsWith, + Split, + Replace, + ReplaceN, + Trim, + TrimLeft, + TrimPrefix, + TrimRight, + TrimSuffix, + TrimSpace, + Sprintf, + + // Encoding + JSONMarshal, + JSONUnmarshal, + Base64Encode, + Base64Decode, + Base64UrlEncode, + Base64UrlDecode, + URLQueryDecode, + URLQueryEncode, + URLQueryEncodeObject, + YAMLMarshal, + YAMLUnmarshal, + + // Object Manipulation + ObjectUnion, + ObjectRemove, + ObjectFilter, + ObjectGet, + + // JSON Object Manipulation + JSONFilter, + JSONRemove, + + // Tokens + JWTDecode, + JWTVerifyRS256, + JWTVerifyPS256, + JWTVerifyES256, + JWTVerifyHS256, + JWTDecodeVerify, + JWTEncodeSignRaw, + JWTEncodeSign, + + // Time + NowNanos, + ParseNanos, + ParseRFC3339Nanos, + ParseDurationNanos, + Date, + Clock, + Weekday, + + // Crypto + CryptoX509ParseCertificates, + CryptoMd5, + CryptoSha1, + CryptoSha256, + + // Graphs + WalkBuiltin, + + // Sort + Sort, + + // Types + IsNumber, + IsString, + IsBoolean, + IsArray, + IsSet, + IsObject, + IsNull, + TypeNameBuiltin, + + // HTTP + HTTPSend, + + // Rego + RegoParseModule, + + // OPA + OPARuntime, + + // Tracing + Trace, + + // CIDR + NetCIDROverlap, + NetCIDRIntersects, + NetCIDRContains, + NetCIDRExpand, + + // Glob + GlobMatch, + GlobQuoteMeta, + + // Units + UnitsParseBytes, +} + +// BuiltinMap provides a convenient mapping of built-in names to +// built-in definitions. +var BuiltinMap map[string]*Builtin + +// IgnoreDuringPartialEval is a set of built-in functions that should not be +// evaluated during partial evaluation. These functions are not partially +// evaluated because they are not pure. +var IgnoreDuringPartialEval = []*Builtin{ + NowNanos, + HTTPSend, +} + +/** + * Unification + */ + +// Equality represents the "=" operator. +var Equality = &Builtin{ + Name: "eq", + Infix: "=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +/** + * Assignment + */ + +// Assign represents the assignment (":=") operator. +var Assign = &Builtin{ + Name: "assign", + Infix: ":=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +/** + * Comparisons + */ + +// GreaterThan represents the ">" comparison operator. +var GreaterThan = &Builtin{ + Name: "gt", + Infix: ">", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +// GreaterThanEq represents the ">=" comparison operator. +var GreaterThanEq = &Builtin{ + Name: "gte", + Infix: ">=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +// LessThan represents the "<" comparison operator. +var LessThan = &Builtin{ + Name: "lt", + Infix: "<", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +// LessThanEq represents the "<=" comparison operator. +var LessThanEq = &Builtin{ + Name: "lte", + Infix: "<=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +// NotEqual represents the "!=" comparison operator. +var NotEqual = &Builtin{ + Name: "neq", + Infix: "!=", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +// Equal represents the "==" comparison operator. +var Equal = &Builtin{ + Name: "equal", + Infix: "==", + Decl: types.NewFunction( + types.Args(types.A, types.A), + types.B, + ), +} + +/** + * Arithmetic + */ + +// Plus adds two numbers together. +var Plus = &Builtin{ + Name: "plus", + Infix: "+", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// Minus subtracts the second number from the first number or computes the diff +// between two sets. +var Minus = &Builtin{ + Name: "minus", + Infix: "-", + Decl: types.NewFunction( + types.Args( + types.NewAny(types.N, types.NewSet(types.A)), + types.NewAny(types.N, types.NewSet(types.A)), + ), + types.NewAny(types.N, types.NewSet(types.A)), + ), +} + +// Multiply multiplies two numbers together. +var Multiply = &Builtin{ + Name: "mul", + Infix: "*", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// Divide divides the first number by the second number. +var Divide = &Builtin{ + Name: "div", + Infix: "/", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// Round rounds the number up to the nearest integer. +var Round = &Builtin{ + Name: "round", + Decl: types.NewFunction( + types.Args(types.N), + types.N, + ), +} + +// Abs returns the number without its sign. +var Abs = &Builtin{ + Name: "abs", + Decl: types.NewFunction( + types.Args(types.N), + types.N, + ), +} + +// Rem returns the remainder for x%y for y != 0. +var Rem = &Builtin{ + Name: "rem", + Infix: "%", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +/** + * Bitwise + */ + +// BitsOr returns the bitwise "or" of two integers. +var BitsOr = &Builtin{ + Name: "bits.or", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// BitsAnd returns the bitwise "and" of two integers. +var BitsAnd = &Builtin{ + Name: "bits.and", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// BitsNegate returns the bitwise "negation" of an integer (i.e. flips each +// bit). +var BitsNegate = &Builtin{ + Name: "bits.negate", + Decl: types.NewFunction( + types.Args(types.N), + types.N, + ), +} + +// BitsXOr returns the bitwise "exclusive-or" of two integers. +var BitsXOr = &Builtin{ + Name: "bits.xor", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// BitsShiftLeft returns a new integer with its bits shifted some value to the +// left. +var BitsShiftLeft = &Builtin{ + Name: "bits.lsh", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +// BitsShiftRight returns a new integer with its bits shifted some value to the +// right. +var BitsShiftRight = &Builtin{ + Name: "bits.rsh", + Decl: types.NewFunction( + types.Args(types.N, types.N), + types.N, + ), +} + +/** + * Sets + */ + +// And performs an intersection operation on sets. +var And = &Builtin{ + Name: "and", + Infix: "&", + Decl: types.NewFunction( + types.Args( + types.NewSet(types.A), + types.NewSet(types.A), + ), + types.NewSet(types.A), + ), +} + +// Or performs a union operation on sets. +var Or = &Builtin{ + Name: "or", + Infix: "|", + Decl: types.NewFunction( + types.Args( + types.NewSet(types.A), + types.NewSet(types.A), + ), + types.NewSet(types.A), + ), +} + +/** + * Aggregates + */ + +// Count takes a collection or string and counts the number of elements in it. +var Count = &Builtin{ + Name: "count", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.A), + types.NewArray(nil, types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.S, + ), + ), + types.N, + ), +} + +// Sum takes an array or set of numbers and sums them. +var Sum = &Builtin{ + Name: "sum", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.N), + types.NewArray(nil, types.N), + ), + ), + types.N, + ), +} + +// Product takes an array or set of numbers and multiplies them. +var Product = &Builtin{ + Name: "product", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.N), + types.NewArray(nil, types.N), + ), + ), + types.N, + ), +} + +// Max returns the maximum value in a collection. +var Max = &Builtin{ + Name: "max", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.A), + types.NewArray(nil, types.A), + ), + ), + types.A, + ), +} + +// Min returns the minimum value in a collection. +var Min = &Builtin{ + Name: "min", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.A), + types.NewArray(nil, types.A), + ), + ), + types.A, + ), +} + +// All takes a list and returns true if all of the items +// are true. A collection of length 0 returns true. +var All = &Builtin{ + Name: "all", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.A), + types.NewArray(nil, types.A), + ), + ), + types.B, + ), +} + +// Any takes a collection and returns true if any of the items +// is true. A collection of length 0 returns false. +var Any = &Builtin{ + Name: "any", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewSet(types.A), + types.NewArray(nil, types.A), + ), + ), + types.B, + ), +} + +/** + * Arrays + */ + +// ArrayConcat returns the result of concatenating two arrays together. +var ArrayConcat = &Builtin{ + Name: "array.concat", + Decl: types.NewFunction( + types.Args( + types.NewArray(nil, types.A), + types.NewArray(nil, types.A), + ), + types.NewArray(nil, types.A), + ), +} + +// ArraySlice returns a slice of a given array +var ArraySlice = &Builtin{ + Name: "array.slice", + Decl: types.NewFunction( + types.Args( + types.NewArray(nil, types.A), + types.NewNumber(), + types.NewNumber(), + ), + types.NewArray(nil, types.A), + ), +} + +/** + * Conversions + */ + +// ToNumber takes a string, bool, or number value and converts it to a number. +// Strings are converted to numbers using strconv.Atoi. +// Boolean false is converted to 0 and boolean true is converted to 1. +var ToNumber = &Builtin{ + Name: "to_number", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.N, + types.S, + types.B, + types.NewNull(), + ), + ), + types.N, + ), +} + +/** + * Regular Expressions + */ + +// RegexMatch takes two strings and evaluates to true if the string in the second +// position matches the pattern in the first position. +var RegexMatch = &Builtin{ + Name: "re_match", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// RegexFindAllStringSubmatch returns an array of all successive matches of the expression. +// It takes two strings and a number, the pattern, the value and number of matches to +// return, -1 means all matches. +var RegexFindAllStringSubmatch = &Builtin{ + Name: "regex.find_all_string_submatch_n", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + types.N, + ), + types.NewArray(nil, types.NewArray(nil, types.S)), + ), +} + +// RegexTemplateMatch takes two strings and evaluates to true if the string in the second +// position matches the pattern in the first position. +var RegexTemplateMatch = &Builtin{ + Name: "regex.template_match", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + types.S, + types.S, + ), + types.B, + ), +} + +// RegexSplit splits the input string by the occurrences of the given pattern. +var RegexSplit = &Builtin{ + Name: "regex.split", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.NewArray(nil, types.S), + ), +} + +// RegexFind takes two strings and a number, the pattern, the value and number of match values to +// return, -1 means all match values. +var RegexFind = &Builtin{ + Name: "regex.find_n", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + types.N, + ), + types.NewArray(nil, types.S), + ), +} + +// GlobsMatch takes two strings regexp-style strings and evaluates to true if their +// intersection matches a non-empty set of non-empty strings. +// Examples: +// - "a.a." and ".b.b" -> true. +// - "[a-z]*" and [0-9]+" -> not true. +var GlobsMatch = &Builtin{ + Name: "regex.globs_match", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +/** + * Strings + */ + +// Concat joins an array of strings with an input string. +var Concat = &Builtin{ + Name: "concat", + Decl: types.NewFunction( + types.Args( + types.S, + types.NewAny( + types.NewSet(types.S), + types.NewArray(nil, types.S), + ), + ), + types.S, + ), +} + +// FormatInt returns the string representation of the number in the given base after converting it to an integer value. +var FormatInt = &Builtin{ + Name: "format_int", + Decl: types.NewFunction( + types.Args( + types.N, + types.N, + ), + types.S, + ), +} + +// IndexOf returns the index of a substring contained inside a string +var IndexOf = &Builtin{ + Name: "indexof", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.N, + ), +} + +// Substring returns the portion of a string for a given start index and a length. +// If the length is less than zero, then substring returns the remainder of the string. +var Substring = &Builtin{ + Name: "substring", + Decl: types.NewFunction( + types.Args( + types.S, + types.N, + types.N, + ), + types.S, + ), +} + +// Contains returns true if the search string is included in the base string +var Contains = &Builtin{ + Name: "contains", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// StartsWith returns true if the search string begins with the base string +var StartsWith = &Builtin{ + Name: "startswith", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// EndsWith returns true if the search string begins with the base string +var EndsWith = &Builtin{ + Name: "endswith", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// Lower returns the input string but with all characters in lower-case +var Lower = &Builtin{ + Name: "lower", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// Upper returns the input string but with all characters in upper-case +var Upper = &Builtin{ + Name: "upper", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// Split returns an array containing elements of the input string split on a delimiter. +var Split = &Builtin{ + Name: "split", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.NewArray(nil, types.S), + ), +} + +// Replace returns the given string with all instances of the second argument replaced +// by the third. +var Replace = &Builtin{ + Name: "replace", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + types.S, + ), + types.S, + ), +} + +// ReplaceN replaces a string from a list of old, new string pairs. +// Replacements are performed in the order they appear in the target string, without overlapping matches. +// The old string comparisons are done in argument order. +var ReplaceN = &Builtin{ + Name: "strings.replace_n", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty( + types.S, + types.S)), + types.S, + ), + types.S, + ), +} + +// Trim returns the given string with all leading or trailing instances of the second +// argument removed. +var Trim = &Builtin{ + Name: "trim", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.S, + ), +} + +// TrimLeft returns the given string with all leading instances of second argument removed. +var TrimLeft = &Builtin{ + Name: "trim_left", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.S, + ), +} + +// TrimPrefix returns the given string without the second argument prefix string. +// If the given string doesn't start with prefix, it is returned unchanged. +var TrimPrefix = &Builtin{ + Name: "trim_prefix", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.S, + ), +} + +// TrimRight returns the given string with all trailing instances of second argument removed. +var TrimRight = &Builtin{ + Name: "trim_right", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.S, + ), +} + +// TrimSuffix returns the given string without the second argument suffix string. +// If the given string doesn't end with suffix, it is returned unchanged. +var TrimSuffix = &Builtin{ + Name: "trim_suffix", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.S, + ), +} + +// TrimSpace return the given string with all leading and trailing white space removed. +var TrimSpace = &Builtin{ + Name: "trim_space", + Decl: types.NewFunction( + types.Args( + types.S, + ), + types.S, + ), +} + +// Sprintf returns the given string, formatted. +var Sprintf = &Builtin{ + Name: "sprintf", + Decl: types.NewFunction( + types.Args( + types.S, + types.NewArray(nil, types.A), + ), + types.S, + ), +} + +// UnitsParseBytes converts strings like 10GB, 5K, 4mb, and the like into an +// integer number of bytes. +var UnitsParseBytes = &Builtin{ + Name: "units.parse_bytes", + Decl: types.NewFunction( + types.Args( + types.S, + ), + types.N, + ), +} + +/** + * JSON + */ + +// JSONMarshal serializes the input term. +var JSONMarshal = &Builtin{ + Name: "json.marshal", + Decl: types.NewFunction( + types.Args(types.A), + types.S, + ), +} + +// JSONUnmarshal deserializes the input string. +var JSONUnmarshal = &Builtin{ + Name: "json.unmarshal", + Decl: types.NewFunction( + types.Args(types.S), + types.A, + ), +} + +// JSONFilter filters the JSON object +var JSONFilter = &Builtin{ + Name: "json.filter", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + types.NewAny( + types.NewArray( + nil, + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + types.NewSet( + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + ), + ), + types.A, + ), +} + +// JSONRemove removes paths in the JSON object +var JSONRemove = &Builtin{ + Name: "json.remove", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + types.NewAny( + types.NewArray( + nil, + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + types.NewSet( + types.NewAny( + types.S, + types.NewArray( + nil, + types.A, + ), + ), + ), + ), + ), + types.A, + ), +} + +// ObjectUnion creates a new object that is the asymmetric union of two objects +var ObjectUnion = &Builtin{ + Name: "object.union", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + ), + types.A, + ), +} + +// ObjectRemove Removes specified keys from an object +var ObjectRemove = &Builtin{ + Name: "object.remove", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + types.NewAny( + types.NewArray(nil, types.A), + types.NewSet(types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + ), + ), + types.A, + ), +} + +// ObjectFilter filters the object by keeping only specified keys +var ObjectFilter = &Builtin{ + Name: "object.filter", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty(types.A, types.A), + ), + types.NewAny( + types.NewArray(nil, types.A), + types.NewSet(types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + ), + ), + types.A, + ), +} + +// Base64Encode serializes the input string into base64 encoding. +var Base64Encode = &Builtin{ + Name: "base64.encode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// Base64Decode deserializes the base64 encoded input string. +var Base64Decode = &Builtin{ + Name: "base64.decode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// Base64UrlEncode serializes the input string into base64url encoding. +var Base64UrlEncode = &Builtin{ + Name: "base64url.encode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// Base64UrlDecode deserializes the base64url encoded input string. +var Base64UrlDecode = &Builtin{ + Name: "base64url.decode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// URLQueryDecode decodes a URL encoded input string. +var URLQueryDecode = &Builtin{ + Name: "urlquery.decode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// URLQueryEncode encodes the input string into a URL encoded string. +var URLQueryEncode = &Builtin{ + Name: "urlquery.encode", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// URLQueryEncodeObject encodes the given JSON into a URL encoded query string. +var URLQueryEncodeObject = &Builtin{ + Name: "urlquery.encode_object", + Decl: types.NewFunction( + types.Args( + types.NewObject( + nil, + types.NewDynamicProperty( + types.S, + types.NewAny( + types.S, + types.NewArray(nil, types.S), + types.NewSet(types.S))))), + types.S, + ), +} + +// YAMLMarshal serializes the input term. +var YAMLMarshal = &Builtin{ + Name: "yaml.marshal", + Decl: types.NewFunction( + types.Args(types.A), + types.S, + ), +} + +// YAMLUnmarshal deserializes the input string. +var YAMLUnmarshal = &Builtin{ + Name: "yaml.unmarshal", + Decl: types.NewFunction( + types.Args(types.S), + types.A, + ), +} + +/** + * Tokens + */ + +// JWTDecode decodes a JSON Web Token and outputs it as an Object. +var JWTDecode = &Builtin{ + Name: "io.jwt.decode", + Decl: types.NewFunction( + types.Args(types.S), + types.NewArray([]types.Type{ + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.S, + }, nil), + ), +} + +// JWTVerifyRS256 verifies if a RS256 JWT signature is valid or not. +var JWTVerifyRS256 = &Builtin{ + Name: "io.jwt.verify_rs256", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// JWTVerifyPS256 verifies if a PS256 JWT signature is valid or not. +var JWTVerifyPS256 = &Builtin{ + Name: "io.jwt.verify_ps256", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// JWTVerifyES256 verifies if a ES256 JWT signature is valid or not. +var JWTVerifyES256 = &Builtin{ + Name: "io.jwt.verify_es256", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// JWTVerifyHS256 verifies if a HS256 (secret) JWT signature is valid or not. +var JWTVerifyHS256 = &Builtin{ + Name: "io.jwt.verify_hs256", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// JWTDecodeVerify verifies a JWT signature under parameterized constraints and decodes the claims if it is valid. +var JWTDecodeVerify = &Builtin{ + Name: "io.jwt.decode_verify", + Decl: types.NewFunction( + types.Args( + types.S, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + ), + types.NewArray([]types.Type{ + types.B, + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + }, nil), + ), +} + +// JWTEncodeSignRaw encodes and optionally sign a JSON Web Token. +// Inputs are protected headers, payload, secret +var JWTEncodeSignRaw = &Builtin{ + Name: "io.jwt.encode_sign_raw", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + types.S, + ), + types.S, + ), +} + +// JWTEncodeSign encodes and optionally sign a JSON Web Token. +// Inputs are protected headers, payload, secret +var JWTEncodeSign = &Builtin{ + Name: "io.jwt.encode_sign", + Decl: types.NewFunction( + types.Args( + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + ), + types.S, + ), +} + +/** + * Time + */ + +// NowNanos returns the current time since epoch in nanoseconds. +var NowNanos = &Builtin{ + Name: "time.now_ns", + Decl: types.NewFunction( + nil, + types.N, + ), +} + +// ParseNanos returns the time in nanoseconds parsed from the string in the given format. +var ParseNanos = &Builtin{ + Name: "time.parse_ns", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.N, + ), +} + +// ParseRFC3339Nanos returns the time in nanoseconds parsed from the string in RFC3339 format. +var ParseRFC3339Nanos = &Builtin{ + Name: "time.parse_rfc3339_ns", + Decl: types.NewFunction( + types.Args(types.S), + types.N, + ), +} + +// ParseDurationNanos returns the duration in nanoseconds represented by a duration string. +// Duration string is similar to the Go time.ParseDuration string +var ParseDurationNanos = &Builtin{ + Name: "time.parse_duration_ns", + Decl: types.NewFunction( + types.Args(types.S), + types.N, + ), +} + +// Date returns the [year, month, day] for the nanoseconds since epoch. +var Date = &Builtin{ + Name: "time.date", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + ), + ), + types.NewArray([]types.Type{types.N, types.N, types.N}, nil), + ), +} + +// Clock returns the [hour, minute, second] of the day for the nanoseconds since epoch. +var Clock = &Builtin{ + Name: "time.clock", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + ), + ), + types.NewArray([]types.Type{types.N, types.N, types.N}, nil), + ), +} + +// Weekday returns the day of the week (Monday, Tuesday, ...) for the nanoseconds since epoch. +var Weekday = &Builtin{ + Name: "time.weekday", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.N, + types.NewArray([]types.Type{types.N, types.S}, nil), + ), + ), + types.S, + ), +} + +/** + * Crypto. + */ + +// CryptoX509ParseCertificates returns one or more certificates from the given +// base64 encoded string containing DER encoded certificates that have been +// concatenated. +var CryptoX509ParseCertificates = &Builtin{ + Name: "crypto.x509.parse_certificates", + Decl: types.NewFunction( + types.Args(types.S), + types.NewArray(nil, types.NewObject(nil, types.NewDynamicProperty(types.S, types.A))), + ), +} + +// CryptoMd5 returns a string representing the input string hashed with the md5 function +var CryptoMd5 = &Builtin{ + Name: "crypto.md5", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// CryptoSha1 returns a string representing the input string hashed with the sha1 function +var CryptoSha1 = &Builtin{ + Name: "crypto.sha1", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +// CryptoSha256 returns a string representing the input string hashed with the sha256 function +var CryptoSha256 = &Builtin{ + Name: "crypto.sha256", + Decl: types.NewFunction( + types.Args(types.S), + types.S, + ), +} + +/** + * Graphs. + */ + +// WalkBuiltin generates [path, value] tuples for all nested documents +// (recursively). +var WalkBuiltin = &Builtin{ + Name: "walk", + Relation: true, + Decl: types.NewFunction( + types.Args(types.A), + types.NewArray( + []types.Type{ + types.NewArray(nil, types.A), + types.A, + }, + nil, + ), + ), +} + +/** + * Sorting + */ + +// Sort returns a sorted array. +var Sort = &Builtin{ + Name: "sort", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.NewArray(nil, types.A), + types.NewSet(types.A), + ), + ), + types.NewArray(nil, types.A), + ), +} + +/** + * Type + */ + +// IsNumber returns true if the input value is a number +var IsNumber = &Builtin{ + Name: "is_number", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsString returns true if the input value is a string. +var IsString = &Builtin{ + Name: "is_string", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsBoolean returns true if the input value is a boolean. +var IsBoolean = &Builtin{ + Name: "is_boolean", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsArray returns true if the input value is an array. +var IsArray = &Builtin{ + Name: "is_array", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsSet returns true if the input value is a set. +var IsSet = &Builtin{ + Name: "is_set", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsObject returns true if the input value is an object. +var IsObject = &Builtin{ + Name: "is_object", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +// IsNull returns true if the input value is null. +var IsNull = &Builtin{ + Name: "is_null", + Decl: types.NewFunction( + types.Args( + types.A, + ), + types.B, + ), +} + +/** + * Type Name + */ + +// TypeNameBuiltin returns the type of the input. +var TypeNameBuiltin = &Builtin{ + Name: "type_name", + Decl: types.NewFunction( + types.Args( + types.NewAny( + types.A, + ), + ), + types.S, + ), +} + +/** + * HTTP Request + */ + +// HTTPSend returns a HTTP response to the given HTTP request. +var HTTPSend = &Builtin{ + Name: "http.send", + Decl: types.NewFunction( + types.Args( + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + ), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + ), +} + +/** + * Rego + */ + +// RegoParseModule parses the input Rego file and returns a JSON representation +// of the AST. +var RegoParseModule = &Builtin{ + Name: "rego.parse_module", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), // TODO(tsandall): import AST schema + ), +} + +/** + * OPA + */ + +// OPARuntime returns an object containing OPA runtime information such as the +// configuration that OPA was booted with. +var OPARuntime = &Builtin{ + Name: "opa.runtime", + Decl: types.NewFunction( + nil, + types.NewObject(nil, types.NewDynamicProperty(types.S, types.A)), + ), +} + +/** + * Trace + */ + +// Trace prints a note that is included in the query explanation. +var Trace = &Builtin{ + Name: "trace", + Decl: types.NewFunction( + types.Args( + types.S, + ), + types.B, + ), +} + +/** + * Set + */ + +// Intersection returns the intersection of the given input sets +var Intersection = &Builtin{ + Name: "intersection", + Decl: types.NewFunction( + types.Args( + types.NewSet(types.NewSet(types.A)), + ), + types.NewSet(types.A), + ), +} + +// Union returns the union of the given input sets +var Union = &Builtin{ + Name: "union", + Decl: types.NewFunction( + types.Args( + types.NewSet(types.NewSet(types.A)), + ), + types.NewSet(types.A), + ), +} + +/** + * Glob + */ + +// GlobMatch - not to be confused with regex.globs_match - parses and matches strings against the glob notation. +var GlobMatch = &Builtin{ + Name: "glob.match", + Decl: types.NewFunction( + types.Args( + types.S, + types.NewArray(nil, types.S), + types.S, + ), + types.B, + ), +} + +// GlobQuoteMeta returns a string which represents a version of the pattern where all asterisks have been escaped. +var GlobQuoteMeta = &Builtin{ + Name: "glob.quote_meta", + Decl: types.NewFunction( + types.Args( + types.S, + ), + types.S, + ), +} + +/** + * Net CIDR + */ + +// NetCIDRIntersects checks if a cidr intersects with another cidr and returns true or false +var NetCIDRIntersects = &Builtin{ + Name: "net.cidr_intersects", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// NetCIDRExpand returns a set of hosts inside the specified cidr. +var NetCIDRExpand = &Builtin{ + Name: "net.cidr_expand", + Decl: types.NewFunction( + types.Args( + types.S, + ), + types.NewSet(types.S), + ), +} + +// NetCIDRContains checks if a cidr or ip is contained within another cidr and returns true or false +var NetCIDRContains = &Builtin{ + Name: "net.cidr_contains", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +/** + * Deprecated built-ins. + */ + +// SetDiff has been replaced by the minus built-in. +var SetDiff = &Builtin{ + Name: "set_diff", + Decl: types.NewFunction( + types.Args( + types.NewSet(types.A), + types.NewSet(types.A), + ), + types.NewSet(types.A), + ), +} + +// NetCIDROverlap has been replaced by the `net.cidr_contains` built-in. +var NetCIDROverlap = &Builtin{ + Name: "net.cidr_overlap", + Decl: types.NewFunction( + types.Args( + types.S, + types.S, + ), + types.B, + ), +} + +// CastArray checks the underlying type of the input. If it is array or set, an array +// containing the values is returned. If it is not an array, an error is thrown. +var CastArray = &Builtin{ + Name: "cast_array", + Decl: types.NewFunction( + types.Args(types.A), + types.NewArray(nil, types.A), + ), +} + +// CastSet checks the underlying type of the input. +// If it is a set, the set is returned. +// If it is an array, the array is returned in set form (all duplicates removed) +// If neither, an error is thrown +var CastSet = &Builtin{ + Name: "cast_set", + Decl: types.NewFunction( + types.Args(types.A), + types.NewSet(types.A), + ), +} + +// CastString returns input if it is a string; if not returns error. +// For formatting variables, see sprintf +var CastString = &Builtin{ + Name: "cast_string", + Decl: types.NewFunction( + types.Args(types.A), + types.S, + ), +} + +// CastBoolean returns input if it is a boolean; if not returns error. +var CastBoolean = &Builtin{ + Name: "cast_boolean", + Decl: types.NewFunction( + types.Args(types.A), + types.B, + ), +} + +// CastNull returns null if input is null; if not returns error. +var CastNull = &Builtin{ + Name: "cast_null", + Decl: types.NewFunction( + types.Args(types.A), + types.NewNull(), + ), +} + +// CastObject returns the given object if it is null; throws an error otherwise +var CastObject = &Builtin{ + Name: "cast_object", + Decl: types.NewFunction( + types.Args(types.A), + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + ), +} + +// ObjectGet returns takes an object and returns a value under its key if +// present, otherwise it returns the default. +var ObjectGet = &Builtin{ + Name: "object.get", + Decl: types.NewFunction( + types.Args( + types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)), + types.A, + types.A, + ), + types.A, + ), +} + +// Builtin represents a built-in function supported by OPA. Every built-in +// function is uniquely identified by a name. +type Builtin struct { + Name string // Unique name of built-in function, e.g., (arg1,arg2,...,argN) + Infix string // Unique name of infix operator. Default should be unset. + Decl *types.Function // Built-in function type declaration. + Relation bool // Indicates if the built-in acts as a relation. +} + +// Expr creates a new expression for the built-in with the given operands. +func (b *Builtin) Expr(operands ...*Term) *Expr { + ts := make([]*Term, len(operands)+1) + ts[0] = NewTerm(b.Ref()) + for i := range operands { + ts[i+1] = operands[i] + } + return &Expr{ + Terms: ts, + } +} + +// Call creates a new term for the built-in with the given operands. +func (b *Builtin) Call(operands ...*Term) *Term { + call := make(Call, len(operands)+1) + call[0] = NewTerm(b.Ref()) + for i := range operands { + call[i+1] = operands[i] + } + return NewTerm(call) +} + +// Ref returns a Ref that refers to the built-in function. +func (b *Builtin) Ref() Ref { + parts := strings.Split(b.Name, ".") + ref := make(Ref, len(parts)) + ref[0] = VarTerm(parts[0]) + for i := 1; i < len(parts); i++ { + ref[i] = StringTerm(parts[i]) + } + return ref +} + +// IsTargetPos returns true if a variable in the i-th position will be bound by +// evaluating the call expression. +func (b *Builtin) IsTargetPos(i int) bool { + return len(b.Decl.Args()) == i +} + +func init() { + BuiltinMap = map[string]*Builtin{} + for _, b := range DefaultBuiltins { + RegisterBuiltin(b) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/check.go b/vendor/github.com/open-policy-agent/opa/ast/check.go new file mode 100644 index 000000000..cff3be288 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/check.go @@ -0,0 +1,983 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "sort" + "strings" + + "github.com/open-policy-agent/opa/types" + "github.com/open-policy-agent/opa/util" +) + +type rewriteVars func(x Ref) Ref + +// exprChecker defines the interface for executing type checking on a single +// expression. The exprChecker must update the provided TypeEnv with inferred +// types of vars. +type exprChecker func(*TypeEnv, *Expr) *Error + +// typeChecker implements type checking on queries and rules. Errors are +// accumulated on the typeChecker so that a single run can report multiple +// issues. +type typeChecker struct { + errs Errors + exprCheckers map[string]exprChecker + varRewriter rewriteVars +} + +// newTypeChecker returns a new typeChecker object that has no errors. +func newTypeChecker() *typeChecker { + tc := &typeChecker{} + tc.exprCheckers = map[string]exprChecker{ + "eq": tc.checkExprEq, + } + return tc +} + +func (tc *typeChecker) WithVarRewriter(f rewriteVars) *typeChecker { + tc.varRewriter = f + return tc +} + +// CheckBody runs type checking on the body and returns a TypeEnv if no errors +// are found. The resulting TypeEnv wraps the provided one. The resulting +// TypeEnv will be able to resolve types of vars contained in the body. +func (tc *typeChecker) CheckBody(env *TypeEnv, body Body) (*TypeEnv, Errors) { + + if env == nil { + env = NewTypeEnv() + } else { + env = env.wrap() + } + + WalkExprs(body, func(expr *Expr) bool { + + closureErrs := tc.checkClosures(env, expr) + for _, err := range closureErrs { + tc.err(err) + } + + hasClosureErrors := len(closureErrs) > 0 + + vis := newRefChecker(env, tc.varRewriter) + NewGenericVisitor(vis.Visit).Walk(expr) + for _, err := range vis.errs { + tc.err(err) + } + + hasRefErrors := len(vis.errs) > 0 + + if err := tc.checkExpr(env, expr); err != nil { + // Suppress this error if a more actionable one has occurred. In + // this case, if an error occurred in a ref or closure contained in + // this expression, and the error is due to a nil type, then it's + // likely to be the result of the more specific error. + skip := (hasClosureErrors || hasRefErrors) && causedByNilType(err) + if !skip { + tc.err(err) + } + } + + return true + }) + + return env, tc.errs +} + +// CheckTypes runs type checking on the rules returns a TypeEnv if no errors +// are found. The resulting TypeEnv wraps the provided one. The resulting +// TypeEnv will be able to resolve types of refs that refer to rules. +func (tc *typeChecker) CheckTypes(env *TypeEnv, sorted []util.T) (*TypeEnv, Errors) { + if env == nil { + env = NewTypeEnv() + } else { + env = env.wrap() + } + for _, s := range sorted { + tc.checkRule(env, s.(*Rule)) + } + tc.errs.Sort() + return env, tc.errs +} + +func (tc *typeChecker) checkClosures(env *TypeEnv, expr *Expr) Errors { + var result Errors + WalkClosures(expr, func(x interface{}) bool { + switch x := x.(type) { + case *ArrayComprehension: + _, errs := newTypeChecker().WithVarRewriter(tc.varRewriter).CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + case *SetComprehension: + _, errs := newTypeChecker().WithVarRewriter(tc.varRewriter).CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + case *ObjectComprehension: + _, errs := newTypeChecker().WithVarRewriter(tc.varRewriter).CheckBody(env, x.Body) + if len(errs) > 0 { + result = errs + return true + } + } + return false + }) + return result +} + +func (tc *typeChecker) checkLanguageBuiltins(env *TypeEnv, builtins map[string]*Builtin) *TypeEnv { + if env == nil { + env = NewTypeEnv() + } else { + env = env.wrap() + } + for _, bi := range builtins { + env.tree.Put(bi.Ref(), bi.Decl) + } + return env +} + +func (tc *typeChecker) checkRule(env *TypeEnv, rule *Rule) { + + cpy, err := tc.CheckBody(env, rule.Body) + + if len(err) == 0 { + + path := rule.Path() + var tpe types.Type + + if len(rule.Head.Args) > 0 { + + // If args are not referred to in body, infer as any. + WalkVars(rule.Head.Args, func(v Var) bool { + if cpy.Get(v) == nil { + cpy.tree.PutOne(v, types.A) + } + return false + }) + + // Construct function type. + args := make([]types.Type, len(rule.Head.Args)) + for i := 0; i < len(rule.Head.Args); i++ { + args[i] = cpy.Get(rule.Head.Args[i]) + } + + f := types.NewFunction(args, cpy.Get(rule.Head.Value)) + + // Union with existing. + exist := env.tree.Get(path) + tpe = types.Or(exist, f) + + } else { + switch rule.Head.DocKind() { + case CompleteDoc: + typeV := cpy.Get(rule.Head.Value) + if typeV != nil { + exist := env.tree.Get(path) + tpe = types.Or(typeV, exist) + } + case PartialObjectDoc: + typeK := cpy.Get(rule.Head.Key) + typeV := cpy.Get(rule.Head.Value) + if typeK != nil && typeV != nil { + exist := env.tree.Get(path) + typeV = types.Or(types.Values(exist), typeV) + typeK = types.Or(types.Keys(exist), typeK) + tpe = types.NewObject(nil, types.NewDynamicProperty(typeK, typeV)) + } + case PartialSetDoc: + typeK := cpy.Get(rule.Head.Key) + if typeK != nil { + exist := env.tree.Get(path) + typeK = types.Or(types.Keys(exist), typeK) + tpe = types.NewSet(typeK) + } + } + } + + if tpe != nil { + env.tree.Put(path, tpe) + } + } +} + +func (tc *typeChecker) checkExpr(env *TypeEnv, expr *Expr) *Error { + if !expr.IsCall() { + return nil + } + + checker := tc.exprCheckers[expr.Operator().String()] + if checker != nil { + return checker(env, expr) + } + + return tc.checkExprBuiltin(env, expr) +} + +func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error { + + args := expr.Operands() + pre := getArgTypes(env, args) + + // NOTE(tsandall): undefined functions will have been caught earlier in the + // compiler. We check for undefined functions before the safety check so + // that references to non-existent functions result in undefined function + // errors as opposed to unsafe var errors. + // + // We cannot run type checking before the safety check because part of the + // type checker relies on reordering (in particular for references to local + // vars). + name := expr.Operator() + tpe := env.Get(name) + + if tpe == nil { + return NewError(TypeErr, expr.Location, "undefined function %v", name) + } + + ftpe, ok := tpe.(*types.Function) + if !ok { + return NewError(TypeErr, expr.Location, "undefined function %v", name) + } + + maxArgs := len(ftpe.Args()) + expArgs := ftpe.Args() + + if ftpe.Result() != nil { + maxArgs++ + expArgs = append(expArgs, ftpe.Result()) + } + + if len(args) > maxArgs { + return newArgError(expr.Location, name, "too many arguments", pre, expArgs) + } else if len(args) < len(ftpe.Args()) { + return newArgError(expr.Location, name, "too few arguments", pre, expArgs) + } + + for i := range args { + if !unify1(env, args[i], expArgs[i], false) { + post := make([]types.Type, len(args)) + for i := range args { + post[i] = env.Get(args[i]) + } + return newArgError(expr.Location, name, "invalid argument(s)", post, expArgs) + } + } + + return nil +} + +func (tc *typeChecker) checkExprEq(env *TypeEnv, expr *Expr) *Error { + + pre := getArgTypes(env, expr.Operands()) + exp := Equality.Decl.Args() + + if len(pre) < len(exp) { + return newArgError(expr.Location, expr.Operator(), "too few arguments", pre, exp) + } else if len(exp) < len(pre) { + return newArgError(expr.Location, expr.Operator(), "too many arguments", pre, exp) + } + + a, b := expr.Operand(0), expr.Operand(1) + typeA, typeB := env.Get(a), env.Get(b) + + if !unify2(env, a, typeA, b, typeB) { + err := NewError(TypeErr, expr.Location, "match error") + err.Details = &UnificationErrDetail{ + Left: typeA, + Right: typeB, + } + return err + } + + return nil +} + +func unify2(env *TypeEnv, a *Term, typeA types.Type, b *Term, typeB types.Type) bool { + + nilA := types.Nil(typeA) + nilB := types.Nil(typeB) + + if nilA && !nilB { + return unify1(env, a, typeB, false) + } else if nilB && !nilA { + return unify1(env, b, typeA, false) + } else if !nilA && !nilB { + return unifies(typeA, typeB) + } + + switch a.Value.(type) { + case Array: + return unify2Array(env, a, typeA, b, typeB) + case Object: + return unify2Object(env, a, typeA, b, typeB) + case Var: + switch b.Value.(type) { + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.Get(a), false) + case Array: + return unify2Array(env, b, typeB, a, typeA) + case Object: + return unify2Object(env, b, typeB, a, typeA) + } + } + + return false +} + +func unify2Array(env *TypeEnv, a *Term, typeA types.Type, b *Term, typeB types.Type) bool { + arr := a.Value.(Array) + switch bv := b.Value.(type) { + case Array: + if len(arr) == len(bv) { + for i := range arr { + if !unify2(env, arr[i], env.Get(arr[i]), bv[i], env.Get(bv[i])) { + return false + } + } + return true + } + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.Get(a), false) + } + return false +} + +func unify2Object(env *TypeEnv, a *Term, typeA types.Type, b *Term, typeB types.Type) bool { + obj := a.Value.(Object) + switch bv := b.Value.(type) { + case Object: + cv := obj.Intersect(bv) + if obj.Len() == bv.Len() && bv.Len() == len(cv) { + for i := range cv { + if !unify2(env, cv[i][1], env.Get(cv[i][1]), cv[i][2], env.Get(cv[i][2])) { + return false + } + } + return true + } + case Var: + return unify1(env, a, types.A, false) && unify1(env, b, env.Get(a), false) + } + return false +} + +func unify1(env *TypeEnv, term *Term, tpe types.Type, union bool) bool { + switch v := term.Value.(type) { + case Array: + switch tpe := tpe.(type) { + case *types.Array: + return unify1Array(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + for i := range v { + unify1(env, v[i], types.A, true) + } + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case Object: + switch tpe := tpe.(type) { + case *types.Object: + return unify1Object(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + v.Foreach(func(key, value *Term) { + unify1(env, key, types.A, true) + unify1(env, value, types.A, true) + }) + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case Set: + switch tpe := tpe.(type) { + case *types.Set: + return unify1Set(env, v, tpe, union) + case types.Any: + if types.Compare(tpe, types.A) == 0 { + v.Foreach(func(elem *Term) { + unify1(env, elem, types.A, true) + }) + return true + } + unifies := false + for i := range tpe { + unifies = unify1(env, term, tpe[i], true) || unifies + } + return unifies + } + return false + case Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return unifies(env.Get(v), tpe) + case Var: + if !union { + if exist := env.Get(v); exist != nil { + return unifies(exist, tpe) + } + env.tree.PutOne(term.Value, tpe) + } else { + env.tree.PutOne(term.Value, types.Or(env.Get(v), tpe)) + } + return true + default: + if !IsConstant(v) { + panic("unreachable") + } + return unifies(env.Get(term), tpe) + } +} + +func unify1Array(env *TypeEnv, val Array, tpe *types.Array, union bool) bool { + if len(val) != tpe.Len() && tpe.Dynamic() == nil { + return false + } + for i := range val { + if !unify1(env, val[i], tpe.Select(i), union) { + return false + } + } + return true +} + +func unify1Object(env *TypeEnv, val Object, tpe *types.Object, union bool) bool { + if val.Len() != len(tpe.Keys()) && tpe.DynamicValue() == nil { + return false + } + stop := val.Until(func(k, v *Term) bool { + if IsConstant(k.Value) { + if child := selectConstant(tpe, k); child != nil { + if !unify1(env, v, child, union) { + return true + } + } else { + return true + } + } else { + // Inferring type of value under dynamic key would involve unioning + // with all property values of tpe whose keys unify. For now, type + // these values as Any. We can investigate stricter inference in + // the future. + unify1(env, v, types.A, union) + } + return false + }) + return !stop +} + +func unify1Set(env *TypeEnv, val Set, tpe *types.Set, union bool) bool { + of := types.Values(tpe) + return !val.Until(func(elem *Term) bool { + return !unify1(env, elem, of, union) + }) +} + +func (tc *typeChecker) err(err *Error) { + tc.errs = append(tc.errs, err) +} + +type refChecker struct { + env *TypeEnv + errs Errors + varRewriter rewriteVars +} + +func newRefChecker(env *TypeEnv, f rewriteVars) *refChecker { + + if f == nil { + f = rewriteVarsNop + } + + return &refChecker{ + env: env, + errs: nil, + varRewriter: f, + } +} + +func (rc *refChecker) Visit(x interface{}) bool { + switch x := x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + case *Expr: + switch terms := x.Terms.(type) { + case []*Term: + for i := 1; i < len(terms); i++ { + NewGenericVisitor(rc.Visit).Walk(terms[i]) + } + return true + case *Term: + NewGenericVisitor(rc.Visit).Walk(terms) + return true + } + case Ref: + if err := rc.checkApply(rc.env, x); err != nil { + rc.errs = append(rc.errs, err) + return true + } + if err := rc.checkRef(rc.env, rc.env.tree, x, 0); err != nil { + rc.errs = append(rc.errs, err) + } + } + return false +} + +func (rc *refChecker) checkApply(curr *TypeEnv, ref Ref) *Error { + if tpe := curr.Get(ref); tpe != nil { + if _, ok := tpe.(*types.Function); ok { + return newRefErrUnsupported(ref[0].Location, rc.varRewriter(ref), len(ref)-1, tpe) + } + } + return nil +} + +func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx int) *Error { + + if idx == len(ref) { + return nil + } + + head := ref[idx] + + // Handle constant ref operands, i.e., strings or the ref head. + if _, ok := head.Value.(String); ok || idx == 0 { + + child := node.Child(head.Value) + if child == nil { + + if curr.next != nil { + next := curr.next + return rc.checkRef(next, next.tree, ref, 0) + } + + if RootDocumentNames.Contains(ref[0]) { + return rc.checkRefLeaf(types.A, ref, 1) + } + + return rc.checkRefLeaf(types.A, ref, 0) + } + + if child.Leaf() { + return rc.checkRefLeaf(child.Value(), ref, idx+1) + } + + return rc.checkRef(curr, child, ref, idx+1) + } + + // Handle dynamic ref operands. + switch value := head.Value.(type) { + + case Var: + + if exist := rc.env.Get(value); exist != nil { + if !unifies(types.S, exist) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, types.S, getOneOfForNode(node)) + } + } else { + rc.env.tree.PutOne(value, types.S) + } + + case Ref: + + exist := rc.env.Get(value) + if exist == nil { + // If ref type is unknown, an error will already be reported so + // stop here. + return nil + } + + if !unifies(types.S, exist) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, types.S, getOneOfForNode(node)) + } + + // Catch other ref operand types here. Non-leaf nodes must be referred to + // with string values. + default: + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, nil, types.S, getOneOfForNode(node)) + } + + // Run checking on remaining portion of the ref. Note, since the ref + // potentially refers to data for which no type information exists, + // checking should never fail. + node.Children().Iter(func(_, child util.T) bool { + rc.checkRef(curr, child.(*typeTreeNode), ref, idx+1) + return false + }) + + return nil +} + +func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error { + + if idx == len(ref) { + return nil + } + + head := ref[idx] + + keys := types.Keys(tpe) + if keys == nil { + return newRefErrUnsupported(ref[0].Location, rc.varRewriter(ref), idx-1, tpe) + } + + switch value := head.Value.(type) { + + case Var: + if exist := rc.env.Get(value); exist != nil { + if !unifies(exist, keys) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, keys, getOneOfForType(tpe)) + } + } else { + rc.env.tree.PutOne(value, types.Keys(tpe)) + } + + case Ref: + if exist := rc.env.Get(value); exist != nil { + if !unifies(exist, keys) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, keys, getOneOfForType(tpe)) + } + } + + case Array, Object, Set: + // Composite references operands may only be used with a set. + if !unifies(tpe, types.NewSet(types.A)) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, tpe, types.NewSet(types.A), nil) + } + if !unify1(rc.env, head, keys, false) { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, rc.env.Get(head), keys, nil) + } + + default: + child := selectConstant(tpe, head) + if child == nil { + return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, nil, types.Keys(tpe), getOneOfForType(tpe)) + } + return rc.checkRefLeaf(child, ref, idx+1) + } + + return rc.checkRefLeaf(types.Values(tpe), ref, idx+1) +} + +func unifies(a, b types.Type) bool { + + if a == nil || b == nil { + return false + } + + anyA, ok1 := a.(types.Any) + if ok1 { + if unifiesAny(anyA, b) { + return true + } + } + + anyB, ok2 := b.(types.Any) + if ok2 { + if unifiesAny(anyB, a) { + return true + } + } + + if ok1 || ok2 { + return false + } + + switch a := a.(type) { + case types.Null: + _, ok := b.(types.Null) + return ok + case types.Boolean: + _, ok := b.(types.Boolean) + return ok + case types.Number: + _, ok := b.(types.Number) + return ok + case types.String: + _, ok := b.(types.String) + return ok + case *types.Array: + b, ok := b.(*types.Array) + if !ok { + return false + } + return unifiesArrays(a, b) + case *types.Object: + b, ok := b.(*types.Object) + if !ok { + return false + } + return unifiesObjects(a, b) + case *types.Set: + b, ok := b.(*types.Set) + if !ok { + return false + } + return unifies(types.Values(a), types.Values(b)) + case *types.Function: + // TODO(tsandall): revisit once functions become first-class values. + return false + default: + panic("unreachable") + } +} + +func unifiesAny(a types.Any, b types.Type) bool { + if _, ok := b.(*types.Function); ok { + return false + } + for i := range a { + if unifies(a[i], b) { + return true + } + } + return len(a) == 0 +} + +func unifiesArrays(a, b *types.Array) bool { + + if !unifiesArraysStatic(a, b) { + return false + } + + if !unifiesArraysStatic(b, a) { + return false + } + + return a.Dynamic() == nil || b.Dynamic() == nil || unifies(a.Dynamic(), b.Dynamic()) +} + +func unifiesArraysStatic(a, b *types.Array) bool { + if a.Len() != 0 { + for i := 0; i < a.Len(); i++ { + if !unifies(a.Select(i), b.Select(i)) { + return false + } + } + } + return true +} + +func unifiesObjects(a, b *types.Object) bool { + if !unifiesObjectsStatic(a, b) { + return false + } + + if !unifiesObjectsStatic(b, a) { + return false + } + + return a.DynamicValue() == nil || b.DynamicValue() == nil || unifies(a.DynamicValue(), b.DynamicValue()) +} + +func unifiesObjectsStatic(a, b *types.Object) bool { + for _, k := range a.Keys() { + if !unifies(a.Select(k), b.Select(k)) { + return false + } + } + return true +} + +// typeErrorCause defines an interface to determine the reason for a type +// error. The type error details implement this interface so that type checking +// can report more actionable errors. +type typeErrorCause interface { + nilType() bool +} + +func causedByNilType(err *Error) bool { + cause, ok := err.Details.(typeErrorCause) + if !ok { + return false + } + return cause.nilType() +} + +// ArgErrDetail represents a generic argument error. +type ArgErrDetail struct { + Have []types.Type `json:"have"` + Want []types.Type `json:"want"` +} + +// Lines returns the string representation of the detail. +func (d *ArgErrDetail) Lines() []string { + lines := make([]string, 2) + lines[0] = fmt.Sprint("have: ", formatArgs(d.Have)) + lines[1] = fmt.Sprint("want: ", formatArgs(d.Want)) + return lines +} + +func (d *ArgErrDetail) nilType() bool { + for i := range d.Have { + if types.Nil(d.Have[i]) { + return true + } + } + return false +} + +// UnificationErrDetail describes a type mismatch error when two values are +// unified (e.g., x = [1,2,y]). +type UnificationErrDetail struct { + Left types.Type `json:"a"` + Right types.Type `json:"b"` +} + +func (a *UnificationErrDetail) nilType() bool { + return types.Nil(a.Left) || types.Nil(a.Right) +} + +// Lines returns the string representation of the detail. +func (a *UnificationErrDetail) Lines() []string { + lines := make([]string, 2) + lines[0] = fmt.Sprint("left : ", types.Sprint(a.Left)) + lines[1] = fmt.Sprint("right : ", types.Sprint(a.Right)) + return lines +} + +// RefErrUnsupportedDetail describes an undefined reference error where the +// referenced value does not support dereferencing (e.g., scalars). +type RefErrUnsupportedDetail struct { + Ref Ref `json:"ref"` // invalid ref + Pos int `json:"pos"` // invalid element + Have types.Type `json:"have"` // referenced type +} + +// Lines returns the string representation of the detail. +func (r *RefErrUnsupportedDetail) Lines() []string { + lines := []string{ + r.Ref.String(), + strings.Repeat("^", len(r.Ref[:r.Pos+1].String())), + fmt.Sprintf("have: %v", r.Have), + } + return lines +} + +// RefErrInvalidDetail describes an undefined reference error where the referenced +// value does not support the reference operand (e.g., missing object key, +// invalid key type, etc.) +type RefErrInvalidDetail struct { + Ref Ref `json:"ref"` // invalid ref + Pos int `json:"pos"` // invalid element + Have types.Type `json:"have,omitempty"` // type of invalid element (for var/ref elements) + Want types.Type `json:"want"` // allowed type (for non-object values) + OneOf []Value `json:"oneOf"` // allowed values (e.g., for object keys) +} + +// Lines returns the string representation of the detail. +func (r *RefErrInvalidDetail) Lines() []string { + lines := []string{r.Ref.String()} + offset := len(r.Ref[:r.Pos].String()) + 1 + pad := strings.Repeat(" ", offset) + lines = append(lines, fmt.Sprintf("%s^", pad)) + if r.Have != nil { + lines = append(lines, fmt.Sprintf("%shave (type): %v", pad, r.Have)) + } else { + lines = append(lines, fmt.Sprintf("%shave: %v", pad, r.Ref[r.Pos])) + } + if len(r.OneOf) > 0 { + lines = append(lines, fmt.Sprintf("%swant (one of): %v", pad, r.OneOf)) + } else { + lines = append(lines, fmt.Sprintf("%swant (type): %v", pad, r.Want)) + } + return lines +} + +func formatArgs(args []types.Type) string { + buf := make([]string, len(args)) + for i := range args { + buf[i] = types.Sprint(args[i]) + } + return "(" + strings.Join(buf, ", ") + ")" +} + +func newRefErrInvalid(loc *Location, ref Ref, idx int, have, want types.Type, oneOf []Value) *Error { + err := newRefError(loc, ref) + err.Details = &RefErrInvalidDetail{ + Ref: ref, + Pos: idx, + Have: have, + Want: want, + OneOf: oneOf, + } + return err +} + +func newRefErrUnsupported(loc *Location, ref Ref, idx int, have types.Type) *Error { + err := newRefError(loc, ref) + err.Details = &RefErrUnsupportedDetail{ + Ref: ref, + Pos: idx, + Have: have, + } + return err +} + +func newRefError(loc *Location, ref Ref) *Error { + return NewError(TypeErr, loc, "undefined ref: %v", ref) +} + +func newArgError(loc *Location, builtinName Ref, msg string, have []types.Type, want []types.Type) *Error { + err := NewError(TypeErr, loc, "%v: %v", builtinName, msg) + err.Details = &ArgErrDetail{ + Have: have, + Want: want, + } + return err +} + +func getOneOfForNode(node *typeTreeNode) (result []Value) { + node.Children().Iter(func(k, _ util.T) bool { + result = append(result, k.(Value)) + return false + }) + + sortValueSlice(result) + return result +} + +func getOneOfForType(tpe types.Type) (result []Value) { + switch tpe := tpe.(type) { + case *types.Object: + for _, k := range tpe.Keys() { + v, err := InterfaceToValue(k) + if err != nil { + panic(err) + } + result = append(result, v) + } + } + sortValueSlice(result) + return result +} + +func sortValueSlice(sl []Value) { + sort.Slice(sl, func(i, j int) bool { + return sl[i].Compare(sl[j]) < 0 + }) +} + +func getArgTypes(env *TypeEnv, args []*Term) []types.Type { + pre := make([]types.Type, len(args)) + for i := range args { + pre[i] = env.Get(args[i]) + } + return pre +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/compare.go b/vendor/github.com/open-policy-agent/opa/ast/compare.go new file mode 100644 index 000000000..46bf71c41 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/compare.go @@ -0,0 +1,327 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + "fmt" + "math/big" +) + +// Compare returns an integer indicating whether two AST values are less than, +// equal to, or greater than each other. +// +// If a is less than b, the return value is negative. If a is greater than b, +// the return value is positive. If a is equal to b, the return value is zero. +// +// Different types are never equal to each other. For comparison purposes, types +// are sorted as follows: +// +// nil < Null < Boolean < Number < String < Var < Ref < Array < Object < Set < +// ArrayComprehension < ObjectComprehension < SetComprehension < Expr < SomeDecl +// < With < Body < Rule < Import < Package < Module. +// +// Arrays and Refs are equal iff both a and b have the same length and all +// corresponding elements are equal. If one element is not equal, the return +// value is the same as for the first differing element. If all elements are +// equal but a and b have different lengths, the shorter is considered less than +// the other. +// +// Objects are considered equal iff both a and b have the same sorted (key, +// value) pairs and are of the same length. Other comparisons are consistent but +// not defined. +// +// Sets are considered equal iff the symmetric difference of a and b is empty. +// Other comparisons are consistent but not defined. +func Compare(a, b interface{}) int { + + if t, ok := a.(*Term); ok { + if t == nil { + a = nil + } else { + a = t.Value + } + } + + if t, ok := b.(*Term); ok { + if t == nil { + b = nil + } else { + b = t.Value + } + } + + if a == nil { + if b == nil { + return 0 + } + return -1 + } + if b == nil { + return 1 + } + + sortA := sortOrder(a) + sortB := sortOrder(b) + + if sortA < sortB { + return -1 + } else if sortB < sortA { + return 1 + } + + switch a := a.(type) { + case Null: + return 0 + case Boolean: + b := b.(Boolean) + if a.Equal(b) { + return 0 + } + if !a { + return -1 + } + return 1 + case Number: + if ai, err := json.Number(a).Int64(); err == nil { + if bi, err := json.Number(b.(Number)).Int64(); err == nil { + if ai == bi { + return 0 + } + if ai < bi { + return -1 + } + return 1 + } + } + + bigA, ok := new(big.Float).SetString(string(a)) + if !ok { + panic("illegal value") + } + bigB, ok := new(big.Float).SetString(string(b.(Number))) + if !ok { + panic("illegal value") + } + return bigA.Cmp(bigB) + case String: + b := b.(String) + if a.Equal(b) { + return 0 + } + if a < b { + return -1 + } + return 1 + case Var: + b := b.(Var) + if a.Equal(b) { + return 0 + } + if a < b { + return -1 + } + return 1 + case Ref: + b := b.(Ref) + return termSliceCompare(a, b) + case Array: + b := b.(Array) + return termSliceCompare(a, b) + case Object: + b := b.(Object) + return a.Compare(b) + case Set: + b := b.(Set) + return a.Compare(b) + case *ArrayComprehension: + b := b.(*ArrayComprehension) + if cmp := Compare(a.Term, b.Term); cmp != 0 { + return cmp + } + return Compare(a.Body, b.Body) + case *ObjectComprehension: + b := b.(*ObjectComprehension) + if cmp := Compare(a.Key, b.Key); cmp != 0 { + return cmp + } + if cmp := Compare(a.Value, b.Value); cmp != 0 { + return cmp + } + return Compare(a.Body, b.Body) + case *SetComprehension: + b := b.(*SetComprehension) + if cmp := Compare(a.Term, b.Term); cmp != 0 { + return cmp + } + return Compare(a.Body, b.Body) + case Call: + b := b.(Call) + return termSliceCompare(a, b) + case *Expr: + b := b.(*Expr) + return a.Compare(b) + case *SomeDecl: + b := b.(*SomeDecl) + return a.Compare(b) + case *With: + b := b.(*With) + return a.Compare(b) + case Body: + b := b.(Body) + return a.Compare(b) + case *Head: + b := b.(*Head) + return a.Compare(b) + case *Rule: + b := b.(*Rule) + return a.Compare(b) + case Args: + b := b.(Args) + return termSliceCompare(a, b) + case *Import: + b := b.(*Import) + return a.Compare(b) + case *Package: + b := b.(*Package) + return a.Compare(b) + case *Module: + b := b.(*Module) + return a.Compare(b) + } + panic(fmt.Sprintf("illegal value: %T", a)) +} + +type termSlice []*Term + +func (s termSlice) Less(i, j int) bool { return Compare(s[i].Value, s[j].Value) < 0 } +func (s termSlice) Swap(i, j int) { x := s[i]; s[i] = s[j]; s[j] = x } +func (s termSlice) Len() int { return len(s) } + +func sortOrder(x interface{}) int { + switch x.(type) { + case Null: + return 0 + case Boolean: + return 1 + case Number: + return 2 + case String: + return 3 + case Var: + return 4 + case Ref: + return 5 + case Array: + return 6 + case Object: + return 7 + case Set: + return 8 + case *ArrayComprehension: + return 9 + case *ObjectComprehension: + return 10 + case *SetComprehension: + return 11 + case Call: + return 12 + case Args: + return 13 + case *Expr: + return 100 + case *SomeDecl: + return 101 + case *With: + return 110 + case *Head: + return 120 + case Body: + return 200 + case *Rule: + return 1000 + case *Import: + return 1001 + case *Package: + return 1002 + case *Module: + return 10000 + } + panic(fmt.Sprintf("illegal value: %T", x)) +} + +func importsCompare(a, b []*Import) int { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for i := 0; i < minLen; i++ { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } + if len(b) < len(a) { + return 1 + } + return 0 +} + +func rulesCompare(a, b []*Rule) int { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for i := 0; i < minLen; i++ { + if cmp := a[i].Compare(b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } + if len(b) < len(a) { + return 1 + } + return 0 +} + +func termSliceCompare(a, b []*Term) int { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for i := 0; i < minLen; i++ { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} + +func withSliceCompare(a, b []*With) int { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for i := 0; i < minLen; i++ { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/compile.go b/vendor/github.com/open-policy-agent/opa/ast/compile.go new file mode 100644 index 000000000..56edbe52c --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/compile.go @@ -0,0 +1,3419 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "sort" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/metrics" + "github.com/open-policy-agent/opa/util" +) + +// CompileErrorLimitDefault is the default number errors a compiler will allow before +// exiting. +const CompileErrorLimitDefault = 10 + +var errLimitReached = NewError(CompileErr, nil, "error limit reached") + +// Compiler contains the state of a compilation process. +type Compiler struct { + + // Errors contains errors that occurred during the compilation process. + // If there are one or more errors, the compilation process is considered + // "failed". + Errors Errors + + // Modules contains the compiled modules. The compiled modules are the + // output of the compilation process. If the compilation process failed, + // there is no guarantee about the state of the modules. + Modules map[string]*Module + + // ModuleTree organizes the modules into a tree where each node is keyed by + // an element in the module's package path. E.g., given modules containing + // the following package directives: "a", "a.b", "a.c", and "a.b", the + // resulting module tree would be: + // + // root + // | + // +--- data (no modules) + // | + // +--- a (1 module) + // | + // +--- b (2 modules) + // | + // +--- c (1 module) + // + ModuleTree *ModuleTreeNode + + // RuleTree organizes rules into a tree where each node is keyed by an + // element in the rule's path. The rule path is the concatenation of the + // containing package and the stringified rule name. E.g., given the + // following module: + // + // package ex + // p[1] { true } + // p[2] { true } + // q = true + // + // root + // | + // +--- data (no rules) + // | + // +--- ex (no rules) + // | + // +--- p (2 rules) + // | + // +--- q (1 rule) + RuleTree *TreeNode + + // Graph contains dependencies between rules. An edge (u,v) is added to the + // graph if rule 'u' refers to the virtual document defined by 'v'. + Graph *Graph + + // TypeEnv holds type information for values inferred by the compiler. + TypeEnv *TypeEnv + + // RewrittenVars is a mapping of variables that have been rewritten + // with the key being the generated name and value being the original. + RewrittenVars map[Var]Var + + localvargen *localVarGenerator + moduleLoader ModuleLoader + ruleIndices *util.HashMap + stages []struct { + name string + metricName string + f func() + } + maxErrs int + sorted []string // list of sorted module names + pathExists func([]string) (bool, error) + after map[string][]CompilerStageDefinition + metrics metrics.Metrics + builtins map[string]*Builtin + unsafeBuiltinsMap map[string]struct{} +} + +// CompilerStage defines the interface for stages in the compiler. +type CompilerStage func(*Compiler) *Error + +// CompilerStageDefinition defines a compiler stage +type CompilerStageDefinition struct { + Name string + MetricName string + Stage CompilerStage +} + +// QueryContext contains contextual information for running an ad-hoc query. +// +// Ad-hoc queries can be run in the context of a package and imports may be +// included to provide concise access to data. +type QueryContext struct { + Package *Package + Imports []*Import +} + +// NewQueryContext returns a new QueryContext object. +func NewQueryContext() *QueryContext { + return &QueryContext{} +} + +// WithPackage sets the pkg on qc. +func (qc *QueryContext) WithPackage(pkg *Package) *QueryContext { + if qc == nil { + qc = NewQueryContext() + } + qc.Package = pkg + return qc +} + +// WithImports sets the imports on qc. +func (qc *QueryContext) WithImports(imports []*Import) *QueryContext { + if qc == nil { + qc = NewQueryContext() + } + qc.Imports = imports + return qc +} + +// Copy returns a deep copy of qc. +func (qc *QueryContext) Copy() *QueryContext { + if qc == nil { + return nil + } + cpy := *qc + if cpy.Package != nil { + cpy.Package = qc.Package.Copy() + } + cpy.Imports = make([]*Import, len(qc.Imports)) + for i := range qc.Imports { + cpy.Imports[i] = qc.Imports[i].Copy() + } + return &cpy +} + +// QueryCompiler defines the interface for compiling ad-hoc queries. +type QueryCompiler interface { + + // Compile should be called to compile ad-hoc queries. The return value is + // the compiled version of the query. + Compile(q Body) (Body, error) + + // TypeEnv returns the type environment built after running type checking + // on the query. + TypeEnv() *TypeEnv + + // WithContext sets the QueryContext on the QueryCompiler. Subsequent calls + // to Compile will take the QueryContext into account. + WithContext(qctx *QueryContext) QueryCompiler + + // WithUnsafeBuiltins sets the built-in functions to treat as unsafe and not + // allow inside of queries. By default the query compiler inherits the + // compiler's unsafe built-in functions. This function allows callers to + // override that set. If an empty (non-nil) map is provided, all built-ins + // are allowed. + WithUnsafeBuiltins(unsafe map[string]struct{}) QueryCompiler + + // WithStageAfter registers a stage to run during query compilation after + // the named stage. + WithStageAfter(after string, stage QueryCompilerStageDefinition) QueryCompiler + + // RewrittenVars maps generated vars in the compiled query to vars from the + // parsed query. For example, given the query "input := 1" the rewritten + // query would be "__local0__ = 1". The mapping would then be {__local0__: input}. + RewrittenVars() map[Var]Var +} + +// QueryCompilerStage defines the interface for stages in the query compiler. +type QueryCompilerStage func(QueryCompiler, Body) (Body, error) + +// QueryCompilerStageDefinition defines a QueryCompiler stage +type QueryCompilerStageDefinition struct { + Name string + MetricName string + Stage QueryCompilerStage +} + +const compileStageMetricPrefex = "ast_compile_stage_" + +// NewCompiler returns a new empty compiler. +func NewCompiler() *Compiler { + + c := &Compiler{ + Modules: map[string]*Module{}, + TypeEnv: NewTypeEnv(), + RewrittenVars: map[Var]Var{}, + ruleIndices: util.NewHashMap(func(a, b util.T) bool { + r1, r2 := a.(Ref), b.(Ref) + return r1.Equal(r2) + }, func(x util.T) int { + return x.(Ref).Hash() + }), + maxErrs: CompileErrorLimitDefault, + after: map[string][]CompilerStageDefinition{}, + unsafeBuiltinsMap: map[string]struct{}{}, + } + + c.ModuleTree = NewModuleTree(nil) + c.RuleTree = NewRuleTree(c.ModuleTree) + + // Initialize the compiler with the statically compiled built-in functions. + // If the caller customizes the compiler, a copy will be made. + c.builtins = BuiltinMap + checker := newTypeChecker() + c.TypeEnv = checker.checkLanguageBuiltins(nil, c.builtins) + + c.stages = []struct { + name string + metricName string + f func() + }{ + // Reference resolution should run first as it may be used to lazily + // load additional modules. If any stages run before resolution, they + // need to be re-run after resolution. + {"ResolveRefs", "compile_stage_resolve_refs", c.resolveAllRefs}, + + // The local variable generator must be initialized after references are + // resolved and the dynamic module loader has run but before subsequent + // stages that need to generate variables. + {"InitLocalVarGen", "compile_stage_init_local_var_gen", c.initLocalVarGen}, + + {"RewriteLocalVars", "compile_stage_rewrite_local_vars", c.rewriteLocalVars}, + {"RewriteExprTerms", "compile_stage_rewrite_expr_terms", c.rewriteExprTerms}, + {"SetModuleTree", "compile_stage_set_module_tree", c.setModuleTree}, + {"SetRuleTree", "compile_stage_set_rule_tree", c.setRuleTree}, + {"SetGraph", "compile_stage_set_graph", c.setGraph}, + {"RewriteComprehensionTerms", "compile_stage_rewrite_comprehension_terms", c.rewriteComprehensionTerms}, + {"RewriteRefsInHead", "compile_stage_rewrite_refs_in_head", c.rewriteRefsInHead}, + {"RewriteWithValues", "compile_stage_rewrite_with_values", c.rewriteWithModifiers}, + {"CheckRuleConflicts", "compile_stage_check_rule_conflicts", c.checkRuleConflicts}, + {"CheckUndefinedFuncs", "compile_stage_check_undefined_funcs", c.checkUndefinedFuncs}, + {"CheckSafetyRuleHeads", "compile_stage_check_safety_rule_heads", c.checkSafetyRuleHeads}, + {"CheckSafetyRuleBodies", "compile_stage_check_safety_rule_bodies", c.checkSafetyRuleBodies}, + {"RewriteEquals", "compile_stage_rewrite_equals", c.rewriteEquals}, + {"RewriteDynamicTerms", "compile_stage_rewrite_dynamic_terms", c.rewriteDynamicTerms}, + {"CheckRecursion", "compile_stage_check_recursion", c.checkRecursion}, + {"CheckTypes", "compile_stage_check_types", c.checkTypes}, + {"CheckUnsafeBuiltins", "compile_state_check_unsafe_builtins", c.checkUnsafeBuiltins}, + {"BuildRuleIndices", "compile_stage_rebuild_indices", c.buildRuleIndices}, + } + + return c +} + +// SetErrorLimit sets the number of errors the compiler can encounter before it +// quits. Zero or a negative number indicates no limit. +func (c *Compiler) SetErrorLimit(limit int) *Compiler { + c.maxErrs = limit + return c +} + +// WithPathConflictsCheck enables base-virtual document conflict +// detection. The compiler will check that rules don't overlap with +// paths that exist as determined by the provided callable. +func (c *Compiler) WithPathConflictsCheck(fn func([]string) (bool, error)) *Compiler { + c.pathExists = fn + return c +} + +// WithStageAfter registers a stage to run during compilation after +// the named stage. +func (c *Compiler) WithStageAfter(after string, stage CompilerStageDefinition) *Compiler { + c.after[after] = append(c.after[after], stage) + return c +} + +// WithMetrics will set a metrics.Metrics and be used for profiling +// the Compiler instance. +func (c *Compiler) WithMetrics(metrics metrics.Metrics) *Compiler { + c.metrics = metrics + return c +} + +// WithBuiltins adds a set of custom built-in functions to the compiler. +func (c *Compiler) WithBuiltins(builtins map[string]*Builtin) *Compiler { + if len(builtins) == 0 { + return c + } + cpy := make(map[string]*Builtin, len(c.builtins)+len(builtins)) + for k, v := range c.builtins { + cpy[k] = v + } + for k, v := range builtins { + cpy[k] = v + } + c.builtins = cpy + // Build type env for custom functions and wrap existing one. + checker := newTypeChecker() + c.TypeEnv = checker.checkLanguageBuiltins(c.TypeEnv, builtins) + return c +} + +// WithUnsafeBuiltins will add all built-ins in the map to the "blacklist". +func (c *Compiler) WithUnsafeBuiltins(unsafeBuiltins map[string]struct{}) *Compiler { + for name := range unsafeBuiltins { + c.unsafeBuiltinsMap[name] = struct{}{} + } + return c +} + +// QueryCompiler returns a new QueryCompiler object. +func (c *Compiler) QueryCompiler() QueryCompiler { + return newQueryCompiler(c) +} + +// Compile runs the compilation process on the input modules. The compiled +// version of the modules and associated data structures are stored on the +// compiler. If the compilation process fails for any reason, the compiler will +// contain a slice of errors. +func (c *Compiler) Compile(modules map[string]*Module) { + + c.Modules = make(map[string]*Module, len(modules)) + + for k, v := range modules { + c.Modules[k] = v.Copy() + c.sorted = append(c.sorted, k) + } + + sort.Strings(c.sorted) + + c.compile() +} + +// Failed returns true if a compilation error has been encountered. +func (c *Compiler) Failed() bool { + return len(c.Errors) > 0 +} + +// GetArity returns the number of args a function referred to by ref takes. If +// ref refers to built-in function, the built-in declaration is consulted, +// otherwise, the ref is used to perform a ruleset lookup. +func (c *Compiler) GetArity(ref Ref) int { + if bi := c.builtins[ref.String()]; bi != nil { + return len(bi.Decl.Args()) + } + rules := c.GetRulesExact(ref) + if len(rules) == 0 { + return -1 + } + return len(rules[0].Head.Args) +} + +// GetRulesExact returns a slice of rules referred to by the reference. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[k] = v { ... } # rule1 +// p[k1] = v1 { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesExact("data.a.b.c.p") => [rule1, rule2] +// GetRulesExact("data.a.b.c.p.x") => nil +// GetRulesExact("data.a.b.c") => nil +func (c *Compiler) GetRulesExact(ref Ref) (rules []*Rule) { + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + } + + return extractRules(node.Values) +} + +// GetRulesForVirtualDocument returns a slice of rules that produce the virtual +// document referred to by the reference. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[k] = v { ... } # rule1 +// p[k1] = v1 { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesForVirtualDocument("data.a.b.c.p") => [rule1, rule2] +// GetRulesForVirtualDocument("data.a.b.c.p.x") => [rule1, rule2] +// GetRulesForVirtualDocument("data.a.b.c") => nil +func (c *Compiler) GetRulesForVirtualDocument(ref Ref) (rules []*Rule) { + + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + if len(node.Values) > 0 { + return extractRules(node.Values) + } + } + + return extractRules(node.Values) +} + +// GetRulesWithPrefix returns a slice of rules that share the prefix ref. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[x] = y { ... } # rule1 +// p[k] = v { ... } # rule2 +// q { ... } # rule3 +// +// The following calls yield the rules on the right. +// +// GetRulesWithPrefix("data.a.b.c.p") => [rule1, rule2] +// GetRulesWithPrefix("data.a.b.c.p.a") => nil +// GetRulesWithPrefix("data.a.b.c") => [rule1, rule2, rule3] +func (c *Compiler) GetRulesWithPrefix(ref Ref) (rules []*Rule) { + + node := c.RuleTree + + for _, x := range ref { + if node = node.Child(x.Value); node == nil { + return nil + } + } + + var acc func(node *TreeNode) + + acc = func(node *TreeNode) { + rules = append(rules, extractRules(node.Values)...) + for _, child := range node.Children { + if child.Hide { + continue + } + acc(child) + } + } + + acc(node) + + return rules +} + +func extractRules(s []util.T) (rules []*Rule) { + for _, r := range s { + rules = append(rules, r.(*Rule)) + } + return rules +} + +// GetRules returns a slice of rules that are referred to by ref. +// +// E.g., given the following module: +// +// package a.b.c +// +// p[x] = y { q[x] = y; ... } # rule1 +// q[x] = y { ... } # rule2 +// +// The following calls yield the rules on the right. +// +// GetRules("data.a.b.c.p") => [rule1] +// GetRules("data.a.b.c.p.x") => [rule1] +// GetRules("data.a.b.c.q") => [rule2] +// GetRules("data.a.b.c") => [rule1, rule2] +// GetRules("data.a.b.d") => nil +func (c *Compiler) GetRules(ref Ref) (rules []*Rule) { + + set := map[*Rule]struct{}{} + + for _, rule := range c.GetRulesForVirtualDocument(ref) { + set[rule] = struct{}{} + } + + for _, rule := range c.GetRulesWithPrefix(ref) { + set[rule] = struct{}{} + } + + for rule := range set { + rules = append(rules, rule) + } + + return rules +} + +// GetRulesDynamic returns a slice of rules that could be referred to by a ref. +// When parts of the ref are statically known, we use that information to narrow +// down which rules the ref could refer to, but in the most general case this +// will be an over-approximation. +// +// E.g., given the following modules: +// +// package a.b.c +// +// r1 = 1 # rule1 +// +// and: +// +// package a.d.c +// +// r2 = 2 # rule2 +// +// The following calls yield the rules on the right. +// +// GetRulesDynamic("data.a[x].c[y]") => [rule1, rule2] +// GetRulesDynamic("data.a[x].c.r2") => [rule2] +// GetRulesDynamic("data.a.b[x][y]") => [rule1] +func (c *Compiler) GetRulesDynamic(ref Ref) (rules []*Rule) { + node := c.RuleTree + + set := map[*Rule]struct{}{} + var walk func(node *TreeNode, i int) + walk = func(node *TreeNode, i int) { + if i >= len(ref) { + // We've reached the end of the reference and want to collect everything + // under this "prefix". + node.DepthFirst(func(descendant *TreeNode) bool { + insertRules(set, descendant.Values) + return descendant.Hide + }) + } else if i == 0 || IsConstant(ref[i].Value) { + // The head of the ref is always grounded. In case another part of the + // ref is also grounded, we can lookup the exact child. If it's not found + // we can immediately return... + if child := node.Child(ref[i].Value); child == nil { + return + } else if len(child.Values) > 0 { + // If there are any rules at this position, it's what the ref would + // refer to. We can just append those and stop here. + insertRules(set, child.Values) + } else { + // Otherwise, we continue using the child node. + walk(child, i+1) + } + } else { + // This part of the ref is a dynamic term. We can't know what it refers + // to and will just need to try all of the children. + for _, child := range node.Children { + if child.Hide { + continue + } + insertRules(set, child.Values) + walk(child, i+1) + } + } + } + + walk(node, 0) + for rule := range set { + rules = append(rules, rule) + } + return rules +} + +// Utility: add all rule values to the set. +func insertRules(set map[*Rule]struct{}, rules []util.T) { + for _, rule := range rules { + set[rule.(*Rule)] = struct{}{} + } +} + +// RuleIndex returns a RuleIndex built for the rule set referred to by path. +// The path must refer to the rule set exactly, i.e., given a rule set at path +// data.a.b.c.p, refs data.a.b.c.p.x and data.a.b.c would not return a +// RuleIndex built for the rule. +func (c *Compiler) RuleIndex(path Ref) RuleIndex { + r, ok := c.ruleIndices.Get(path) + if !ok { + return nil + } + return r.(RuleIndex) +} + +// ModuleLoader defines the interface that callers can implement to enable lazy +// loading of modules during compilation. +type ModuleLoader func(resolved map[string]*Module) (parsed map[string]*Module, err error) + +// WithModuleLoader sets f as the ModuleLoader on the compiler. +// +// The compiler will invoke the ModuleLoader after resolving all references in +// the current set of input modules. The ModuleLoader can return a new +// collection of parsed modules that are to be included in the compilation +// process. This process will repeat until the ModuleLoader returns an empty +// collection or an error. If an error is returned, compilation will stop +// immediately. +func (c *Compiler) WithModuleLoader(f ModuleLoader) *Compiler { + c.moduleLoader = f + return c +} + +// buildRuleIndices constructs indices for rules. +func (c *Compiler) buildRuleIndices() { + + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + if len(node.Values) == 0 { + return false + } + index := newBaseDocEqIndex(func(ref Ref) bool { + return isVirtual(c.RuleTree, ref.GroundPrefix()) + }) + if rules := extractRules(node.Values); index.Build(rules) { + c.ruleIndices.Put(rules[0].Path(), index) + } + return false + }) + +} + +// checkRecursion ensures that there are no recursive definitions, i.e., there are +// no cycles in the Graph. +func (c *Compiler) checkRecursion() { + eq := func(a, b util.T) bool { + return a.(*Rule) == b.(*Rule) + } + + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + for _, rule := range node.Values { + for node := rule.(*Rule); node != nil; node = node.Else { + c.checkSelfPath(node.Loc(), eq, node, node) + } + } + return false + }) +} + +func (c *Compiler) checkSelfPath(loc *Location, eq func(a, b util.T) bool, a, b util.T) { + tr := NewGraphTraversal(c.Graph) + if p := util.DFSPath(tr, eq, a, b); len(p) > 0 { + n := []string{} + for _, x := range p { + n = append(n, astNodeToString(x)) + } + c.err(NewError(RecursionErr, loc, "rule %v is recursive: %v", astNodeToString(a), strings.Join(n, " -> "))) + } +} + +func astNodeToString(x interface{}) string { + switch x := x.(type) { + case *Rule: + return string(x.Head.Name) + default: + panic("not reached") + } +} + +// checkRuleConflicts ensures that rules definitions are not in conflict. +func (c *Compiler) checkRuleConflicts() { + c.RuleTree.DepthFirst(func(node *TreeNode) bool { + if len(node.Values) == 0 { + return false + } + + kinds := map[DocKind]struct{}{} + defaultRules := 0 + arities := map[int]struct{}{} + declared := false + + for _, rule := range node.Values { + r := rule.(*Rule) + kinds[r.Head.DocKind()] = struct{}{} + arities[len(r.Head.Args)] = struct{}{} + if r.Head.Assign { + declared = true + } + if r.Default { + defaultRules++ + } + } + + name := Var(node.Key.(String)) + + if declared && len(node.Values) > 1 { + c.err(NewError(TypeErr, node.Values[0].(*Rule).Loc(), "rule named %v redeclared at %v", name, node.Values[1].(*Rule).Loc())) + } else if len(kinds) > 1 || len(arities) > 1 { + c.err(NewError(TypeErr, node.Values[0].(*Rule).Loc(), "conflicting rules named %v found", name)) + } else if defaultRules > 1 { + c.err(NewError(TypeErr, node.Values[0].(*Rule).Loc(), "multiple default rules named %s found", name)) + } + + return false + }) + + if c.pathExists != nil { + for _, err := range CheckPathConflicts(c, c.pathExists) { + c.err(err) + } + } + + c.ModuleTree.DepthFirst(func(node *ModuleTreeNode) bool { + for _, mod := range node.Modules { + for _, rule := range mod.Rules { + if childNode, ok := node.Children[String(rule.Head.Name)]; ok { + for _, childMod := range childNode.Modules { + msg := fmt.Sprintf("%v conflicts with rule defined at %v", childMod.Package, rule.Loc()) + c.err(NewError(TypeErr, mod.Package.Loc(), msg)) + } + } + } + } + return false + }) +} + +func (c *Compiler) checkUndefinedFuncs() { + for _, name := range c.sorted { + m := c.Modules[name] + for _, err := range checkUndefinedFuncs(m, c.GetArity) { + c.err(err) + } + } +} + +func checkUndefinedFuncs(x interface{}, arity func(Ref) int) Errors { + + var errs Errors + + WalkExprs(x, func(expr *Expr) bool { + if !expr.IsCall() { + return false + } + ref := expr.Operator() + if arity(ref) >= 0 { + return false + } + errs = append(errs, NewError(TypeErr, expr.Loc(), "undefined function %v", ref)) + return true + }) + + return errs +} + +// checkSafetyRuleBodies ensures that variables appearing in negated expressions or non-target +// positions of built-in expressions will be bound when evaluating the rule from left +// to right, re-ordering as necessary. +func (c *Compiler) checkSafetyRuleBodies() { + for _, name := range c.sorted { + m := c.Modules[name] + WalkRules(m, func(r *Rule) bool { + safe := ReservedVars.Copy() + safe.Update(r.Head.Args.Vars()) + r.Body = c.checkBodySafety(safe, m, r.Body) + return false + }) + } +} + +func (c *Compiler) checkBodySafety(safe VarSet, m *Module, b Body) Body { + reordered, unsafe := reorderBodyForSafety(c.builtins, c.GetArity, safe, b) + if errs := safetyErrorSlice(unsafe); len(errs) > 0 { + for _, err := range errs { + c.err(err) + } + return b + } + return reordered +} + +var safetyCheckVarVisitorParams = VarVisitorParams{ + SkipRefCallHead: true, + SkipClosures: true, +} + +// checkSafetyRuleHeads ensures that variables appearing in the head of a +// rule also appear in the body. +func (c *Compiler) checkSafetyRuleHeads() { + + for _, name := range c.sorted { + m := c.Modules[name] + WalkRules(m, func(r *Rule) bool { + safe := r.Body.Vars(safetyCheckVarVisitorParams) + safe.Update(r.Head.Args.Vars()) + unsafe := r.Head.Vars().Diff(safe) + for v := range unsafe { + if !v.IsGenerated() { + c.err(NewError(UnsafeVarErr, r.Loc(), "var %v is unsafe", v)) + } + } + return false + }) + } +} + +// checkTypes runs the type checker on all rules. The type checker builds a +// TypeEnv that is stored on the compiler. +func (c *Compiler) checkTypes() { + // Recursion is caught in earlier step, so this cannot fail. + sorted, _ := c.Graph.Sort() + checker := newTypeChecker().WithVarRewriter(rewriteVarsInRef(c.RewrittenVars)) + env, errs := checker.CheckTypes(c.TypeEnv, sorted) + for _, err := range errs { + c.err(err) + } + c.TypeEnv = env +} + +func (c *Compiler) checkUnsafeBuiltins() { + for _, name := range c.sorted { + errs := checkUnsafeBuiltins(c.unsafeBuiltinsMap, c.Modules[name]) + for _, err := range errs { + c.err(err) + } + } +} + +func (c *Compiler) runStage(metricName string, f func()) { + if c.metrics != nil { + c.metrics.Timer(metricName).Start() + defer c.metrics.Timer(metricName).Stop() + } + f() +} + +func (c *Compiler) runStageAfter(metricName string, s CompilerStage) *Error { + if c.metrics != nil { + c.metrics.Timer(metricName).Start() + defer c.metrics.Timer(metricName).Stop() + } + return s(c) +} + +func (c *Compiler) compile() { + defer func() { + if r := recover(); r != nil && r != errLimitReached { + panic(r) + } + }() + + for _, s := range c.stages { + c.runStage(s.metricName, s.f) + if c.Failed() { + return + } + for _, s := range c.after[s.name] { + err := c.runStageAfter(s.MetricName, s.Stage) + if err != nil { + c.err(err) + } + } + } +} + +func (c *Compiler) err(err *Error) { + if c.maxErrs > 0 && len(c.Errors) >= c.maxErrs { + c.Errors = append(c.Errors, errLimitReached) + panic(errLimitReached) + } + c.Errors = append(c.Errors, err) +} + +func (c *Compiler) getExports() *util.HashMap { + + rules := util.NewHashMap(func(a, b util.T) bool { + r1 := a.(Ref) + r2 := a.(Ref) + return r1.Equal(r2) + }, func(v util.T) int { + return v.(Ref).Hash() + }) + + for _, name := range c.sorted { + mod := c.Modules[name] + rv, ok := rules.Get(mod.Package.Path) + if !ok { + rv = []Var{} + } + rvs := rv.([]Var) + + for _, rule := range mod.Rules { + rvs = append(rvs, rule.Head.Name) + } + rules.Put(mod.Package.Path, rvs) + } + + return rules +} + +// resolveAllRefs resolves references in expressions to their fully qualified values. +// +// For instance, given the following module: +// +// package a.b +// import data.foo.bar +// p[x] { bar[_] = x } +// +// The reference "bar[_]" would be resolved to "data.foo.bar[_]". +func (c *Compiler) resolveAllRefs() { + + rules := c.getExports() + + for _, name := range c.sorted { + mod := c.Modules[name] + + var ruleExports []Var + if x, ok := rules.Get(mod.Package.Path); ok { + ruleExports = x.([]Var) + } + + globals := getGlobals(mod.Package, ruleExports, mod.Imports) + + WalkRules(mod, func(rule *Rule) bool { + err := resolveRefsInRule(globals, rule) + if err != nil { + c.err(NewError(CompileErr, rule.Location, err.Error())) + } + return false + }) + + // Once imports have been resolved, they are no longer needed. + mod.Imports = nil + } + + if c.moduleLoader != nil { + + parsed, err := c.moduleLoader(c.Modules) + if err != nil { + c.err(NewError(CompileErr, nil, err.Error())) + return + } + + if len(parsed) == 0 { + return + } + + for id, module := range parsed { + c.Modules[id] = module.Copy() + c.sorted = append(c.sorted, id) + } + + sort.Strings(c.sorted) + c.resolveAllRefs() + } +} + +func (c *Compiler) initLocalVarGen() { + c.localvargen = newLocalVarGeneratorForModuleSet(c.sorted, c.Modules) +} + +func (c *Compiler) rewriteComprehensionTerms() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + rewriteComprehensionTerms(f, mod) + } +} + +func (c *Compiler) rewriteExprTerms() { + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + rewriteExprTermsInHead(c.localvargen, rule) + rule.Body = rewriteExprTermsInBody(c.localvargen, rule.Body) + return false + }) + } +} + +// rewriteTermsInHead will rewrite rules so that the head does not contain any +// terms that require evaluation (e.g., refs or comprehensions). If the key or +// value contains or more of these terms, the key or value will be moved into +// the body and assigned to a new variable. The new variable will replace the +// key or value in the head. +// +// For instance, given the following rule: +// +// p[{"foo": data.foo[i]}] { i < 100 } +// +// The rule would be re-written as: +// +// p[__local0__] { i < 100; __local0__ = {"foo": data.foo[i]} } +func (c *Compiler) rewriteRefsInHead() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + if requiresEval(rule.Head.Key) { + expr := f.Generate(rule.Head.Key) + rule.Head.Key = expr.Operand(0) + rule.Body.Append(expr) + } + if requiresEval(rule.Head.Value) { + expr := f.Generate(rule.Head.Value) + rule.Head.Value = expr.Operand(0) + rule.Body.Append(expr) + } + for i := 0; i < len(rule.Head.Args); i++ { + if requiresEval(rule.Head.Args[i]) { + expr := f.Generate(rule.Head.Args[i]) + rule.Head.Args[i] = expr.Operand(0) + rule.Body.Append(expr) + } + } + return false + }) + } +} + +func (c *Compiler) rewriteEquals() { + for _, name := range c.sorted { + mod := c.Modules[name] + rewriteEquals(mod) + } +} + +func (c *Compiler) rewriteDynamicTerms() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + rule.Body = rewriteDynamics(f, rule.Body) + return false + }) + } +} + +func (c *Compiler) rewriteLocalVars() { + + for _, name := range c.sorted { + mod := c.Modules[name] + gen := c.localvargen + + WalkRules(mod, func(rule *Rule) bool { + + var errs Errors + + // Rewrite assignments contained in head of rule. Assignments can + // occur in rule head if they're inside a comprehension. Note, + // assigned vars in comprehensions in the head will be rewritten + // first to preserve scoping rules. For example: + // + // p = [x | x := 1] { x := 2 } becomes p = [__local0__ | __local0__ = 1] { __local1__ = 2 } + // + // This behaviour is consistent scoping inside the body. For example: + // + // p = xs { x := 2; xs = [x | x := 1] } becomes p = xs { __local0__ = 2; xs = [__local1__ | __local1__ = 1] } + WalkTerms(rule.Head, func(term *Term) bool { + stop := false + stack := newLocalDeclaredVars() + switch v := term.Value.(type) { + case *ArrayComprehension: + errs = rewriteDeclaredVarsInArrayComprehension(gen, stack, v, errs) + stop = true + case *SetComprehension: + errs = rewriteDeclaredVarsInSetComprehension(gen, stack, v, errs) + stop = true + case *ObjectComprehension: + errs = rewriteDeclaredVarsInObjectComprehension(gen, stack, v, errs) + stop = true + } + + for k, v := range stack.rewritten { + c.RewrittenVars[k] = v + } + + return stop + }) + + for _, err := range errs { + c.err(err) + } + + // Rewrite assignments in body. + used := NewVarSet() + + if rule.Head.Key != nil { + used.Update(rule.Head.Key.Vars()) + } + + if rule.Head.Value != nil { + used.Update(rule.Head.Value.Vars()) + } + + stack := newLocalDeclaredVars() + + c.rewriteLocalArgVars(gen, stack, rule) + + body, declared, errs := rewriteLocalVars(gen, stack, used, rule.Body) + for _, err := range errs { + c.err(err) + } + + // For rewritten vars use the collection of all variables that + // were in the stack at some point in time. + for k, v := range stack.rewritten { + c.RewrittenVars[k] = v + } + + rule.Body = body + + // Rewrite vars in head that refer to locally declared vars in the body. + vis := NewGenericVisitor(func(x interface{}) bool { + + term, ok := x.(*Term) + if !ok { + return false + } + + switch v := term.Value.(type) { + case Object: + // Make a copy of the object because the keys may be mutated. + cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { + if vark, ok := k.Value.(Var); ok { + if gv, ok := declared[vark]; ok { + k = k.Copy() + k.Value = gv + } + } + return k, v, nil + }) + term.Value = cpy + case Var: + if gv, ok := declared[v]; ok { + term.Value = gv + return true + } + } + + return false + }) + + vis.Walk(rule.Head.Args) + + if rule.Head.Key != nil { + vis.Walk(rule.Head.Key) + } + + if rule.Head.Value != nil { + vis.Walk(rule.Head.Value) + } + + return false + }) + } +} + +func (c *Compiler) rewriteLocalArgVars(gen *localVarGenerator, stack *localDeclaredVars, rule *Rule) { + + vis := &ruleArgLocalRewriter{ + stack: stack, + gen: gen, + } + + for i := range rule.Head.Args { + Walk(vis, rule.Head.Args[i]) + } + + for i := range vis.errs { + c.err(vis.errs[i]) + } +} + +type ruleArgLocalRewriter struct { + stack *localDeclaredVars + gen *localVarGenerator + errs []*Error +} + +func (vis *ruleArgLocalRewriter) Visit(x interface{}) Visitor { + + t, ok := x.(*Term) + if !ok { + return vis + } + + switch v := t.Value.(type) { + case Var: + gv, ok := vis.stack.Declared(v) + if !ok { + gv = vis.gen.Generate() + vis.stack.Insert(v, gv, argVar) + } + t.Value = gv + return nil + case Object: + if cpy, err := v.Map(func(k, v *Term) (*Term, *Term, error) { + vcpy := v.Copy() + Walk(vis, vcpy) + return k, vcpy, nil + }); err != nil { + vis.errs = append(vis.errs, NewError(CompileErr, t.Location, err.Error())) + } else { + t.Value = cpy + } + return nil + case Null, Boolean, Number, String, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Set: + // Scalars are no-ops. Comprehensions are handled above. Sets must not + // contain variables. + return nil + default: + // Recurse on refs, arrays, and calls. Any embedded + // variables can be rewritten. + return vis + } +} + +func (c *Compiler) rewriteWithModifiers() { + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + t := NewGenericTransformer(func(x interface{}) (interface{}, error) { + body, ok := x.(Body) + if !ok { + return x, nil + } + body, err := rewriteWithModifiersInBody(c, f, body) + if err != nil { + c.err(err) + } + + return body, nil + }) + Transform(t, mod) + } +} + +func (c *Compiler) setModuleTree() { + c.ModuleTree = NewModuleTree(c.Modules) +} + +func (c *Compiler) setRuleTree() { + c.RuleTree = NewRuleTree(c.ModuleTree) +} + +func (c *Compiler) setGraph() { + c.Graph = NewGraph(c.Modules, c.GetRulesDynamic) +} + +type queryCompiler struct { + compiler *Compiler + qctx *QueryContext + typeEnv *TypeEnv + rewritten map[Var]Var + after map[string][]QueryCompilerStageDefinition + unsafeBuiltins map[string]struct{} +} + +func newQueryCompiler(compiler *Compiler) QueryCompiler { + qc := &queryCompiler{ + compiler: compiler, + qctx: nil, + after: map[string][]QueryCompilerStageDefinition{}, + } + return qc +} + +func (qc *queryCompiler) WithContext(qctx *QueryContext) QueryCompiler { + qc.qctx = qctx + return qc +} + +func (qc *queryCompiler) WithStageAfter(after string, stage QueryCompilerStageDefinition) QueryCompiler { + qc.after[after] = append(qc.after[after], stage) + return qc +} + +func (qc *queryCompiler) WithUnsafeBuiltins(unsafe map[string]struct{}) QueryCompiler { + qc.unsafeBuiltins = unsafe + return qc +} + +func (qc *queryCompiler) RewrittenVars() map[Var]Var { + return qc.rewritten +} + +func (qc *queryCompiler) runStage(metricName string, qctx *QueryContext, query Body, s func(*QueryContext, Body) (Body, error)) (Body, error) { + if qc.compiler.metrics != nil { + qc.compiler.metrics.Timer(metricName).Start() + defer qc.compiler.metrics.Timer(metricName).Stop() + } + return s(qctx, query) +} + +func (qc *queryCompiler) runStageAfter(metricName string, query Body, s QueryCompilerStage) (Body, error) { + if qc.compiler.metrics != nil { + qc.compiler.metrics.Timer(metricName).Start() + defer qc.compiler.metrics.Timer(metricName).Stop() + } + return s(qc, query) +} + +func (qc *queryCompiler) Compile(query Body) (Body, error) { + + query = query.Copy() + + stages := []struct { + name string + metricName string + f func(*QueryContext, Body) (Body, error) + }{ + {"ResolveRefs", "query_compile_stage_resolve_refs", qc.resolveRefs}, + {"RewriteLocalVars", "query_compile_stage_rewrite_local_vars", qc.rewriteLocalVars}, + {"RewriteExprTerms", "query_compile_stage_rewrite_expr_terms", qc.rewriteExprTerms}, + {"RewriteComprehensionTerms", "query_compile_stage_rewrite_comprehension_terms", qc.rewriteComprehensionTerms}, + {"RewriteWithValues", "query_compile_stage_rewrite_with_values", qc.rewriteWithModifiers}, + {"CheckUndefinedFuncs", "query_compile_stage_check_undefined_funcs", qc.checkUndefinedFuncs}, + {"CheckSafety", "query_compile_stage_check_safety", qc.checkSafety}, + {"RewriteDynamicTerms", "query_compile_stage_rewrite_dynamic_terms", qc.rewriteDynamicTerms}, + {"CheckTypes", "query_compile_stage_check_types", qc.checkTypes}, + {"CheckUnsafeBuiltins", "query_compile_stage_check_unsafe_builtins", qc.checkUnsafeBuiltins}, + } + + qctx := qc.qctx.Copy() + + for _, s := range stages { + var err error + query, err = qc.runStage(s.metricName, qctx, query, s.f) + if err != nil { + return nil, qc.applyErrorLimit(err) + } + for _, s := range qc.after[s.name] { + query, err = qc.runStageAfter(s.MetricName, query, s.Stage) + if err != nil { + return nil, qc.applyErrorLimit(err) + } + } + } + + return query, nil +} + +func (qc *queryCompiler) TypeEnv() *TypeEnv { + return qc.typeEnv +} + +func (qc *queryCompiler) applyErrorLimit(err error) error { + if errs, ok := err.(Errors); ok { + if qc.compiler.maxErrs > 0 && len(errs) > qc.compiler.maxErrs { + err = append(errs[:qc.compiler.maxErrs], errLimitReached) + } + } + return err +} + +func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error) { + + var globals map[Var]Ref + + if qctx != nil && qctx.Package != nil { + var ruleExports []Var + rules := qc.compiler.getExports() + if exist, ok := rules.Get(qctx.Package.Path); ok { + ruleExports = exist.([]Var) + } + + globals = getGlobals(qctx.Package, ruleExports, qc.qctx.Imports) + qctx.Imports = nil + } + + ignore := &declaredVarStack{declaredVars(body)} + + return resolveRefsInBody(globals, ignore, body), nil +} + +func (qc *queryCompiler) rewriteComprehensionTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + f := newEqualityFactory(gen) + node, err := rewriteComprehensionTerms(f, body) + if err != nil { + return nil, err + } + return node.(Body), nil +} + +func (qc *queryCompiler) rewriteDynamicTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + f := newEqualityFactory(gen) + return rewriteDynamics(f, body), nil +} + +func (qc *queryCompiler) rewriteExprTerms(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + return rewriteExprTermsInBody(gen, body), nil +} + +func (qc *queryCompiler) rewriteLocalVars(_ *QueryContext, body Body) (Body, error) { + gen := newLocalVarGenerator("q", body) + stack := newLocalDeclaredVars() + body, _, err := rewriteLocalVars(gen, stack, nil, body) + if len(err) != 0 { + return nil, err + } + qc.rewritten = make(map[Var]Var, len(stack.rewritten)) + for k, v := range stack.rewritten { + // The vars returned during the rewrite will include all seen vars, + // even if they're not declared with an assignment operation. We don't + // want to include these inside the rewritten set though. + qc.rewritten[k] = v + } + return body, nil +} + +func (qc *queryCompiler) checkUndefinedFuncs(_ *QueryContext, body Body) (Body, error) { + if errs := checkUndefinedFuncs(body, qc.compiler.GetArity); len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) checkSafety(_ *QueryContext, body Body) (Body, error) { + safe := ReservedVars.Copy() + reordered, unsafe := reorderBodyForSafety(qc.compiler.builtins, qc.compiler.GetArity, safe, body) + if errs := safetyErrorSlice(unsafe); len(errs) > 0 { + return nil, errs + } + return reordered, nil +} + +func (qc *queryCompiler) checkTypes(qctx *QueryContext, body Body) (Body, error) { + var errs Errors + checker := newTypeChecker().WithVarRewriter(rewriteVarsInRef(qc.rewritten, qc.compiler.RewrittenVars)) + qc.typeEnv, errs = checker.CheckBody(qc.compiler.TypeEnv, body) + if len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) checkUnsafeBuiltins(qctx *QueryContext, body Body) (Body, error) { + var unsafe map[string]struct{} + if qc.unsafeBuiltins != nil { + unsafe = qc.unsafeBuiltins + } else { + unsafe = qc.compiler.unsafeBuiltinsMap + } + errs := checkUnsafeBuiltins(unsafe, body) + if len(errs) > 0 { + return nil, errs + } + return body, nil +} + +func (qc *queryCompiler) rewriteWithModifiers(qctx *QueryContext, body Body) (Body, error) { + f := newEqualityFactory(newLocalVarGenerator("q", body)) + body, err := rewriteWithModifiersInBody(qc.compiler, f, body) + if err != nil { + return nil, Errors{err} + } + return body, nil +} + +// ModuleTreeNode represents a node in the module tree. The module +// tree is keyed by the package path. +type ModuleTreeNode struct { + Key Value + Modules []*Module + Children map[Value]*ModuleTreeNode + Hide bool +} + +// NewModuleTree returns a new ModuleTreeNode that represents the root +// of the module tree populated with the given modules. +func NewModuleTree(mods map[string]*Module) *ModuleTreeNode { + root := &ModuleTreeNode{ + Children: map[Value]*ModuleTreeNode{}, + } + for _, m := range mods { + node := root + for i, x := range m.Package.Path { + c, ok := node.Children[x.Value] + if !ok { + var hide bool + if i == 1 && x.Value.Compare(SystemDocumentKey) == 0 { + hide = true + } + c = &ModuleTreeNode{ + Key: x.Value, + Children: map[Value]*ModuleTreeNode{}, + Hide: hide, + } + node.Children[x.Value] = c + } + node = c + } + node.Modules = append(node.Modules, m) + } + return root +} + +// Size returns the number of modules in the tree. +func (n *ModuleTreeNode) Size() int { + s := len(n.Modules) + for _, c := range n.Children { + s += c.Size() + } + return s +} + +// DepthFirst performs a depth-first traversal of the module tree rooted at n. +// If f returns true, traversal will not continue to the children of n. +func (n *ModuleTreeNode) DepthFirst(f func(node *ModuleTreeNode) bool) { + if !f(n) { + for _, node := range n.Children { + node.DepthFirst(f) + } + } +} + +// TreeNode represents a node in the rule tree. The rule tree is keyed by +// rule path. +type TreeNode struct { + Key Value + Values []util.T + Children map[Value]*TreeNode + Hide bool +} + +// NewRuleTree returns a new TreeNode that represents the root +// of the rule tree populated with the given rules. +func NewRuleTree(mtree *ModuleTreeNode) *TreeNode { + + ruleSets := map[String][]util.T{} + + // Build rule sets for this package. + for _, mod := range mtree.Modules { + for _, rule := range mod.Rules { + key := String(rule.Head.Name) + ruleSets[key] = append(ruleSets[key], rule) + } + } + + // Each rule set becomes a leaf node. + children := map[Value]*TreeNode{} + + for key, rules := range ruleSets { + children[key] = &TreeNode{ + Key: key, + Children: nil, + Values: rules, + } + } + + // Each module in subpackage becomes child node. + for _, child := range mtree.Children { + children[child.Key] = NewRuleTree(child) + } + + return &TreeNode{ + Key: mtree.Key, + Values: nil, + Children: children, + Hide: mtree.Hide, + } +} + +// Size returns the number of rules in the tree. +func (n *TreeNode) Size() int { + s := len(n.Values) + for _, c := range n.Children { + s += c.Size() + } + return s +} + +// Child returns n's child with key k. +func (n *TreeNode) Child(k Value) *TreeNode { + switch k.(type) { + case String, Var: + return n.Children[k] + } + return nil +} + +// DepthFirst performs a depth-first traversal of the rule tree rooted at n. If +// f returns true, traversal will not continue to the children of n. +func (n *TreeNode) DepthFirst(f func(node *TreeNode) bool) { + if !f(n) { + for _, node := range n.Children { + node.DepthFirst(f) + } + } +} + +// Graph represents the graph of dependencies between rules. +type Graph struct { + adj map[util.T]map[util.T]struct{} + nodes map[util.T]struct{} + sorted []util.T +} + +// NewGraph returns a new Graph based on modules. The list function must return +// the rules referred to directly by the ref. +func NewGraph(modules map[string]*Module, list func(Ref) []*Rule) *Graph { + + graph := &Graph{ + adj: map[util.T]map[util.T]struct{}{}, + nodes: map[util.T]struct{}{}, + sorted: nil, + } + + // Create visitor to walk a rule AST and add edges to the rule graph for + // each dependency. + vis := func(a *Rule) *GenericVisitor { + stop := false + return NewGenericVisitor(func(x interface{}) bool { + switch x := x.(type) { + case Ref: + for _, b := range list(x) { + for node := b; node != nil; node = node.Else { + graph.addDependency(a, node) + } + } + case *Rule: + if stop { + // Do not recurse into else clauses (which will be handled + // by the outer visitor.) + return true + } + stop = true + } + return false + }) + } + + // Walk over all rules, add them to graph, and build adjencency lists. + for _, module := range modules { + WalkRules(module, func(a *Rule) bool { + graph.addNode(a) + vis(a).Walk(a) + return false + }) + } + + return graph +} + +// Dependencies returns the set of rules that x depends on. +func (g *Graph) Dependencies(x util.T) map[util.T]struct{} { + return g.adj[x] +} + +// Sort returns a slice of rules sorted by dependencies. If a cycle is found, +// ok is set to false. +func (g *Graph) Sort() (sorted []util.T, ok bool) { + if g.sorted != nil { + return g.sorted, true + } + + sort := &graphSort{ + sorted: make([]util.T, 0, len(g.nodes)), + deps: g.Dependencies, + marked: map[util.T]struct{}{}, + temp: map[util.T]struct{}{}, + } + + for node := range g.nodes { + if !sort.Visit(node) { + return nil, false + } + } + + g.sorted = sort.sorted + return g.sorted, true +} + +func (g *Graph) addDependency(u util.T, v util.T) { + + if _, ok := g.nodes[u]; !ok { + g.addNode(u) + } + + if _, ok := g.nodes[v]; !ok { + g.addNode(v) + } + + edges, ok := g.adj[u] + if !ok { + edges = map[util.T]struct{}{} + g.adj[u] = edges + } + + edges[v] = struct{}{} +} + +func (g *Graph) addNode(n util.T) { + g.nodes[n] = struct{}{} +} + +type graphSort struct { + sorted []util.T + deps func(util.T) map[util.T]struct{} + marked map[util.T]struct{} + temp map[util.T]struct{} +} + +func (sort *graphSort) Marked(node util.T) bool { + _, marked := sort.marked[node] + return marked +} + +func (sort *graphSort) Visit(node util.T) (ok bool) { + if _, ok := sort.temp[node]; ok { + return false + } + if sort.Marked(node) { + return true + } + sort.temp[node] = struct{}{} + for other := range sort.deps(node) { + if !sort.Visit(other) { + return false + } + } + sort.marked[node] = struct{}{} + delete(sort.temp, node) + sort.sorted = append(sort.sorted, node) + return true +} + +// GraphTraversal is a Traversal that understands the dependency graph +type GraphTraversal struct { + graph *Graph + visited map[util.T]struct{} +} + +// NewGraphTraversal returns a Traversal for the dependency graph +func NewGraphTraversal(graph *Graph) *GraphTraversal { + return &GraphTraversal{ + graph: graph, + visited: map[util.T]struct{}{}, + } +} + +// Edges lists all dependency connections for a given node +func (g *GraphTraversal) Edges(x util.T) []util.T { + r := []util.T{} + for v := range g.graph.Dependencies(x) { + r = append(r, v) + } + return r +} + +// Visited returns whether a node has been visited, setting a node to visited if not +func (g *GraphTraversal) Visited(u util.T) bool { + _, ok := g.visited[u] + g.visited[u] = struct{}{} + return ok +} + +type unsafePair struct { + Expr *Expr + Vars VarSet +} + +type unsafeVarLoc struct { + Var Var + Loc *Location +} + +type unsafeVars map[*Expr]VarSet + +func (vs unsafeVars) Add(e *Expr, v Var) { + if u, ok := vs[e]; ok { + u[v] = struct{}{} + } else { + vs[e] = VarSet{v: struct{}{}} + } +} + +func (vs unsafeVars) Set(e *Expr, s VarSet) { + vs[e] = s +} + +func (vs unsafeVars) Update(o unsafeVars) { + for k, v := range o { + if _, ok := vs[k]; !ok { + vs[k] = VarSet{} + } + vs[k].Update(v) + } +} + +func (vs unsafeVars) Vars() (result []unsafeVarLoc) { + + locs := map[Var]*Location{} + + // If var appears in multiple sets then pick first by location. + for expr, vars := range vs { + for v := range vars { + if locs[v].Compare(expr.Location) > 0 { + locs[v] = expr.Location + } + } + } + + for v, loc := range locs { + result = append(result, unsafeVarLoc{ + Var: v, + Loc: loc, + }) + } + + sort.Slice(result, func(i, j int) bool { + return result[i].Loc.Compare(result[j].Loc) < 0 + }) + + return result +} + +func (vs unsafeVars) Slice() (result []unsafePair) { + for expr, vs := range vs { + result = append(result, unsafePair{ + Expr: expr, + Vars: vs, + }) + } + return +} + +// reorderBodyForSafety returns a copy of the body ordered such that +// left to right evaluation of the body will not encounter unbound variables +// in input positions or negated expressions. +// +// Expressions are added to the re-ordered body as soon as they are considered +// safe. If multiple expressions become safe in the same pass, they are added +// in their original order. This results in minimal re-ordering of the body. +// +// If the body cannot be reordered to ensure safety, the second return value +// contains a mapping of expressions to unsafe variables in those expressions. +func reorderBodyForSafety(builtins map[string]*Builtin, arity func(Ref) int, globals VarSet, body Body) (Body, unsafeVars) { + + body, unsafe := reorderBodyForClosures(builtins, arity, globals, body) + if len(unsafe) != 0 { + return nil, unsafe + } + + reordered := Body{} + safe := VarSet{} + + for _, e := range body { + for v := range e.Vars(safetyCheckVarVisitorParams) { + if globals.Contains(v) { + safe.Add(v) + } else { + unsafe.Add(e, v) + } + } + } + + for { + n := len(reordered) + + for _, e := range body { + if reordered.Contains(e) { + continue + } + + safe.Update(outputVarsForExpr(e, builtins, arity, safe)) + + for v := range unsafe[e] { + if safe.Contains(v) { + delete(unsafe[e], v) + } + } + + if len(unsafe[e]) == 0 { + delete(unsafe, e) + reordered = append(reordered, e) + } + } + + if len(reordered) == n { + break + } + } + + // Recursively visit closures and perform the safety checks on them. + // Update the globals at each expression to include the variables that could + // be closed over. + g := globals.Copy() + for i, e := range reordered { + if i > 0 { + g.Update(reordered[i-1].Vars(safetyCheckVarVisitorParams)) + } + vis := &bodySafetyVisitor{ + builtins: builtins, + arity: arity, + current: e, + globals: g, + unsafe: unsafe, + } + NewGenericVisitor(vis.Visit).Walk(e) + } + + // Need to reset expression indices as re-ordering may have + // changed them. + setExprIndices(reordered) + + return reordered, unsafe +} + +type bodySafetyVisitor struct { + builtins map[string]*Builtin + arity func(Ref) int + current *Expr + globals VarSet + unsafe unsafeVars +} + +func (vis *bodySafetyVisitor) Visit(x interface{}) bool { + switch x := x.(type) { + case *Expr: + cpy := *vis + cpy.current = x + + switch ts := x.Terms.(type) { + case *SomeDecl: + NewGenericVisitor(cpy.Visit).Walk(ts) + case []*Term: + for _, t := range ts { + NewGenericVisitor(cpy.Visit).Walk(t) + } + case *Term: + NewGenericVisitor(cpy.Visit).Walk(ts) + } + for i := range x.With { + NewGenericVisitor(cpy.Visit).Walk(x.With[i]) + } + return true + case *ArrayComprehension: + vis.checkArrayComprehensionSafety(x) + return true + case *ObjectComprehension: + vis.checkObjectComprehensionSafety(x) + return true + case *SetComprehension: + vis.checkSetComprehensionSafety(x) + return true + } + return false +} + +// Check term for safety. This is analogous to the rule head safety check. +func (vis *bodySafetyVisitor) checkComprehensionSafety(tv VarSet, body Body) Body { + bv := body.Vars(safetyCheckVarVisitorParams) + bv.Update(vis.globals) + uv := tv.Diff(bv) + for v := range uv { + vis.unsafe.Add(vis.current, v) + } + + // Check body for safety, reordering as necessary. + r, u := reorderBodyForSafety(vis.builtins, vis.arity, vis.globals, body) + if len(u) == 0 { + return r + } + + vis.unsafe.Update(u) + return body +} + +func (vis *bodySafetyVisitor) checkArrayComprehensionSafety(ac *ArrayComprehension) { + ac.Body = vis.checkComprehensionSafety(ac.Term.Vars(), ac.Body) +} + +func (vis *bodySafetyVisitor) checkObjectComprehensionSafety(oc *ObjectComprehension) { + tv := oc.Key.Vars() + tv.Update(oc.Value.Vars()) + oc.Body = vis.checkComprehensionSafety(tv, oc.Body) +} + +func (vis *bodySafetyVisitor) checkSetComprehensionSafety(sc *SetComprehension) { + sc.Body = vis.checkComprehensionSafety(sc.Term.Vars(), sc.Body) +} + +// reorderBodyForClosures returns a copy of the body ordered such that +// expressions (such as array comprehensions) that close over variables are ordered +// after other expressions that contain the same variable in an output position. +func reorderBodyForClosures(builtins map[string]*Builtin, arity func(Ref) int, globals VarSet, body Body) (Body, unsafeVars) { + + reordered := Body{} + unsafe := unsafeVars{} + + for { + n := len(reordered) + + for _, e := range body { + if reordered.Contains(e) { + continue + } + + // Collect vars that are contained in closures within this + // expression. + vs := VarSet{} + WalkClosures(e, func(x interface{}) bool { + vis := &VarVisitor{vars: vs} + vis.Walk(x) + return true + }) + + // Compute vars that are closed over from the body but not yet + // contained in the output position of an expression in the reordered + // body. These vars are considered unsafe. + cv := vs.Intersect(body.Vars(safetyCheckVarVisitorParams)).Diff(globals) + uv := cv.Diff(outputVarsForBody(reordered, builtins, arity, globals)) + + if len(uv) == 0 { + reordered = append(reordered, e) + delete(unsafe, e) + } else { + unsafe.Set(e, uv) + } + } + + if len(reordered) == n { + break + } + } + + return reordered, unsafe +} + +func outputVarsForBody(body Body, builtins map[string]*Builtin, arity func(Ref) int, safe VarSet) VarSet { + o := safe.Copy() + for _, e := range body { + o.Update(outputVarsForExpr(e, builtins, arity, o)) + } + return o.Diff(safe) +} + +func outputVarsForExpr(expr *Expr, builtins map[string]*Builtin, arity func(Ref) int, safe VarSet) VarSet { + + // Negated expressions must be safe. + if expr.Negated { + return VarSet{} + } + + // With modifier inputs must be safe. + for _, with := range expr.With { + unsafe := false + WalkVars(with, func(v Var) bool { + if !safe.Contains(v) { + unsafe = true + return true + } + return false + }) + if unsafe { + return VarSet{} + } + } + + if !expr.IsCall() { + return outputVarsForExprRefs(expr, safe) + } + + terms := expr.Terms.([]*Term) + name := terms[0].String() + + if b := builtins[name]; b != nil { + if b.Name == Equality.Name { + return outputVarsForExprEq(expr, safe) + } + return outputVarsForExprBuiltin(expr, b, safe) + } + + return outputVarsForExprCall(expr, builtins, arity, safe, terms) +} + +func outputVarsForExprBuiltin(expr *Expr, b *Builtin, safe VarSet) VarSet { + + output := outputVarsForExprRefs(expr, safe) + terms := expr.Terms.([]*Term) + + // Check that all input terms are safe. + for i, t := range terms[1:] { + if b.IsTargetPos(i) { + continue + } + vis := NewVarVisitor().WithParams(VarVisitorParams{ + SkipClosures: true, + SkipSets: true, + SkipObjectKeys: true, + SkipRefHead: true, + }) + vis.Walk(t) + unsafe := vis.Vars().Diff(output).Diff(safe) + if len(unsafe) > 0 { + return VarSet{} + } + } + + // Add vars in target positions to result. + for i, t := range terms[1:] { + if b.IsTargetPos(i) { + vis := NewVarVisitor().WithParams(VarVisitorParams{ + SkipRefHead: true, + SkipSets: true, + SkipObjectKeys: true, + SkipClosures: true, + }) + vis.Walk(t) + output.Update(vis.vars) + } + } + + return output +} + +func outputVarsForExprEq(expr *Expr, safe VarSet) VarSet { + if !validEqAssignArgCount(expr) { + return safe + } + output := outputVarsForExprRefs(expr, safe) + output.Update(safe) + output.Update(Unify(output, expr.Operand(0), expr.Operand(1))) + return output.Diff(safe) +} + +func outputVarsForExprCall(expr *Expr, builtins map[string]*Builtin, arity func(Ref) int, safe VarSet, terms []*Term) VarSet { + + output := outputVarsForExprRefs(expr, safe) + + ref, ok := terms[0].Value.(Ref) + if !ok { + return VarSet{} + } + + numArgs := arity(ref) + if numArgs == -1 { + return VarSet{} + } + + numInputTerms := numArgs + 1 + + if numInputTerms >= len(terms) { + return output + } + + vis := NewVarVisitor().WithParams(VarVisitorParams{ + SkipClosures: true, + SkipSets: true, + SkipObjectKeys: true, + SkipRefHead: true, + }) + + vis.Walk(Args(terms[:numInputTerms])) + unsafe := vis.Vars().Diff(output).Diff(safe) + + if len(unsafe) > 0 { + return VarSet{} + } + + vis = NewVarVisitor().WithParams(VarVisitorParams{ + SkipRefHead: true, + SkipSets: true, + SkipObjectKeys: true, + SkipClosures: true, + }) + + vis.Walk(Args(terms[numInputTerms:])) + output.Update(vis.vars) + return output +} + +func outputVarsForExprRefs(expr *Expr, safe VarSet) VarSet { + output := VarSet{} + WalkRefs(expr, func(r Ref) bool { + if safe.Contains(r[0].Value.(Var)) { + output.Update(r.OutputVars()) + return false + } + return true + }) + return output +} + +type equalityFactory struct { + gen *localVarGenerator +} + +func newEqualityFactory(gen *localVarGenerator) *equalityFactory { + return &equalityFactory{gen} +} + +func (f *equalityFactory) Generate(other *Term) *Expr { + term := NewTerm(f.gen.Generate()).SetLocation(other.Location) + expr := Equality.Expr(term, other) + expr.Generated = true + expr.Location = other.Location + return expr +} + +type localVarGenerator struct { + exclude VarSet + suffix string + next int +} + +func newLocalVarGeneratorForModuleSet(sorted []string, modules map[string]*Module) *localVarGenerator { + exclude := NewVarSet() + vis := &VarVisitor{vars: exclude} + for _, key := range sorted { + vis.Walk(modules[key]) + } + return &localVarGenerator{exclude: exclude, next: 0} +} + +func newLocalVarGenerator(suffix string, node interface{}) *localVarGenerator { + exclude := NewVarSet() + vis := &VarVisitor{vars: exclude} + vis.Walk(node) + return &localVarGenerator{exclude: exclude, suffix: suffix, next: 0} +} + +func (l *localVarGenerator) Generate() Var { + for { + result := Var("__local" + l.suffix + strconv.Itoa(l.next) + "__") + l.next++ + if !l.exclude.Contains(result) { + return result + } + } +} + +func getGlobals(pkg *Package, rules []Var, imports []*Import) map[Var]Ref { + + globals := map[Var]Ref{} + + // Populate globals with exports within the package. + for _, v := range rules { + global := append(Ref{}, pkg.Path...) + global = append(global, &Term{Value: String(v)}) + globals[v] = global + } + + // Populate globals with imports. + for _, i := range imports { + if len(i.Alias) > 0 { + path := i.Path.Value.(Ref) + globals[i.Alias] = path + } else { + path := i.Path.Value.(Ref) + if len(path) == 1 { + globals[path[0].Value.(Var)] = path + } else { + v := path[len(path)-1].Value.(String) + globals[Var(v)] = path + } + } + } + + return globals +} + +func requiresEval(x *Term) bool { + if x == nil { + return false + } + return ContainsRefs(x) || ContainsComprehensions(x) +} + +func resolveRef(globals map[Var]Ref, ignore *declaredVarStack, ref Ref) Ref { + + r := Ref{} + for i, x := range ref { + switch v := x.Value.(type) { + case Var: + if g, ok := globals[v]; ok && !ignore.Contains(v) { + cpy := g.Copy() + for i := range cpy { + cpy[i].SetLocation(x.Location) + } + if i == 0 { + r = cpy + } else { + r = append(r, NewTerm(cpy).SetLocation(x.Location)) + } + } else { + r = append(r, x) + } + case Ref, Array, Object, Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + r = append(r, resolveRefsInTerm(globals, ignore, x)) + default: + r = append(r, x) + } + } + + return r +} + +func resolveRefsInRule(globals map[Var]Ref, rule *Rule) error { + ignore := &declaredVarStack{} + + vars := NewVarSet() + var vis *GenericVisitor + var err error + + // Walk args to collect vars and transform body so that callers can shadow + // root documents. + vis = NewGenericVisitor(func(x interface{}) bool { + if err != nil { + return true + } + switch x := x.(type) { + case Var: + vars.Add(x) + + // Object keys cannot be pattern matched so only walk values. + case Object: + for _, k := range x.Keys() { + vis.Walk(x.Get(k)) + } + + // Skip terms that could contain vars that cannot be pattern matched. + case Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + return true + + case *Term: + if _, ok := x.Value.(Ref); ok { + if RootDocumentRefs.Contains(x) { + // We could support args named input, data, etc. however + // this would require rewriting terms in the head and body. + // Preventing root document shadowing is simpler, and + // arguably, will prevent confusing names from being used. + err = fmt.Errorf("args must not shadow %v (use a different variable name)", x) + return true + } + } + } + return false + }) + + vis.Walk(rule.Head.Args) + + if err != nil { + return err + } + + ignore.Push(vars) + ignore.Push(declaredVars(rule.Body)) + + if rule.Head.Key != nil { + rule.Head.Key = resolveRefsInTerm(globals, ignore, rule.Head.Key) + } + + if rule.Head.Value != nil { + rule.Head.Value = resolveRefsInTerm(globals, ignore, rule.Head.Value) + } + + rule.Body = resolveRefsInBody(globals, ignore, rule.Body) + return nil +} + +func resolveRefsInBody(globals map[Var]Ref, ignore *declaredVarStack, body Body) Body { + r := Body{} + for _, expr := range body { + r = append(r, resolveRefsInExpr(globals, ignore, expr)) + } + return r +} + +func resolveRefsInExpr(globals map[Var]Ref, ignore *declaredVarStack, expr *Expr) *Expr { + cpy := *expr + switch ts := expr.Terms.(type) { + case *Term: + cpy.Terms = resolveRefsInTerm(globals, ignore, ts) + case []*Term: + buf := make([]*Term, len(ts)) + for i := 0; i < len(ts); i++ { + buf[i] = resolveRefsInTerm(globals, ignore, ts[i]) + } + cpy.Terms = buf + } + for _, w := range cpy.With { + w.Target = resolveRefsInTerm(globals, ignore, w.Target) + w.Value = resolveRefsInTerm(globals, ignore, w.Value) + } + return &cpy +} + +func resolveRefsInTerm(globals map[Var]Ref, ignore *declaredVarStack, term *Term) *Term { + switch v := term.Value.(type) { + case Var: + if g, ok := globals[v]; ok && !ignore.Contains(v) { + cpy := g.Copy() + for i := range cpy { + cpy[i].SetLocation(term.Location) + } + return NewTerm(cpy).SetLocation(term.Location) + } + return term + case Ref: + fqn := resolveRef(globals, ignore, v) + cpy := *term + cpy.Value = fqn + return &cpy + case Object: + cpy := *term + cpy.Value, _ = v.Map(func(k, v *Term) (*Term, *Term, error) { + k = resolveRefsInTerm(globals, ignore, k) + v = resolveRefsInTerm(globals, ignore, v) + return k, v, nil + }) + return &cpy + case Array: + cpy := *term + cpy.Value = Array(resolveRefsInTermSlice(globals, ignore, v)) + return &cpy + case Call: + cpy := *term + cpy.Value = Call(resolveRefsInTermSlice(globals, ignore, v)) + return &cpy + case Set: + s, _ := v.Map(func(e *Term) (*Term, error) { + return resolveRefsInTerm(globals, ignore, e), nil + }) + cpy := *term + cpy.Value = s + return &cpy + case *ArrayComprehension: + ac := &ArrayComprehension{} + ignore.Push(declaredVars(v.Body)) + ac.Term = resolveRefsInTerm(globals, ignore, v.Term) + ac.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = ac + ignore.Pop() + return &cpy + case *ObjectComprehension: + oc := &ObjectComprehension{} + ignore.Push(declaredVars(v.Body)) + oc.Key = resolveRefsInTerm(globals, ignore, v.Key) + oc.Value = resolveRefsInTerm(globals, ignore, v.Value) + oc.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = oc + ignore.Pop() + return &cpy + case *SetComprehension: + sc := &SetComprehension{} + ignore.Push(declaredVars(v.Body)) + sc.Term = resolveRefsInTerm(globals, ignore, v.Term) + sc.Body = resolveRefsInBody(globals, ignore, v.Body) + cpy := *term + cpy.Value = sc + ignore.Pop() + return &cpy + default: + return term + } +} + +func resolveRefsInTermSlice(globals map[Var]Ref, ignore *declaredVarStack, terms []*Term) []*Term { + cpy := make([]*Term, len(terms)) + for i := 0; i < len(terms); i++ { + cpy[i] = resolveRefsInTerm(globals, ignore, terms[i]) + } + return cpy +} + +type declaredVarStack []VarSet + +func (s declaredVarStack) Contains(v Var) bool { + for i := len(s) - 1; i >= 0; i-- { + if _, ok := s[i][v]; ok { + return ok + } + } + return false +} + +func (s declaredVarStack) Add(v Var) { + s[len(s)-1].Add(v) +} + +func (s *declaredVarStack) Push(vs VarSet) { + *s = append(*s, vs) +} + +func (s *declaredVarStack) Pop() { + curr := *s + *s = curr[:len(curr)-1] +} + +func declaredVars(x interface{}) VarSet { + vars := NewVarSet() + vis := NewGenericVisitor(func(x interface{}) bool { + switch x := x.(type) { + case *Expr: + if x.IsAssignment() && validEqAssignArgCount(x) { + WalkVars(x.Operand(0), func(v Var) bool { + vars.Add(v) + return false + }) + } else if decl, ok := x.Terms.(*SomeDecl); ok { + for i := range decl.Symbols { + vars.Add(decl.Symbols[i].Value.(Var)) + } + } + case *ArrayComprehension, *SetComprehension, *ObjectComprehension: + return true + } + return false + }) + vis.Walk(x) + return vars +} + +// rewriteComprehensionTerms will rewrite comprehensions so that the term part +// is bound to a variable in the body. This allows any type of term to be used +// in the term part (even if the term requires evaluation.) +// +// For instance, given the following comprehension: +// +// [x[0] | x = y[_]; y = [1,2,3]] +// +// The comprehension would be rewritten as: +// +// [__local0__ | x = y[_]; y = [1,2,3]; __local0__ = x[0]] +func rewriteComprehensionTerms(f *equalityFactory, node interface{}) (interface{}, error) { + return TransformComprehensions(node, func(x interface{}) (Value, error) { + switch x := x.(type) { + case *ArrayComprehension: + if requiresEval(x.Term) { + expr := f.Generate(x.Term) + x.Term = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + case *SetComprehension: + if requiresEval(x.Term) { + expr := f.Generate(x.Term) + x.Term = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + case *ObjectComprehension: + if requiresEval(x.Key) { + expr := f.Generate(x.Key) + x.Key = expr.Operand(0) + x.Body.Append(expr) + } + if requiresEval(x.Value) { + expr := f.Generate(x.Value) + x.Value = expr.Operand(0) + x.Body.Append(expr) + } + return x, nil + } + panic("illegal type") + }) +} + +// rewriteEquals will rewrite exprs under x as unification calls instead of == +// calls. For example: +// +// data.foo == data.bar is rewritten as data.foo = data.bar +// +// This stage should only run the safety check (since == is a built-in with no +// outputs, so the inputs must not be marked as safe.) +// +// This stage is not executed by the query compiler by default because when +// callers specify == instead of = they expect to receive a true/false/undefined +// result back whereas with = the result is only ever true/undefined. For +// partial evaluation cases we do want to rewrite == to = to simplify the +// result. +func rewriteEquals(x interface{}) { + doubleEq := Equal.Ref() + unifyOp := Equality.Ref() + WalkExprs(x, func(x *Expr) bool { + if x.IsCall() { + operator := x.Operator() + if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + x.SetOperator(NewTerm(unifyOp)) + } + } + return false + }) +} + +// rewriteDynamics will rewrite the body so that dynamic terms (i.e., refs and +// comprehensions) are bound to vars earlier in the query. This translation +// results in eager evaluation. +// +// For instance, given the following query: +// +// foo(data.bar) = 1 +// +// The rewritten version will be: +// +// __local0__ = data.bar; foo(__local0__) = 1 +func rewriteDynamics(f *equalityFactory, body Body) Body { + result := make(Body, 0, len(body)) + for _, expr := range body { + if expr.IsEquality() { + result = rewriteDynamicsEqExpr(f, expr, result) + } else if expr.IsCall() { + result = rewriteDynamicsCallExpr(f, expr, result) + } else { + result = rewriteDynamicsTermExpr(f, expr, result) + } + } + return result +} + +func appendExpr(body Body, expr *Expr) Body { + body.Append(expr) + return body +} + +func rewriteDynamicsEqExpr(f *equalityFactory, expr *Expr, result Body) Body { + if !validEqAssignArgCount(expr) { + return appendExpr(result, expr) + } + terms := expr.Terms.([]*Term) + result, terms[1] = rewriteDynamicsInTerm(expr, f, terms[1], result) + result, terms[2] = rewriteDynamicsInTerm(expr, f, terms[2], result) + return appendExpr(result, expr) +} + +func rewriteDynamicsCallExpr(f *equalityFactory, expr *Expr, result Body) Body { + terms := expr.Terms.([]*Term) + for i := 1; i < len(terms); i++ { + result, terms[i] = rewriteDynamicsOne(expr, f, terms[i], result) + } + return appendExpr(result, expr) +} + +func rewriteDynamicsTermExpr(f *equalityFactory, expr *Expr, result Body) Body { + term := expr.Terms.(*Term) + result, expr.Terms = rewriteDynamicsInTerm(expr, f, term, result) + return appendExpr(result, expr) +} + +func rewriteDynamicsInTerm(original *Expr, f *equalityFactory, term *Term, result Body) (Body, *Term) { + switch v := term.Value.(type) { + case Ref: + for i := 1; i < len(v); i++ { + result, v[i] = rewriteDynamicsOne(original, f, v[i], result) + } + case *ArrayComprehension: + v.Body = rewriteDynamics(f, v.Body) + case *SetComprehension: + v.Body = rewriteDynamics(f, v.Body) + case *ObjectComprehension: + v.Body = rewriteDynamics(f, v.Body) + default: + result, term = rewriteDynamicsOne(original, f, term, result) + } + return result, term +} + +func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result Body) (Body, *Term) { + switch v := term.Value.(type) { + case Ref: + for i := 1; i < len(v); i++ { + result, v[i] = rewriteDynamicsOne(original, f, v[i], result) + } + generated := f.Generate(term) + generated.With = original.With + result.Append(generated) + return result, result[len(result)-1].Operand(0) + case Array: + for i := 0; i < len(v); i++ { + result, v[i] = rewriteDynamicsOne(original, f, v[i], result) + } + return result, term + case Object: + cpy := NewObject() + for _, key := range v.Keys() { + value := v.Get(key) + result, key = rewriteDynamicsOne(original, f, key, result) + result, value = rewriteDynamicsOne(original, f, value, result) + cpy.Insert(key, value) + } + return result, NewTerm(cpy).SetLocation(term.Location) + case Set: + cpy := NewSet() + for _, term := range v.Slice() { + var rw *Term + result, rw = rewriteDynamicsOne(original, f, term, result) + cpy.Add(rw) + } + return result, NewTerm(cpy).SetLocation(term.Location) + case *ArrayComprehension: + var extra *Expr + v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) + result.Append(extra) + return result, result[len(result)-1].Operand(0) + case *SetComprehension: + var extra *Expr + v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) + result.Append(extra) + return result, result[len(result)-1].Operand(0) + case *ObjectComprehension: + var extra *Expr + v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) + result.Append(extra) + return result, result[len(result)-1].Operand(0) + } + return result, term +} + +func rewriteDynamicsComprehensionBody(original *Expr, f *equalityFactory, body Body, term *Term) (Body, *Expr) { + body = rewriteDynamics(f, body) + generated := f.Generate(term) + generated.With = original.With + return body, generated +} + +func rewriteExprTermsInHead(gen *localVarGenerator, rule *Rule) { + if rule.Head.Key != nil { + support, output := expandExprTerm(gen, rule.Head.Key) + for i := range support { + rule.Body.Append(support[i]) + } + rule.Head.Key = output + } + if rule.Head.Value != nil { + support, output := expandExprTerm(gen, rule.Head.Value) + for i := range support { + rule.Body.Append(support[i]) + } + rule.Head.Value = output + } +} + +func rewriteExprTermsInBody(gen *localVarGenerator, body Body) Body { + cpy := make(Body, 0, len(body)) + for i := 0; i < len(body); i++ { + for _, expr := range expandExpr(gen, body[i]) { + cpy.Append(expr) + } + } + return cpy +} + +func expandExpr(gen *localVarGenerator, expr *Expr) (result []*Expr) { + for i := range expr.With { + extras, value := expandExprTerm(gen, expr.With[i].Value) + expr.With[i].Value = value + result = append(result, extras...) + } + switch terms := expr.Terms.(type) { + case *Term: + extras, term := expandExprTerm(gen, terms) + if len(expr.With) > 0 { + for i := range extras { + extras[i].With = expr.With + } + } + result = append(result, extras...) + expr.Terms = term + result = append(result, expr) + case []*Term: + for i := 1; i < len(terms); i++ { + var extras []*Expr + extras, terms[i] = expandExprTerm(gen, terms[i]) + if len(expr.With) > 0 { + for i := range extras { + extras[i].With = expr.With + } + } + result = append(result, extras...) + } + result = append(result, expr) + } + return +} + +func expandExprTerm(gen *localVarGenerator, term *Term) (support []*Expr, output *Term) { + output = term + switch v := term.Value.(type) { + case Call: + for i := 1; i < len(v); i++ { + var extras []*Expr + extras, v[i] = expandExprTerm(gen, v[i]) + support = append(support, extras...) + } + output = NewTerm(gen.Generate()).SetLocation(term.Location) + expr := v.MakeExpr(output).SetLocation(term.Location) + expr.Generated = true + support = append(support, expr) + case Ref: + support = expandExprRef(gen, v) + case Array: + support = expandExprTermSlice(gen, v) + case Object: + cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { + extras1, expandedKey := expandExprTerm(gen, k) + extras2, expandedValue := expandExprTerm(gen, v) + support = append(support, extras1...) + support = append(support, extras2...) + return expandedKey, expandedValue, nil + }) + output = NewTerm(cpy).SetLocation(term.Location) + case Set: + cpy, _ := v.Map(func(x *Term) (*Term, error) { + extras, expanded := expandExprTerm(gen, x) + support = append(support, extras...) + return expanded, nil + }) + output = NewTerm(cpy).SetLocation(term.Location) + case *ArrayComprehension: + support, term := expandExprTerm(gen, v.Term) + for i := range support { + v.Body.Append(support[i]) + } + v.Term = term + v.Body = rewriteExprTermsInBody(gen, v.Body) + case *SetComprehension: + support, term := expandExprTerm(gen, v.Term) + for i := range support { + v.Body.Append(support[i]) + } + v.Term = term + v.Body = rewriteExprTermsInBody(gen, v.Body) + case *ObjectComprehension: + support, key := expandExprTerm(gen, v.Key) + for i := range support { + v.Body.Append(support[i]) + } + v.Key = key + support, value := expandExprTerm(gen, v.Value) + for i := range support { + v.Body.Append(support[i]) + } + v.Value = value + v.Body = rewriteExprTermsInBody(gen, v.Body) + } + return +} + +func expandExprRef(gen *localVarGenerator, v []*Term) (support []*Expr) { + // Start by calling a normal expandExprTerm on all terms. + support = expandExprTermSlice(gen, v) + + // Rewrite references in order to support indirect references. We rewrite + // e.g. + // + // [1, 2, 3][i] + // + // to + // + // __local_var = [1, 2, 3] + // __local_var[i] + // + // to support these. This only impacts the reference subject, i.e. the + // first item in the slice. + var subject = v[0] + switch subject.Value.(type) { + case Array, Object, Set, *ArrayComprehension, *SetComprehension, *ObjectComprehension, Call: + f := newEqualityFactory(gen) + assignToLocal := f.Generate(subject) + support = append(support, assignToLocal) + v[0] = assignToLocal.Operand(0) + } + return +} + +func expandExprTermSlice(gen *localVarGenerator, v []*Term) (support []*Expr) { + for i := 0; i < len(v); i++ { + var extras []*Expr + extras, v[i] = expandExprTerm(gen, v[i]) + support = append(support, extras...) + } + return +} + +type localDeclaredVars struct { + vars []*declaredVarSet + + // rewritten contains a mapping of *all* user-defined variables + // that have been rewritten whereas vars contains the state + // from the current query (not not any nested queries, and all + // vars seen). + rewritten map[Var]Var +} + +type varOccurrence int + +const ( + newVar varOccurrence = iota + argVar + seenVar + assignedVar + declaredVar +) + +type declaredVarSet struct { + vs map[Var]Var + reverse map[Var]Var + occurrence map[Var]varOccurrence +} + +func newDeclaredVarSet() *declaredVarSet { + return &declaredVarSet{ + vs: map[Var]Var{}, + reverse: map[Var]Var{}, + occurrence: map[Var]varOccurrence{}, + } +} + +func newLocalDeclaredVars() *localDeclaredVars { + return &localDeclaredVars{ + vars: []*declaredVarSet{newDeclaredVarSet()}, + rewritten: map[Var]Var{}, + } +} + +func (s *localDeclaredVars) Push() { + s.vars = append(s.vars, newDeclaredVarSet()) +} + +func (s *localDeclaredVars) Pop() *declaredVarSet { + sl := s.vars + curr := sl[len(sl)-1] + s.vars = sl[:len(sl)-1] + return curr +} + +func (s localDeclaredVars) Peek() *declaredVarSet { + return s.vars[len(s.vars)-1] +} + +func (s localDeclaredVars) Insert(x, y Var, occurrence varOccurrence) { + elem := s.vars[len(s.vars)-1] + elem.vs[x] = y + elem.reverse[y] = x + elem.occurrence[x] = occurrence + + // If the variable has been rewritten (where x != y, with y being + // the generated value), store it in the map of rewritten vars. + // Assume that the generated values are unique for the compilation. + if !x.Equal(y) { + s.rewritten[y] = x + } +} + +func (s localDeclaredVars) Declared(x Var) (y Var, ok bool) { + for i := len(s.vars) - 1; i >= 0; i-- { + if y, ok = s.vars[i].vs[x]; ok { + return + } + } + return +} + +// Occurrence returns a flag that indicates whether x has occurred in the +// current scope. +func (s localDeclaredVars) Occurrence(x Var) varOccurrence { + return s.vars[len(s.vars)-1].occurrence[x] +} + +// rewriteLocalVars rewrites bodies to remove assignment/declaration +// expressions. For example: +// +// a := 1; p[a] +// +// Is rewritten to: +// +// __local0__ = 1; p[__local0__] +// +// During rewriting, assignees are validated to prevent use before declaration. +func rewriteLocalVars(g *localVarGenerator, stack *localDeclaredVars, used VarSet, body Body) (Body, map[Var]Var, Errors) { + var errs Errors + body, errs = rewriteDeclaredVarsInBody(g, stack, used, body, errs) + return body, stack.Pop().vs, errs +} + +func rewriteDeclaredVarsInBody(g *localVarGenerator, stack *localDeclaredVars, used VarSet, body Body, errs Errors) (Body, Errors) { + + var cpy Body + + for i := range body { + var expr *Expr + if body[i].IsAssignment() { + expr, errs = rewriteDeclaredAssignment(g, stack, body[i], errs) + } else if decl, ok := body[i].Terms.(*SomeDecl); ok { + errs = rewriteSomeDeclStatement(g, stack, decl, errs) + } else { + expr, errs = rewriteDeclaredVarsInExpr(g, stack, body[i], errs) + } + if expr != nil { + cpy.Append(expr) + } + } + + // If the body only contained a var statement it will be empty at this + // point. Append true to the body to ensure that it's non-empty (zero length + // bodies are not supported.) + if len(cpy) == 0 { + cpy.Append(NewExpr(BooleanTerm(true))) + } + + return cpy, checkUnusedDeclaredVars(body[0].Loc(), stack, used, cpy, errs) +} + +func checkUnusedDeclaredVars(loc *Location, stack *localDeclaredVars, used VarSet, cpy Body, errs Errors) Errors { + + // NOTE(tsandall): Do not generate more errors if there are existing + // declaration errors. + if len(errs) > 0 { + return errs + } + + dvs := stack.Peek() + declared := NewVarSet() + + for v, occ := range dvs.occurrence { + if occ == declaredVar { + declared.Add(dvs.vs[v]) + } + } + + bodyvars := cpy.Vars(VarVisitorParams{}) + + for v := range used { + if gv, ok := stack.Declared(v); ok { + bodyvars.Add(gv) + } else { + bodyvars.Add(v) + } + } + + unused := declared.Diff(bodyvars).Diff(used) + + for _, gv := range unused.Sorted() { + errs = append(errs, NewError(CompileErr, loc, "declared var %v unused", dvs.reverse[gv])) + } + + return errs +} + +func rewriteSomeDeclStatement(g *localVarGenerator, stack *localDeclaredVars, decl *SomeDecl, errs Errors) Errors { + for i := range decl.Symbols { + v := decl.Symbols[i].Value.(Var) + if _, err := rewriteDeclaredVar(g, stack, v, declaredVar); err != nil { + errs = append(errs, NewError(CompileErr, decl.Loc(), err.Error())) + } + } + return errs +} + +func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors) (*Expr, Errors) { + vis := NewGenericVisitor(func(x interface{}) bool { + var stop bool + switch x := x.(type) { + case *Term: + stop, errs = rewriteDeclaredVarsInTerm(g, stack, x, errs) + case *With: + _, errs = rewriteDeclaredVarsInTerm(g, stack, x.Value, errs) + stop = true + } + return stop + }) + vis.Walk(expr) + return expr, errs +} + +func rewriteDeclaredAssignment(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors) (*Expr, Errors) { + + if expr.Negated { + errs = append(errs, NewError(CompileErr, expr.Location, "cannot assign vars inside negated expression")) + return expr, errs + } + + numErrsBefore := len(errs) + + if !validEqAssignArgCount(expr) { + return expr, errs + } + + // Rewrite terms on right hand side capture seen vars and recursively + // process comprehensions before left hand side is processed. Also + // rewrite with modifier. + errs = rewriteDeclaredVarsInTermRecursive(g, stack, expr.Operand(1), errs) + + for _, w := range expr.With { + errs = rewriteDeclaredVarsInTermRecursive(g, stack, w.Value, errs) + } + + // Rewrite vars on left hand side with unique names. Catch redeclaration + // and invalid term types here. + var vis func(t *Term) bool + + vis = func(t *Term) bool { + switch v := t.Value.(type) { + case Var: + if gv, err := rewriteDeclaredVar(g, stack, v, assignedVar); err != nil { + errs = append(errs, NewError(CompileErr, t.Location, err.Error())) + } else { + t.Value = gv + } + return true + case Array: + return false + case Object: + v.Foreach(func(_, v *Term) { + WalkTerms(v, vis) + }) + return true + case Ref: + if RootDocumentRefs.Contains(t) { + if gv, err := rewriteDeclaredVar(g, stack, v[0].Value.(Var), assignedVar); err != nil { + errs = append(errs, NewError(CompileErr, t.Location, err.Error())) + } else { + t.Value = gv + } + return true + } + } + errs = append(errs, NewError(CompileErr, t.Location, "cannot assign to %v", TypeName(t.Value))) + return true + } + + WalkTerms(expr.Operand(0), vis) + + if len(errs) == numErrsBefore { + loc := expr.Operator()[0].Location + expr.SetOperator(RefTerm(VarTerm(Equality.Name).SetLocation(loc)).SetLocation(loc)) + } + + return expr, errs +} + +func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors) (bool, Errors) { + switch v := term.Value.(type) { + case Var: + if gv, ok := stack.Declared(v); ok { + term.Value = gv + } else if stack.Occurrence(v) == newVar { + stack.Insert(v, v, seenVar) + } + case Ref: + if RootDocumentRefs.Contains(term) { + if gv, ok := stack.Declared(v[0].Value.(Var)); ok { + term.Value = gv + } + return true, errs + } + return false, errs + case Object: + cpy, _ := v.Map(func(k, v *Term) (*Term, *Term, error) { + kcpy := k.Copy() + errs = rewriteDeclaredVarsInTermRecursive(g, stack, kcpy, errs) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v, errs) + return kcpy, v, nil + }) + term.Value = cpy + case Set: + cpy, _ := v.Map(func(elem *Term) (*Term, error) { + elemcpy := elem.Copy() + errs = rewriteDeclaredVarsInTermRecursive(g, stack, elemcpy, errs) + return elemcpy, nil + }) + term.Value = cpy + case *ArrayComprehension: + errs = rewriteDeclaredVarsInArrayComprehension(g, stack, v, errs) + case *SetComprehension: + errs = rewriteDeclaredVarsInSetComprehension(g, stack, v, errs) + case *ObjectComprehension: + errs = rewriteDeclaredVarsInObjectComprehension(g, stack, v, errs) + default: + return false, errs + } + return true, errs +} + +func rewriteDeclaredVarsInTermRecursive(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors) Errors { + WalkNodes(term, func(n Node) bool { + var stop bool + switch n := n.(type) { + case *With: + _, errs = rewriteDeclaredVarsInTerm(g, stack, n.Value, errs) + stop = true + case *Term: + stop, errs = rewriteDeclaredVarsInTerm(g, stack, n, errs) + } + return stop + }) + return errs +} + +func rewriteDeclaredVarsInArrayComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ArrayComprehension, errs Errors) Errors { + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, nil, v.Body, errs) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Term, errs) + stack.Pop() + return errs +} + +func rewriteDeclaredVarsInSetComprehension(g *localVarGenerator, stack *localDeclaredVars, v *SetComprehension, errs Errors) Errors { + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, nil, v.Body, errs) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Term, errs) + stack.Pop() + return errs +} + +func rewriteDeclaredVarsInObjectComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ObjectComprehension, errs Errors) Errors { + stack.Push() + v.Body, errs = rewriteDeclaredVarsInBody(g, stack, nil, v.Body, errs) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Key, errs) + errs = rewriteDeclaredVarsInTermRecursive(g, stack, v.Value, errs) + stack.Pop() + return errs +} + +func rewriteDeclaredVar(g *localVarGenerator, stack *localDeclaredVars, v Var, occ varOccurrence) (gv Var, err error) { + switch stack.Occurrence(v) { + case seenVar: + return gv, fmt.Errorf("var %v referenced above", v) + case assignedVar: + return gv, fmt.Errorf("var %v assigned above", v) + case declaredVar: + return gv, fmt.Errorf("var %v declared above", v) + case argVar: + return gv, fmt.Errorf("arg %v redeclared", v) + } + gv = g.Generate() + stack.Insert(v, gv, occ) + return +} + +// rewriteWithModifiersInBody will rewrite the body so that with modifiers do +// not contain terms that require evaluation as values. If this function +// encounters an invalid with modifier target then it will raise an error. +func rewriteWithModifiersInBody(c *Compiler, f *equalityFactory, body Body) (Body, *Error) { + var result Body + for i := range body { + exprs, err := rewriteWithModifier(c, f, body[i]) + if err != nil { + return nil, err + } + if len(exprs) > 0 { + for _, expr := range exprs { + result.Append(expr) + } + } else { + result.Append(body[i]) + } + } + return result, nil +} + +func rewriteWithModifier(c *Compiler, f *equalityFactory, expr *Expr) ([]*Expr, *Error) { + + var result []*Expr + for i := range expr.With { + err := validateTarget(c, expr.With[i].Target) + if err != nil { + return nil, err + } + + if requiresEval(expr.With[i].Value) { + eq := f.Generate(expr.With[i].Value) + result = append(result, eq) + expr.With[i].Value = eq.Operand(0) + } + } + + // If any of the with modifiers in this expression were rewritten then result + // will be non-empty. In this case, the expression will have been modified and + // it should also be added to the result. + if len(result) > 0 { + result = append(result, expr) + } + return result, nil +} + +func validateTarget(c *Compiler, term *Term) *Error { + if !isInputRef(term) && !isDataRef(term) { + return NewError(TypeErr, term.Location, "with keyword target must start with %v or %v", InputRootDocument, DefaultRootDocument) + } + + if isDataRef(term) { + ref := term.Value.(Ref) + node := c.RuleTree + for i := 0; i < len(ref)-1; i++ { + child := node.Child(ref[i].Value) + if child == nil { + break + } else if len(child.Values) > 0 { + return NewError(CompileErr, term.Loc(), "with keyword cannot partially replace virtual document(s)") + } + node = child + } + + if node != nil { + if child := node.Child(ref[len(ref)-1].Value); child != nil { + for _, value := range child.Values { + if len(value.(*Rule).Head.Args) > 0 { + return NewError(CompileErr, term.Loc(), "with keyword cannot replace functions") + } + } + } + } + + } + return nil +} + +func isInputRef(term *Term) bool { + if ref, ok := term.Value.(Ref); ok { + if ref.HasPrefix(InputRootRef) { + return true + } + } + return false +} + +func isDataRef(term *Term) bool { + if ref, ok := term.Value.(Ref); ok { + if ref.HasPrefix(DefaultRootRef) { + return true + } + } + return false +} + +func isVirtual(node *TreeNode, ref Ref) bool { + for i := 0; i < len(ref); i++ { + child := node.Child(ref[i].Value) + if child == nil { + return false + } else if len(child.Values) > 0 { + return true + } + node = child + } + return true +} + +func safetyErrorSlice(unsafe unsafeVars) (result Errors) { + + if len(unsafe) == 0 { + return + } + + for _, pair := range unsafe.Vars() { + if !pair.Var.IsGenerated() { + result = append(result, NewError(UnsafeVarErr, pair.Loc, "var %v is unsafe", pair.Var)) + } + } + + if len(result) > 0 { + return + } + + // If the expression contains unsafe generated variables, report which + // expressions are unsafe instead of the variables that are unsafe (since + // the latter are not meaningful to the user.) + pairs := unsafe.Slice() + + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].Expr.Location.Compare(pairs[j].Expr.Location) < 0 + }) + + // Report at most one error per generated variable. + seen := NewVarSet() + + for _, expr := range pairs { + before := len(seen) + for v := range expr.Vars { + if v.IsGenerated() { + seen.Add(v) + } + } + if len(seen) > before { + result = append(result, NewError(UnsafeVarErr, expr.Expr.Location, "expression is unsafe")) + } + } + + return +} + +func checkUnsafeBuiltins(unsafeBuiltinsMap map[string]struct{}, node interface{}) Errors { + errs := make(Errors, 0) + WalkExprs(node, func(x *Expr) bool { + if x.IsCall() { + operator := x.Operator().String() + if _, ok := unsafeBuiltinsMap[operator]; ok { + errs = append(errs, NewError(TypeErr, x.Loc(), "unsafe built-in function calls in expression: %v", operator)) + } + } + return false + }) + return errs +} + +func rewriteVarsInRef(vars ...map[Var]Var) func(Ref) Ref { + return func(node Ref) Ref { + i, _ := TransformVars(node, func(v Var) (Value, error) { + for _, m := range vars { + if u, ok := m[v]; ok { + return u, nil + } + } + return v, nil + }) + return i.(Ref) + } +} + +func rewriteVarsNop(node Ref) Ref { + return node +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/compilehelper.go b/vendor/github.com/open-policy-agent/opa/ast/compilehelper.go new file mode 100644 index 000000000..37a81ddc9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/compilehelper.go @@ -0,0 +1,42 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +// CompileModules takes a set of Rego modules represented as strings and +// compiles them for evaluation. The keys of the map are used as filenames. +func CompileModules(modules map[string]string) (*Compiler, error) { + + parsed := make(map[string]*Module, len(modules)) + + for f, module := range modules { + var pm *Module + var err error + if pm, err = ParseModule(f, module); err != nil { + return nil, err + } + parsed[f] = pm + } + + compiler := NewCompiler() + compiler.Compile(parsed) + + if compiler.Failed() { + return nil, compiler.Errors + } + + return compiler, nil +} + +// MustCompileModules compiles a set of Rego modules represented as strings. If +// the compilation process fails, this function panics. +func MustCompileModules(modules map[string]string) *Compiler { + + compiler, err := CompileModules(modules) + if err != nil { + panic(err) + } + + return compiler +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/conflicts.go b/vendor/github.com/open-policy-agent/opa/ast/conflicts.go new file mode 100644 index 000000000..d1013cced --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/conflicts.go @@ -0,0 +1,48 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "strings" +) + +// CheckPathConflicts returns a set of errors indicating paths that +// are in conflict with the result of the provided callable. +func CheckPathConflicts(c *Compiler, exists func([]string) (bool, error)) Errors { + var errs Errors + + root := c.RuleTree.Child(DefaultRootDocument.Value) + if root == nil { + return nil + } + + for _, node := range root.Children { + errs = append(errs, checkDocumentConflicts(node, exists, nil)...) + } + + return errs +} + +func checkDocumentConflicts(node *TreeNode, exists func([]string) (bool, error), path []string) Errors { + + path = append(path, string(node.Key.(String))) + + if len(node.Values) > 0 { + s := strings.Join(path, "/") + if ok, err := exists(path); err != nil { + return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflict check for data path %v: %v", s, err.Error())} + } else if ok { + return Errors{NewError(CompileErr, node.Values[0].(*Rule).Loc(), "conflicting rule for data path %v found", s)} + } + } + + var errs Errors + + for _, child := range node.Children { + errs = append(errs, checkDocumentConflicts(child, exists, path)...) + } + + return errs +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/doc.go b/vendor/github.com/open-policy-agent/opa/ast/doc.go new file mode 100644 index 000000000..363660cf9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/doc.go @@ -0,0 +1,36 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ast declares Rego syntax tree types and also includes a parser and compiler for preparing policies for execution in the policy engine. +// +// Rego policies are defined using a relatively small set of types: modules, package and import declarations, rules, expressions, and terms. At their core, policies consist of rules that are defined by one or more expressions over documents available to the policy engine. The expressions are defined by intrinsic values (terms) such as strings, objects, variables, etc. +// +// Rego policies are typically defined in text files and then parsed and compiled by the policy engine at runtime. The parsing stage takes the text or string representation of the policy and converts it into an abstract syntax tree (AST) that consists of the types mentioned above. The AST is organized as follows: +// +// Module +// | +// +--- Package (Reference) +// | +// +--- Imports +// | | +// | +--- Import (Term) +// | +// +--- Rules +// | +// +--- Rule +// | +// +--- Head +// | | +// | +--- Name (Variable) +// | | +// | +--- Key (Term) +// | | +// | +--- Value (Term) +// | +// +--- Body +// | +// +--- Expression (Term | Terms | Variable Declaration) +// +// At query time, the policy engine expects policies to have been compiled. The compilation stage takes one or more modules and compiles them into a format that the policy engine supports. +package ast diff --git a/vendor/github.com/open-policy-agent/opa/ast/env.go b/vendor/github.com/open-policy-agent/opa/ast/env.go new file mode 100644 index 000000000..0519c30be --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/env.go @@ -0,0 +1,323 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "github.com/open-policy-agent/opa/types" + "github.com/open-policy-agent/opa/util" +) + +// TypeEnv contains type info for static analysis such as type checking. +type TypeEnv struct { + tree *typeTreeNode + next *TypeEnv +} + +// NewTypeEnv returns an empty TypeEnv. +func NewTypeEnv() *TypeEnv { + return &TypeEnv{ + tree: newTypeTree(), + } +} + +// Get returns the type of x. +func (env *TypeEnv) Get(x interface{}) types.Type { + + if term, ok := x.(*Term); ok { + x = term.Value + } + + switch x := x.(type) { + + // Scalars. + case Null: + return types.NewNull() + case Boolean: + return types.NewBoolean() + case Number: + return types.NewNumber() + case String: + return types.NewString() + + // Composites. + case Array: + static := make([]types.Type, len(x)) + for i := range static { + tpe := env.Get(x[i].Value) + static[i] = tpe + } + + var dynamic types.Type + if len(static) == 0 { + dynamic = types.A + } + + return types.NewArray(static, dynamic) + + case Object: + static := []*types.StaticProperty{} + var dynamic *types.DynamicProperty + + x.Foreach(func(k, v *Term) { + if IsConstant(k.Value) { + kjson, err := JSON(k.Value) + if err == nil { + tpe := env.Get(v) + static = append(static, types.NewStaticProperty(kjson, tpe)) + return + } + } + // Can't handle it as a static property, fallback to dynamic + typeK := env.Get(k.Value) + typeV := env.Get(v.Value) + dynamic = types.NewDynamicProperty(typeK, typeV) + }) + + if len(static) == 0 && dynamic == nil { + dynamic = types.NewDynamicProperty(types.A, types.A) + } + + return types.NewObject(static, dynamic) + + case Set: + var tpe types.Type + x.Foreach(func(elem *Term) { + other := env.Get(elem.Value) + tpe = types.Or(tpe, other) + }) + if tpe == nil { + tpe = types.A + } + return types.NewSet(tpe) + + // Comprehensions. + case *ArrayComprehension: + checker := newTypeChecker() + cpy, errs := checker.CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewArray(nil, cpy.Get(x.Term)) + } + return nil + case *ObjectComprehension: + checker := newTypeChecker() + cpy, errs := checker.CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewObject(nil, types.NewDynamicProperty(cpy.Get(x.Key), cpy.Get(x.Value))) + } + return nil + case *SetComprehension: + checker := newTypeChecker() + cpy, errs := checker.CheckBody(env, x.Body) + if len(errs) == 0 { + return types.NewSet(cpy.Get(x.Term)) + } + return nil + + // Refs. + case Ref: + return env.getRef(x) + + // Vars. + case Var: + if node := env.tree.Child(x); node != nil { + return node.Value() + } + if env.next != nil { + return env.next.Get(x) + } + return nil + + default: + panic("unreachable") + } +} + +func (env *TypeEnv) getRef(ref Ref) types.Type { + + node := env.tree.Child(ref[0].Value) + if node == nil { + return env.getRefFallback(ref) + } + + return env.getRefRec(node, ref, ref[1:]) +} + +func (env *TypeEnv) getRefFallback(ref Ref) types.Type { + + if env.next != nil { + return env.next.Get(ref) + } + + if RootDocumentNames.Contains(ref[0]) { + return types.A + } + + return nil +} + +func (env *TypeEnv) getRefRec(node *typeTreeNode, ref, tail Ref) types.Type { + if len(tail) == 0 { + return env.getRefRecExtent(node) + } + + if node.Leaf() { + return selectRef(node.Value(), tail) + } + + if !IsConstant(tail[0].Value) { + return selectRef(env.getRefRecExtent(node), tail) + } + + child := node.Child(tail[0].Value) + if child == nil { + return env.getRefFallback(ref) + } + + return env.getRefRec(child, ref, tail[1:]) +} + +func (env *TypeEnv) getRefRecExtent(node *typeTreeNode) types.Type { + + if node.Leaf() { + return node.Value() + } + + children := []*types.StaticProperty{} + + node.Children().Iter(func(k, v util.T) bool { + key := k.(Value) + child := v.(*typeTreeNode) + + tpe := env.getRefRecExtent(child) + // TODO(tsandall): handle non-string keys? + if s, ok := key.(String); ok { + children = append(children, types.NewStaticProperty(string(s), tpe)) + } + return false + }) + + // TODO(tsandall): for now, these objects can have any dynamic properties + // because we don't have schema for base docs. Once schemas are supported + // we can improve this. + return types.NewObject(children, types.NewDynamicProperty(types.S, types.A)) +} + +func (env *TypeEnv) wrap() *TypeEnv { + cpy := *env + cpy.next = env + cpy.tree = newTypeTree() + return &cpy +} + +// typeTreeNode is used to store type information in a tree. +type typeTreeNode struct { + key Value + value types.Type + children *util.HashMap +} + +func newTypeTree() *typeTreeNode { + return &typeTreeNode{ + key: nil, + value: nil, + children: util.NewHashMap(valueEq, valueHash), + } +} + +func (n *typeTreeNode) Child(key Value) *typeTreeNode { + value, ok := n.children.Get(key) + if !ok { + return nil + } + return value.(*typeTreeNode) +} + +func (n *typeTreeNode) Children() *util.HashMap { + return n.children +} + +func (n *typeTreeNode) Get(path Ref) types.Type { + curr := n + for _, term := range path { + child, ok := curr.children.Get(term.Value) + if !ok { + return nil + } + curr = child.(*typeTreeNode) + } + return curr.Value() +} + +func (n *typeTreeNode) Leaf() bool { + return n.value != nil +} + +func (n *typeTreeNode) PutOne(key Value, tpe types.Type) { + c, ok := n.children.Get(key) + + var child *typeTreeNode + if !ok { + child = newTypeTree() + child.key = key + n.children.Put(key, child) + } else { + child = c.(*typeTreeNode) + } + + child.value = tpe +} + +func (n *typeTreeNode) Put(path Ref, tpe types.Type) { + curr := n + for _, term := range path { + c, ok := curr.children.Get(term.Value) + + var child *typeTreeNode + if !ok { + child = newTypeTree() + child.key = term.Value + curr.children.Put(child.key, child) + } else { + child = c.(*typeTreeNode) + } + + curr = child + } + curr.value = tpe +} + +func (n *typeTreeNode) Value() types.Type { + return n.value +} + +// selectConstant returns the attribute of the type referred to by the term. If +// the attribute type cannot be determined, nil is returned. +func selectConstant(tpe types.Type, term *Term) types.Type { + x, err := JSON(term.Value) + if err == nil { + return types.Select(tpe, x) + } + return nil +} + +// selectRef returns the type of the nested attribute referred to by ref. If +// the attribute type cannot be determined, nil is returned. If the ref +// contains vars or refs, then the returned type will be a union of the +// possible types. +func selectRef(tpe types.Type, ref Ref) types.Type { + + if tpe == nil || len(ref) == 0 { + return tpe + } + + head, tail := ref[0], ref[1:] + + switch head.Value.(type) { + case Var, Ref, Array, Object, Set: + return selectRef(types.Values(tpe), tail) + default: + return selectRef(selectConstant(tpe, head), tail) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/errors.go b/vendor/github.com/open-policy-agent/opa/ast/errors.go new file mode 100644 index 000000000..76a084214 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/errors.go @@ -0,0 +1,133 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "sort" + "strings" +) + +// Errors represents a series of errors encountered during parsing, compiling, +// etc. +type Errors []*Error + +func (e Errors) Error() string { + + if len(e) == 0 { + return "no error(s)" + } + + if len(e) == 1 { + return fmt.Sprintf("1 error occurred: %v", e[0].Error()) + } + + s := []string{} + for _, err := range e { + s = append(s, err.Error()) + } + + return fmt.Sprintf("%d errors occurred:\n%s", len(e), strings.Join(s, "\n")) +} + +// Sort sorts the error slice by location. If the locations are equal then the +// error message is compared. +func (e Errors) Sort() { + sort.Slice(e, func(i, j int) bool { + a := e[i] + b := e[j] + + if cmp := a.Location.Compare(b.Location); cmp != 0 { + return cmp < 0 + } + + return a.Error() < b.Error() + }) +} + +const ( + // ParseErr indicates an unclassified parse error occurred. + ParseErr = "rego_parse_error" + + // CompileErr indicates an unclassified compile error occurred. + CompileErr = "rego_compile_error" + + // TypeErr indicates a type error was caught. + TypeErr = "rego_type_error" + + // UnsafeVarErr indicates an unsafe variable was found during compilation. + UnsafeVarErr = "rego_unsafe_var_error" + + // RecursionErr indicates recursion was found during compilation. + RecursionErr = "rego_recursion_error" +) + +// IsError returns true if err is an AST error with code. +func IsError(code string, err error) bool { + if err, ok := err.(*Error); ok { + return err.Code == code + } + return false +} + +// ErrorDetails defines the interface for detailed error messages. +type ErrorDetails interface { + Lines() []string +} + +// Error represents a single error caught during parsing, compiling, etc. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` + Location *Location `json:"location,omitempty"` + Details ErrorDetails `json:"details,omitempty"` +} + +func (e *Error) Error() string { + + var prefix string + + if e.Location != nil { + + if len(e.Location.File) > 0 { + prefix += e.Location.File + ":" + fmt.Sprint(e.Location.Row) + } else { + prefix += fmt.Sprint(e.Location.Row) + ":" + fmt.Sprint(e.Location.Col) + } + } + + msg := fmt.Sprintf("%v: %v", e.Code, e.Message) + + if len(prefix) > 0 { + msg = prefix + ": " + msg + } + + if e.Details != nil { + for _, line := range e.Details.Lines() { + msg += "\n\t" + line + } + } + + return msg +} + +// NewError returns a new Error object. +func NewError(code string, loc *Location, f string, a ...interface{}) *Error { + return &Error{ + Code: code, + Location: loc, + Message: fmt.Sprintf(f, a...), + } +} + +var ( + errPartialRuleAssignOperator = fmt.Errorf("partial rules must use = operator (not := operator)") + errElseAssignOperator = fmt.Errorf("else keyword cannot be used on rule declared with := operator") + errFunctionAssignOperator = fmt.Errorf("functions must use = operator (not := operator)") +) + +func errTermAssignOperator(x interface{}) error { + return fmt.Errorf("cannot assign to %v", TypeName(x)) +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/fuzz.go b/vendor/github.com/open-policy-agent/opa/ast/fuzz.go new file mode 100644 index 000000000..449729b9e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/fuzz.go @@ -0,0 +1,28 @@ +// +build gofuzz + +package ast + +import ( + "regexp" +) + +// nested { and [ tokens cause the parse time to explode. +// see: https://github.com/mna/pigeon/issues/75 +var blacklistRegexp = regexp.MustCompile(`[{(\[]{5,}`) + +func Fuzz(data []byte) int { + + if blacklistRegexp.Match(data) { + return -1 + } + + str := string(data) + _, _, err := ParseStatements("", str) + + if err == nil { + CompileModules(map[string]string{"": str}) + return 1 + } + + return 0 +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/index.go b/vendor/github.com/open-policy-agent/opa/ast/index.go new file mode 100644 index 000000000..d387353ab --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/index.go @@ -0,0 +1,798 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "io" + "sort" + "strings" + + "github.com/open-policy-agent/opa/util" +) + +// RuleIndex defines the interface for rule indices. +type RuleIndex interface { + + // Build tries to construct an index for the given rules. If the index was + // constructed, ok is true, otherwise false. + Build(rules []*Rule) (ok bool) + + // Lookup searches the index for rules that will match the provided + // resolver. If the resolver returns an error, it is returned via err. + Lookup(resolver ValueResolver) (result *IndexResult, err error) + + // AllRules traverses the index and returns all rules that will match + // the provided resolver without any optimizations (effectively with + // indexing disabled). If the resolver returns an error, it is returned + // via err. + AllRules(resolver ValueResolver) (result *IndexResult, err error) +} + +// IndexResult contains the result of an index lookup. +type IndexResult struct { + Kind DocKind + Rules []*Rule + Else map[*Rule][]*Rule + Default *Rule +} + +// NewIndexResult returns a new IndexResult object. +func NewIndexResult(kind DocKind) *IndexResult { + return &IndexResult{ + Kind: kind, + Else: map[*Rule][]*Rule{}, + } +} + +// Empty returns true if there are no rules to evaluate. +func (ir *IndexResult) Empty() bool { + return len(ir.Rules) == 0 && ir.Default == nil +} + +type baseDocEqIndex struct { + isVirtual func(Ref) bool + root *trieNode + defaultRule *Rule + kind DocKind +} + +func newBaseDocEqIndex(isVirtual func(Ref) bool) *baseDocEqIndex { + return &baseDocEqIndex{ + isVirtual: isVirtual, + root: newTrieNodeImpl(), + } +} + +func (i *baseDocEqIndex) Build(rules []*Rule) bool { + if len(rules) == 0 { + return false + } + + i.kind = rules[0].Head.DocKind() + indices := newrefindices(i.isVirtual) + + // build indices for each rule. + for idx := range rules { + WalkRules(rules[idx], func(rule *Rule) bool { + if rule.Default { + i.defaultRule = rule + return false + } + for _, expr := range rule.Body { + indices.Update(rule, expr) + } + return false + }) + } + + // build trie out of indices. + for idx := range rules { + var prio int + WalkRules(rules[idx], func(rule *Rule) bool { + if rule.Default { + return false + } + node := i.root + if indices.Indexed(rule) { + for _, ref := range indices.Sorted() { + node = node.Insert(ref, indices.Value(rule, ref), indices.Mapper(rule, ref)) + } + } + // Insert rule into trie with (insertion order, priority order) + // tuple. Retaining the insertion order allows us to return rules + // in the order they were passed to this function. + node.rules = append(node.rules, &ruleNode{[...]int{idx, prio}, rule}) + prio++ + return false + }) + + } + + return true +} + +func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { + + tr := newTrieTraversalResult() + + err := i.root.Traverse(resolver, tr) + if err != nil { + return nil, err + } + + result := NewIndexResult(i.kind) + result.Default = i.defaultRule + result.Rules = make([]*Rule, 0, len(tr.ordering)) + + for _, pos := range tr.ordering { + sort.Slice(tr.unordered[pos], func(i, j int) bool { + return tr.unordered[pos][i].prio[1] < tr.unordered[pos][j].prio[1] + }) + nodes := tr.unordered[pos] + root := nodes[0].rule + result.Rules = append(result.Rules, root) + if len(nodes) > 1 { + result.Else[root] = make([]*Rule, len(nodes)-1) + for i := 1; i < len(nodes); i++ { + result.Else[root][i-1] = nodes[i].rule + } + } + } + + return result, nil +} + +func (i *baseDocEqIndex) AllRules(resolver ValueResolver) (*IndexResult, error) { + tr := newTrieTraversalResult() + + // Walk over the rule trie and accumulate _all_ rules + rw := &ruleWalker{result: tr} + i.root.Do(rw) + + result := NewIndexResult(i.kind) + result.Default = i.defaultRule + result.Rules = make([]*Rule, 0, len(tr.ordering)) + + for _, pos := range tr.ordering { + sort.Slice(tr.unordered[pos], func(i, j int) bool { + return tr.unordered[pos][i].prio[1] < tr.unordered[pos][j].prio[1] + }) + nodes := tr.unordered[pos] + root := nodes[0].rule + result.Rules = append(result.Rules, root) + if len(nodes) > 1 { + result.Else[root] = make([]*Rule, len(nodes)-1) + for i := 1; i < len(nodes); i++ { + result.Else[root][i-1] = nodes[i].rule + } + } + } + + return result, nil +} + +type ruleWalker struct { + result *trieTraversalResult +} + +func (r *ruleWalker) Do(x interface{}) trieWalker { + tn := x.(*trieNode) + for _, rn := range tn.rules { + r.result.Add(rn) + } + return r +} + +type valueMapper func(Value) Value + +type refindex struct { + Ref Ref + Value Value + Mapper func(Value) Value +} + +type refindices struct { + isVirtual func(Ref) bool + rules map[*Rule][]*refindex + frequency *util.HashMap + sorted []Ref +} + +func newrefindices(isVirtual func(Ref) bool) *refindices { + return &refindices{ + isVirtual: isVirtual, + rules: map[*Rule][]*refindex{}, + frequency: util.NewHashMap(func(a, b util.T) bool { + r1, r2 := a.(Ref), b.(Ref) + return r1.Equal(r2) + }, func(x util.T) int { + return x.(Ref).Hash() + }), + } +} + +// Update attempts to update the refindices for the given expression in the +// given rule. If the expression cannot be indexed the update does not affect +// the indices. +func (i *refindices) Update(rule *Rule, expr *Expr) { + + if expr.Negated { + return + } + + if len(expr.With) > 0 { + // NOTE(tsandall): In the future, we may need to consider expressions + // that have with statements applied to them. + return + } + + op := expr.Operator() + + if op.Equal(Equality.Ref()) || op.Equal(Equal.Ref()) { + + i.updateEq(rule, expr) + + } else if op.Equal(GlobMatch.Ref()) { + + i.updateGlobMatch(rule, expr) + } +} + +// Sorted returns a sorted list of references that the indices were built from. +// References that appear more frequently in the indexed rules are ordered +// before less frequently appearing references. +func (i *refindices) Sorted() []Ref { + + if i.sorted == nil { + counts := make([]int, 0, i.frequency.Len()) + i.sorted = make([]Ref, 0, i.frequency.Len()) + + i.frequency.Iter(func(k, v util.T) bool { + counts = append(counts, v.(int)) + i.sorted = append(i.sorted, k.(Ref)) + return false + }) + + sort.Slice(i.sorted, func(i, j int) bool { + return counts[i] > counts[j] + }) + } + + return i.sorted +} + +func (i *refindices) Indexed(rule *Rule) bool { + return len(i.rules[rule]) > 0 +} + +func (i *refindices) Value(rule *Rule, ref Ref) Value { + if index := i.index(rule, ref); index != nil { + return index.Value + } + return nil +} + +func (i *refindices) Mapper(rule *Rule, ref Ref) valueMapper { + if index := i.index(rule, ref); index != nil { + return index.Mapper + } + return nil +} + +func (i *refindices) updateEq(rule *Rule, expr *Expr) { + a, b := expr.Operand(0), expr.Operand(1) + if ref, value, ok := eqOperandsToRefAndValue(i.isVirtual, a, b); ok { + i.insert(rule, &refindex{ + Ref: ref, + Value: value, + }) + } else if ref, value, ok := eqOperandsToRefAndValue(i.isVirtual, b, a); ok { + i.insert(rule, &refindex{ + Ref: ref, + Value: value, + }) + } +} + +func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) { + + delim, ok := globDelimiterToString(expr.Operand(1)) + if !ok { + return + } + + if arr := globPatternToArray(expr.Operand(0), delim); arr != nil { + // The 3rd operand of glob.match is the value to match. We assume the + // 3rd operand was a reference that has been rewritten and bound to a + // variable earlier in the query. + match := expr.Operand(2) + if _, ok := match.Value.(Var); ok { + for _, other := range i.rules[rule] { + if _, ok := other.Value.(Var); ok && other.Value.Compare(match.Value) == 0 { + i.insert(rule, &refindex{ + Ref: other.Ref, + Value: arr.Value, + Mapper: func(v Value) Value { + if s, ok := v.(String); ok { + return stringSliceToArray(splitStringEscaped(string(s), delim)) + } + return v + }, + }) + } + } + } + } +} + +func (i *refindices) insert(rule *Rule, index *refindex) { + + count, ok := i.frequency.Get(index.Ref) + if !ok { + count = 0 + } + + i.frequency.Put(index.Ref, count.(int)+1) + + for pos, other := range i.rules[rule] { + if other.Ref.Equal(index.Ref) { + i.rules[rule][pos] = index + return + } + } + + i.rules[rule] = append(i.rules[rule], index) +} + +func (i *refindices) index(rule *Rule, ref Ref) *refindex { + for _, index := range i.rules[rule] { + if index.Ref.Equal(ref) { + return index + } + } + return nil +} + +type trieWalker interface { + Do(x interface{}) trieWalker +} + +type trieTraversalResult struct { + unordered map[int][]*ruleNode + ordering []int +} + +func newTrieTraversalResult() *trieTraversalResult { + return &trieTraversalResult{ + unordered: map[int][]*ruleNode{}, + } +} + +func (tr *trieTraversalResult) Add(node *ruleNode) { + root := node.prio[0] + nodes, ok := tr.unordered[root] + if !ok { + tr.ordering = append(tr.ordering, root) + } + tr.unordered[root] = append(nodes, node) +} + +type trieNode struct { + ref Ref + mapper valueMapper + next *trieNode + any *trieNode + undefined *trieNode + scalars map[Value]*trieNode + array *trieNode + rules []*ruleNode +} + +func (node *trieNode) String() string { + var flags []string + flags = append(flags, fmt.Sprintf("self:%p", node)) + if len(node.ref) > 0 { + flags = append(flags, node.ref.String()) + } + if node.next != nil { + flags = append(flags, fmt.Sprintf("next:%p", node.next)) + } + if node.any != nil { + flags = append(flags, fmt.Sprintf("any:%p", node.any)) + } + if node.undefined != nil { + flags = append(flags, fmt.Sprintf("undefined:%p", node.undefined)) + } + if node.array != nil { + flags = append(flags, fmt.Sprintf("array:%p", node.array)) + } + if len(node.scalars) > 0 { + buf := []string{} + for k, v := range node.scalars { + buf = append(buf, fmt.Sprintf("scalar(%v):%p", k, v)) + } + sort.Strings(buf) + flags = append(flags, strings.Join(buf, " ")) + } + if len(node.rules) > 0 { + flags = append(flags, fmt.Sprintf("%d rule(s)", len(node.rules))) + } + if node.mapper != nil { + flags = append(flags, "mapper") + } + return strings.Join(flags, " ") +} + +type ruleNode struct { + prio [2]int + rule *Rule +} + +func newTrieNodeImpl() *trieNode { + return &trieNode{ + scalars: map[Value]*trieNode{}, + } +} + +func (node *trieNode) Do(walker trieWalker) { + next := walker.Do(node) + if next == nil { + return + } + if node.any != nil { + node.any.Do(next) + } + if node.undefined != nil { + node.undefined.Do(next) + } + for _, child := range node.scalars { + child.Do(next) + } + if node.array != nil { + node.array.Do(next) + } + if node.next != nil { + node.next.Do(next) + } +} + +func (node *trieNode) Insert(ref Ref, value Value, mapper valueMapper) *trieNode { + + if node.next == nil { + node.next = newTrieNodeImpl() + node.next.ref = ref + } + + node.next.mapper = mapper + + return node.next.insertValue(value) +} + +func (node *trieNode) Traverse(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + for i := range node.rules { + tr.Add(node.rules[i]) + } + + return node.next.traverse(resolver, tr) +} + +func (node *trieNode) insertValue(value Value) *trieNode { + + switch value := value.(type) { + case nil: + if node.undefined == nil { + node.undefined = newTrieNodeImpl() + } + return node.undefined + case Var: + if node.any == nil { + node.any = newTrieNodeImpl() + } + return node.any + case Null, Boolean, Number, String: + child, ok := node.scalars[value] + if !ok { + child = newTrieNodeImpl() + node.scalars[value] = child + } + return child + case Array: + if node.array == nil { + node.array = newTrieNodeImpl() + } + return node.array.insertArray(value) + } + + panic("illegal value") +} + +func (node *trieNode) insertArray(arr Array) *trieNode { + + if len(arr) == 0 { + return node + } + + switch head := arr[0].Value.(type) { + case Var: + if node.any == nil { + node.any = newTrieNodeImpl() + } + return node.any.insertArray(arr[1:]) + case Null, Boolean, Number, String: + child, ok := node.scalars[head] + if !ok { + child = newTrieNodeImpl() + node.scalars[head] = child + } + return child.insertArray(arr[1:]) + } + + panic("illegal value") +} + +func (node *trieNode) traverse(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + v, err := resolver.Resolve(node.ref) + if err != nil { + if IsUnknownValueErr(err) { + return node.traverseUnknown(resolver, tr) + } + return err + } + + if node.undefined != nil { + node.undefined.Traverse(resolver, tr) + } + + if v == nil { + return nil + } + + if node.any != nil { + node.any.Traverse(resolver, tr) + } + + if node.mapper != nil { + v = node.mapper(v) + } + + return node.traverseValue(resolver, tr, v) +} + +func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalResult, value Value) error { + + switch value := value.(type) { + case Array: + if node.array == nil { + return nil + } + return node.array.traverseArray(resolver, tr, value) + + case Null, Boolean, Number, String: + child, ok := node.scalars[value] + if !ok { + return nil + } + return child.Traverse(resolver, tr) + } + + return nil +} + +func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalResult, arr Array) error { + + if len(arr) == 0 { + return node.Traverse(resolver, tr) + } + + head := arr[0].Value + + if !IsScalar(head) { + return nil + } + + if node.any != nil { + node.any.traverseArray(resolver, tr, arr[1:]) + } + + child, ok := node.scalars[head] + if !ok { + return nil + } + + return child.traverseArray(resolver, tr, arr[1:]) +} + +func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error { + + if node == nil { + return nil + } + + if err := node.Traverse(resolver, tr); err != nil { + return err + } + + if err := node.undefined.traverseUnknown(resolver, tr); err != nil { + return err + } + + if err := node.any.traverseUnknown(resolver, tr); err != nil { + return err + } + + if err := node.array.traverseUnknown(resolver, tr); err != nil { + return err + } + + for _, child := range node.scalars { + if err := child.traverseUnknown(resolver, tr); err != nil { + return err + } + } + + return nil +} + +type triePrinter struct { + depth int + w io.Writer +} + +func (p triePrinter) Do(x interface{}) trieWalker { + padding := strings.Repeat(" ", p.depth) + fmt.Fprintf(p.w, "%v%v\n", padding, x) + p.depth++ + return p +} + +func eqOperandsToRefAndValue(isVirtual func(Ref) bool, a, b *Term) (Ref, Value, bool) { + + ref, ok := a.Value.(Ref) + if !ok { + return nil, nil, false + } + + if !RootDocumentNames.Contains(ref[0]) { + return nil, nil, false + } + + if isVirtual(ref) { + return nil, nil, false + } + + if ref.IsNested() || !ref.IsGround() { + return nil, nil, false + } + + switch b := b.Value.(type) { + case Null, Boolean, Number, String, Var: + return ref, b, true + case Array: + stop := false + first := true + vis := NewGenericVisitor(func(x interface{}) bool { + if first { + first = false + return false + } + switch x.(type) { + // No nested structures or values that require evaluation (other than var). + case Array, Object, Set, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Ref: + stop = true + } + return stop + }) + vis.Walk(b) + if !stop { + return ref, b, true + } + } + + return nil, nil, false +} + +func globDelimiterToString(delim *Term) (string, bool) { + + arr, ok := delim.Value.(Array) + if !ok { + return "", false + } + + var result string + + if len(arr) == 0 { + result = "." + } else { + for _, term := range arr { + s, ok := term.Value.(String) + if !ok { + return "", false + } + result += string(s) + } + } + + return result, true +} + +func globPatternToArray(pattern *Term, delim string) *Term { + + s, ok := pattern.Value.(String) + if !ok { + return nil + } + + parts := splitStringEscaped(string(s), delim) + result := make(Array, len(parts)) + + for i := range parts { + if parts[i] == "*" { + result[i] = VarTerm("$globwildcard") + } else { + var escaped bool + for _, c := range parts[i] { + if c == '\\' { + escaped = !escaped + continue + } + if !escaped { + switch c { + case '[', '?', '{', '*': + // TODO(tsandall): super glob and character pattern + // matching not supported yet. + return nil + } + } + escaped = false + } + result[i] = StringTerm(parts[i]) + } + } + + return NewTerm(result) +} + +// splits s on characters in delim except if delim characters have been escaped +// with reverse solidus. +func splitStringEscaped(s string, delim string) []string { + + var last, curr int + var escaped bool + var result []string + + for ; curr < len(s); curr++ { + if s[curr] == '\\' || escaped { + escaped = !escaped + continue + } + if strings.ContainsRune(delim, rune(s[curr])) { + result = append(result, s[last:curr]) + last = curr + 1 + } + } + + result = append(result, s[last:]) + + return result +} + +func stringSliceToArray(s []string) (result Array) { + result = make(Array, len(s)) + for i := range s { + result[i] = StringTerm(s[i]) + } + return +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/map.go b/vendor/github.com/open-policy-agent/opa/ast/map.go new file mode 100644 index 000000000..b0cc9eb60 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/map.go @@ -0,0 +1,133 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "encoding/json" + + "github.com/open-policy-agent/opa/util" +) + +// ValueMap represents a key/value map between AST term values. Any type of term +// can be used as a key in the map. +type ValueMap struct { + hashMap *util.HashMap +} + +// NewValueMap returns a new ValueMap. +func NewValueMap() *ValueMap { + vs := &ValueMap{ + hashMap: util.NewHashMap(valueEq, valueHash), + } + return vs +} + +// MarshalJSON provides a custom marshaller for the ValueMap which +// will include the key, value, and value type. +func (vs *ValueMap) MarshalJSON() ([]byte, error) { + var tmp []map[string]interface{} + vs.Iter(func(k Value, v Value) bool { + tmp = append(tmp, map[string]interface{}{ + "name": k.String(), + "type": TypeName(v), + "value": v, + }) + return false + }) + return json.Marshal(tmp) +} + +// Copy returns a shallow copy of the ValueMap. +func (vs *ValueMap) Copy() *ValueMap { + if vs == nil { + return nil + } + cpy := NewValueMap() + cpy.hashMap = vs.hashMap.Copy() + return cpy +} + +// Equal returns true if this ValueMap equals the other. +func (vs *ValueMap) Equal(other *ValueMap) bool { + if vs == nil { + return other == nil || other.Len() == 0 + } + if other == nil { + return vs == nil || vs.Len() == 0 + } + return vs.hashMap.Equal(other.hashMap) +} + +// Len returns the number of elements in the map. +func (vs *ValueMap) Len() int { + if vs == nil { + return 0 + } + return vs.hashMap.Len() +} + +// Get returns the value in the map for k. +func (vs *ValueMap) Get(k Value) Value { + if vs != nil { + if v, ok := vs.hashMap.Get(k); ok { + return v.(Value) + } + } + return nil +} + +// Hash returns a hash code for this ValueMap. +func (vs *ValueMap) Hash() int { + if vs == nil { + return 0 + } + return vs.hashMap.Hash() +} + +// Iter calls the iter function for each key/value pair in the map. If the iter +// function returns true, iteration stops. +func (vs *ValueMap) Iter(iter func(Value, Value) bool) bool { + if vs == nil { + return false + } + return vs.hashMap.Iter(func(kt, vt util.T) bool { + k := kt.(Value) + v := vt.(Value) + return iter(k, v) + }) +} + +// Put inserts a key k into the map with value v. +func (vs *ValueMap) Put(k, v Value) { + if vs == nil { + panic("put on nil value map") + } + vs.hashMap.Put(k, v) +} + +// Delete removes a key k from the map. +func (vs *ValueMap) Delete(k Value) { + if vs == nil { + return + } + vs.hashMap.Delete(k) +} + +func (vs *ValueMap) String() string { + if vs == nil { + return "{}" + } + return vs.hashMap.String() +} + +func valueHash(v util.T) int { + return v.(Value).Hash() +} + +func valueEq(a, b util.T) bool { + av := a.(Value) + bv := b.(Value) + return av.Compare(bv) == 0 +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser.go b/vendor/github.com/open-policy-agent/opa/ast/parser.go new file mode 100644 index 000000000..d2236de7e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/parser.go @@ -0,0 +1,5150 @@ +// Code generated by pigeon; DO NOT EDIT. + +package ast + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "sort" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +var g = &grammar{ + rules: []*rule{ + { + name: "Program", + pos: position{line: 5, col: 1, offset: 17}, + expr: &actionExpr{ + pos: position{line: 5, col: 12, offset: 28}, + run: (*parser).callonProgram1, + expr: &seqExpr{ + pos: position{line: 5, col: 12, offset: 28}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 5, col: 12, offset: 28}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 5, col: 14, offset: 30}, + label: "vals", + expr: &zeroOrOneExpr{ + pos: position{line: 5, col: 19, offset: 35}, + expr: &seqExpr{ + pos: position{line: 5, col: 20, offset: 36}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 5, col: 20, offset: 36}, + name: "Stmt", + }, + &zeroOrMoreExpr{ + pos: position{line: 5, col: 25, offset: 41}, + expr: &seqExpr{ + pos: position{line: 5, col: 26, offset: 42}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 5, col: 26, offset: 42}, + name: "ws", + }, + &ruleRefExpr{ + pos: position{line: 5, col: 29, offset: 45}, + name: "Stmt", + }, + }, + }, + }, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 5, col: 38, offset: 54}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 5, col: 40, offset: 56}, + name: "EOF", + }, + }, + }, + }, + }, + { + name: "Stmt", + pos: position{line: 9, col: 1, offset: 97}, + expr: &actionExpr{ + pos: position{line: 9, col: 9, offset: 105}, + run: (*parser).callonStmt1, + expr: &labeledExpr{ + pos: position{line: 9, col: 9, offset: 105}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 9, col: 14, offset: 110}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 9, col: 14, offset: 110}, + name: "Package", + }, + &ruleRefExpr{ + pos: position{line: 9, col: 24, offset: 120}, + name: "Import", + }, + &ruleRefExpr{ + pos: position{line: 9, col: 33, offset: 129}, + name: "Rules", + }, + &ruleRefExpr{ + pos: position{line: 9, col: 41, offset: 137}, + name: "Body", + }, + &ruleRefExpr{ + pos: position{line: 9, col: 48, offset: 144}, + name: "Comment", + }, + }, + }, + }, + }, + }, + { + name: "Package", + pos: position{line: 13, col: 1, offset: 178}, + expr: &actionExpr{ + pos: position{line: 13, col: 12, offset: 189}, + run: (*parser).callonPackage1, + expr: &seqExpr{ + pos: position{line: 13, col: 12, offset: 189}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 13, col: 12, offset: 189}, + val: "package", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 13, col: 22, offset: 199}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 13, col: 25, offset: 202}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 13, col: 30, offset: 207}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 13, col: 30, offset: 207}, + name: "Ref", + }, + &ruleRefExpr{ + pos: position{line: 13, col: 36, offset: 213}, + name: "Var", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Import", + pos: position{line: 17, col: 1, offset: 271}, + expr: &actionExpr{ + pos: position{line: 17, col: 11, offset: 281}, + run: (*parser).callonImport1, + expr: &seqExpr{ + pos: position{line: 17, col: 11, offset: 281}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 17, col: 11, offset: 281}, + val: "import", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 17, col: 20, offset: 290}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 17, col: 23, offset: 293}, + label: "path", + expr: &choiceExpr{ + pos: position{line: 17, col: 29, offset: 299}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 17, col: 29, offset: 299}, + name: "Ref", + }, + &ruleRefExpr{ + pos: position{line: 17, col: 35, offset: 305}, + name: "Var", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 17, col: 40, offset: 310}, + label: "alias", + expr: &zeroOrOneExpr{ + pos: position{line: 17, col: 46, offset: 316}, + expr: &seqExpr{ + pos: position{line: 17, col: 47, offset: 317}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 17, col: 47, offset: 317}, + name: "ws", + }, + &litMatcher{ + pos: position{line: 17, col: 50, offset: 320}, + val: "as", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 17, col: 55, offset: 325}, + name: "ws", + }, + &ruleRefExpr{ + pos: position{line: 17, col: 58, offset: 328}, + name: "Var", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Rules", + pos: position{line: 21, col: 1, offset: 394}, + expr: &choiceExpr{ + pos: position{line: 21, col: 10, offset: 403}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 21, col: 10, offset: 403}, + name: "DefaultRules", + }, + &ruleRefExpr{ + pos: position{line: 21, col: 25, offset: 418}, + name: "NormalRules", + }, + }, + }, + }, + { + name: "DefaultRules", + pos: position{line: 23, col: 1, offset: 431}, + expr: &actionExpr{ + pos: position{line: 23, col: 17, offset: 447}, + run: (*parser).callonDefaultRules1, + expr: &seqExpr{ + pos: position{line: 23, col: 17, offset: 447}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 23, col: 17, offset: 447}, + val: "default", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 23, col: 27, offset: 457}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 23, col: 30, offset: 460}, + label: "name", + expr: &ruleRefExpr{ + pos: position{line: 23, col: 35, offset: 465}, + name: "Var", + }, + }, + &ruleRefExpr{ + pos: position{line: 23, col: 39, offset: 469}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 23, col: 41, offset: 471}, + label: "operator", + expr: &choiceExpr{ + pos: position{line: 23, col: 52, offset: 482}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 23, col: 52, offset: 482}, + val: ":=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 23, col: 59, offset: 489}, + val: "=", + ignoreCase: false, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 23, col: 65, offset: 495}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 23, col: 67, offset: 497}, + label: "value", + expr: &ruleRefExpr{ + pos: position{line: 23, col: 73, offset: 503}, + name: "Term", + }, + }, + }, + }, + }, + }, + { + name: "NormalRules", + pos: position{line: 27, col: 1, offset: 583}, + expr: &actionExpr{ + pos: position{line: 27, col: 16, offset: 598}, + run: (*parser).callonNormalRules1, + expr: &seqExpr{ + pos: position{line: 27, col: 16, offset: 598}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 27, col: 16, offset: 598}, + label: "head", + expr: &choiceExpr{ + pos: position{line: 27, col: 22, offset: 604}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 27, col: 22, offset: 604}, + name: "PartialRuleHead", + }, + &ruleRefExpr{ + pos: position{line: 27, col: 40, offset: 622}, + name: "RuleHead", + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 27, col: 50, offset: 632}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 27, col: 52, offset: 634}, + label: "rest", + expr: &seqExpr{ + pos: position{line: 27, col: 58, offset: 640}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 27, col: 58, offset: 640}, + name: "NonEmptyBraceEnclosedBody", + }, + &zeroOrMoreExpr{ + pos: position{line: 27, col: 84, offset: 666}, + expr: &seqExpr{ + pos: position{line: 27, col: 86, offset: 668}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 27, col: 86, offset: 668}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 27, col: 88, offset: 670}, + name: "RuleExt", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "PartialRuleHead", + pos: position{line: 31, col: 1, offset: 739}, + expr: &actionExpr{ + pos: position{line: 31, col: 20, offset: 758}, + run: (*parser).callonPartialRuleHead1, + expr: &seqExpr{ + pos: position{line: 31, col: 20, offset: 758}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 31, col: 20, offset: 758}, + label: "name", + expr: &ruleRefExpr{ + pos: position{line: 31, col: 25, offset: 763}, + name: "Var", + }, + }, + &labeledExpr{ + pos: position{line: 31, col: 29, offset: 767}, + label: "args", + expr: &seqExpr{ + pos: position{line: 31, col: 36, offset: 774}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 31, col: 36, offset: 774}, + name: "_", + }, + &litMatcher{ + pos: position{line: 31, col: 38, offset: 776}, + val: "(", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 31, col: 42, offset: 780}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 31, col: 44, offset: 782}, + name: "Args", + }, + &ruleRefExpr{ + pos: position{line: 31, col: 49, offset: 787}, + name: "_", + }, + &litMatcher{ + pos: position{line: 31, col: 51, offset: 789}, + val: ")", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 31, col: 55, offset: 793}, + name: "_", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 31, col: 59, offset: 797}, + label: "value", + expr: &zeroOrOneExpr{ + pos: position{line: 31, col: 65, offset: 803}, + expr: &seqExpr{ + pos: position{line: 31, col: 67, offset: 805}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 31, col: 67, offset: 805}, + name: "_", + }, + &choiceExpr{ + pos: position{line: 31, col: 71, offset: 809}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 31, col: 71, offset: 809}, + val: ":=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 31, col: 78, offset: 816}, + val: "=", + ignoreCase: false, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 31, col: 84, offset: 822}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 31, col: 86, offset: 824}, + name: "ExprTerm", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "RuleHead", + pos: position{line: 35, col: 1, offset: 909}, + expr: &actionExpr{ + pos: position{line: 35, col: 13, offset: 921}, + run: (*parser).callonRuleHead1, + expr: &seqExpr{ + pos: position{line: 35, col: 13, offset: 921}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 35, col: 13, offset: 921}, + label: "name", + expr: &ruleRefExpr{ + pos: position{line: 35, col: 18, offset: 926}, + name: "Var", + }, + }, + &labeledExpr{ + pos: position{line: 35, col: 22, offset: 930}, + label: "key", + expr: &zeroOrOneExpr{ + pos: position{line: 35, col: 26, offset: 934}, + expr: &seqExpr{ + pos: position{line: 35, col: 28, offset: 936}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 35, col: 28, offset: 936}, + name: "_", + }, + &litMatcher{ + pos: position{line: 35, col: 30, offset: 938}, + val: "[", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 35, col: 34, offset: 942}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 35, col: 36, offset: 944}, + name: "ExprTerm", + }, + &ruleRefExpr{ + pos: position{line: 35, col: 45, offset: 953}, + name: "_", + }, + &litMatcher{ + pos: position{line: 35, col: 47, offset: 955}, + val: "]", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 35, col: 51, offset: 959}, + name: "_", + }, + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 35, col: 56, offset: 964}, + label: "value", + expr: &zeroOrOneExpr{ + pos: position{line: 35, col: 62, offset: 970}, + expr: &seqExpr{ + pos: position{line: 35, col: 64, offset: 972}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 35, col: 64, offset: 972}, + name: "_", + }, + &choiceExpr{ + pos: position{line: 35, col: 68, offset: 976}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 35, col: 68, offset: 976}, + val: ":=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 35, col: 75, offset: 983}, + val: "=", + ignoreCase: false, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 35, col: 81, offset: 989}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 35, col: 83, offset: 991}, + name: "ExprTerm", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Args", + pos: position{line: 39, col: 1, offset: 1075}, + expr: &actionExpr{ + pos: position{line: 39, col: 9, offset: 1083}, + run: (*parser).callonArgs1, + expr: &labeledExpr{ + pos: position{line: 39, col: 9, offset: 1083}, + label: "list", + expr: &ruleRefExpr{ + pos: position{line: 39, col: 14, offset: 1088}, + name: "ExprTermList", + }, + }, + }, + }, + { + name: "Else", + pos: position{line: 43, col: 1, offset: 1132}, + expr: &actionExpr{ + pos: position{line: 43, col: 9, offset: 1140}, + run: (*parser).callonElse1, + expr: &seqExpr{ + pos: position{line: 43, col: 9, offset: 1140}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 43, col: 9, offset: 1140}, + val: "else", + ignoreCase: false, + }, + &labeledExpr{ + pos: position{line: 43, col: 16, offset: 1147}, + label: "value", + expr: &zeroOrOneExpr{ + pos: position{line: 43, col: 22, offset: 1153}, + expr: &seqExpr{ + pos: position{line: 43, col: 24, offset: 1155}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 43, col: 24, offset: 1155}, + name: "_", + }, + &litMatcher{ + pos: position{line: 43, col: 26, offset: 1157}, + val: "=", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 43, col: 30, offset: 1161}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 43, col: 32, offset: 1163}, + name: "Term", + }, + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 43, col: 40, offset: 1171}, + label: "body", + expr: &seqExpr{ + pos: position{line: 43, col: 47, offset: 1178}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 43, col: 47, offset: 1178}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 43, col: 49, offset: 1180}, + name: "NonEmptyBraceEnclosedBody", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "RuleDup", + pos: position{line: 47, col: 1, offset: 1269}, + expr: &actionExpr{ + pos: position{line: 47, col: 12, offset: 1280}, + run: (*parser).callonRuleDup1, + expr: &labeledExpr{ + pos: position{line: 47, col: 12, offset: 1280}, + label: "b", + expr: &ruleRefExpr{ + pos: position{line: 47, col: 14, offset: 1282}, + name: "NonEmptyBraceEnclosedBody", + }, + }, + }, + }, + { + name: "RuleExt", + pos: position{line: 51, col: 1, offset: 1378}, + expr: &choiceExpr{ + pos: position{line: 51, col: 12, offset: 1389}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 51, col: 12, offset: 1389}, + name: "Else", + }, + &ruleRefExpr{ + pos: position{line: 51, col: 19, offset: 1396}, + name: "RuleDup", + }, + }, + }, + }, + { + name: "Body", + pos: position{line: 53, col: 1, offset: 1405}, + expr: &choiceExpr{ + pos: position{line: 53, col: 9, offset: 1413}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 53, col: 9, offset: 1413}, + name: "NonWhitespaceBody", + }, + &ruleRefExpr{ + pos: position{line: 53, col: 29, offset: 1433}, + name: "BraceEnclosedBody", + }, + }, + }, + }, + { + name: "NonEmptyBraceEnclosedBody", + pos: position{line: 55, col: 1, offset: 1452}, + expr: &actionExpr{ + pos: position{line: 55, col: 30, offset: 1481}, + run: (*parser).callonNonEmptyBraceEnclosedBody1, + expr: &seqExpr{ + pos: position{line: 55, col: 30, offset: 1481}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 55, col: 30, offset: 1481}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 55, col: 34, offset: 1485}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 55, col: 36, offset: 1487}, + label: "val", + expr: &zeroOrOneExpr{ + pos: position{line: 55, col: 40, offset: 1491}, + expr: &ruleRefExpr{ + pos: position{line: 55, col: 40, offset: 1491}, + name: "WhitespaceBody", + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 55, col: 56, offset: 1507}, + name: "_", + }, + &litMatcher{ + pos: position{line: 55, col: 58, offset: 1509}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "BraceEnclosedBody", + pos: position{line: 62, col: 1, offset: 1621}, + expr: &actionExpr{ + pos: position{line: 62, col: 22, offset: 1642}, + run: (*parser).callonBraceEnclosedBody1, + expr: &seqExpr{ + pos: position{line: 62, col: 22, offset: 1642}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 62, col: 22, offset: 1642}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 62, col: 26, offset: 1646}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 62, col: 28, offset: 1648}, + label: "val", + expr: &zeroOrOneExpr{ + pos: position{line: 62, col: 32, offset: 1652}, + expr: &ruleRefExpr{ + pos: position{line: 62, col: 32, offset: 1652}, + name: "WhitespaceBody", + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 62, col: 48, offset: 1668}, + name: "_", + }, + &litMatcher{ + pos: position{line: 62, col: 50, offset: 1670}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "WhitespaceBody", + pos: position{line: 66, col: 1, offset: 1737}, + expr: &actionExpr{ + pos: position{line: 66, col: 19, offset: 1755}, + run: (*parser).callonWhitespaceBody1, + expr: &seqExpr{ + pos: position{line: 66, col: 19, offset: 1755}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 66, col: 19, offset: 1755}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 66, col: 24, offset: 1760}, + name: "Literal", + }, + }, + &labeledExpr{ + pos: position{line: 66, col: 32, offset: 1768}, + label: "tail", + expr: &zeroOrMoreExpr{ + pos: position{line: 66, col: 37, offset: 1773}, + expr: &seqExpr{ + pos: position{line: 66, col: 38, offset: 1774}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 66, col: 38, offset: 1774}, + name: "WhitespaceLiteralSeparator", + }, + &ruleRefExpr{ + pos: position{line: 66, col: 65, offset: 1801}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 66, col: 67, offset: 1803}, + name: "Literal", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "NonWhitespaceBody", + pos: position{line: 70, col: 1, offset: 1853}, + expr: &actionExpr{ + pos: position{line: 70, col: 22, offset: 1874}, + run: (*parser).callonNonWhitespaceBody1, + expr: &seqExpr{ + pos: position{line: 70, col: 22, offset: 1874}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 70, col: 22, offset: 1874}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 70, col: 27, offset: 1879}, + name: "Literal", + }, + }, + &labeledExpr{ + pos: position{line: 70, col: 35, offset: 1887}, + label: "tail", + expr: &zeroOrMoreExpr{ + pos: position{line: 70, col: 40, offset: 1892}, + expr: &seqExpr{ + pos: position{line: 70, col: 42, offset: 1894}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 70, col: 42, offset: 1894}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 70, col: 44, offset: 1896}, + name: "NonWhitespaceLiteralSeparator", + }, + &ruleRefExpr{ + pos: position{line: 70, col: 74, offset: 1926}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 70, col: 76, offset: 1928}, + name: "Literal", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "WhitespaceLiteralSeparator", + pos: position{line: 74, col: 1, offset: 1978}, + expr: &seqExpr{ + pos: position{line: 74, col: 31, offset: 2008}, + exprs: []interface{}{ + &zeroOrMoreExpr{ + pos: position{line: 74, col: 31, offset: 2008}, + expr: &charClassMatcher{ + pos: position{line: 74, col: 31, offset: 2008}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + &choiceExpr{ + pos: position{line: 74, col: 39, offset: 2016}, + alternatives: []interface{}{ + &seqExpr{ + pos: position{line: 74, col: 40, offset: 2017}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 74, col: 40, offset: 2017}, + name: "NonWhitespaceLiteralSeparator", + }, + &zeroOrOneExpr{ + pos: position{line: 74, col: 70, offset: 2047}, + expr: &ruleRefExpr{ + pos: position{line: 74, col: 70, offset: 2047}, + name: "Comment", + }, + }, + }, + }, + &seqExpr{ + pos: position{line: 74, col: 83, offset: 2060}, + exprs: []interface{}{ + &zeroOrOneExpr{ + pos: position{line: 74, col: 83, offset: 2060}, + expr: &ruleRefExpr{ + pos: position{line: 74, col: 83, offset: 2060}, + name: "Comment", + }, + }, + &charClassMatcher{ + pos: position{line: 74, col: 92, offset: 2069}, + val: "[\\r\\n]", + chars: []rune{'\r', '\n'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "NonWhitespaceLiteralSeparator", + pos: position{line: 76, col: 1, offset: 2079}, + expr: &litMatcher{ + pos: position{line: 76, col: 34, offset: 2112}, + val: ";", + ignoreCase: false, + }, + }, + { + name: "Literal", + pos: position{line: 78, col: 1, offset: 2117}, + expr: &choiceExpr{ + pos: position{line: 78, col: 12, offset: 2128}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 78, col: 12, offset: 2128}, + name: "TermExpr", + }, + &ruleRefExpr{ + pos: position{line: 78, col: 23, offset: 2139}, + name: "SomeDecl", + }, + }, + }, + }, + { + name: "SomeDecl", + pos: position{line: 80, col: 1, offset: 2149}, + expr: &actionExpr{ + pos: position{line: 80, col: 13, offset: 2161}, + run: (*parser).callonSomeDecl1, + expr: &seqExpr{ + pos: position{line: 80, col: 13, offset: 2161}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 80, col: 13, offset: 2161}, + val: "some", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 80, col: 20, offset: 2168}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 80, col: 23, offset: 2171}, + label: "symbols", + expr: &ruleRefExpr{ + pos: position{line: 80, col: 31, offset: 2179}, + name: "SomeDeclList", + }, + }, + }, + }, + }, + }, + { + name: "SomeDeclList", + pos: position{line: 84, col: 1, offset: 2257}, + expr: &actionExpr{ + pos: position{line: 84, col: 17, offset: 2273}, + run: (*parser).callonSomeDeclList1, + expr: &seqExpr{ + pos: position{line: 84, col: 17, offset: 2273}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 84, col: 17, offset: 2273}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 84, col: 22, offset: 2278}, + name: "Var", + }, + }, + &labeledExpr{ + pos: position{line: 84, col: 26, offset: 2282}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 84, col: 31, offset: 2287}, + expr: &seqExpr{ + pos: position{line: 84, col: 33, offset: 2289}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 84, col: 33, offset: 2289}, + name: "_", + }, + &litMatcher{ + pos: position{line: 84, col: 35, offset: 2291}, + val: ",", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 84, col: 39, offset: 2295}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 84, col: 41, offset: 2297}, + name: "Var", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "TermExpr", + pos: position{line: 88, col: 1, offset: 2351}, + expr: &actionExpr{ + pos: position{line: 88, col: 13, offset: 2363}, + run: (*parser).callonTermExpr1, + expr: &seqExpr{ + pos: position{line: 88, col: 13, offset: 2363}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 88, col: 13, offset: 2363}, + label: "negated", + expr: &zeroOrOneExpr{ + pos: position{line: 88, col: 21, offset: 2371}, + expr: &ruleRefExpr{ + pos: position{line: 88, col: 21, offset: 2371}, + name: "NotKeyword", + }, + }, + }, + &labeledExpr{ + pos: position{line: 88, col: 33, offset: 2383}, + label: "value", + expr: &ruleRefExpr{ + pos: position{line: 88, col: 39, offset: 2389}, + name: "LiteralExpr", + }, + }, + &labeledExpr{ + pos: position{line: 88, col: 51, offset: 2401}, + label: "with", + expr: &zeroOrOneExpr{ + pos: position{line: 88, col: 56, offset: 2406}, + expr: &ruleRefExpr{ + pos: position{line: 88, col: 56, offset: 2406}, + name: "WithKeywordList", + }, + }, + }, + }, + }, + }, + }, + { + name: "LiteralExpr", + pos: position{line: 92, col: 1, offset: 2473}, + expr: &actionExpr{ + pos: position{line: 92, col: 16, offset: 2488}, + run: (*parser).callonLiteralExpr1, + expr: &seqExpr{ + pos: position{line: 92, col: 16, offset: 2488}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 92, col: 16, offset: 2488}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 92, col: 20, offset: 2492}, + name: "ExprTerm", + }, + }, + &labeledExpr{ + pos: position{line: 92, col: 29, offset: 2501}, + label: "rest", + expr: &zeroOrOneExpr{ + pos: position{line: 92, col: 34, offset: 2506}, + expr: &seqExpr{ + pos: position{line: 92, col: 36, offset: 2508}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 92, col: 36, offset: 2508}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 92, col: 38, offset: 2510}, + name: "LiteralExprOperator", + }, + &ruleRefExpr{ + pos: position{line: 92, col: 58, offset: 2530}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 92, col: 60, offset: 2532}, + name: "ExprTerm", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "LiteralExprOperator", + pos: position{line: 96, col: 1, offset: 2606}, + expr: &actionExpr{ + pos: position{line: 96, col: 24, offset: 2629}, + run: (*parser).callonLiteralExprOperator1, + expr: &labeledExpr{ + pos: position{line: 96, col: 24, offset: 2629}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 96, col: 30, offset: 2635}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 96, col: 30, offset: 2635}, + val: ":=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 96, col: 37, offset: 2642}, + val: "=", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "NotKeyword", + pos: position{line: 100, col: 1, offset: 2710}, + expr: &actionExpr{ + pos: position{line: 100, col: 15, offset: 2724}, + run: (*parser).callonNotKeyword1, + expr: &labeledExpr{ + pos: position{line: 100, col: 15, offset: 2724}, + label: "val", + expr: &zeroOrOneExpr{ + pos: position{line: 100, col: 19, offset: 2728}, + expr: &seqExpr{ + pos: position{line: 100, col: 20, offset: 2729}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 100, col: 20, offset: 2729}, + val: "not", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 100, col: 26, offset: 2735}, + name: "ws", + }, + }, + }, + }, + }, + }, + }, + { + name: "WithKeywordList", + pos: position{line: 104, col: 1, offset: 2772}, + expr: &actionExpr{ + pos: position{line: 104, col: 20, offset: 2791}, + run: (*parser).callonWithKeywordList1, + expr: &seqExpr{ + pos: position{line: 104, col: 20, offset: 2791}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 104, col: 20, offset: 2791}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 104, col: 23, offset: 2794}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 104, col: 28, offset: 2799}, + name: "WithKeyword", + }, + }, + &labeledExpr{ + pos: position{line: 104, col: 40, offset: 2811}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 104, col: 45, offset: 2816}, + expr: &seqExpr{ + pos: position{line: 104, col: 47, offset: 2818}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 104, col: 47, offset: 2818}, + name: "ws", + }, + &ruleRefExpr{ + pos: position{line: 104, col: 50, offset: 2821}, + name: "WithKeyword", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "WithKeyword", + pos: position{line: 108, col: 1, offset: 2884}, + expr: &actionExpr{ + pos: position{line: 108, col: 16, offset: 2899}, + run: (*parser).callonWithKeyword1, + expr: &seqExpr{ + pos: position{line: 108, col: 16, offset: 2899}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 108, col: 16, offset: 2899}, + val: "with", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 108, col: 23, offset: 2906}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 108, col: 26, offset: 2909}, + label: "target", + expr: &ruleRefExpr{ + pos: position{line: 108, col: 33, offset: 2916}, + name: "ExprTerm", + }, + }, + &ruleRefExpr{ + pos: position{line: 108, col: 42, offset: 2925}, + name: "ws", + }, + &litMatcher{ + pos: position{line: 108, col: 45, offset: 2928}, + val: "as", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 108, col: 50, offset: 2933}, + name: "ws", + }, + &labeledExpr{ + pos: position{line: 108, col: 53, offset: 2936}, + label: "value", + expr: &ruleRefExpr{ + pos: position{line: 108, col: 59, offset: 2942}, + name: "ExprTerm", + }, + }, + }, + }, + }, + }, + { + name: "ExprTerm", + pos: position{line: 112, col: 1, offset: 3018}, + expr: &actionExpr{ + pos: position{line: 112, col: 13, offset: 3030}, + run: (*parser).callonExprTerm1, + expr: &seqExpr{ + pos: position{line: 112, col: 13, offset: 3030}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 112, col: 13, offset: 3030}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 112, col: 17, offset: 3034}, + name: "RelationExpr", + }, + }, + &labeledExpr{ + pos: position{line: 112, col: 30, offset: 3047}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 112, col: 35, offset: 3052}, + expr: &seqExpr{ + pos: position{line: 112, col: 37, offset: 3054}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 112, col: 37, offset: 3054}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 112, col: 39, offset: 3056}, + name: "RelationOperator", + }, + &ruleRefExpr{ + pos: position{line: 112, col: 56, offset: 3073}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 112, col: 58, offset: 3075}, + name: "RelationExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "ExprTermPairList", + pos: position{line: 116, col: 1, offset: 3151}, + expr: &actionExpr{ + pos: position{line: 116, col: 21, offset: 3171}, + run: (*parser).callonExprTermPairList1, + expr: &seqExpr{ + pos: position{line: 116, col: 21, offset: 3171}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 116, col: 21, offset: 3171}, + label: "head", + expr: &zeroOrOneExpr{ + pos: position{line: 116, col: 26, offset: 3176}, + expr: &ruleRefExpr{ + pos: position{line: 116, col: 26, offset: 3176}, + name: "ExprTermPair", + }, + }, + }, + &labeledExpr{ + pos: position{line: 116, col: 40, offset: 3190}, + label: "tail", + expr: &zeroOrMoreExpr{ + pos: position{line: 116, col: 45, offset: 3195}, + expr: &seqExpr{ + pos: position{line: 116, col: 47, offset: 3197}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 116, col: 47, offset: 3197}, + name: "_", + }, + &litMatcher{ + pos: position{line: 116, col: 49, offset: 3199}, + val: ",", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 116, col: 53, offset: 3203}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 116, col: 55, offset: 3205}, + name: "ExprTermPair", + }, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 116, col: 71, offset: 3221}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 116, col: 73, offset: 3223}, + expr: &litMatcher{ + pos: position{line: 116, col: 73, offset: 3223}, + val: ",", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "ExprTermList", + pos: position{line: 120, col: 1, offset: 3277}, + expr: &actionExpr{ + pos: position{line: 120, col: 17, offset: 3293}, + run: (*parser).callonExprTermList1, + expr: &seqExpr{ + pos: position{line: 120, col: 17, offset: 3293}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 120, col: 17, offset: 3293}, + label: "head", + expr: &zeroOrOneExpr{ + pos: position{line: 120, col: 22, offset: 3298}, + expr: &ruleRefExpr{ + pos: position{line: 120, col: 22, offset: 3298}, + name: "ExprTerm", + }, + }, + }, + &labeledExpr{ + pos: position{line: 120, col: 32, offset: 3308}, + label: "tail", + expr: &zeroOrMoreExpr{ + pos: position{line: 120, col: 37, offset: 3313}, + expr: &seqExpr{ + pos: position{line: 120, col: 39, offset: 3315}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 120, col: 39, offset: 3315}, + name: "_", + }, + &litMatcher{ + pos: position{line: 120, col: 41, offset: 3317}, + val: ",", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 120, col: 45, offset: 3321}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 120, col: 47, offset: 3323}, + name: "ExprTerm", + }, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 120, col: 59, offset: 3335}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 120, col: 61, offset: 3337}, + expr: &litMatcher{ + pos: position{line: 120, col: 61, offset: 3337}, + val: ",", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "ExprTermPair", + pos: position{line: 124, col: 1, offset: 3388}, + expr: &actionExpr{ + pos: position{line: 124, col: 17, offset: 3404}, + run: (*parser).callonExprTermPair1, + expr: &seqExpr{ + pos: position{line: 124, col: 17, offset: 3404}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 124, col: 17, offset: 3404}, + label: "key", + expr: &ruleRefExpr{ + pos: position{line: 124, col: 21, offset: 3408}, + name: "ExprTerm", + }, + }, + &ruleRefExpr{ + pos: position{line: 124, col: 30, offset: 3417}, + name: "_", + }, + &litMatcher{ + pos: position{line: 124, col: 32, offset: 3419}, + val: ":", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 124, col: 36, offset: 3423}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 124, col: 38, offset: 3425}, + label: "value", + expr: &ruleRefExpr{ + pos: position{line: 124, col: 44, offset: 3431}, + name: "ExprTerm", + }, + }, + }, + }, + }, + }, + { + name: "RelationOperator", + pos: position{line: 128, col: 1, offset: 3485}, + expr: &actionExpr{ + pos: position{line: 128, col: 21, offset: 3505}, + run: (*parser).callonRelationOperator1, + expr: &labeledExpr{ + pos: position{line: 128, col: 21, offset: 3505}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 128, col: 26, offset: 3510}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 128, col: 26, offset: 3510}, + val: "==", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 128, col: 33, offset: 3517}, + val: "!=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 128, col: 40, offset: 3524}, + val: "<=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 128, col: 47, offset: 3531}, + val: ">=", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 128, col: 54, offset: 3538}, + val: ">", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 128, col: 60, offset: 3544}, + val: "<", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "RelationExpr", + pos: position{line: 132, col: 1, offset: 3611}, + expr: &actionExpr{ + pos: position{line: 132, col: 17, offset: 3627}, + run: (*parser).callonRelationExpr1, + expr: &seqExpr{ + pos: position{line: 132, col: 17, offset: 3627}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 132, col: 17, offset: 3627}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 132, col: 21, offset: 3631}, + name: "BitwiseOrExpr", + }, + }, + &labeledExpr{ + pos: position{line: 132, col: 35, offset: 3645}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 132, col: 40, offset: 3650}, + expr: &seqExpr{ + pos: position{line: 132, col: 42, offset: 3652}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 132, col: 42, offset: 3652}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 132, col: 44, offset: 3654}, + name: "BitwiseOrOperator", + }, + &ruleRefExpr{ + pos: position{line: 132, col: 62, offset: 3672}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 132, col: 64, offset: 3674}, + name: "BitwiseOrExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "BitwiseOrOperator", + pos: position{line: 136, col: 1, offset: 3750}, + expr: &actionExpr{ + pos: position{line: 136, col: 22, offset: 3771}, + run: (*parser).callonBitwiseOrOperator1, + expr: &labeledExpr{ + pos: position{line: 136, col: 22, offset: 3771}, + label: "val", + expr: &litMatcher{ + pos: position{line: 136, col: 26, offset: 3775}, + val: "|", + ignoreCase: false, + }, + }, + }, + }, + { + name: "BitwiseOrExpr", + pos: position{line: 140, col: 1, offset: 3841}, + expr: &actionExpr{ + pos: position{line: 140, col: 18, offset: 3858}, + run: (*parser).callonBitwiseOrExpr1, + expr: &seqExpr{ + pos: position{line: 140, col: 18, offset: 3858}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 140, col: 18, offset: 3858}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 140, col: 22, offset: 3862}, + name: "BitwiseAndExpr", + }, + }, + &labeledExpr{ + pos: position{line: 140, col: 37, offset: 3877}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 140, col: 42, offset: 3882}, + expr: &seqExpr{ + pos: position{line: 140, col: 44, offset: 3884}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 140, col: 44, offset: 3884}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 140, col: 46, offset: 3886}, + name: "BitwiseAndOperator", + }, + &ruleRefExpr{ + pos: position{line: 140, col: 65, offset: 3905}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 140, col: 67, offset: 3907}, + name: "BitwiseAndExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "BitwiseAndOperator", + pos: position{line: 144, col: 1, offset: 3984}, + expr: &actionExpr{ + pos: position{line: 144, col: 23, offset: 4006}, + run: (*parser).callonBitwiseAndOperator1, + expr: &labeledExpr{ + pos: position{line: 144, col: 23, offset: 4006}, + label: "val", + expr: &litMatcher{ + pos: position{line: 144, col: 27, offset: 4010}, + val: "&", + ignoreCase: false, + }, + }, + }, + }, + { + name: "BitwiseAndExpr", + pos: position{line: 148, col: 1, offset: 4076}, + expr: &actionExpr{ + pos: position{line: 148, col: 19, offset: 4094}, + run: (*parser).callonBitwiseAndExpr1, + expr: &seqExpr{ + pos: position{line: 148, col: 19, offset: 4094}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 148, col: 19, offset: 4094}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 148, col: 23, offset: 4098}, + name: "ArithExpr", + }, + }, + &labeledExpr{ + pos: position{line: 148, col: 33, offset: 4108}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 148, col: 38, offset: 4113}, + expr: &seqExpr{ + pos: position{line: 148, col: 40, offset: 4115}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 148, col: 40, offset: 4115}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 148, col: 42, offset: 4117}, + name: "ArithOperator", + }, + &ruleRefExpr{ + pos: position{line: 148, col: 56, offset: 4131}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 148, col: 58, offset: 4133}, + name: "ArithExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "ArithOperator", + pos: position{line: 152, col: 1, offset: 4205}, + expr: &actionExpr{ + pos: position{line: 152, col: 18, offset: 4222}, + run: (*parser).callonArithOperator1, + expr: &labeledExpr{ + pos: position{line: 152, col: 18, offset: 4222}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 152, col: 23, offset: 4227}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 152, col: 23, offset: 4227}, + val: "+", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 152, col: 29, offset: 4233}, + val: "-", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "ArithExpr", + pos: position{line: 156, col: 1, offset: 4300}, + expr: &actionExpr{ + pos: position{line: 156, col: 14, offset: 4313}, + run: (*parser).callonArithExpr1, + expr: &seqExpr{ + pos: position{line: 156, col: 14, offset: 4313}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 156, col: 14, offset: 4313}, + label: "lhs", + expr: &ruleRefExpr{ + pos: position{line: 156, col: 18, offset: 4317}, + name: "FactorExpr", + }, + }, + &labeledExpr{ + pos: position{line: 156, col: 29, offset: 4328}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 156, col: 34, offset: 4333}, + expr: &seqExpr{ + pos: position{line: 156, col: 36, offset: 4335}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 156, col: 36, offset: 4335}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 156, col: 38, offset: 4337}, + name: "FactorOperator", + }, + &ruleRefExpr{ + pos: position{line: 156, col: 53, offset: 4352}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 156, col: 55, offset: 4354}, + name: "FactorExpr", + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "FactorOperator", + pos: position{line: 160, col: 1, offset: 4428}, + expr: &actionExpr{ + pos: position{line: 160, col: 19, offset: 4446}, + run: (*parser).callonFactorOperator1, + expr: &labeledExpr{ + pos: position{line: 160, col: 19, offset: 4446}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 160, col: 24, offset: 4451}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 160, col: 24, offset: 4451}, + val: "*", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 160, col: 30, offset: 4457}, + val: "/", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 160, col: 36, offset: 4463}, + val: "%", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + { + name: "FactorExpr", + pos: position{line: 164, col: 1, offset: 4529}, + expr: &choiceExpr{ + pos: position{line: 164, col: 15, offset: 4543}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 164, col: 15, offset: 4543}, + run: (*parser).callonFactorExpr2, + expr: &seqExpr{ + pos: position{line: 164, col: 17, offset: 4545}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 164, col: 17, offset: 4545}, + val: "(", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 164, col: 21, offset: 4549}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 164, col: 23, offset: 4551}, + label: "expr", + expr: &ruleRefExpr{ + pos: position{line: 164, col: 28, offset: 4556}, + name: "ExprTerm", + }, + }, + &ruleRefExpr{ + pos: position{line: 164, col: 37, offset: 4565}, + name: "_", + }, + &litMatcher{ + pos: position{line: 164, col: 39, offset: 4567}, + val: ")", + ignoreCase: false, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 166, col: 5, offset: 4600}, + run: (*parser).callonFactorExpr10, + expr: &labeledExpr{ + pos: position{line: 166, col: 5, offset: 4600}, + label: "term", + expr: &ruleRefExpr{ + pos: position{line: 166, col: 10, offset: 4605}, + name: "Term", + }, + }, + }, + }, + }, + }, + { + name: "Call", + pos: position{line: 170, col: 1, offset: 4636}, + expr: &actionExpr{ + pos: position{line: 170, col: 9, offset: 4644}, + run: (*parser).callonCall1, + expr: &seqExpr{ + pos: position{line: 170, col: 9, offset: 4644}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 170, col: 9, offset: 4644}, + label: "operator", + expr: &choiceExpr{ + pos: position{line: 170, col: 19, offset: 4654}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 170, col: 19, offset: 4654}, + name: "Ref", + }, + &ruleRefExpr{ + pos: position{line: 170, col: 25, offset: 4660}, + name: "Var", + }, + }, + }, + }, + &litMatcher{ + pos: position{line: 170, col: 30, offset: 4665}, + val: "(", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 170, col: 34, offset: 4669}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 170, col: 36, offset: 4671}, + label: "args", + expr: &ruleRefExpr{ + pos: position{line: 170, col: 41, offset: 4676}, + name: "ExprTermList", + }, + }, + &ruleRefExpr{ + pos: position{line: 170, col: 54, offset: 4689}, + name: "_", + }, + &litMatcher{ + pos: position{line: 170, col: 56, offset: 4691}, + val: ")", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Term", + pos: position{line: 174, col: 1, offset: 4756}, + expr: &actionExpr{ + pos: position{line: 174, col: 9, offset: 4764}, + run: (*parser).callonTerm1, + expr: &seqExpr{ + pos: position{line: 174, col: 9, offset: 4764}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 174, col: 9, offset: 4764}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 174, col: 15, offset: 4770}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 174, col: 15, offset: 4770}, + name: "Comprehension", + }, + &ruleRefExpr{ + pos: position{line: 174, col: 31, offset: 4786}, + name: "Composite", + }, + &ruleRefExpr{ + pos: position{line: 174, col: 43, offset: 4798}, + name: "Scalar", + }, + &ruleRefExpr{ + pos: position{line: 174, col: 52, offset: 4807}, + name: "Call", + }, + &ruleRefExpr{ + pos: position{line: 174, col: 59, offset: 4814}, + name: "Var", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 174, col: 65, offset: 4820}, + label: "refs", + expr: &zeroOrMoreExpr{ + pos: position{line: 174, col: 70, offset: 4825}, + expr: &ruleRefExpr{ + pos: position{line: 174, col: 70, offset: 4825}, + name: "RefOperand", + }, + }, + }, + }, + }, + }, + }, + { + name: "TermPair", + pos: position{line: 178, col: 1, offset: 4892}, + expr: &actionExpr{ + pos: position{line: 178, col: 13, offset: 4904}, + run: (*parser).callonTermPair1, + expr: &seqExpr{ + pos: position{line: 178, col: 13, offset: 4904}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 178, col: 13, offset: 4904}, + label: "key", + expr: &ruleRefExpr{ + pos: position{line: 178, col: 17, offset: 4908}, + name: "Term", + }, + }, + &ruleRefExpr{ + pos: position{line: 178, col: 22, offset: 4913}, + name: "_", + }, + &litMatcher{ + pos: position{line: 178, col: 24, offset: 4915}, + val: ":", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 178, col: 28, offset: 4919}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 178, col: 30, offset: 4921}, + label: "value", + expr: &ruleRefExpr{ + pos: position{line: 178, col: 36, offset: 4927}, + name: "Term", + }, + }, + }, + }, + }, + }, + { + name: "Comprehension", + pos: position{line: 182, col: 1, offset: 4977}, + expr: &choiceExpr{ + pos: position{line: 182, col: 18, offset: 4994}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 182, col: 18, offset: 4994}, + name: "ArrayComprehension", + }, + &ruleRefExpr{ + pos: position{line: 182, col: 39, offset: 5015}, + name: "ObjectComprehension", + }, + &ruleRefExpr{ + pos: position{line: 182, col: 61, offset: 5037}, + name: "SetComprehension", + }, + }, + }, + }, + { + name: "ArrayComprehension", + pos: position{line: 184, col: 1, offset: 5055}, + expr: &actionExpr{ + pos: position{line: 184, col: 23, offset: 5077}, + run: (*parser).callonArrayComprehension1, + expr: &seqExpr{ + pos: position{line: 184, col: 23, offset: 5077}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 184, col: 23, offset: 5077}, + val: "[", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 184, col: 27, offset: 5081}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 184, col: 29, offset: 5083}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 184, col: 34, offset: 5088}, + name: "Term", + }, + }, + &ruleRefExpr{ + pos: position{line: 184, col: 39, offset: 5093}, + name: "_", + }, + &litMatcher{ + pos: position{line: 184, col: 41, offset: 5095}, + val: "|", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 184, col: 45, offset: 5099}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 184, col: 47, offset: 5101}, + label: "body", + expr: &ruleRefExpr{ + pos: position{line: 184, col: 52, offset: 5106}, + name: "WhitespaceBody", + }, + }, + &ruleRefExpr{ + pos: position{line: 184, col: 67, offset: 5121}, + name: "_", + }, + &litMatcher{ + pos: position{line: 184, col: 69, offset: 5123}, + val: "]", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "ObjectComprehension", + pos: position{line: 188, col: 1, offset: 5198}, + expr: &actionExpr{ + pos: position{line: 188, col: 24, offset: 5221}, + run: (*parser).callonObjectComprehension1, + expr: &seqExpr{ + pos: position{line: 188, col: 24, offset: 5221}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 188, col: 24, offset: 5221}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 188, col: 28, offset: 5225}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 188, col: 30, offset: 5227}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 188, col: 35, offset: 5232}, + name: "TermPair", + }, + }, + &ruleRefExpr{ + pos: position{line: 188, col: 45, offset: 5242}, + name: "_", + }, + &litMatcher{ + pos: position{line: 188, col: 47, offset: 5244}, + val: "|", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 188, col: 51, offset: 5248}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 188, col: 53, offset: 5250}, + label: "body", + expr: &ruleRefExpr{ + pos: position{line: 188, col: 58, offset: 5255}, + name: "WhitespaceBody", + }, + }, + &ruleRefExpr{ + pos: position{line: 188, col: 73, offset: 5270}, + name: "_", + }, + &litMatcher{ + pos: position{line: 188, col: 75, offset: 5272}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "SetComprehension", + pos: position{line: 192, col: 1, offset: 5348}, + expr: &actionExpr{ + pos: position{line: 192, col: 21, offset: 5368}, + run: (*parser).callonSetComprehension1, + expr: &seqExpr{ + pos: position{line: 192, col: 21, offset: 5368}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 192, col: 21, offset: 5368}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 192, col: 25, offset: 5372}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 192, col: 27, offset: 5374}, + label: "head", + expr: &ruleRefExpr{ + pos: position{line: 192, col: 32, offset: 5379}, + name: "Term", + }, + }, + &ruleRefExpr{ + pos: position{line: 192, col: 37, offset: 5384}, + name: "_", + }, + &litMatcher{ + pos: position{line: 192, col: 39, offset: 5386}, + val: "|", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 192, col: 43, offset: 5390}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 192, col: 45, offset: 5392}, + label: "body", + expr: &ruleRefExpr{ + pos: position{line: 192, col: 50, offset: 5397}, + name: "WhitespaceBody", + }, + }, + &ruleRefExpr{ + pos: position{line: 192, col: 65, offset: 5412}, + name: "_", + }, + &litMatcher{ + pos: position{line: 192, col: 67, offset: 5414}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Composite", + pos: position{line: 196, col: 1, offset: 5487}, + expr: &choiceExpr{ + pos: position{line: 196, col: 14, offset: 5500}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 196, col: 14, offset: 5500}, + name: "Object", + }, + &ruleRefExpr{ + pos: position{line: 196, col: 23, offset: 5509}, + name: "Array", + }, + &ruleRefExpr{ + pos: position{line: 196, col: 31, offset: 5517}, + name: "Set", + }, + }, + }, + }, + { + name: "Scalar", + pos: position{line: 198, col: 1, offset: 5522}, + expr: &choiceExpr{ + pos: position{line: 198, col: 11, offset: 5532}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 198, col: 11, offset: 5532}, + name: "Number", + }, + &ruleRefExpr{ + pos: position{line: 198, col: 20, offset: 5541}, + name: "String", + }, + &ruleRefExpr{ + pos: position{line: 198, col: 29, offset: 5550}, + name: "Bool", + }, + &ruleRefExpr{ + pos: position{line: 198, col: 36, offset: 5557}, + name: "Null", + }, + }, + }, + }, + { + name: "Object", + pos: position{line: 200, col: 1, offset: 5563}, + expr: &actionExpr{ + pos: position{line: 200, col: 11, offset: 5573}, + run: (*parser).callonObject1, + expr: &seqExpr{ + pos: position{line: 200, col: 11, offset: 5573}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 200, col: 11, offset: 5573}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 200, col: 15, offset: 5577}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 200, col: 17, offset: 5579}, + label: "list", + expr: &ruleRefExpr{ + pos: position{line: 200, col: 22, offset: 5584}, + name: "ExprTermPairList", + }, + }, + &ruleRefExpr{ + pos: position{line: 200, col: 39, offset: 5601}, + name: "_", + }, + &litMatcher{ + pos: position{line: 200, col: 41, offset: 5603}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Array", + pos: position{line: 204, col: 1, offset: 5660}, + expr: &actionExpr{ + pos: position{line: 204, col: 10, offset: 5669}, + run: (*parser).callonArray1, + expr: &seqExpr{ + pos: position{line: 204, col: 10, offset: 5669}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 204, col: 10, offset: 5669}, + val: "[", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 204, col: 14, offset: 5673}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 204, col: 16, offset: 5675}, + label: "list", + expr: &ruleRefExpr{ + pos: position{line: 204, col: 21, offset: 5680}, + name: "ExprTermList", + }, + }, + &ruleRefExpr{ + pos: position{line: 204, col: 34, offset: 5693}, + name: "_", + }, + &litMatcher{ + pos: position{line: 204, col: 36, offset: 5695}, + val: "]", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Set", + pos: position{line: 208, col: 1, offset: 5751}, + expr: &choiceExpr{ + pos: position{line: 208, col: 8, offset: 5758}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 208, col: 8, offset: 5758}, + name: "SetEmpty", + }, + &ruleRefExpr{ + pos: position{line: 208, col: 19, offset: 5769}, + name: "SetNonEmpty", + }, + }, + }, + }, + { + name: "SetEmpty", + pos: position{line: 210, col: 1, offset: 5782}, + expr: &actionExpr{ + pos: position{line: 210, col: 13, offset: 5794}, + run: (*parser).callonSetEmpty1, + expr: &seqExpr{ + pos: position{line: 210, col: 13, offset: 5794}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 210, col: 13, offset: 5794}, + val: "set(", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 210, col: 20, offset: 5801}, + name: "_", + }, + &litMatcher{ + pos: position{line: 210, col: 22, offset: 5803}, + val: ")", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "SetNonEmpty", + pos: position{line: 215, col: 1, offset: 5880}, + expr: &actionExpr{ + pos: position{line: 215, col: 16, offset: 5895}, + run: (*parser).callonSetNonEmpty1, + expr: &seqExpr{ + pos: position{line: 215, col: 16, offset: 5895}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 215, col: 16, offset: 5895}, + val: "{", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 215, col: 20, offset: 5899}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 215, col: 22, offset: 5901}, + label: "list", + expr: &ruleRefExpr{ + pos: position{line: 215, col: 27, offset: 5906}, + name: "ExprTermList", + }, + }, + &ruleRefExpr{ + pos: position{line: 215, col: 40, offset: 5919}, + name: "_", + }, + &litMatcher{ + pos: position{line: 215, col: 42, offset: 5921}, + val: "}", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Ref", + pos: position{line: 219, col: 1, offset: 5975}, + expr: &actionExpr{ + pos: position{line: 219, col: 8, offset: 5982}, + run: (*parser).callonRef1, + expr: &seqExpr{ + pos: position{line: 219, col: 8, offset: 5982}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 219, col: 8, offset: 5982}, + label: "head", + expr: &choiceExpr{ + pos: position{line: 219, col: 14, offset: 5988}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 219, col: 14, offset: 5988}, + name: "Composite", + }, + &ruleRefExpr{ + pos: position{line: 219, col: 26, offset: 6000}, + name: "Var", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 219, col: 31, offset: 6005}, + label: "rest", + expr: &oneOrMoreExpr{ + pos: position{line: 219, col: 36, offset: 6010}, + expr: &ruleRefExpr{ + pos: position{line: 219, col: 36, offset: 6010}, + name: "RefOperand", + }, + }, + }, + }, + }, + }, + }, + { + name: "RefOperand", + pos: position{line: 223, col: 1, offset: 6078}, + expr: &choiceExpr{ + pos: position{line: 223, col: 15, offset: 6092}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 223, col: 15, offset: 6092}, + name: "RefOperandDot", + }, + &ruleRefExpr{ + pos: position{line: 223, col: 31, offset: 6108}, + name: "RefOperandCanonical", + }, + }, + }, + }, + { + name: "RefOperandDot", + pos: position{line: 225, col: 1, offset: 6129}, + expr: &actionExpr{ + pos: position{line: 225, col: 18, offset: 6146}, + run: (*parser).callonRefOperandDot1, + expr: &seqExpr{ + pos: position{line: 225, col: 18, offset: 6146}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 225, col: 18, offset: 6146}, + val: ".", + ignoreCase: false, + }, + &labeledExpr{ + pos: position{line: 225, col: 22, offset: 6150}, + label: "val", + expr: &ruleRefExpr{ + pos: position{line: 225, col: 26, offset: 6154}, + name: "Var", + }, + }, + }, + }, + }, + }, + { + name: "RefOperandCanonical", + pos: position{line: 229, col: 1, offset: 6217}, + expr: &actionExpr{ + pos: position{line: 229, col: 24, offset: 6240}, + run: (*parser).callonRefOperandCanonical1, + expr: &seqExpr{ + pos: position{line: 229, col: 24, offset: 6240}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 229, col: 24, offset: 6240}, + val: "[", + ignoreCase: false, + }, + &labeledExpr{ + pos: position{line: 229, col: 28, offset: 6244}, + label: "val", + expr: &ruleRefExpr{ + pos: position{line: 229, col: 32, offset: 6248}, + name: "ExprTerm", + }, + }, + &litMatcher{ + pos: position{line: 229, col: 41, offset: 6257}, + val: "]", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Var", + pos: position{line: 233, col: 1, offset: 6286}, + expr: &actionExpr{ + pos: position{line: 233, col: 8, offset: 6293}, + run: (*parser).callonVar1, + expr: &labeledExpr{ + pos: position{line: 233, col: 8, offset: 6293}, + label: "val", + expr: &ruleRefExpr{ + pos: position{line: 233, col: 12, offset: 6297}, + name: "VarChecked", + }, + }, + }, + }, + { + name: "VarChecked", + pos: position{line: 237, col: 1, offset: 6352}, + expr: &seqExpr{ + pos: position{line: 237, col: 15, offset: 6366}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 237, col: 15, offset: 6366}, + label: "val", + expr: &ruleRefExpr{ + pos: position{line: 237, col: 19, offset: 6370}, + name: "VarUnchecked", + }, + }, + ¬CodeExpr{ + pos: position{line: 237, col: 32, offset: 6383}, + run: (*parser).callonVarChecked4, + }, + }, + }, + }, + { + name: "VarUnchecked", + pos: position{line: 241, col: 1, offset: 6448}, + expr: &actionExpr{ + pos: position{line: 241, col: 17, offset: 6464}, + run: (*parser).callonVarUnchecked1, + expr: &seqExpr{ + pos: position{line: 241, col: 17, offset: 6464}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 241, col: 17, offset: 6464}, + name: "VarStart", + }, + &zeroOrMoreExpr{ + pos: position{line: 241, col: 26, offset: 6473}, + expr: &ruleRefExpr{ + pos: position{line: 241, col: 26, offset: 6473}, + name: "VarChar", + }, + }, + }, + }, + }, + }, + { + name: "Number", + pos: position{line: 245, col: 1, offset: 6534}, + expr: &actionExpr{ + pos: position{line: 245, col: 11, offset: 6544}, + run: (*parser).callonNumber1, + expr: &seqExpr{ + pos: position{line: 245, col: 11, offset: 6544}, + exprs: []interface{}{ + &zeroOrOneExpr{ + pos: position{line: 245, col: 11, offset: 6544}, + expr: &litMatcher{ + pos: position{line: 245, col: 11, offset: 6544}, + val: "-", + ignoreCase: false, + }, + }, + &choiceExpr{ + pos: position{line: 245, col: 18, offset: 6551}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 245, col: 18, offset: 6551}, + name: "Float", + }, + &ruleRefExpr{ + pos: position{line: 245, col: 26, offset: 6559}, + name: "Integer", + }, + }, + }, + }, + }, + }, + }, + { + name: "Float", + pos: position{line: 249, col: 1, offset: 6624}, + expr: &choiceExpr{ + pos: position{line: 249, col: 10, offset: 6633}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 249, col: 10, offset: 6633}, + name: "ExponentFloat", + }, + &ruleRefExpr{ + pos: position{line: 249, col: 26, offset: 6649}, + name: "PointFloat", + }, + }, + }, + }, + { + name: "ExponentFloat", + pos: position{line: 251, col: 1, offset: 6661}, + expr: &seqExpr{ + pos: position{line: 251, col: 18, offset: 6678}, + exprs: []interface{}{ + &choiceExpr{ + pos: position{line: 251, col: 20, offset: 6680}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 251, col: 20, offset: 6680}, + name: "PointFloat", + }, + &ruleRefExpr{ + pos: position{line: 251, col: 33, offset: 6693}, + name: "Integer", + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 251, col: 43, offset: 6703}, + name: "Exponent", + }, + }, + }, + }, + { + name: "PointFloat", + pos: position{line: 253, col: 1, offset: 6713}, + expr: &seqExpr{ + pos: position{line: 253, col: 15, offset: 6727}, + exprs: []interface{}{ + &zeroOrOneExpr{ + pos: position{line: 253, col: 15, offset: 6727}, + expr: &ruleRefExpr{ + pos: position{line: 253, col: 15, offset: 6727}, + name: "Integer", + }, + }, + &ruleRefExpr{ + pos: position{line: 253, col: 24, offset: 6736}, + name: "Fraction", + }, + }, + }, + }, + { + name: "Fraction", + pos: position{line: 255, col: 1, offset: 6746}, + expr: &seqExpr{ + pos: position{line: 255, col: 13, offset: 6758}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 255, col: 13, offset: 6758}, + val: ".", + ignoreCase: false, + }, + &oneOrMoreExpr{ + pos: position{line: 255, col: 17, offset: 6762}, + expr: &ruleRefExpr{ + pos: position{line: 255, col: 17, offset: 6762}, + name: "DecimalDigit", + }, + }, + }, + }, + }, + { + name: "Exponent", + pos: position{line: 257, col: 1, offset: 6777}, + expr: &seqExpr{ + pos: position{line: 257, col: 13, offset: 6789}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 257, col: 13, offset: 6789}, + val: "e", + ignoreCase: true, + }, + &zeroOrOneExpr{ + pos: position{line: 257, col: 18, offset: 6794}, + expr: &charClassMatcher{ + pos: position{line: 257, col: 18, offset: 6794}, + val: "[+-]", + chars: []rune{'+', '-'}, + ignoreCase: false, + inverted: false, + }, + }, + &oneOrMoreExpr{ + pos: position{line: 257, col: 24, offset: 6800}, + expr: &ruleRefExpr{ + pos: position{line: 257, col: 24, offset: 6800}, + name: "DecimalDigit", + }, + }, + }, + }, + }, + { + name: "Integer", + pos: position{line: 259, col: 1, offset: 6815}, + expr: &choiceExpr{ + pos: position{line: 259, col: 12, offset: 6826}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 259, col: 12, offset: 6826}, + val: "0", + ignoreCase: false, + }, + &seqExpr{ + pos: position{line: 259, col: 20, offset: 6834}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 259, col: 20, offset: 6834}, + name: "NonZeroDecimalDigit", + }, + &zeroOrMoreExpr{ + pos: position{line: 259, col: 40, offset: 6854}, + expr: &ruleRefExpr{ + pos: position{line: 259, col: 40, offset: 6854}, + name: "DecimalDigit", + }, + }, + }, + }, + }, + }, + }, + { + name: "String", + pos: position{line: 261, col: 1, offset: 6871}, + expr: &choiceExpr{ + pos: position{line: 261, col: 11, offset: 6881}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 261, col: 11, offset: 6881}, + name: "QuotedString", + }, + &ruleRefExpr{ + pos: position{line: 261, col: 26, offset: 6896}, + name: "RawString", + }, + }, + }, + }, + { + name: "QuotedString", + pos: position{line: 263, col: 1, offset: 6907}, + expr: &choiceExpr{ + pos: position{line: 263, col: 17, offset: 6923}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 263, col: 17, offset: 6923}, + run: (*parser).callonQuotedString2, + expr: &seqExpr{ + pos: position{line: 263, col: 17, offset: 6923}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 263, col: 17, offset: 6923}, + val: "\"", + ignoreCase: false, + }, + &zeroOrMoreExpr{ + pos: position{line: 263, col: 21, offset: 6927}, + expr: &ruleRefExpr{ + pos: position{line: 263, col: 21, offset: 6927}, + name: "Char", + }, + }, + &litMatcher{ + pos: position{line: 263, col: 27, offset: 6933}, + val: "\"", + ignoreCase: false, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 265, col: 5, offset: 6993}, + run: (*parser).callonQuotedString8, + expr: &seqExpr{ + pos: position{line: 265, col: 5, offset: 6993}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 265, col: 5, offset: 6993}, + val: "\"", + ignoreCase: false, + }, + &zeroOrMoreExpr{ + pos: position{line: 265, col: 9, offset: 6997}, + expr: &ruleRefExpr{ + pos: position{line: 265, col: 9, offset: 6997}, + name: "Char", + }, + }, + ¬Expr{ + pos: position{line: 265, col: 15, offset: 7003}, + expr: &litMatcher{ + pos: position{line: 265, col: 16, offset: 7004}, + val: "\"", + ignoreCase: false, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "RawString", + pos: position{line: 269, col: 1, offset: 7084}, + expr: &actionExpr{ + pos: position{line: 269, col: 14, offset: 7097}, + run: (*parser).callonRawString1, + expr: &seqExpr{ + pos: position{line: 269, col: 14, offset: 7097}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 269, col: 14, offset: 7097}, + val: "`", + ignoreCase: false, + }, + &zeroOrMoreExpr{ + pos: position{line: 269, col: 18, offset: 7101}, + expr: &charClassMatcher{ + pos: position{line: 269, col: 18, offset: 7101}, + val: "[^`]", + chars: []rune{'`'}, + ignoreCase: false, + inverted: true, + }, + }, + &litMatcher{ + pos: position{line: 269, col: 24, offset: 7107}, + val: "`", + ignoreCase: false, + }, + }, + }, + }, + }, + { + name: "Bool", + pos: position{line: 273, col: 1, offset: 7169}, + expr: &actionExpr{ + pos: position{line: 273, col: 9, offset: 7177}, + run: (*parser).callonBool1, + expr: &seqExpr{ + pos: position{line: 273, col: 9, offset: 7177}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 273, col: 9, offset: 7177}, + label: "val", + expr: &choiceExpr{ + pos: position{line: 273, col: 14, offset: 7182}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 273, col: 14, offset: 7182}, + val: "true", + ignoreCase: false, + }, + &litMatcher{ + pos: position{line: 273, col: 23, offset: 7191}, + val: "false", + ignoreCase: false, + }, + }, + }, + }, + ¬Expr{ + pos: position{line: 273, col: 32, offset: 7200}, + expr: &ruleRefExpr{ + pos: position{line: 273, col: 33, offset: 7201}, + name: "VarChar", + }, + }, + }, + }, + }, + }, + { + name: "Null", + pos: position{line: 277, col: 1, offset: 7262}, + expr: &actionExpr{ + pos: position{line: 277, col: 9, offset: 7270}, + run: (*parser).callonNull1, + expr: &seqExpr{ + pos: position{line: 277, col: 9, offset: 7270}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 277, col: 9, offset: 7270}, + val: "null", + ignoreCase: false, + }, + ¬Expr{ + pos: position{line: 277, col: 16, offset: 7277}, + expr: &ruleRefExpr{ + pos: position{line: 277, col: 17, offset: 7278}, + name: "VarChar", + }, + }, + }, + }, + }, + }, + { + name: "VarStart", + pos: position{line: 281, col: 1, offset: 7331}, + expr: &ruleRefExpr{ + pos: position{line: 281, col: 13, offset: 7343}, + name: "AsciiLetter", + }, + }, + { + name: "VarChar", + pos: position{line: 283, col: 1, offset: 7356}, + expr: &choiceExpr{ + pos: position{line: 283, col: 12, offset: 7367}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 283, col: 12, offset: 7367}, + name: "AsciiLetter", + }, + &ruleRefExpr{ + pos: position{line: 283, col: 26, offset: 7381}, + name: "DecimalDigit", + }, + }, + }, + }, + { + name: "AsciiLetter", + pos: position{line: 285, col: 1, offset: 7395}, + expr: &charClassMatcher{ + pos: position{line: 285, col: 16, offset: 7410}, + val: "[A-Za-z_]", + chars: []rune{'_'}, + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "Char", + pos: position{line: 287, col: 1, offset: 7421}, + expr: &choiceExpr{ + pos: position{line: 287, col: 9, offset: 7429}, + alternatives: []interface{}{ + &seqExpr{ + pos: position{line: 287, col: 11, offset: 7431}, + exprs: []interface{}{ + ¬Expr{ + pos: position{line: 287, col: 11, offset: 7431}, + expr: &ruleRefExpr{ + pos: position{line: 287, col: 12, offset: 7432}, + name: "EscapedChar", + }, + }, + &anyMatcher{ + line: 287, col: 24, offset: 7444, + }, + }, + }, + &seqExpr{ + pos: position{line: 287, col: 32, offset: 7452}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 287, col: 32, offset: 7452}, + val: "\\", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 287, col: 37, offset: 7457}, + name: "EscapeSequence", + }, + }, + }, + }, + }, + }, + { + name: "EscapedChar", + pos: position{line: 289, col: 1, offset: 7475}, + expr: &charClassMatcher{ + pos: position{line: 289, col: 16, offset: 7490}, + val: "[\\x00-\\x1f\"\\\\]", + chars: []rune{'"', '\\'}, + ranges: []rune{'\x00', '\x1f'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "EscapeSequence", + pos: position{line: 291, col: 1, offset: 7506}, + expr: &choiceExpr{ + pos: position{line: 291, col: 19, offset: 7524}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 291, col: 19, offset: 7524}, + name: "SingleCharEscape", + }, + &ruleRefExpr{ + pos: position{line: 291, col: 38, offset: 7543}, + name: "UnicodeEscape", + }, + }, + }, + }, + { + name: "SingleCharEscape", + pos: position{line: 293, col: 1, offset: 7558}, + expr: &charClassMatcher{ + pos: position{line: 293, col: 21, offset: 7578}, + val: "[ \" \\\\ / b f n r t ]", + chars: []rune{' ', '"', ' ', '\\', ' ', '/', ' ', 'b', ' ', 'f', ' ', 'n', ' ', 'r', ' ', 't', ' '}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "UnicodeEscape", + pos: position{line: 295, col: 1, offset: 7600}, + expr: &seqExpr{ + pos: position{line: 295, col: 18, offset: 7617}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 295, col: 18, offset: 7617}, + val: "u", + ignoreCase: false, + }, + &ruleRefExpr{ + pos: position{line: 295, col: 22, offset: 7621}, + name: "HexDigit", + }, + &ruleRefExpr{ + pos: position{line: 295, col: 31, offset: 7630}, + name: "HexDigit", + }, + &ruleRefExpr{ + pos: position{line: 295, col: 40, offset: 7639}, + name: "HexDigit", + }, + &ruleRefExpr{ + pos: position{line: 295, col: 49, offset: 7648}, + name: "HexDigit", + }, + }, + }, + }, + { + name: "DecimalDigit", + pos: position{line: 297, col: 1, offset: 7658}, + expr: &charClassMatcher{ + pos: position{line: 297, col: 17, offset: 7674}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "NonZeroDecimalDigit", + pos: position{line: 299, col: 1, offset: 7681}, + expr: &charClassMatcher{ + pos: position{line: 299, col: 24, offset: 7704}, + val: "[1-9]", + ranges: []rune{'1', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "HexDigit", + pos: position{line: 301, col: 1, offset: 7711}, + expr: &charClassMatcher{ + pos: position{line: 301, col: 13, offset: 7723}, + val: "[0-9a-fA-F]", + ranges: []rune{'0', '9', 'a', 'f', 'A', 'F'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "ws", + displayName: "\"whitespace\"", + pos: position{line: 303, col: 1, offset: 7736}, + expr: &oneOrMoreExpr{ + pos: position{line: 303, col: 20, offset: 7755}, + expr: &charClassMatcher{ + pos: position{line: 303, col: 20, offset: 7755}, + val: "[ \\t\\r\\n]", + chars: []rune{' ', '\t', '\r', '\n'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + { + name: "_", + displayName: "\"whitespace\"", + pos: position{line: 305, col: 1, offset: 7767}, + expr: &zeroOrMoreExpr{ + pos: position{line: 305, col: 19, offset: 7785}, + expr: &choiceExpr{ + pos: position{line: 305, col: 21, offset: 7787}, + alternatives: []interface{}{ + &charClassMatcher{ + pos: position{line: 305, col: 21, offset: 7787}, + val: "[ \\t\\r\\n]", + chars: []rune{' ', '\t', '\r', '\n'}, + ignoreCase: false, + inverted: false, + }, + &ruleRefExpr{ + pos: position{line: 305, col: 33, offset: 7799}, + name: "Comment", + }, + }, + }, + }, + }, + { + name: "Comment", + pos: position{line: 307, col: 1, offset: 7811}, + expr: &actionExpr{ + pos: position{line: 307, col: 12, offset: 7822}, + run: (*parser).callonComment1, + expr: &seqExpr{ + pos: position{line: 307, col: 12, offset: 7822}, + exprs: []interface{}{ + &zeroOrMoreExpr{ + pos: position{line: 307, col: 12, offset: 7822}, + expr: &charClassMatcher{ + pos: position{line: 307, col: 12, offset: 7822}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + &litMatcher{ + pos: position{line: 307, col: 19, offset: 7829}, + val: "#", + ignoreCase: false, + }, + &labeledExpr{ + pos: position{line: 307, col: 23, offset: 7833}, + label: "text", + expr: &zeroOrMoreExpr{ + pos: position{line: 307, col: 28, offset: 7838}, + expr: &charClassMatcher{ + pos: position{line: 307, col: 28, offset: 7838}, + val: "[^\\r\\n]", + chars: []rune{'\r', '\n'}, + ignoreCase: false, + inverted: true, + }, + }, + }, + }, + }, + }, + }, + { + name: "EOF", + pos: position{line: 311, col: 1, offset: 7885}, + expr: ¬Expr{ + pos: position{line: 311, col: 8, offset: 7892}, + expr: &anyMatcher{ + line: 311, col: 9, offset: 7893, + }, + }, + }, + }, +} + +func (c *current) onProgram1(vals interface{}) (interface{}, error) { + return makeProgram(c, vals) +} + +func (p *parser) callonProgram1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onProgram1(stack["vals"]) +} + +func (c *current) onStmt1(val interface{}) (interface{}, error) { + return val, nil +} + +func (p *parser) callonStmt1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onStmt1(stack["val"]) +} + +func (c *current) onPackage1(val interface{}) (interface{}, error) { + return makePackage(currentLocation(c), val) +} + +func (p *parser) callonPackage1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onPackage1(stack["val"]) +} + +func (c *current) onImport1(path, alias interface{}) (interface{}, error) { + return makeImport(currentLocation(c), path, alias) +} + +func (p *parser) callonImport1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onImport1(stack["path"], stack["alias"]) +} + +func (c *current) onDefaultRules1(name, operator, value interface{}) (interface{}, error) { + return makeDefaultRule(currentLocation(c), name, operator, value) +} + +func (p *parser) callonDefaultRules1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onDefaultRules1(stack["name"], stack["operator"], stack["value"]) +} + +func (c *current) onNormalRules1(head, rest interface{}) (interface{}, error) { + return makeRule(currentLocation(c), head, rest) +} + +func (p *parser) callonNormalRules1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNormalRules1(stack["head"], stack["rest"]) +} + +func (c *current) onPartialRuleHead1(name, args, value interface{}) (interface{}, error) { + return makeRuleHead(currentLocation(c), name, args, nil, value) +} + +func (p *parser) callonPartialRuleHead1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onPartialRuleHead1(stack["name"], stack["args"], stack["value"]) +} + +func (c *current) onRuleHead1(name, key, value interface{}) (interface{}, error) { + return makeRuleHead(currentLocation(c), name, nil, key, value) +} + +func (p *parser) callonRuleHead1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRuleHead1(stack["name"], stack["key"], stack["value"]) +} + +func (c *current) onArgs1(list interface{}) (interface{}, error) { + return makeArgs(list) +} + +func (p *parser) callonArgs1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onArgs1(stack["list"]) +} + +func (c *current) onElse1(value, body interface{}) (interface{}, error) { + return makeRuleExt(currentLocation(c), value, body) +} + +func (p *parser) callonElse1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onElse1(stack["value"], stack["body"]) +} + +func (c *current) onRuleDup1(b interface{}) (interface{}, error) { + return ruleExt{loc: currentLocation(c), body: b.(Body)}, nil +} + +func (p *parser) callonRuleDup1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRuleDup1(stack["b"]) +} + +func (c *current) onNonEmptyBraceEnclosedBody1(val interface{}) (interface{}, error) { + if val == nil { + return NewBody(), fmt.Errorf("found empty body") + } + return val, nil +} + +func (p *parser) callonNonEmptyBraceEnclosedBody1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNonEmptyBraceEnclosedBody1(stack["val"]) +} + +func (c *current) onBraceEnclosedBody1(val interface{}) (interface{}, error) { + return makeBraceEnclosedBody(currentLocation(c), val) +} + +func (p *parser) callonBraceEnclosedBody1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBraceEnclosedBody1(stack["val"]) +} + +func (c *current) onWhitespaceBody1(head, tail interface{}) (interface{}, error) { + return makeBody(head, tail, 2) +} + +func (p *parser) callonWhitespaceBody1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onWhitespaceBody1(stack["head"], stack["tail"]) +} + +func (c *current) onNonWhitespaceBody1(head, tail interface{}) (interface{}, error) { + return makeBody(head, tail, 3) +} + +func (p *parser) callonNonWhitespaceBody1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNonWhitespaceBody1(stack["head"], stack["tail"]) +} + +func (c *current) onSomeDecl1(symbols interface{}) (interface{}, error) { + return makeSomeDeclLiteral(currentLocation(c), symbols) +} + +func (p *parser) callonSomeDecl1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onSomeDecl1(stack["symbols"]) +} + +func (c *current) onSomeDeclList1(head, rest interface{}) (interface{}, error) { + return makeSomeDeclSymbols(head, rest) +} + +func (p *parser) callonSomeDeclList1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onSomeDeclList1(stack["head"], stack["rest"]) +} + +func (c *current) onTermExpr1(negated, value, with interface{}) (interface{}, error) { + return makeLiteral(negated, value, with) +} + +func (p *parser) callonTermExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onTermExpr1(stack["negated"], stack["value"], stack["with"]) +} + +func (c *current) onLiteralExpr1(lhs, rest interface{}) (interface{}, error) { + return makeLiteralExpr(currentLocation(c), lhs, rest) +} + +func (p *parser) callonLiteralExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onLiteralExpr1(stack["lhs"], stack["rest"]) +} + +func (c *current) onLiteralExprOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonLiteralExprOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onLiteralExprOperator1(stack["val"]) +} + +func (c *current) onNotKeyword1(val interface{}) (interface{}, error) { + return val != nil, nil +} + +func (p *parser) callonNotKeyword1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNotKeyword1(stack["val"]) +} + +func (c *current) onWithKeywordList1(head, rest interface{}) (interface{}, error) { + return makeWithKeywordList(head, rest) +} + +func (p *parser) callonWithKeywordList1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onWithKeywordList1(stack["head"], stack["rest"]) +} + +func (c *current) onWithKeyword1(target, value interface{}) (interface{}, error) { + return makeWithKeyword(currentLocation(c), target, value) +} + +func (p *parser) callonWithKeyword1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onWithKeyword1(stack["target"], stack["value"]) +} + +func (c *current) onExprTerm1(lhs, rest interface{}) (interface{}, error) { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +func (p *parser) callonExprTerm1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onExprTerm1(stack["lhs"], stack["rest"]) +} + +func (c *current) onExprTermPairList1(head, tail interface{}) (interface{}, error) { + return makeExprTermPairList(head, tail) +} + +func (p *parser) callonExprTermPairList1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onExprTermPairList1(stack["head"], stack["tail"]) +} + +func (c *current) onExprTermList1(head, tail interface{}) (interface{}, error) { + return makeExprTermList(head, tail) +} + +func (p *parser) callonExprTermList1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onExprTermList1(stack["head"], stack["tail"]) +} + +func (c *current) onExprTermPair1(key, value interface{}) (interface{}, error) { + return makeExprTermPair(key, value) +} + +func (p *parser) callonExprTermPair1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onExprTermPair1(stack["key"], stack["value"]) +} + +func (c *current) onRelationOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonRelationOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRelationOperator1(stack["val"]) +} + +func (c *current) onRelationExpr1(lhs, rest interface{}) (interface{}, error) { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +func (p *parser) callonRelationExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRelationExpr1(stack["lhs"], stack["rest"]) +} + +func (c *current) onBitwiseOrOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonBitwiseOrOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBitwiseOrOperator1(stack["val"]) +} + +func (c *current) onBitwiseOrExpr1(lhs, rest interface{}) (interface{}, error) { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +func (p *parser) callonBitwiseOrExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBitwiseOrExpr1(stack["lhs"], stack["rest"]) +} + +func (c *current) onBitwiseAndOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonBitwiseAndOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBitwiseAndOperator1(stack["val"]) +} + +func (c *current) onBitwiseAndExpr1(lhs, rest interface{}) (interface{}, error) { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +func (p *parser) callonBitwiseAndExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBitwiseAndExpr1(stack["lhs"], stack["rest"]) +} + +func (c *current) onArithOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonArithOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onArithOperator1(stack["val"]) +} + +func (c *current) onArithExpr1(lhs, rest interface{}) (interface{}, error) { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +func (p *parser) callonArithExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onArithExpr1(stack["lhs"], stack["rest"]) +} + +func (c *current) onFactorOperator1(val interface{}) (interface{}, error) { + return makeInfixOperator(currentLocation(c), c.text) +} + +func (p *parser) callonFactorOperator1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFactorOperator1(stack["val"]) +} + +func (c *current) onFactorExpr2(expr interface{}) (interface{}, error) { + return expr, nil +} + +func (p *parser) callonFactorExpr2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFactorExpr2(stack["expr"]) +} + +func (c *current) onFactorExpr10(term interface{}) (interface{}, error) { + return term, nil +} + +func (p *parser) callonFactorExpr10() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onFactorExpr10(stack["term"]) +} + +func (c *current) onCall1(operator, args interface{}) (interface{}, error) { + return makeCall(currentLocation(c), operator, args) +} + +func (p *parser) callonCall1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onCall1(stack["operator"], stack["args"]) +} + +func (c *current) onTerm1(val, refs interface{}) (interface{}, error) { + return makeRef(currentLocation(c), val, refs) +} + +func (p *parser) callonTerm1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onTerm1(stack["val"], stack["refs"]) +} + +func (c *current) onTermPair1(key, value interface{}) (interface{}, error) { + return makeExprTermPair(key, value) +} + +func (p *parser) callonTermPair1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onTermPair1(stack["key"], stack["value"]) +} + +func (c *current) onArrayComprehension1(head, body interface{}) (interface{}, error) { + return makeArrayComprehension(currentLocation(c), head, body) +} + +func (p *parser) callonArrayComprehension1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onArrayComprehension1(stack["head"], stack["body"]) +} + +func (c *current) onObjectComprehension1(head, body interface{}) (interface{}, error) { + return makeObjectComprehension(currentLocation(c), head, body) +} + +func (p *parser) callonObjectComprehension1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onObjectComprehension1(stack["head"], stack["body"]) +} + +func (c *current) onSetComprehension1(head, body interface{}) (interface{}, error) { + return makeSetComprehension(currentLocation(c), head, body) +} + +func (p *parser) callonSetComprehension1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onSetComprehension1(stack["head"], stack["body"]) +} + +func (c *current) onObject1(list interface{}) (interface{}, error) { + return makeObject(currentLocation(c), list) +} + +func (p *parser) callonObject1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onObject1(stack["list"]) +} + +func (c *current) onArray1(list interface{}) (interface{}, error) { + return makeArray(currentLocation(c), list) +} + +func (p *parser) callonArray1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onArray1(stack["list"]) +} + +func (c *current) onSetEmpty1() (interface{}, error) { + var empty []*Term + return makeSet(currentLocation(c), empty) +} + +func (p *parser) callonSetEmpty1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onSetEmpty1() +} + +func (c *current) onSetNonEmpty1(list interface{}) (interface{}, error) { + return makeSet(currentLocation(c), list) +} + +func (p *parser) callonSetNonEmpty1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onSetNonEmpty1(stack["list"]) +} + +func (c *current) onRef1(head, rest interface{}) (interface{}, error) { + return makeRef(currentLocation(c), head, rest) +} + +func (p *parser) callonRef1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRef1(stack["head"], stack["rest"]) +} + +func (c *current) onRefOperandDot1(val interface{}) (interface{}, error) { + return makeRefOperandDot(currentLocation(c), val) +} + +func (p *parser) callonRefOperandDot1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRefOperandDot1(stack["val"]) +} + +func (c *current) onRefOperandCanonical1(val interface{}) (interface{}, error) { + return val, nil +} + +func (p *parser) callonRefOperandCanonical1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRefOperandCanonical1(stack["val"]) +} + +func (c *current) onVar1(val interface{}) (interface{}, error) { + return val.([]interface{})[0], nil +} + +func (p *parser) callonVar1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onVar1(stack["val"]) +} + +func (c *current) onVarChecked4(val interface{}) (bool, error) { + return IsKeyword(string(val.(*Term).Value.(Var))), nil +} + +func (p *parser) callonVarChecked4() (bool, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onVarChecked4(stack["val"]) +} + +func (c *current) onVarUnchecked1() (interface{}, error) { + return makeVar(currentLocation(c), c.text) +} + +func (p *parser) callonVarUnchecked1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onVarUnchecked1() +} + +func (c *current) onNumber1() (interface{}, error) { + return makeNumber(currentLocation(c), c.text) +} + +func (p *parser) callonNumber1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNumber1() +} + +func (c *current) onQuotedString2() (interface{}, error) { + return makeString(currentLocation(c), c.text) +} + +func (p *parser) callonQuotedString2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onQuotedString2() +} + +func (c *current) onQuotedString8() (interface{}, error) { + return makeNonterminatedString(currentLocation(c), string(c.text)) +} + +func (p *parser) callonQuotedString8() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onQuotedString8() +} + +func (c *current) onRawString1() (interface{}, error) { + return makeRawString(currentLocation(c), c.text) +} + +func (p *parser) callonRawString1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onRawString1() +} + +func (c *current) onBool1(val interface{}) (interface{}, error) { + return makeBool(currentLocation(c), c.text) +} + +func (p *parser) callonBool1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onBool1(stack["val"]) +} + +func (c *current) onNull1() (interface{}, error) { + return makeNull(currentLocation(c)) +} + +func (p *parser) callonNull1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNull1() +} + +func (c *current) onComment1(text interface{}) (interface{}, error) { + return makeComments(c, text) +} + +func (p *parser) callonComment1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onComment1(stack["text"]) +} + +var ( + // errNoRule is returned when the grammar to parse has no rule. + errNoRule = errors.New("grammar has no rule") + + // errInvalidEntrypoint is returned when the specified entrypoint rule + // does not exit. + errInvalidEntrypoint = errors.New("invalid entrypoint") + + // errInvalidEncoding is returned when the source is not properly + // utf8-encoded. + errInvalidEncoding = errors.New("invalid encoding") + + // errMaxExprCnt is used to signal that the maximum number of + // expressions have been parsed. + errMaxExprCnt = errors.New("max number of expresssions parsed") +) + +// Option is a function that can set an option on the parser. It returns +// the previous setting as an Option. +type Option func(*parser) Option + +// MaxExpressions creates an Option to stop parsing after the provided +// number of expressions have been parsed, if the value is 0 then the parser will +// parse for as many steps as needed (possibly an infinite number). +// +// The default for maxExprCnt is 0. +func MaxExpressions(maxExprCnt uint64) Option { + return func(p *parser) Option { + oldMaxExprCnt := p.maxExprCnt + p.maxExprCnt = maxExprCnt + return MaxExpressions(oldMaxExprCnt) + } +} + +// Entrypoint creates an Option to set the rule name to use as entrypoint. +// The rule name must have been specified in the -alternate-entrypoints +// if generating the parser with the -optimize-grammar flag, otherwise +// it may have been optimized out. Passing an empty string sets the +// entrypoint to the first rule in the grammar. +// +// The default is to start parsing at the first rule in the grammar. +func Entrypoint(ruleName string) Option { + return func(p *parser) Option { + oldEntrypoint := p.entrypoint + p.entrypoint = ruleName + if ruleName == "" { + p.entrypoint = g.rules[0].name + } + return Entrypoint(oldEntrypoint) + } +} + +// Statistics adds a user provided Stats struct to the parser to allow +// the user to process the results after the parsing has finished. +// Also the key for the "no match" counter is set. +// +// Example usage: +// +// input := "input" +// stats := Stats{} +// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match")) +// if err != nil { +// log.Panicln(err) +// } +// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ") +// if err != nil { +// log.Panicln(err) +// } +// fmt.Println(string(b)) +// +func Statistics(stats *Stats, choiceNoMatch string) Option { + return func(p *parser) Option { + oldStats := p.Stats + p.Stats = stats + oldChoiceNoMatch := p.choiceNoMatch + p.choiceNoMatch = choiceNoMatch + if p.Stats.ChoiceAltCnt == nil { + p.Stats.ChoiceAltCnt = make(map[string]map[string]int) + } + return Statistics(oldStats, oldChoiceNoMatch) + } +} + +// Debug creates an Option to set the debug flag to b. When set to true, +// debugging information is printed to stdout while parsing. +// +// The default is false. +func Debug(b bool) Option { + return func(p *parser) Option { + old := p.debug + p.debug = b + return Debug(old) + } +} + +// Memoize creates an Option to set the memoize flag to b. When set to true, +// the parser will cache all results so each expression is evaluated only +// once. This guarantees linear parsing time even for pathological cases, +// at the expense of more memory and slower times for typical cases. +// +// The default is false. +func Memoize(b bool) Option { + return func(p *parser) Option { + old := p.memoize + p.memoize = b + return Memoize(old) + } +} + +// AllowInvalidUTF8 creates an Option to allow invalid UTF-8 bytes. +// Every invalid UTF-8 byte is treated as a utf8.RuneError (U+FFFD) +// by character class matchers and is matched by the any matcher. +// The returned matched value, c.text and c.offset are NOT affected. +// +// The default is false. +func AllowInvalidUTF8(b bool) Option { + return func(p *parser) Option { + old := p.allowInvalidUTF8 + p.allowInvalidUTF8 = b + return AllowInvalidUTF8(old) + } +} + +// Recover creates an Option to set the recover flag to b. When set to +// true, this causes the parser to recover from panics and convert it +// to an error. Setting it to false can be useful while debugging to +// access the full stack trace. +// +// The default is true. +func Recover(b bool) Option { + return func(p *parser) Option { + old := p.recover + p.recover = b + return Recover(old) + } +} + +// GlobalStore creates an Option to set a key to a certain value in +// the globalStore. +func GlobalStore(key string, value interface{}) Option { + return func(p *parser) Option { + old := p.cur.globalStore[key] + p.cur.globalStore[key] = value + return GlobalStore(key, old) + } +} + +// InitState creates an Option to set a key to a certain value in +// the global "state" store. +func InitState(key string, value interface{}) Option { + return func(p *parser) Option { + old := p.cur.state[key] + p.cur.state[key] = value + return InitState(key, old) + } +} + +// ParseFile parses the file identified by filename. +func ParseFile(filename string, opts ...Option) (i interface{}, err error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func() { + if closeErr := f.Close(); closeErr != nil { + err = closeErr + } + }() + return ParseReader(filename, f, opts...) +} + +// ParseReader parses the data from r using filename as information in the +// error messages. +func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return Parse(filename, b, opts...) +} + +// Parse parses the data from b using filename as information in the +// error messages. +func Parse(filename string, b []byte, opts ...Option) (interface{}, error) { + return newParser(filename, b, opts...).parse(g) +} + +// position records a position in the text. +type position struct { + line, col, offset int +} + +func (p position) String() string { + return fmt.Sprintf("%d:%d [%d]", p.line, p.col, p.offset) +} + +// savepoint stores all state required to go back to this point in the +// parser. +type savepoint struct { + position + rn rune + w int +} + +type current struct { + pos position // start position of the match + text []byte // raw text of the match + + // state is a store for arbitrary key,value pairs that the user wants to be + // tied to the backtracking of the parser. + // This is always rolled back if a parsing rule fails. + state storeDict + + // globalStore is a general store for the user to store arbitrary key-value + // pairs that they need to manage and that they do not want tied to the + // backtracking of the parser. This is only modified by the user and never + // rolled back by the parser. It is always up to the user to keep this in a + // consistent state. + globalStore storeDict +} + +type storeDict map[string]interface{} + +// the AST types... + +type grammar struct { + pos position + rules []*rule +} + +type rule struct { + pos position + name string + displayName string + expr interface{} +} + +type choiceExpr struct { + pos position + alternatives []interface{} +} + +type actionExpr struct { + pos position + expr interface{} + run func(*parser) (interface{}, error) +} + +type recoveryExpr struct { + pos position + expr interface{} + recoverExpr interface{} + failureLabel []string +} + +type seqExpr struct { + pos position + exprs []interface{} +} + +type throwExpr struct { + pos position + label string +} + +type labeledExpr struct { + pos position + label string + expr interface{} +} + +type expr struct { + pos position + expr interface{} +} + +type andExpr expr +type notExpr expr +type zeroOrOneExpr expr +type zeroOrMoreExpr expr +type oneOrMoreExpr expr + +type ruleRefExpr struct { + pos position + name string +} + +type stateCodeExpr struct { + pos position + run func(*parser) error +} + +type andCodeExpr struct { + pos position + run func(*parser) (bool, error) +} + +type notCodeExpr struct { + pos position + run func(*parser) (bool, error) +} + +type litMatcher struct { + pos position + val string + ignoreCase bool +} + +type charClassMatcher struct { + pos position + val string + basicLatinChars [128]bool + chars []rune + ranges []rune + classes []*unicode.RangeTable + ignoreCase bool + inverted bool +} + +type anyMatcher position + +// errList cumulates the errors found by the parser. +type errList []error + +func (e *errList) add(err error) { + *e = append(*e, err) +} + +func (e errList) err() error { + if len(e) == 0 { + return nil + } + e.dedupe() + return e +} + +func (e *errList) dedupe() { + var cleaned []error + set := make(map[string]bool) + for _, err := range *e { + if msg := err.Error(); !set[msg] { + set[msg] = true + cleaned = append(cleaned, err) + } + } + *e = cleaned +} + +func (e errList) Error() string { + switch len(e) { + case 0: + return "" + case 1: + return e[0].Error() + default: + var buf bytes.Buffer + + for i, err := range e { + if i > 0 { + buf.WriteRune('\n') + } + buf.WriteString(err.Error()) + } + return buf.String() + } +} + +// parserError wraps an error with a prefix indicating the rule in which +// the error occurred. The original error is stored in the Inner field. +type parserError struct { + Inner error + pos position + prefix string + expected []string +} + +// Error returns the error message. +func (p *parserError) Error() string { + return p.prefix + ": " + p.Inner.Error() +} + +// newParser creates a parser with the specified input source and options. +func newParser(filename string, b []byte, opts ...Option) *parser { + stats := Stats{ + ChoiceAltCnt: make(map[string]map[string]int), + } + + p := &parser{ + filename: filename, + errs: new(errList), + data: b, + pt: savepoint{position: position{line: 1}}, + recover: true, + cur: current{ + state: make(storeDict), + globalStore: make(storeDict), + }, + maxFailPos: position{col: 1, line: 1}, + maxFailExpected: make([]string, 0, 20), + Stats: &stats, + // start rule is rule [0] unless an alternate entrypoint is specified + entrypoint: g.rules[0].name, + } + p.setOptions(opts) + + if p.maxExprCnt == 0 { + p.maxExprCnt = math.MaxUint64 + } + + return p +} + +// setOptions applies the options to the parser. +func (p *parser) setOptions(opts []Option) { + for _, opt := range opts { + opt(p) + } +} + +type resultTuple struct { + v interface{} + b bool + end savepoint +} + +const choiceNoMatch = -1 + +// Stats stores some statistics, gathered during parsing +type Stats struct { + // ExprCnt counts the number of expressions processed during parsing + // This value is compared to the maximum number of expressions allowed + // (set by the MaxExpressions option). + ExprCnt uint64 + + // ChoiceAltCnt is used to count for each ordered choice expression, + // which alternative is used how may times. + // These numbers allow to optimize the order of the ordered choice expression + // to increase the performance of the parser + // + // The outer key of ChoiceAltCnt is composed of the name of the rule as well + // as the line and the column of the ordered choice. + // The inner key of ChoiceAltCnt is the number (one-based) of the matching alternative. + // For each alternative the number of matches are counted. If an ordered choice does not + // match, a special counter is incremented. The name of this counter is set with + // the parser option Statistics. + // For an alternative to be included in ChoiceAltCnt, it has to match at least once. + ChoiceAltCnt map[string]map[string]int +} + +type parser struct { + filename string + pt savepoint + cur current + + data []byte + errs *errList + + depth int + recover bool + debug bool + + memoize bool + // memoization table for the packrat algorithm: + // map[offset in source] map[expression or rule] {value, match} + memo map[int]map[interface{}]resultTuple + + // rules table, maps the rule identifier to the rule node + rules map[string]*rule + // variables stack, map of label to value + vstack []map[string]interface{} + // rule stack, allows identification of the current rule in errors + rstack []*rule + + // parse fail + maxFailPos position + maxFailExpected []string + maxFailInvertExpected bool + + // max number of expressions to be parsed + maxExprCnt uint64 + // entrypoint for the parser + entrypoint string + + allowInvalidUTF8 bool + + *Stats + + choiceNoMatch string + // recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse + recoveryStack []map[string]interface{} +} + +// push a variable set on the vstack. +func (p *parser) pushV() { + if cap(p.vstack) == len(p.vstack) { + // create new empty slot in the stack + p.vstack = append(p.vstack, nil) + } else { + // slice to 1 more + p.vstack = p.vstack[:len(p.vstack)+1] + } + + // get the last args set + m := p.vstack[len(p.vstack)-1] + if m != nil && len(m) == 0 { + // empty map, all good + return + } + + m = make(map[string]interface{}) + p.vstack[len(p.vstack)-1] = m +} + +// pop a variable set from the vstack. +func (p *parser) popV() { + // if the map is not empty, clear it + m := p.vstack[len(p.vstack)-1] + if len(m) > 0 { + // GC that map + p.vstack[len(p.vstack)-1] = nil + } + p.vstack = p.vstack[:len(p.vstack)-1] +} + +// push a recovery expression with its labels to the recoveryStack +func (p *parser) pushRecovery(labels []string, expr interface{}) { + if cap(p.recoveryStack) == len(p.recoveryStack) { + // create new empty slot in the stack + p.recoveryStack = append(p.recoveryStack, nil) + } else { + // slice to 1 more + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1] + } + + m := make(map[string]interface{}, len(labels)) + for _, fl := range labels { + m[fl] = expr + } + p.recoveryStack[len(p.recoveryStack)-1] = m +} + +// pop a recovery expression from the recoveryStack +func (p *parser) popRecovery() { + // GC that map + p.recoveryStack[len(p.recoveryStack)-1] = nil + + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1] +} + +func (p *parser) print(prefix, s string) string { + if !p.debug { + return s + } + + fmt.Printf("%s %d:%d:%d: %s [%#U]\n", + prefix, p.pt.line, p.pt.col, p.pt.offset, s, p.pt.rn) + return s +} + +func (p *parser) in(s string) string { + p.depth++ + return p.print(strings.Repeat(" ", p.depth)+">", s) +} + +func (p *parser) out(s string) string { + p.depth-- + return p.print(strings.Repeat(" ", p.depth)+"<", s) +} + +func (p *parser) addErr(err error) { + p.addErrAt(err, p.pt.position, []string{}) +} + +func (p *parser) addErrAt(err error, pos position, expected []string) { + var buf bytes.Buffer + if p.filename != "" { + buf.WriteString(p.filename) + } + if buf.Len() > 0 { + buf.WriteString(":") + } + buf.WriteString(fmt.Sprintf("%d:%d (%d)", pos.line, pos.col, pos.offset)) + if len(p.rstack) > 0 { + if buf.Len() > 0 { + buf.WriteString(": ") + } + rule := p.rstack[len(p.rstack)-1] + if rule.displayName != "" { + buf.WriteString("rule " + rule.displayName) + } else { + buf.WriteString("rule " + rule.name) + } + } + pe := &parserError{Inner: err, pos: pos, prefix: buf.String(), expected: expected} + p.errs.add(pe) +} + +func (p *parser) failAt(fail bool, pos position, want string) { + // process fail if parsing fails and not inverted or parsing succeeds and invert is set + if fail == p.maxFailInvertExpected { + if pos.offset < p.maxFailPos.offset { + return + } + + if pos.offset > p.maxFailPos.offset { + p.maxFailPos = pos + p.maxFailExpected = p.maxFailExpected[:0] + } + + if p.maxFailInvertExpected { + want = "!" + want + } + p.maxFailExpected = append(p.maxFailExpected, want) + } +} + +// read advances the parser to the next rune. +func (p *parser) read() { + p.pt.offset += p.pt.w + rn, n := utf8.DecodeRune(p.data[p.pt.offset:]) + p.pt.rn = rn + p.pt.w = n + p.pt.col++ + if rn == '\n' { + p.pt.line++ + p.pt.col = 0 + } + + if rn == utf8.RuneError && n == 1 { // see utf8.DecodeRune + if !p.allowInvalidUTF8 { + p.addErr(errInvalidEncoding) + } + } +} + +// restore parser position to the savepoint pt. +func (p *parser) restore(pt savepoint) { + if p.debug { + defer p.out(p.in("restore")) + } + if pt.offset == p.pt.offset { + return + } + p.pt = pt +} + +// Cloner is implemented by any value that has a Clone method, which returns a +// copy of the value. This is mainly used for types which are not passed by +// value (e.g map, slice, chan) or structs that contain such types. +// +// This is used in conjunction with the global state feature to create proper +// copies of the state to allow the parser to properly restore the state in +// the case of backtracking. +type Cloner interface { + Clone() interface{} +} + +// clone and return parser current state. +func (p *parser) cloneState() storeDict { + if p.debug { + defer p.out(p.in("cloneState")) + } + + state := make(storeDict, len(p.cur.state)) + for k, v := range p.cur.state { + if c, ok := v.(Cloner); ok { + state[k] = c.Clone() + } else { + state[k] = v + } + } + return state +} + +// restore parser current state to the state storeDict. +// every restoreState should applied only one time for every cloned state +func (p *parser) restoreState(state storeDict) { + if p.debug { + defer p.out(p.in("restoreState")) + } + p.cur.state = state +} + +// get the slice of bytes from the savepoint start to the current position. +func (p *parser) sliceFrom(start savepoint) []byte { + return p.data[start.position.offset:p.pt.position.offset] +} + +func (p *parser) getMemoized(node interface{}) (resultTuple, bool) { + if len(p.memo) == 0 { + return resultTuple{}, false + } + m := p.memo[p.pt.offset] + if len(m) == 0 { + return resultTuple{}, false + } + res, ok := m[node] + return res, ok +} + +func (p *parser) setMemoized(pt savepoint, node interface{}, tuple resultTuple) { + if p.memo == nil { + p.memo = make(map[int]map[interface{}]resultTuple) + } + m := p.memo[pt.offset] + if m == nil { + m = make(map[interface{}]resultTuple) + p.memo[pt.offset] = m + } + m[node] = tuple +} + +func (p *parser) buildRulesTable(g *grammar) { + p.rules = make(map[string]*rule, len(g.rules)) + for _, r := range g.rules { + p.rules[r.name] = r + } +} + +func (p *parser) parse(g *grammar) (val interface{}, err error) { + if len(g.rules) == 0 { + p.addErr(errNoRule) + return nil, p.errs.err() + } + + // TODO : not super critical but this could be generated + p.buildRulesTable(g) + + if p.recover { + // panic can be used in action code to stop parsing immediately + // and return the panic as an error. + defer func() { + if e := recover(); e != nil { + if p.debug { + defer p.out(p.in("panic handler")) + } + val = nil + switch e := e.(type) { + case error: + p.addErr(e) + default: + p.addErr(fmt.Errorf("%v", e)) + } + err = p.errs.err() + } + }() + } + + startRule, ok := p.rules[p.entrypoint] + if !ok { + p.addErr(errInvalidEntrypoint) + return nil, p.errs.err() + } + + p.read() // advance to first rune + val, ok = p.parseRule(startRule) + if !ok { + if len(*p.errs) == 0 { + // If parsing fails, but no errors have been recorded, the expected values + // for the farthest parser position are returned as error. + maxFailExpectedMap := make(map[string]struct{}, len(p.maxFailExpected)) + for _, v := range p.maxFailExpected { + maxFailExpectedMap[v] = struct{}{} + } + expected := make([]string, 0, len(maxFailExpectedMap)) + eof := false + if _, ok := maxFailExpectedMap["!."]; ok { + delete(maxFailExpectedMap, "!.") + eof = true + } + for k := range maxFailExpectedMap { + expected = append(expected, k) + } + sort.Strings(expected) + if eof { + expected = append(expected, "EOF") + } + p.addErrAt(errors.New("no match found, expected: "+listJoin(expected, ", ", "or")), p.maxFailPos, expected) + } + + return nil, p.errs.err() + } + return val, p.errs.err() +} + +func listJoin(list []string, sep string, lastSep string) string { + switch len(list) { + case 0: + return "" + case 1: + return list[0] + default: + return fmt.Sprintf("%s %s %s", strings.Join(list[:len(list)-1], sep), lastSep, list[len(list)-1]) + } +} + +func (p *parser) parseRule(rule *rule) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRule " + rule.name)) + } + + if p.memoize { + res, ok := p.getMemoized(rule) + if ok { + p.restore(res.end) + return res.v, res.b + } + } + + start := p.pt + p.rstack = append(p.rstack, rule) + p.pushV() + val, ok := p.parseExpr(rule.expr) + p.popV() + p.rstack = p.rstack[:len(p.rstack)-1] + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } + + if p.memoize { + p.setMemoized(start, rule, resultTuple{val, ok, p.pt}) + } + return val, ok +} + +func (p *parser) parseExpr(expr interface{}) (interface{}, bool) { + var pt savepoint + + if p.memoize { + res, ok := p.getMemoized(expr) + if ok { + p.restore(res.end) + return res.v, res.b + } + pt = p.pt + } + + p.ExprCnt++ + if p.ExprCnt > p.maxExprCnt { + panic(errMaxExprCnt) + } + + var val interface{} + var ok bool + switch expr := expr.(type) { + case *actionExpr: + val, ok = p.parseActionExpr(expr) + case *andCodeExpr: + val, ok = p.parseAndCodeExpr(expr) + case *andExpr: + val, ok = p.parseAndExpr(expr) + case *anyMatcher: + val, ok = p.parseAnyMatcher(expr) + case *charClassMatcher: + val, ok = p.parseCharClassMatcher(expr) + case *choiceExpr: + val, ok = p.parseChoiceExpr(expr) + case *labeledExpr: + val, ok = p.parseLabeledExpr(expr) + case *litMatcher: + val, ok = p.parseLitMatcher(expr) + case *notCodeExpr: + val, ok = p.parseNotCodeExpr(expr) + case *notExpr: + val, ok = p.parseNotExpr(expr) + case *oneOrMoreExpr: + val, ok = p.parseOneOrMoreExpr(expr) + case *recoveryExpr: + val, ok = p.parseRecoveryExpr(expr) + case *ruleRefExpr: + val, ok = p.parseRuleRefExpr(expr) + case *seqExpr: + val, ok = p.parseSeqExpr(expr) + case *stateCodeExpr: + val, ok = p.parseStateCodeExpr(expr) + case *throwExpr: + val, ok = p.parseThrowExpr(expr) + case *zeroOrMoreExpr: + val, ok = p.parseZeroOrMoreExpr(expr) + case *zeroOrOneExpr: + val, ok = p.parseZeroOrOneExpr(expr) + default: + panic(fmt.Sprintf("unknown expression type %T", expr)) + } + if p.memoize { + p.setMemoized(pt, expr, resultTuple{val, ok, p.pt}) + } + return val, ok +} + +func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseActionExpr")) + } + + start := p.pt + val, ok := p.parseExpr(act.expr) + if ok { + p.cur.pos = start.position + p.cur.text = p.sliceFrom(start) + state := p.cloneState() + actVal, err := act.run(p) + if err != nil { + p.addErrAt(err, start.position, []string{}) + } + p.restoreState(state) + + val = actVal + } + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } + return val, ok +} + +func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAndCodeExpr")) + } + + state := p.cloneState() + + ok, err := and.run(p) + if err != nil { + p.addErr(err) + } + p.restoreState(state) + + return nil, ok +} + +func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAndExpr")) + } + + pt := p.pt + state := p.cloneState() + p.pushV() + _, ok := p.parseExpr(and.expr) + p.popV() + p.restoreState(state) + p.restore(pt) + + return nil, ok +} + +func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAnyMatcher")) + } + + if p.pt.rn == utf8.RuneError && p.pt.w == 0 { + // EOF - see utf8.DecodeRune + p.failAt(false, p.pt.position, ".") + return nil, false + } + start := p.pt + p.read() + p.failAt(true, start.position, ".") + return p.sliceFrom(start), true +} + +func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseCharClassMatcher")) + } + + cur := p.pt.rn + start := p.pt + + // can't match EOF + if cur == utf8.RuneError && p.pt.w == 0 { // see utf8.DecodeRune + p.failAt(false, start.position, chr.val) + return nil, false + } + + if chr.ignoreCase { + cur = unicode.ToLower(cur) + } + + // try to match in the list of available chars + for _, rn := range chr.chars { + if rn == cur { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of ranges + for i := 0; i < len(chr.ranges); i += 2 { + if cur >= chr.ranges[i] && cur <= chr.ranges[i+1] { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of Unicode classes + for _, cl := range chr.classes { + if unicode.Is(cl, cur) { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + if chr.inverted { + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + p.failAt(false, start.position, chr.val) + return nil, false +} + +func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) { + choiceIdent := fmt.Sprintf("%s %d:%d", p.rstack[len(p.rstack)-1].name, ch.pos.line, ch.pos.col) + m := p.ChoiceAltCnt[choiceIdent] + if m == nil { + m = make(map[string]int) + p.ChoiceAltCnt[choiceIdent] = m + } + // We increment altI by 1, so the keys do not start at 0 + alt := strconv.Itoa(altI + 1) + if altI == choiceNoMatch { + alt = p.choiceNoMatch + } + m[alt]++ +} + +func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseChoiceExpr")) + } + + for altI, alt := range ch.alternatives { + // dummy assignment to prevent compile error if optimized + _ = altI + + state := p.cloneState() + + p.pushV() + val, ok := p.parseExpr(alt) + p.popV() + if ok { + p.incChoiceAltCnt(ch, altI) + return val, ok + } + p.restoreState(state) + } + p.incChoiceAltCnt(ch, choiceNoMatch) + return nil, false +} + +func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseLabeledExpr")) + } + + p.pushV() + val, ok := p.parseExpr(lab.expr) + p.popV() + if ok && lab.label != "" { + m := p.vstack[len(p.vstack)-1] + m[lab.label] = val + } + return val, ok +} + +func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseLitMatcher")) + } + + ignoreCase := "" + if lit.ignoreCase { + ignoreCase = "i" + } + val := fmt.Sprintf("%q%s", lit.val, ignoreCase) + start := p.pt + for _, want := range lit.val { + cur := p.pt.rn + if lit.ignoreCase { + cur = unicode.ToLower(cur) + } + if cur != want { + p.failAt(false, start.position, val) + p.restore(start) + return nil, false + } + p.read() + } + p.failAt(true, start.position, val) + return p.sliceFrom(start), true +} + +func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseNotCodeExpr")) + } + + state := p.cloneState() + + ok, err := not.run(p) + if err != nil { + p.addErr(err) + } + p.restoreState(state) + + return nil, !ok +} + +func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseNotExpr")) + } + + pt := p.pt + state := p.cloneState() + p.pushV() + p.maxFailInvertExpected = !p.maxFailInvertExpected + _, ok := p.parseExpr(not.expr) + p.maxFailInvertExpected = !p.maxFailInvertExpected + p.popV() + p.restoreState(state) + p.restore(pt) + + return nil, !ok +} + +func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseOneOrMoreExpr")) + } + + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + if len(vals) == 0 { + // did not match once, no match + return nil, false + } + return vals, true + } + vals = append(vals, val) + } +} + +func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")")) + } + + p.pushRecovery(recover.failureLabel, recover.recoverExpr) + val, ok := p.parseExpr(recover.expr) + p.popRecovery() + + return val, ok +} + +func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRuleRefExpr " + ref.name)) + } + + if ref.name == "" { + panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) + } + + rule := p.rules[ref.name] + if rule == nil { + p.addErr(fmt.Errorf("undefined rule: %s", ref.name)) + return nil, false + } + return p.parseRule(rule) +} + +func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseSeqExpr")) + } + + vals := make([]interface{}, 0, len(seq.exprs)) + + pt := p.pt + state := p.cloneState() + for _, expr := range seq.exprs { + val, ok := p.parseExpr(expr) + if !ok { + p.restoreState(state) + p.restore(pt) + return nil, false + } + vals = append(vals, val) + } + return vals, true +} + +func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseStateCodeExpr")) + } + + err := state.run(p) + if err != nil { + p.addErr(err) + } + return nil, true +} + +func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseThrowExpr")) + } + + for i := len(p.recoveryStack) - 1; i >= 0; i-- { + if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok { + if val, ok := p.parseExpr(recoverExpr); ok { + return val, ok + } + } + } + + return nil, false +} + +func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrMoreExpr")) + } + + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + return vals, true + } + vals = append(vals, val) + } +} + +func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrOneExpr")) + } + + p.pushV() + val, _ := p.parseExpr(expr.expr) + p.popV() + // whether it matched or not, consider it a match + return val, true +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go new file mode 100644 index 000000000..03bd20f4d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go @@ -0,0 +1,838 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// This file contains extra functions for parsing Rego. +// Most of the parsing is handled by the auto-generated code in +// parser.go, however, there are additional utilities that are +// helpful for dealing with Rego source inputs (e.g., REPL +// statements, source files, etc.) + +package ast + +import ( + "fmt" + "sort" + "strings" + "unicode" + + "github.com/pkg/errors" +) + +// MustParseBody returns a parsed body. +// If an error occurs during parsing, panic. +func MustParseBody(input string) Body { + parsed, err := ParseBody(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseExpr returns a parsed expression. +// If an error occurs during parsing, panic. +func MustParseExpr(input string) *Expr { + parsed, err := ParseExpr(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseImports returns a slice of imports. +// If an error occurs during parsing, panic. +func MustParseImports(input string) []*Import { + parsed, err := ParseImports(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseModule returns a parsed module. +// If an error occurs during parsing, panic. +func MustParseModule(input string) *Module { + parsed, err := ParseModule("", input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParsePackage returns a Package. +// If an error occurs during parsing, panic. +func MustParsePackage(input string) *Package { + parsed, err := ParsePackage(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatements returns a slice of parsed statements. +// If an error occurs during parsing, panic. +func MustParseStatements(input string) []Statement { + parsed, _, err := ParseStatements("", input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseStatement returns exactly one statement. +// If an error occurs during parsing, panic. +func MustParseStatement(input string) Statement { + parsed, err := ParseStatement(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRef returns a parsed reference. +// If an error occurs during parsing, panic. +func MustParseRef(input string) Ref { + parsed, err := ParseRef(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseRule returns a parsed rule. +// If an error occurs during parsing, panic. +func MustParseRule(input string) *Rule { + parsed, err := ParseRule(input) + if err != nil { + panic(err) + } + return parsed +} + +// MustParseTerm returns a parsed term. +// If an error occurs during parsing, panic. +func MustParseTerm(input string) *Term { + parsed, err := ParseTerm(input) + if err != nil { + panic(err) + } + return parsed +} + +// ParseRuleFromBody returns a rule if the body can be interpreted as a rule +// definition. Otherwise, an error is returned. +func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { + + if len(body) != 1 { + return nil, fmt.Errorf("multiple expressions cannot be used for rule head") + } + + return ParseRuleFromExpr(module, body[0]) +} + +// ParseRuleFromExpr returns a rule if the expression can be interpreted as a +// rule definition. +func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { + + if len(expr.With) > 0 { + return nil, fmt.Errorf("expressions using with keyword cannot be used for rule head") + } + + if expr.Negated { + return nil, fmt.Errorf("negated expressions cannot be used for rule head") + } + + if _, ok := expr.Terms.(*SomeDecl); ok { + return nil, errors.New("some declarations cannot be used for rule head") + } + + if term, ok := expr.Terms.(*Term); ok { + switch v := term.Value.(type) { + case Ref: + return ParsePartialSetDocRuleFromTerm(module, term) + default: + return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(v)) + } + } + + if _, ok := expr.Terms.([]*Term); !ok { + // This is a defensive check in case other kinds of expression terms are + // introduced in the future. + return nil, errors.New("expression cannot be used for rule head") + } + + if expr.IsAssignment() { + + lhs, rhs := expr.Operand(0), expr.Operand(1) + rule, err := ParseCompleteDocRuleFromAssignmentExpr(module, lhs, rhs) + + if err == nil { + return rule, nil + } else if _, ok := lhs.Value.(Call); ok { + return nil, errFunctionAssignOperator + } else if _, ok := lhs.Value.(Ref); ok { + return nil, errPartialRuleAssignOperator + } + + return nil, errTermAssignOperator(lhs.Value) + } + + if expr.IsEquality() { + + lhs, rhs := expr.Operand(0), expr.Operand(1) + rule, err := ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) + + if err == nil { + return rule, nil + } + + rule, err = ParseRuleFromCallEqExpr(module, lhs, rhs) + if err == nil { + return rule, nil + } + + return ParsePartialObjectDocRuleFromEqExpr(module, lhs, rhs) + } + + if _, ok := BuiltinMap[expr.Operator().String()]; ok { + return nil, fmt.Errorf("rule name conflicts with built-in function") + } + + return ParseRuleFromCallExpr(module, expr.Terms.([]*Term)) +} + +// ParseCompleteDocRuleFromAssignmentExpr returns a rule if the expression can +// be interpreted as a complete document definition declared with the assignment +// operator. +func ParseCompleteDocRuleFromAssignmentExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + rule, err := ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) + if err != nil { + return nil, err + } + + rule.Head.Assign = true + + return rule, nil +} + +// ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a complete document definition. +func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + var name Var + + if RootDocumentRefs.Contains(lhs) { + name = lhs.Value.(Ref)[0].Value.(Var) + } else if v, ok := lhs.Value.(Var); ok { + name = v + } else { + return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(lhs.Value)) + } + + rule := &Rule{ + Location: rhs.Location, + Head: &Head{ + Location: rhs.Location, + Name: name, + Value: rhs, + }, + Body: NewBody( + NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location), + ), + Module: module, + } + + return rule, nil +} + +// ParsePartialObjectDocRuleFromEqExpr returns a rule if the expression can be +// interpreted as a partial object document definition. +func ParsePartialObjectDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + ref, ok := lhs.Value.(Ref) + if !ok || len(ref) != 2 { + return nil, fmt.Errorf("%v cannot be used for rule name", TypeName(lhs.Value)) + } + + name := ref[0].Value.(Var) + key := ref[1] + + rule := &Rule{ + Location: rhs.Location, + Head: &Head{ + Location: rhs.Location, + Name: name, + Key: key, + Value: rhs, + }, + Body: NewBody( + NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location), + ), + Module: module, + } + + return rule, nil +} + +// ParsePartialSetDocRuleFromTerm returns a rule if the term can be interpreted +// as a partial set document definition. +func ParsePartialSetDocRuleFromTerm(module *Module, term *Term) (*Rule, error) { + + ref, ok := term.Value.(Ref) + if !ok { + return nil, fmt.Errorf("%vs cannot be used for rule head", TypeName(term.Value)) + } + + if len(ref) != 2 { + return nil, fmt.Errorf("refs cannot be used for rule") + } + + rule := &Rule{ + Location: term.Location, + Head: &Head{ + Location: term.Location, + Name: ref[0].Value.(Var), + Key: ref[1], + }, + Body: NewBody( + NewExpr(BooleanTerm(true).SetLocation(term.Location)).SetLocation(term.Location), + ), + Module: module, + } + + return rule, nil +} + +// ParseRuleFromCallEqExpr returns a rule if the term can be interpreted as a +// function definition (e.g., f(x) = y => f(x) = y { true }). +func ParseRuleFromCallEqExpr(module *Module, lhs, rhs *Term) (*Rule, error) { + + call, ok := lhs.Value.(Call) + if !ok { + return nil, fmt.Errorf("must be call") + } + + rule := &Rule{ + Location: lhs.Location, + Head: &Head{ + Location: lhs.Location, + Name: call[0].Value.(Ref)[0].Value.(Var), + Args: Args(call[1:]), + Value: rhs, + }, + Body: NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location)), + Module: module, + } + + return rule, nil +} + +// ParseRuleFromCallExpr returns a rule if the terms can be interpreted as a +// function returning true or some value (e.g., f(x) => f(x) = true { true }). +func ParseRuleFromCallExpr(module *Module, terms []*Term) (*Rule, error) { + + if len(terms) <= 1 { + return nil, fmt.Errorf("rule argument list must take at least one argument") + } + + loc := terms[0].Location + args := terms[1:] + value := BooleanTerm(true).SetLocation(loc) + + rule := &Rule{ + Location: loc, + Head: &Head{ + Location: loc, + Name: Var(terms[0].String()), + Args: args, + Value: value, + }, + Module: module, + Body: NewBody(NewExpr(BooleanTerm(true).SetLocation(loc)).SetLocation(loc)), + } + return rule, nil +} + +// ParseImports returns a slice of Import objects. +func ParseImports(input string) ([]*Import, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + result := []*Import{} + for _, stmt := range stmts { + if imp, ok := stmt.(*Import); ok { + result = append(result, imp) + } else { + return nil, fmt.Errorf("expected import but got %T", stmt) + } + } + return result, nil +} + +// ParseModule returns a parsed Module object. +// For details on Module objects and their fields, see policy.go. +// Empty input will return nil, nil. +func ParseModule(filename, input string) (*Module, error) { + stmts, comments, err := ParseStatements(filename, input) + if err != nil { + return nil, err + } + return parseModule(filename, stmts, comments) +} + +// ParseBody returns exactly one body. +// If multiple bodies are parsed, an error is returned. +func ParseBody(input string) (Body, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + + result := Body{} + + for _, stmt := range stmts { + switch stmt := stmt.(type) { + case Body: + result = append(result, stmt...) + case *Comment: + // skip + default: + return nil, fmt.Errorf("expected body but got %T", stmt) + } + } + + setExprIndices(result) + + return result, nil +} + +// ParseExpr returns exactly one expression. +// If multiple expressions are parsed, an error is returned. +func ParseExpr(input string) (*Expr, error) { + body, err := ParseBody(input) + if err != nil { + return nil, errors.Wrap(err, "failed to parse expression") + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one expression but got: %v", body) + } + return body[0], nil +} + +// ParsePackage returns exactly one Package. +// If multiple statements are parsed, an error is returned. +func ParsePackage(input string) (*Package, error) { + stmt, err := ParseStatement(input) + if err != nil { + return nil, err + } + pkg, ok := stmt.(*Package) + if !ok { + return nil, fmt.Errorf("expected package but got %T", stmt) + } + return pkg, nil +} + +// ParseTerm returns exactly one term. +// If multiple terms are parsed, an error is returned. +func ParseTerm(input string) (*Term, error) { + body, err := ParseBody(input) + if err != nil { + return nil, errors.Wrap(err, "failed to parse term") + } + if len(body) != 1 { + return nil, fmt.Errorf("expected exactly one term but got: %v", body) + } + term, ok := body[0].Terms.(*Term) + if !ok { + return nil, fmt.Errorf("expected term but got %v", body[0].Terms) + } + return term, nil +} + +// ParseRef returns exactly one reference. +func ParseRef(input string) (Ref, error) { + term, err := ParseTerm(input) + if err != nil { + return nil, errors.Wrap(err, "failed to parse ref") + } + ref, ok := term.Value.(Ref) + if !ok { + return nil, fmt.Errorf("expected ref but got %v", term) + } + return ref, nil +} + +// ParseRule returns exactly one rule. +// If multiple rules are parsed, an error is returned. +func ParseRule(input string) (*Rule, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, fmt.Errorf("expected exactly one statement (rule)") + } + rule, ok := stmts[0].(*Rule) + if !ok { + return nil, fmt.Errorf("expected rule but got %T", stmts[0]) + } + return rule, nil +} + +// ParseStatement returns exactly one statement. +// A statement might be a term, expression, rule, etc. Regardless, +// this function expects *exactly* one statement. If multiple +// statements are parsed, an error is returned. +func ParseStatement(input string) (Statement, error) { + stmts, _, err := ParseStatements("", input) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + return nil, fmt.Errorf("expected exactly one statement") + } + return stmts[0], nil +} + +// CommentsOption returns a parser option to initialize the comments store within +// the parser. +func CommentsOption() Option { + return GlobalStore(commentsKey, map[commentKey]*Comment{}) +} + +type commentKey struct { + File string + Row int + Col int +} + +func (a commentKey) Compare(other commentKey) int { + if a.File < other.File { + return -1 + } else if a.File > other.File { + return 1 + } else if a.Row < other.Row { + return -1 + } else if a.Row > other.Row { + return 1 + } else if a.Col < other.Col { + return -1 + } else if a.Col > other.Col { + return 1 + } + return 0 +} + +// ParseStatements returns a slice of parsed statements. +// This is the default return value from the parser. +func ParseStatements(filename, input string) ([]Statement, []*Comment, error) { + + bs := []byte(input) + + parsed, err := Parse(filename, bs, GlobalStore(filenameKey, filename), CommentsOption()) + if err != nil { + return nil, nil, formatParserErrors(filename, bs, err) + } + + var comments []*Comment + var sl []interface{} + if p, ok := parsed.(program); ok { + sl = p.buf + commentMap := p.comments.(map[commentKey]*Comment) + commentKeys := []commentKey{} + for k := range commentMap { + commentKeys = append(commentKeys, k) + } + sort.Slice(commentKeys, func(i, j int) bool { + return commentKeys[i].Compare(commentKeys[j]) < 0 + }) + for _, k := range commentKeys { + comments = append(comments, commentMap[k]) + } + } else { + sl = parsed.([]interface{}) + } + stmts := make([]Statement, 0, len(sl)) + + for _, x := range sl { + if rules, ok := x.([]*Rule); ok { + for _, rule := range rules { + stmts = append(stmts, rule) + } + } else { + // Unchecked cast should be safe. A panic indicates grammar is + // out-of-sync. + stmts = append(stmts, x.(Statement)) + } + } + + return stmts, comments, postProcess(filename, stmts) +} + +func formatParserErrors(filename string, bs []byte, err error) error { + // Errors returned by the parser are always of type errList and the errList + // always contains *parserError. + // https://godoc.org/github.com/mna/pigeon#hdr-Error_reporting. + errs := err.(errList) + r := make(Errors, len(errs)) + for i, e := range errs { + r[i] = formatParserError(filename, bs, e.(*parserError)) + } + return r +} + +func formatParserError(filename string, bs []byte, e *parserError) *Error { + loc := NewLocation(nil, filename, e.pos.line, e.pos.col) + inner := e.Inner.Error() + idx := strings.Index(inner, "no match found") + if idx >= 0 { + // Match errors end with "no match found, expected: ...". We do not want to + // include ", expected: ..." as it does not provide any value, so truncate the + // string here. + inner = inner[:idx+14] + } + err := NewError(ParseErr, loc, inner) + err.Details = newParserErrorDetail(bs, e.pos) + return err +} + +func parseModule(filename string, stmts []Statement, comments []*Comment) (*Module, error) { + + if len(stmts) == 0 { + return nil, NewError(ParseErr, &Location{File: filename}, "empty module") + } + + var errs Errors + + _package, ok := stmts[0].(*Package) + if !ok { + loc := stmts[0].(Statement).Loc() + errs = append(errs, NewError(ParseErr, loc, "package expected")) + } + + mod := &Module{ + Package: _package, + } + + // The comments slice only holds comments that were not their own statements. + mod.Comments = append(mod.Comments, comments...) + + for _, stmt := range stmts[1:] { + switch stmt := stmt.(type) { + case *Import: + mod.Imports = append(mod.Imports, stmt) + case *Rule: + setRuleModule(stmt, mod) + mod.Rules = append(mod.Rules, stmt) + case Body: + rule, err := ParseRuleFromBody(mod, stmt) + if err != nil { + errs = append(errs, NewError(ParseErr, stmt[0].Location, err.Error())) + } else { + mod.Rules = append(mod.Rules, rule) + } + case *Package: + errs = append(errs, NewError(ParseErr, stmt.Loc(), "unexpected package")) + case *Comment: // Ignore comments, they're handled above. + default: + panic("illegal value") // Indicates grammar is out-of-sync with code. + } + } + + if len(errs) == 0 { + return mod, nil + } + + return nil, errs +} + +func postProcess(filename string, stmts []Statement) error { + + if err := mangleDataVars(stmts); err != nil { + return err + } + + if err := mangleInputVars(stmts); err != nil { + return err + } + + mangleWildcards(stmts) + mangleExprIndices(stmts) + + return nil +} + +func mangleDataVars(stmts []Statement) error { + for i := range stmts { + vt := newVarToRefTransformer(DefaultRootDocument.Value.(Var), DefaultRootRef.Copy()) + stmt, err := Transform(vt, stmts[i]) + if err != nil { + return err + } + stmts[i] = stmt.(Statement) + } + return nil +} + +func mangleInputVars(stmts []Statement) error { + for i := range stmts { + vt := newVarToRefTransformer(InputRootDocument.Value.(Var), InputRootRef.Copy()) + stmt, err := Transform(vt, stmts[i]) + if err != nil { + return err + } + stmts[i] = stmt.(Statement) + } + return nil +} + +func mangleExprIndices(stmts []Statement) { + for _, stmt := range stmts { + setExprIndices(stmt) + } +} + +func setExprIndices(x interface{}) { + WalkBodies(x, func(b Body) bool { + for i, expr := range b { + expr.Index = i + } + return false + }) +} + +func mangleWildcards(stmts []Statement) { + m := &wildcardMangler{} + for i := range stmts { + stmt, _ := Transform(m, stmts[i]) + stmts[i] = stmt.(Statement) + } +} + +type wildcardMangler struct { + c int +} + +func (m *wildcardMangler) Transform(x interface{}) (interface{}, error) { + if term, ok := x.(Var); ok { + if term.Equal(Wildcard.Value) { + name := fmt.Sprintf("%s%d", WildcardPrefix, m.c) + m.c++ + return Var(name), nil + } + } + return x, nil +} + +func setRuleModule(rule *Rule, module *Module) { + rule.Module = module + if rule.Else != nil { + setRuleModule(rule.Else, module) + } +} + +type varToRefTransformer struct { + orig Var + target Ref + // skip set to true to avoid recursively processing the result of + // transformation. + skip bool +} + +func newVarToRefTransformer(orig Var, target Ref) *varToRefTransformer { + return &varToRefTransformer{ + orig: orig, + target: target, + skip: false, + } +} + +func (vt *varToRefTransformer) Transform(x interface{}) (interface{}, error) { + if vt.skip { + vt.skip = false + return x, nil + } + switch x := x.(type) { + case *Head: + // The next AST node will be the rule name (which should not be + // transformed). + vt.skip = true + case Ref: + // The next AST node will be the ref head (which should not be + // transformed). + vt.skip = true + case Var: + if x.Equal(vt.orig) { + vt.skip = true + return vt.target, nil + } + } + return x, nil +} + +// ParserErrorDetail holds additional details for parser errors. +type ParserErrorDetail struct { + Line string `json:"line"` + Idx int `json:"idx"` +} + +func newParserErrorDetail(bs []byte, pos position) *ParserErrorDetail { + + offset := pos.offset + + // Find first non-space character at or before offset position. + if offset >= len(bs) { + offset = len(bs) - 1 + } else if offset < 0 { + offset = 0 + } + + for offset > 0 && unicode.IsSpace(rune(bs[offset])) { + offset-- + } + + // Find beginning of line containing offset. + begin := offset + + for begin > 0 && !isNewLineChar(bs[begin]) { + begin-- + } + + if isNewLineChar(bs[begin]) { + begin++ + } + + // Find end of line containing offset. + end := offset + + for end < len(bs) && !isNewLineChar(bs[end]) { + end++ + } + + if begin > end { + begin = end + } + + // Extract line and compute index of offset byte in line. + line := bs[begin:end] + index := offset - begin + + return &ParserErrorDetail{ + Line: string(line), + Idx: index, + } +} + +// Lines returns the pretty formatted line output for the error details. +func (d ParserErrorDetail) Lines() []string { + line := strings.TrimLeft(d.Line, "\t") // remove leading tabs + tabCount := len(d.Line) - len(line) + return []string{line, strings.Repeat(" ", d.Idx-tabCount) + "^"} +} + +func isNewLineChar(b byte) bool { + return b == '\r' || b == '\n' +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser_internal.go b/vendor/github.com/open-policy-agent/opa/ast/parser_internal.go new file mode 100644 index 000000000..f4e32688d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/parser_internal.go @@ -0,0 +1,620 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file.op + +package ast + +import ( + "bytes" + "encoding/json" + "fmt" + "math/big" +) + +const ( + // commentsKey is the global map key for the comments slice. + commentsKey = "comments" + + // filenameKey is the global map key for the filename. + filenameKey = "filename" +) + +type program struct { + buf []interface{} + comments interface{} +} + +type ruleExt struct { + loc *Location + term *Term + body Body +} + +// currentLocation converts the parser context to a Location object. +func currentLocation(c *current) *Location { + return NewLocation(c.text, c.globalStore[filenameKey].(string), c.pos.line, c.pos.col) +} + +func makeProgram(c *current, vals interface{}) (interface{}, error) { + var buf []interface{} + if vals == nil { + return buf, nil + } + ifaceSlice := vals.([]interface{}) + head := ifaceSlice[0] + buf = append(buf, head) + for _, tail := range ifaceSlice[1].([]interface{}) { + stmt := tail.([]interface{})[1] + buf = append(buf, stmt) + } + return program{buf, c.globalStore[commentsKey]}, nil +} + +func makePackage(loc *Location, value interface{}) (interface{}, error) { + // All packages are implicitly declared under the default root document. + term := value.(*Term) + path := Ref{DefaultRootDocument.Copy().SetLocation(term.Location)} + switch v := term.Value.(type) { + case Ref: + // Convert head of package Ref to String because it will be prefixed + // with the root document variable. + head := StringTerm(string(v[0].Value.(Var))).SetLocation(v[0].Location) + tail := v[1:] + if !tail.IsGround() { + return nil, fmt.Errorf("package name cannot contain variables: %v", v) + } + + // We do not allow non-string values in package names. + // Because documents are typically represented as JSON, non-string keys are + // not allowed for now. + // TODO(tsandall): consider special syntax for namespacing under arrays. + for _, p := range tail { + _, ok := p.Value.(String) + if !ok { + return nil, fmt.Errorf("package name cannot contain non-string values: %v", v) + } + } + path = append(path, head) + path = append(path, tail...) + case Var: + s := StringTerm(string(v)).SetLocation(term.Location) + path = append(path, s) + } + pkg := &Package{Location: loc, Path: path} + return pkg, nil +} + +func makeImport(loc *Location, path, alias interface{}) (interface{}, error) { + imp := &Import{} + imp.Location = loc + imp.Path = path.(*Term) + if err := IsValidImportPath(imp.Path.Value); err != nil { + return nil, err + } + if alias == nil { + return imp, nil + } + aliasSlice := alias.([]interface{}) + // Import definition above describes the "alias" slice. We only care about the "Var" element. + imp.Alias = aliasSlice[3].(*Term).Value.(Var) + return imp, nil +} + +func makeDefaultRule(loc *Location, name, operator, value interface{}) (interface{}, error) { + + if string(operator.([]uint8)) == Assign.Infix { + return nil, fmt.Errorf("default rules must use = operator (not := operator)") + } + + term := value.(*Term) + var err error + + vis := NewGenericVisitor(func(x interface{}) bool { + if err != nil { + return true + } + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: // skip closures + return true + case Ref, Var: + err = fmt.Errorf("default rule value cannot contain %v", TypeName(x)) + return true + } + return false + }) + + vis.Walk(term) + + if err != nil { + return nil, err + } + + body := NewBody(NewExpr(BooleanTerm(true).SetLocation(loc))) + + rule := &Rule{ + Location: loc, + Default: true, + Head: &Head{ + Location: loc, + Name: name.(*Term).Value.(Var), + Value: value.(*Term), + }, + Body: body, + } + rule.Body[0].Location = loc + + return []*Rule{rule}, nil +} + +func makeRule(loc *Location, head, rest interface{}) (interface{}, error) { + + if head == nil { + return nil, nil + } + + sl := rest.([]interface{}) + + rules := []*Rule{ + { + Location: loc, + Head: head.(*Head), + Body: sl[0].(Body), + }, + } + + var ordered bool + prev := rules[0] + + for i, elem := range sl[1].([]interface{}) { + + next := elem.([]interface{}) + re := next[1].(ruleExt) + + if rules[0].Head.Assign { + return nil, errElseAssignOperator + } + + if re.term == nil { + if ordered { + return nil, fmt.Errorf("expected 'else' keyword") + } + rules = append(rules, &Rule{ + Location: re.loc, + Head: prev.Head.Copy(), + Body: re.body, + }) + } else { + if (rules[0].Head.DocKind() != CompleteDoc) || (i != 0 && !ordered) { + return nil, fmt.Errorf("unexpected 'else' keyword") + } + ordered = true + curr := &Rule{ + Location: re.loc, + Head: &Head{ + Name: prev.Head.Name, + Args: prev.Head.Args.Copy(), + Value: re.term, + Location: re.term.Location, + }, + Body: re.body, + } + prev.Else = curr + prev = curr + } + } + + return rules, nil +} + +func makeRuleHead(loc *Location, name, args, key, value interface{}) (interface{}, error) { + + head := &Head{} + + head.Location = loc + head.Name = name.(*Term).Value.(Var) + + if args != nil && key != nil { + return nil, fmt.Errorf("partial rules cannot take arguments") + } + + if args != nil { + argSlice := args.([]interface{}) + head.Args = argSlice[3].(Args) + } + + if key != nil { + keySlice := key.([]interface{}) + // Head definition above describes the "key" slice. We care about the "Term" element. + head.Key = keySlice[3].(*Term) + } + + if value != nil { + valueSlice := value.([]interface{}) + operator := string(valueSlice[1].([]uint8)) + + if operator == Assign.Infix { + if head.Key != nil { + return nil, errPartialRuleAssignOperator + } else if len(head.Args) > 0 { + return nil, errFunctionAssignOperator + } + head.Assign = true + } + + // Head definition above describes the "value" slice. We care about the "Term" element. + head.Value = valueSlice[len(valueSlice)-1].(*Term) + } + + if key == nil && value == nil { + head.Value = BooleanTerm(true).SetLocation(head.Location) + } + + if key != nil && value != nil { + switch head.Key.Value.(type) { + case Var, String, Ref: // nop + default: + return nil, fmt.Errorf("object key must be string, var, or ref, not %v", TypeName(head.Key.Value)) + } + } + + return head, nil +} + +func makeArgs(list interface{}) (interface{}, error) { + termSlice := list.([]*Term) + args := make(Args, len(termSlice)) + for i := 0; i < len(args); i++ { + args[i] = termSlice[i] + } + return args, nil +} + +func makeRuleExt(loc *Location, val, b interface{}) (interface{}, error) { + bs := b.([]interface{}) + body := bs[1].(Body) + + if val == nil { + term := BooleanTerm(true) + term.Location = loc + return ruleExt{term.Location, term, body}, nil + } + + vs := val.([]interface{}) + t := vs[3].(*Term) + return ruleExt{loc, t, body}, nil +} + +func makeLiteral(negated, value, with interface{}) (interface{}, error) { + + expr := value.(*Expr) + + expr.Negated = negated.(bool) + + if with != nil { + expr.With = with.([]*With) + } + + return expr, nil +} + +func makeLiteralExpr(loc *Location, lhs, rest interface{}) (interface{}, error) { + + if rest == nil { + if call, ok := lhs.(*Term).Value.(Call); ok { + return NewExpr([]*Term(call)).SetLocation(loc), nil + } + return NewExpr(lhs).SetLocation(loc), nil + } + + termSlice := rest.([]interface{}) + terms := []*Term{ + termSlice[1].(*Term), + lhs.(*Term), + termSlice[3].(*Term), + } + + expr := NewExpr(terms).SetLocation(loc) + + return expr, nil +} + +func makeSomeDeclLiteral(loc *Location, sl interface{}) (interface{}, error) { + symbols := sl.([]*Term) + return NewExpr(&SomeDecl{Location: loc, Symbols: symbols}).SetLocation(loc), nil +} + +func makeSomeDeclSymbols(head interface{}, rest interface{}) (interface{}, error) { + + var symbols []*Term + + symbols = append(symbols, head.(*Term)) + + if sl1, ok := rest.([]interface{}); ok { + for i := range sl1 { + if sl2, ok := sl1[i].([]interface{}); ok { + symbols = append(symbols, sl2[3].(*Term)) + } + } + } + + return symbols, nil +} + +func makeWithKeywordList(head, tail interface{}) (interface{}, error) { + var withs []*With + + if head == nil { + return withs, nil + } + + sl := tail.([]interface{}) + + withs = make([]*With, 0, len(sl)+1) + withs = append(withs, head.(*With)) + + for i := range sl { + withSlice := sl[i].([]interface{}) + withs = append(withs, withSlice[1].(*With)) + } + + return withs, nil +} + +func makeWithKeyword(loc *Location, target, value interface{}) (interface{}, error) { + w := &With{ + Target: target.(*Term), + Value: value.(*Term), + } + return w.SetLocation(loc), nil +} + +func makeExprTerm(loc *Location, lhs, rest interface{}) (interface{}, error) { + + if rest == nil { + return lhs, nil + } + + sl := rest.([]interface{}) + + if len(sl) == 0 { + return lhs, nil + } + + for i := range sl { + termSlice := sl[i].([]interface{}) + call := Call{ + termSlice[1].(*Term), + lhs.(*Term), + termSlice[3].(*Term), + } + lhs = NewTerm(call).SetLocation(loc) + } + + return lhs, nil +} + +func makeCall(loc *Location, operator, args interface{}) (interface{}, error) { + + termSlice := args.([]*Term) + termOperator := operator.(*Term) + + call := make(Call, len(termSlice)+1) + + if _, ok := termOperator.Value.(Var); ok { + termOperator = RefTerm(termOperator).SetLocation(loc) + } + + call[0] = termOperator + + for i := 1; i < len(call); i++ { + call[i] = termSlice[i-1] + } + + return NewTerm(call).SetLocation(loc), nil +} + +func makeBraceEnclosedBody(loc *Location, body interface{}) (interface{}, error) { + if body != nil { + return body, nil + } + return NewBody(NewExpr(ObjectTerm().SetLocation(loc)).SetLocation(loc)), nil +} + +func makeBody(head, tail interface{}, pos int) (interface{}, error) { + + sl := tail.([]interface{}) + body := make(Body, len(sl)+1) + body[0] = head.(*Expr) + + for i := 1; i < len(body); i++ { + body[i] = sl[i-1].([]interface{})[pos].(*Expr) + } + + return body, nil +} + +func makeExprTermList(head, tail interface{}) (interface{}, error) { + + var terms []*Term + + if head == nil { + return terms, nil + } + + sl := tail.([]interface{}) + + terms = make([]*Term, 0, len(sl)+1) + terms = append(terms, head.(*Term)) + + for i := range sl { + termSlice := sl[i].([]interface{}) + terms = append(terms, termSlice[3].(*Term)) + } + + return terms, nil +} + +func makeExprTermPairList(head, tail interface{}) (interface{}, error) { + + var terms [][2]*Term + + if head == nil { + return terms, nil + } + + sl := tail.([]interface{}) + + terms = make([][2]*Term, 0, len(sl)+1) + terms = append(terms, head.([2]*Term)) + + for i := range sl { + termSlice := sl[i].([]interface{}) + terms = append(terms, termSlice[3].([2]*Term)) + } + + return terms, nil +} + +func makeExprTermPair(key, value interface{}) (interface{}, error) { + return [2]*Term{key.(*Term), value.(*Term)}, nil +} + +func makeInfixOperator(loc *Location, text []byte) (interface{}, error) { + op := string(text) + for _, b := range Builtins { + if string(b.Infix) == op { + op = string(b.Name) + } + } + operator := RefTerm(VarTerm(op).SetLocation(loc)).SetLocation(loc) + return operator, nil +} + +func makeArray(loc *Location, list interface{}) (interface{}, error) { + termSlice := list.([]*Term) + return ArrayTerm(termSlice...).SetLocation(loc), nil +} + +func makeObject(loc *Location, list interface{}) (interface{}, error) { + termPairSlice := list.([][2]*Term) + return ObjectTerm(termPairSlice...).SetLocation(loc), nil +} + +func makeSet(loc *Location, list interface{}) (interface{}, error) { + termSlice := list.([]*Term) + return SetTerm(termSlice...).SetLocation(loc), nil +} + +func makeArrayComprehension(loc *Location, head, body interface{}) (interface{}, error) { + return ArrayComprehensionTerm(head.(*Term), body.(Body)).SetLocation(loc), nil +} + +func makeSetComprehension(loc *Location, head, body interface{}) (interface{}, error) { + return SetComprehensionTerm(head.(*Term), body.(Body)).SetLocation(loc), nil +} + +func makeObjectComprehension(loc *Location, head, body interface{}) (interface{}, error) { + pair := head.([2]*Term) + return ObjectComprehensionTerm(pair[0], pair[1], body.(Body)).SetLocation(loc), nil +} + +func makeRef(loc *Location, head, rest interface{}) (interface{}, error) { + + headTerm := head.(*Term) + ifaceSlice := rest.([]interface{}) + + if len(ifaceSlice) == 0 { + return headTerm, nil + } + + ref := make(Ref, len(ifaceSlice)+1) + ref[0] = headTerm + + for i := 1; i < len(ref); i++ { + ref[i] = ifaceSlice[i-1].(*Term) + } + + return NewTerm(ref).SetLocation(loc), nil +} + +func makeRefOperandDot(loc *Location, val interface{}) (interface{}, error) { + return StringTerm(string(val.(*Term).Value.(Var))).SetLocation(loc), nil +} + +func makeVar(loc *Location, text interface{}) (interface{}, error) { + str := string(text.([]byte)) + return VarTerm(str).SetLocation(loc), nil +} + +func makeNumber(loc *Location, text interface{}) (interface{}, error) { + f, ok := new(big.Float).SetString(string(text.([]byte))) + if !ok { + // This indicates the grammar is out-of-sync with what the string + // representation of floating point numbers. This should not be + // possible. + panic("illegal value") + } + + // Put limit on size of exponent to prevent non-linear cost of String() + // function on big.Float from causing denial of service: https://github.com/golang/go/issues/11068 + // + // n == sign * mantissa * 2^exp + // 0.5 <= mantissa < 1.0 + // + // The limit is arbitrary. + exp := f.MantExp(nil) + if exp > 1e5 || exp < -1e5 { + return nil, fmt.Errorf("number too big") + } + + return NumberTerm(json.Number(f.String())).SetLocation(loc), nil +} + +func makeString(loc *Location, text interface{}) (interface{}, error) { + var v string + err := json.Unmarshal(text.([]byte), &v) + return StringTerm(v).SetLocation(loc), err +} + +func makeRawString(loc *Location, text interface{}) (interface{}, error) { + s := string(text.([]byte)) + s = s[1 : len(s)-1] // Trim surrounding quotes. + return StringTerm(s).SetLocation(loc), nil +} + +func makeNonterminatedString(loc *Location, s string) (interface{}, error) { + return StringTerm(s).SetLocation(loc), fmt.Errorf("found non-terminated string literal") +} + +func makeBool(loc *Location, text interface{}) (interface{}, error) { + var term *Term + if string(text.([]byte)) == "true" { + term = BooleanTerm(true) + } else { + term = BooleanTerm(false) + } + return term.SetLocation(loc), nil +} + +func makeNull(loc *Location) (interface{}, error) { + return NullTerm().SetLocation(loc), nil +} + +func makeComments(c *current, text interface{}) (interface{}, error) { + + var buf bytes.Buffer + for _, x := range text.([]interface{}) { + buf.Write(x.([]byte)) + } + + comment := NewComment(buf.Bytes()) + comment.Location = currentLocation(c) + comments := c.globalStore[commentsKey].(map[commentKey]*Comment) + key := commentKey{ + File: comment.Location.File, + Row: comment.Location.Row, + Col: comment.Location.Col, + } + comments[key] = comment + return comment, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/policy.go b/vendor/github.com/open-policy-agent/opa/ast/policy.go new file mode 100644 index 000000000..d66f7f06f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/policy.go @@ -0,0 +1,1352 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "fmt" + "math/rand" + "strings" + "time" + + "github.com/open-policy-agent/opa/util" +) + +// Initialize seed for term hashing. This is intentionally placed before the +// root document sets are constructed to ensure they use the same hash seed as +// subsequent lookups. If the hash seeds are out of sync, lookups will fail. +var hashSeed = rand.New(rand.NewSource(time.Now().UnixNano())) +var hashSeed0 = (uint64(hashSeed.Uint32()) << 32) | uint64(hashSeed.Uint32()) +var hashSeed1 = (uint64(hashSeed.Uint32()) << 32) | uint64(hashSeed.Uint32()) + +// DefaultRootDocument is the default root document. +// +// All package directives inside source files are implicitly prefixed with the +// DefaultRootDocument value. +var DefaultRootDocument = VarTerm("data") + +// InputRootDocument names the document containing query arguments. +var InputRootDocument = VarTerm("input") + +// RootDocumentNames contains the names of top-level documents that can be +// referred to in modules and queries. +var RootDocumentNames = NewSet( + DefaultRootDocument, + InputRootDocument, +) + +// DefaultRootRef is a reference to the root of the default document. +// +// All refs to data in the policy engine's storage layer are prefixed with this ref. +var DefaultRootRef = Ref{DefaultRootDocument} + +// InputRootRef is a reference to the root of the input document. +// +// All refs to query arguments are prefixed with this ref. +var InputRootRef = Ref{InputRootDocument} + +// RootDocumentRefs contains the prefixes of top-level documents that all +// non-local references start with. +var RootDocumentRefs = NewSet( + NewTerm(DefaultRootRef), + NewTerm(InputRootRef), +) + +// SystemDocumentKey is the name of the top-level key that identifies the system +// document. +var SystemDocumentKey = String("system") + +// ReservedVars is the set of names that refer to implicitly ground vars. +var ReservedVars = NewVarSet( + DefaultRootDocument.Value.(Var), + InputRootDocument.Value.(Var), +) + +// Wildcard represents the wildcard variable as defined in the language. +var Wildcard = &Term{Value: Var("_")} + +// WildcardPrefix is the special character that all wildcard variables are +// prefixed with when the statement they are contained in is parsed. +var WildcardPrefix = "$" + +// Keywords contains strings that map to language keywords. +var Keywords = [...]string{ + "not", + "package", + "import", + "as", + "default", + "else", + "with", + "null", + "true", + "false", + "some", +} + +// IsKeyword returns true if s is a language keyword. +func IsKeyword(s string) bool { + for _, x := range Keywords { + if x == s { + return true + } + } + return false +} + +type ( + // Node represents a node in an AST. Nodes may be statements in a policy module + // or elements of an ad-hoc query, expression, etc. + Node interface { + fmt.Stringer + Loc() *Location + SetLoc(*Location) + } + + // Statement represents a single statement in a policy module. + Statement interface { + Node + } +) + +type ( + + // Module represents a collection of policies (defined by rules) + // within a namespace (defined by the package) and optional + // dependencies on external documents (defined by imports). + Module struct { + Package *Package `json:"package"` + Imports []*Import `json:"imports,omitempty"` + Rules []*Rule `json:"rules,omitempty"` + Comments []*Comment `json:"comments,omitempty"` + } + + // Comment contains the raw text from the comment in the definition. + Comment struct { + Text []byte + Location *Location + } + + // Package represents the namespace of the documents produced + // by rules inside the module. + Package struct { + Location *Location `json:"-"` + Path Ref `json:"path"` + } + + // Import represents a dependency on a document outside of the policy + // namespace. Imports are optional. + Import struct { + Location *Location `json:"-"` + Path *Term `json:"path"` + Alias Var `json:"alias,omitempty"` + } + + // Rule represents a rule as defined in the language. Rules define the + // content of documents that represent policy decisions. + Rule struct { + Location *Location `json:"-"` + Default bool `json:"default,omitempty"` + Head *Head `json:"head"` + Body Body `json:"body"` + Else *Rule `json:"else,omitempty"` + + // Module is a pointer to the module containing this rule. If the rule + // was NOT created while parsing/constructing a module, this should be + // left unset. The pointer is not included in any standard operations + // on the rule (e.g., printing, comparison, visiting, etc.) + Module *Module `json:"-"` + } + + // Head represents the head of a rule. + Head struct { + Location *Location `json:"-"` + Name Var `json:"name"` + Args Args `json:"args,omitempty"` + Key *Term `json:"key,omitempty"` + Value *Term `json:"value,omitempty"` + Assign bool `json:"assign,omitempty"` + } + + // Args represents zero or more arguments to a rule. + Args []*Term + + // Body represents one or more expressions contained inside a rule or user + // function. + Body []*Expr + + // Expr represents a single expression contained inside the body of a rule. + Expr struct { + Location *Location `json:"-"` + Generated bool `json:"generated,omitempty"` + Index int `json:"index"` + Negated bool `json:"negated,omitempty"` + Terms interface{} `json:"terms"` + With []*With `json:"with,omitempty"` + } + + // SomeDecl represents a variable declaration statement. The symbols are variables. + SomeDecl struct { + Location *Location `json:"-"` + Symbols []*Term `json:"symbols"` + } + + // With represents a modifier on an expression. + With struct { + Location *Location `json:"-"` + Target *Term `json:"target"` + Value *Term `json:"value"` + } +) + +// Compare returns an integer indicating whether mod is less than, equal to, +// or greater than other. +func (mod *Module) Compare(other *Module) int { + if mod == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := mod.Package.Compare(other.Package); cmp != 0 { + return cmp + } + if cmp := importsCompare(mod.Imports, other.Imports); cmp != 0 { + return cmp + } + return rulesCompare(mod.Rules, other.Rules) +} + +// Copy returns a deep copy of mod. +func (mod *Module) Copy() *Module { + cpy := *mod + cpy.Rules = make([]*Rule, len(mod.Rules)) + for i := range mod.Rules { + cpy.Rules[i] = mod.Rules[i].Copy() + } + cpy.Imports = make([]*Import, len(mod.Imports)) + for i := range mod.Imports { + cpy.Imports[i] = mod.Imports[i].Copy() + } + cpy.Package = mod.Package.Copy() + return &cpy +} + +// Equal returns true if mod equals other. +func (mod *Module) Equal(other *Module) bool { + return mod.Compare(other) == 0 +} + +func (mod *Module) String() string { + buf := []string{} + buf = append(buf, mod.Package.String()) + if len(mod.Imports) > 0 { + buf = append(buf, "") + for _, imp := range mod.Imports { + buf = append(buf, imp.String()) + } + } + if len(mod.Rules) > 0 { + buf = append(buf, "") + for _, rule := range mod.Rules { + buf = append(buf, rule.String()) + } + } + return strings.Join(buf, "\n") +} + +// RuleSet returns a RuleSet containing named rules in the mod. +func (mod *Module) RuleSet(name Var) RuleSet { + rs := NewRuleSet() + for _, rule := range mod.Rules { + if rule.Head.Name.Equal(name) { + rs.Add(rule) + } + } + return rs +} + +// UnmarshalJSON parses bs and stores the result in mod. The rules in the module +// will have their module pointer set to mod. +func (mod *Module) UnmarshalJSON(bs []byte) error { + + // Declare a new type and use a type conversion to avoid recursively calling + // Module#UnmarshalJSON. + type module Module + + if err := util.UnmarshalJSON(bs, (*module)(mod)); err != nil { + return err + } + + WalkRules(mod, func(rule *Rule) bool { + rule.Module = mod + return false + }) + + return nil +} + +// NewComment returns a new Comment object. +func NewComment(text []byte) *Comment { + return &Comment{ + Text: text, + } +} + +// Loc returns the location of the comment in the definition. +func (c *Comment) Loc() *Location { + if c == nil { + return nil + } + return c.Location +} + +// SetLoc sets the location on c. +func (c *Comment) SetLoc(loc *Location) { + c.Location = loc +} + +func (c *Comment) String() string { + return "#" + string(c.Text) +} + +// Equal returns true if this comment equals the other comment. +// Unlike other equality checks on AST nodes, comment equality +// depends on location. +func (c *Comment) Equal(other *Comment) bool { + return c.Location.Equal(other.Location) && bytes.Equal(c.Text, other.Text) +} + +// Compare returns an integer indicating whether pkg is less than, equal to, +// or greater than other. +func (pkg *Package) Compare(other *Package) int { + return Compare(pkg.Path, other.Path) +} + +// Copy returns a deep copy of pkg. +func (pkg *Package) Copy() *Package { + cpy := *pkg + cpy.Path = pkg.Path.Copy() + return &cpy +} + +// Equal returns true if pkg is equal to other. +func (pkg *Package) Equal(other *Package) bool { + return pkg.Compare(other) == 0 +} + +// Loc returns the location of the Package in the definition. +func (pkg *Package) Loc() *Location { + if pkg == nil { + return nil + } + return pkg.Location +} + +// SetLoc sets the location on pkg. +func (pkg *Package) SetLoc(loc *Location) { + pkg.Location = loc +} + +func (pkg *Package) String() string { + if pkg == nil { + return "" + } else if len(pkg.Path) <= 1 { + return fmt.Sprintf("package ", pkg.Path) + } + // Omit head as all packages have the DefaultRootDocument prepended at parse time. + path := make(Ref, len(pkg.Path)-1) + path[0] = VarTerm(string(pkg.Path[1].Value.(String))) + copy(path[1:], pkg.Path[2:]) + return fmt.Sprintf("package %v", path) +} + +// IsValidImportPath returns an error indicating if the import path is invalid. +// If the import path is invalid, err is nil. +func IsValidImportPath(v Value) (err error) { + switch v := v.(type) { + case Var: + if !v.Equal(DefaultRootDocument.Value) && !v.Equal(InputRootDocument.Value) { + return fmt.Errorf("invalid path %v: path must begin with input or data", v) + } + case Ref: + if err := IsValidImportPath(v[0].Value); err != nil { + return fmt.Errorf("invalid path %v: path must begin with input or data", v) + } + for _, e := range v[1:] { + if _, ok := e.Value.(String); !ok { + return fmt.Errorf("invalid path %v: path elements must be strings", v) + } + } + default: + return fmt.Errorf("invalid path %v: path must be ref or var", v) + } + return nil +} + +// Compare returns an integer indicating whether imp is less than, equal to, +// or greater than other. +func (imp *Import) Compare(other *Import) int { + if imp == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := Compare(imp.Path, other.Path); cmp != 0 { + return cmp + } + return Compare(imp.Alias, other.Alias) +} + +// Copy returns a deep copy of imp. +func (imp *Import) Copy() *Import { + cpy := *imp + cpy.Path = imp.Path.Copy() + return &cpy +} + +// Equal returns true if imp is equal to other. +func (imp *Import) Equal(other *Import) bool { + return imp.Compare(other) == 0 +} + +// Loc returns the location of the Import in the definition. +func (imp *Import) Loc() *Location { + if imp == nil { + return nil + } + return imp.Location +} + +// SetLoc sets the location on imp. +func (imp *Import) SetLoc(loc *Location) { + imp.Location = loc +} + +// Name returns the variable that is used to refer to the imported virtual +// document. This is the alias if defined otherwise the last element in the +// path. +func (imp *Import) Name() Var { + if len(imp.Alias) != 0 { + return imp.Alias + } + switch v := imp.Path.Value.(type) { + case Var: + return v + case Ref: + if len(v) == 1 { + return v[0].Value.(Var) + } + return Var(v[len(v)-1].Value.(String)) + } + panic("illegal import") +} + +func (imp *Import) String() string { + buf := []string{"import", imp.Path.String()} + if len(imp.Alias) > 0 { + buf = append(buf, "as "+imp.Alias.String()) + } + return strings.Join(buf, " ") +} + +// Compare returns an integer indicating whether rule is less than, equal to, +// or greater than other. +func (rule *Rule) Compare(other *Rule) int { + if rule == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := rule.Head.Compare(other.Head); cmp != 0 { + return cmp + } + if cmp := util.Compare(rule.Default, other.Default); cmp != 0 { + return cmp + } + if cmp := rule.Body.Compare(other.Body); cmp != 0 { + return cmp + } + return rule.Else.Compare(other.Else) +} + +// Copy returns a deep copy of rule. +func (rule *Rule) Copy() *Rule { + cpy := *rule + cpy.Head = rule.Head.Copy() + cpy.Body = rule.Body.Copy() + if cpy.Else != nil { + cpy.Else = rule.Else.Copy() + } + return &cpy +} + +// Equal returns true if rule is equal to other. +func (rule *Rule) Equal(other *Rule) bool { + return rule.Compare(other) == 0 +} + +// Loc returns the location of the Rule in the definition. +func (rule *Rule) Loc() *Location { + if rule == nil { + return nil + } + return rule.Location +} + +// SetLoc sets the location on rule. +func (rule *Rule) SetLoc(loc *Location) { + rule.Location = loc +} + +// Path returns a ref referring to the document produced by this rule. If rule +// is not contained in a module, this function panics. +func (rule *Rule) Path() Ref { + if rule.Module == nil { + panic("assertion failed") + } + return rule.Module.Package.Path.Append(StringTerm(string(rule.Head.Name))) +} + +func (rule *Rule) String() string { + buf := []string{} + if rule.Default { + buf = append(buf, "default") + } + buf = append(buf, rule.Head.String()) + if !rule.Default { + buf = append(buf, "{") + buf = append(buf, rule.Body.String()) + buf = append(buf, "}") + } + if rule.Else != nil { + buf = append(buf, rule.Else.elseString()) + } + return strings.Join(buf, " ") +} + +func (rule *Rule) elseString() string { + var buf []string + + buf = append(buf, "else") + + value := rule.Head.Value + if value != nil { + buf = append(buf, "=") + buf = append(buf, value.String()) + } + + buf = append(buf, "{") + buf = append(buf, rule.Body.String()) + buf = append(buf, "}") + + if rule.Else != nil { + buf = append(buf, rule.Else.elseString()) + } + + return strings.Join(buf, " ") +} + +// NewHead returns a new Head object. If args are provided, the first will be +// used for the key and the second will be used for the value. +func NewHead(name Var, args ...*Term) *Head { + head := &Head{ + Name: name, + } + if len(args) == 0 { + return head + } + head.Key = args[0] + if len(args) == 1 { + return head + } + head.Value = args[1] + return head +} + +// DocKind represents the collection of document types that can be produced by rules. +type DocKind int + +const ( + // CompleteDoc represents a document that is completely defined by the rule. + CompleteDoc = iota + + // PartialSetDoc represents a set document that is partially defined by the rule. + PartialSetDoc = iota + + // PartialObjectDoc represents an object document that is partially defined by the rule. + PartialObjectDoc = iota +) + +// DocKind returns the type of document produced by this rule. +func (head *Head) DocKind() DocKind { + if head.Key != nil { + if head.Value != nil { + return PartialObjectDoc + } + return PartialSetDoc + } + return CompleteDoc +} + +// Compare returns an integer indicating whether head is less than, equal to, +// or greater than other. +func (head *Head) Compare(other *Head) int { + if head == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if head.Assign && !other.Assign { + return -1 + } else if !head.Assign && other.Assign { + return 1 + } + if cmp := Compare(head.Args, other.Args); cmp != 0 { + return cmp + } + if cmp := Compare(head.Name, other.Name); cmp != 0 { + return cmp + } + if cmp := Compare(head.Key, other.Key); cmp != 0 { + return cmp + } + return Compare(head.Value, other.Value) +} + +// Copy returns a deep copy of head. +func (head *Head) Copy() *Head { + cpy := *head + cpy.Args = head.Args.Copy() + cpy.Key = head.Key.Copy() + cpy.Value = head.Value.Copy() + return &cpy +} + +// Equal returns true if this head equals other. +func (head *Head) Equal(other *Head) bool { + return head.Compare(other) == 0 +} + +func (head *Head) String() string { + var buf []string + if len(head.Args) != 0 { + buf = append(buf, head.Name.String()+head.Args.String()) + } else if head.Key != nil { + buf = append(buf, head.Name.String()+"["+head.Key.String()+"]") + } else { + buf = append(buf, head.Name.String()) + } + if head.Value != nil { + if head.Assign { + buf = append(buf, ":=") + } else { + buf = append(buf, "=") + } + buf = append(buf, head.Value.String()) + } + return strings.Join(buf, " ") +} + +// Vars returns a set of vars found in the head. +func (head *Head) Vars() VarSet { + vis := &VarVisitor{vars: VarSet{}} + // TODO: improve test coverage for this. + if head.Args != nil { + vis.Walk(head.Args) + } + if head.Key != nil { + vis.Walk(head.Key) + } + if head.Value != nil { + vis.Walk(head.Value) + } + return vis.vars +} + +// Loc returns the Location of head. +func (head *Head) Loc() *Location { + if head == nil { + return nil + } + return head.Location +} + +// SetLoc sets the location on head. +func (head *Head) SetLoc(loc *Location) { + head.Location = loc +} + +// Copy returns a deep copy of a. +func (a Args) Copy() Args { + cpy := Args{} + for _, t := range a { + cpy = append(cpy, t.Copy()) + } + return cpy +} + +func (a Args) String() string { + var buf []string + for _, t := range a { + buf = append(buf, t.String()) + } + return "(" + strings.Join(buf, ", ") + ")" +} + +// Loc returns the Location of a. +func (a Args) Loc() *Location { + if len(a) == 0 { + return nil + } + return a[0].Location +} + +// SetLoc sets the location on a. +func (a Args) SetLoc(loc *Location) { + if len(a) != 0 { + a[0].SetLocation(loc) + } +} + +// Vars returns a set of vars that appear in a. +func (a Args) Vars() VarSet { + vis := &VarVisitor{vars: VarSet{}} + vis.Walk(a) + return vis.vars +} + +// NewBody returns a new Body containing the given expressions. The indices of +// the immediate expressions will be reset. +func NewBody(exprs ...*Expr) Body { + for i, expr := range exprs { + expr.Index = i + } + return Body(exprs) +} + +// MarshalJSON returns JSON encoded bytes representing body. +func (body Body) MarshalJSON() ([]byte, error) { + // Serialize empty Body to empty array. This handles both the empty case and the + // nil case (whereas by default the result would be null if body was nil.) + if len(body) == 0 { + return []byte(`[]`), nil + } + return json.Marshal([]*Expr(body)) +} + +// Append adds the expr to the body and updates the expr's index accordingly. +func (body *Body) Append(expr *Expr) { + n := len(*body) + expr.Index = n + *body = append(*body, expr) +} + +// Set sets the expr in the body at the specified position and updates the +// expr's index accordingly. +func (body Body) Set(expr *Expr, pos int) { + body[pos] = expr + expr.Index = pos +} + +// Compare returns an integer indicating whether body is less than, equal to, +// or greater than other. +// +// If body is a subset of other, it is considered less than (and vice versa). +func (body Body) Compare(other Body) int { + minLen := len(body) + if len(other) < minLen { + minLen = len(other) + } + for i := 0; i < minLen; i++ { + if cmp := body[i].Compare(other[i]); cmp != 0 { + return cmp + } + } + if len(body) < len(other) { + return -1 + } + if len(other) < len(body) { + return 1 + } + return 0 +} + +// Copy returns a deep copy of body. +func (body Body) Copy() Body { + cpy := make(Body, len(body)) + for i := range body { + cpy[i] = body[i].Copy() + } + return cpy +} + +// Contains returns true if this body contains the given expression. +func (body Body) Contains(x *Expr) bool { + for _, e := range body { + if e.Equal(x) { + return true + } + } + return false +} + +// Equal returns true if this Body is equal to the other Body. +func (body Body) Equal(other Body) bool { + return body.Compare(other) == 0 +} + +// Hash returns the hash code for the Body. +func (body Body) Hash() int { + s := 0 + for _, e := range body { + s += e.Hash() + } + return s +} + +// IsGround returns true if all of the expressions in the Body are ground. +func (body Body) IsGround() bool { + for _, e := range body { + if !e.IsGround() { + return false + } + } + return true +} + +// Loc returns the location of the Body in the definition. +func (body Body) Loc() *Location { + if len(body) == 0 { + return nil + } + return body[0].Location +} + +// SetLoc sets the location on body. +func (body Body) SetLoc(loc *Location) { + if len(body) != 0 { + body[0].SetLocation(loc) + } +} + +func (body Body) String() string { + var buf []string + for _, v := range body { + buf = append(buf, v.String()) + } + return strings.Join(buf, "; ") +} + +// Vars returns a VarSet containing variables in body. The params can be set to +// control which vars are included. +func (body Body) Vars(params VarVisitorParams) VarSet { + vis := NewVarVisitor().WithParams(params) + vis.Walk(body) + return vis.Vars() +} + +// NewExpr returns a new Expr object. +func NewExpr(terms interface{}) *Expr { + return &Expr{ + Negated: false, + Terms: terms, + Index: 0, + With: nil, + } +} + +// Complement returns a copy of this expression with the negation flag flipped. +func (expr *Expr) Complement() *Expr { + cpy := *expr + cpy.Negated = !cpy.Negated + return &cpy +} + +// Equal returns true if this Expr equals the other Expr. +func (expr *Expr) Equal(other *Expr) bool { + return expr.Compare(other) == 0 +} + +// Compare returns an integer indicating whether expr is less than, equal to, +// or greater than other. +// +// Expressions are compared as follows: +// +// 1. Declarations are always less than other expressions. +// 2. Preceding expression (by Index) is always less than the other expression. +// 3. Non-negated expressions are always less than than negated expressions. +// 4. Single term expressions are always less than built-in expressions. +// +// Otherwise, the expression terms are compared normally. If both expressions +// have the same terms, the modifiers are compared. +func (expr *Expr) Compare(other *Expr) int { + + if expr == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + + o1 := expr.sortOrder() + o2 := other.sortOrder() + if o1 < o2 { + return -1 + } else if o2 < o1 { + return 1 + } + + switch { + case expr.Index < other.Index: + return -1 + case expr.Index > other.Index: + return 1 + } + + switch { + case expr.Negated && !other.Negated: + return 1 + case !expr.Negated && other.Negated: + return -1 + } + + switch t := expr.Terms.(type) { + case *Term: + if cmp := Compare(t.Value, other.Terms.(*Term).Value); cmp != 0 { + return cmp + } + case []*Term: + if cmp := termSliceCompare(t, other.Terms.([]*Term)); cmp != 0 { + return cmp + } + case *SomeDecl: + if cmp := Compare(t, other.Terms.(*SomeDecl)); cmp != 0 { + return cmp + } + } + + return withSliceCompare(expr.With, other.With) +} + +func (expr *Expr) sortOrder() int { + switch expr.Terms.(type) { + case *SomeDecl: + return 0 + case *Term: + return 1 + case []*Term: + return 2 + } + return -1 +} + +// Copy returns a deep copy of expr. +func (expr *Expr) Copy() *Expr { + + cpy := *expr + + switch ts := expr.Terms.(type) { + case *SomeDecl: + cpy.Terms = ts.Copy() + case []*Term: + cpyTs := make([]*Term, len(ts)) + for i := range ts { + cpyTs[i] = ts[i].Copy() + } + cpy.Terms = cpyTs + case *Term: + cpy.Terms = ts.Copy() + } + + cpy.With = make([]*With, len(expr.With)) + for i := range expr.With { + cpy.With[i] = expr.With[i].Copy() + } + + return &cpy +} + +// Hash returns the hash code of the Expr. +func (expr *Expr) Hash() int { + s := expr.Index + switch ts := expr.Terms.(type) { + case *SomeDecl: + s += ts.Hash() + case []*Term: + for _, t := range ts { + s += t.Value.Hash() + } + case *Term: + s += ts.Value.Hash() + } + if expr.Negated { + s++ + } + for _, w := range expr.With { + s += w.Hash() + } + return s +} + +// IncludeWith returns a copy of expr with the with modifier appended. +func (expr *Expr) IncludeWith(target *Term, value *Term) *Expr { + cpy := *expr + cpy.With = append(cpy.With, &With{Target: target, Value: value}) + return &cpy +} + +// NoWith returns a copy of expr where the with modifier has been removed. +func (expr *Expr) NoWith() *Expr { + cpy := *expr + cpy.With = nil + return &cpy +} + +// IsEquality returns true if this is an equality expression. +func (expr *Expr) IsEquality() bool { + return isglobalbuiltin(expr, Var(Equality.Name)) +} + +// IsAssignment returns true if this an assignment expression. +func (expr *Expr) IsAssignment() bool { + return isglobalbuiltin(expr, Var(Assign.Name)) +} + +// IsCall returns true if this expression calls a function. +func (expr *Expr) IsCall() bool { + _, ok := expr.Terms.([]*Term) + return ok +} + +// Operator returns the name of the function or built-in this expression refers +// to. If this expression is not a function call, returns nil. +func (expr *Expr) Operator() Ref { + terms, ok := expr.Terms.([]*Term) + if !ok || len(terms) == 0 { + return nil + } + return terms[0].Value.(Ref) +} + +// Operand returns the term at the zero-based pos. If the expr does not include +// at least pos+1 terms, this function returns nil. +func (expr *Expr) Operand(pos int) *Term { + terms, ok := expr.Terms.([]*Term) + if !ok { + return nil + } + idx := pos + 1 + if idx < len(terms) { + return terms[idx] + } + return nil +} + +// Operands returns the built-in function operands. +func (expr *Expr) Operands() []*Term { + terms, ok := expr.Terms.([]*Term) + if !ok { + return nil + } + return terms[1:] +} + +// IsGround returns true if all of the expression terms are ground. +func (expr *Expr) IsGround() bool { + switch ts := expr.Terms.(type) { + case []*Term: + for _, t := range ts[1:] { + if !t.IsGround() { + return false + } + } + case *Term: + return ts.IsGround() + } + return true +} + +// SetOperator sets the expr's operator and returns the expr itself. If expr is +// not a call expr, this function will panic. +func (expr *Expr) SetOperator(term *Term) *Expr { + expr.Terms.([]*Term)[0] = term + return expr +} + +// SetLocation sets the expr's location and returns the expr itself. +func (expr *Expr) SetLocation(loc *Location) *Expr { + expr.Location = loc + return expr +} + +// Loc returns the Location of expr. +func (expr *Expr) Loc() *Location { + if expr == nil { + return nil + } + return expr.Location +} + +// SetLoc sets the location on expr. +func (expr *Expr) SetLoc(loc *Location) { + expr.SetLocation(loc) +} + +func (expr *Expr) String() string { + var buf []string + if expr.Negated { + buf = append(buf, "not") + } + switch t := expr.Terms.(type) { + case []*Term: + if expr.IsEquality() && validEqAssignArgCount(expr) { + buf = append(buf, fmt.Sprintf("%v %v %v", t[1], Equality.Infix, t[2])) + } else { + buf = append(buf, Call(t).String()) + } + case *Term: + buf = append(buf, t.String()) + case *SomeDecl: + buf = append(buf, t.String()) + } + + for i := range expr.With { + buf = append(buf, expr.With[i].String()) + } + + return strings.Join(buf, " ") +} + +// UnmarshalJSON parses the byte array and stores the result in expr. +func (expr *Expr) UnmarshalJSON(bs []byte) error { + v := map[string]interface{}{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + return unmarshalExpr(expr, v) +} + +// Vars returns a VarSet containing variables in expr. The params can be set to +// control which vars are included. +func (expr *Expr) Vars(params VarVisitorParams) VarSet { + vis := NewVarVisitor().WithParams(params) + vis.Walk(expr) + return vis.Vars() +} + +// NewBuiltinExpr creates a new Expr object with the supplied terms. +// The builtin operator must be the first term. +func NewBuiltinExpr(terms ...*Term) *Expr { + return &Expr{Terms: terms} +} + +func (d *SomeDecl) String() string { + buf := make([]string, len(d.Symbols)) + for i := range buf { + buf[i] = d.Symbols[i].String() + } + return "some " + strings.Join(buf, ", ") +} + +// SetLoc sets the Location on d. +func (d *SomeDecl) SetLoc(loc *Location) { + d.Location = loc +} + +// Loc returns the Location of d. +func (d *SomeDecl) Loc() *Location { + return d.Location +} + +// Copy returns a deep copy of d. +func (d *SomeDecl) Copy() *SomeDecl { + cpy := *d + cpy.Symbols = termSliceCopy(d.Symbols) + return &cpy +} + +// Compare returns an integer indicating whether d is less than, equal to, or +// greater than other. +func (d *SomeDecl) Compare(other *SomeDecl) int { + return termSliceCompare(d.Symbols, other.Symbols) +} + +// Hash returns a hash code of d. +func (d *SomeDecl) Hash() int { + return termSliceHash(d.Symbols) +} + +func (w *With) String() string { + return "with " + w.Target.String() + " as " + w.Value.String() +} + +// Equal returns true if this With is equals the other With. +func (w *With) Equal(other *With) bool { + return Compare(w, other) == 0 +} + +// Compare returns an integer indicating whether w is less than, equal to, or +// greater than other. +func (w *With) Compare(other *With) int { + if w == nil { + if other == nil { + return 0 + } + return -1 + } else if other == nil { + return 1 + } + if cmp := Compare(w.Target, other.Target); cmp != 0 { + return cmp + } + return Compare(w.Value, other.Value) +} + +// Copy returns a deep copy of w. +func (w *With) Copy() *With { + cpy := *w + cpy.Value = w.Value.Copy() + cpy.Target = w.Target.Copy() + return &cpy +} + +// Hash returns the hash code of the With. +func (w With) Hash() int { + return w.Target.Hash() + w.Value.Hash() +} + +// SetLocation sets the location on w. +func (w *With) SetLocation(loc *Location) *With { + w.Location = loc + return w +} + +// Loc returns the Location of w. +func (w *With) Loc() *Location { + if w == nil { + return nil + } + return w.Location +} + +// SetLoc sets the location on w. +func (w *With) SetLoc(loc *Location) { + w.Location = loc +} + +// RuleSet represents a collection of rules that produce a virtual document. +type RuleSet []*Rule + +// NewRuleSet returns a new RuleSet containing the given rules. +func NewRuleSet(rules ...*Rule) RuleSet { + rs := make(RuleSet, 0, len(rules)) + for _, rule := range rules { + rs.Add(rule) + } + return rs +} + +// Add inserts the rule into rs. +func (rs *RuleSet) Add(rule *Rule) { + for _, exist := range *rs { + if exist.Equal(rule) { + return + } + } + *rs = append(*rs, rule) +} + +// Contains returns true if rs contains rule. +func (rs RuleSet) Contains(rule *Rule) bool { + for i := range rs { + if rs[i].Equal(rule) { + return true + } + } + return false +} + +// Diff returns a new RuleSet containing rules in rs that are not in other. +func (rs RuleSet) Diff(other RuleSet) RuleSet { + result := NewRuleSet() + for i := range rs { + if !other.Contains(rs[i]) { + result.Add(rs[i]) + } + } + return result +} + +// Equal returns true if rs equals other. +func (rs RuleSet) Equal(other RuleSet) bool { + return len(rs.Diff(other)) == 0 && len(other.Diff(rs)) == 0 +} + +// Merge returns a ruleset containing the union of rules from rs an other. +func (rs RuleSet) Merge(other RuleSet) RuleSet { + result := NewRuleSet() + for i := range rs { + result.Add(rs[i]) + } + for i := range other { + result.Add(other[i]) + } + return result +} + +func (rs RuleSet) String() string { + buf := make([]string, 0, len(rs)) + for _, rule := range rs { + buf = append(buf, rule.String()) + } + return "{" + strings.Join(buf, ", ") + "}" +} + +type ruleSlice []*Rule + +func (s ruleSlice) Less(i, j int) bool { return Compare(s[i], s[j]) < 0 } +func (s ruleSlice) Swap(i, j int) { x := s[i]; s[i] = s[j]; s[j] = x } +func (s ruleSlice) Len() int { return len(s) } + +// Returns true if the equality or assignment expression referred to by expr +// has a valid number of arguments. +func validEqAssignArgCount(expr *Expr) bool { + return len(expr.Operands()) == 2 +} + +// this function checks if the expr refers to a non-namespaced (global) built-in +// function like eq, gt, plus, etc. +func isglobalbuiltin(expr *Expr, name Var) bool { + terms, ok := expr.Terms.([]*Term) + if !ok { + return false + } + + // NOTE(tsandall): do not use Term#Equal or Value#Compare to avoid + // allocation here. + ref, ok := terms[0].Value.(Ref) + if !ok || len(ref) != 1 { + return false + } else if head, ok := ref[0].Value.(Var); !ok { + return false + } else { + return head.Equal(name) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/pretty.go b/vendor/github.com/open-policy-agent/opa/ast/pretty.go new file mode 100644 index 000000000..b4f05ad50 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/pretty.go @@ -0,0 +1,82 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "io" + "strings" +) + +// Pretty writes a pretty representation of the AST rooted at x to w. +// +// This is function is intended for debug purposes when inspecting ASTs. +func Pretty(w io.Writer, x interface{}) { + pp := &prettyPrinter{ + depth: -1, + w: w, + } + NewBeforeAfterVisitor(pp.Before, pp.After).Walk(x) +} + +type prettyPrinter struct { + depth int + w io.Writer +} + +func (pp *prettyPrinter) Before(x interface{}) bool { + switch x.(type) { + case *Term: + default: + pp.depth++ + } + + switch x := x.(type) { + case *Term: + return false + case Args: + if len(x) == 0 { + return false + } + pp.writeType(x) + case *Expr: + extras := []string{} + if x.Negated { + extras = append(extras, "negated") + } + extras = append(extras, fmt.Sprintf("index=%d", x.Index)) + pp.writeIndent("%v %v", TypeName(x), strings.Join(extras, " ")) + case Null, Boolean, Number, String, Var: + pp.writeValue(x) + default: + pp.writeType(x) + } + return false +} + +func (pp *prettyPrinter) After(x interface{}) { + switch x.(type) { + case *Term: + default: + pp.depth-- + } +} + +func (pp *prettyPrinter) writeValue(x interface{}) { + pp.writeIndent(fmt.Sprint(x)) +} + +func (pp *prettyPrinter) writeType(x interface{}) { + pp.writeIndent(TypeName(x)) +} + +func (pp *prettyPrinter) writeIndent(f string, a ...interface{}) { + pad := strings.Repeat(" ", pp.depth) + pp.write(pad+f, a...) +} + +func (pp *prettyPrinter) write(f string, a ...interface{}) { + fmt.Fprintf(pp.w, f+"\n", a...) +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/rego.peg b/vendor/github.com/open-policy-agent/opa/ast/rego.peg new file mode 100644 index 000000000..71d04e727 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/rego.peg @@ -0,0 +1,311 @@ +{ +package ast +} + +Program <- _ vals:(Stmt (ws Stmt)*)? _ EOF { + return makeProgram(c, vals) +} + +Stmt <- val:(Package / Import / Rules / Body / Comment) { + return val, nil +} + +Package <- "package" ws val:(Ref / Var) { + return makePackage(currentLocation(c), val) +} + +Import <- "import" ws path:(Ref / Var) alias:(ws "as" ws Var)? { + return makeImport(currentLocation(c), path, alias) +} + +Rules <- DefaultRules / NormalRules + +DefaultRules <- "default" ws name:Var _ operator:( ":=" / "=" ) _ value:Term { + return makeDefaultRule(currentLocation(c), name, operator, value) +} + +NormalRules <- head:(PartialRuleHead / RuleHead) _ rest:(NonEmptyBraceEnclosedBody ( _ RuleExt)* ) { + return makeRule(currentLocation(c), head, rest) +} + +PartialRuleHead <- name:Var args:( _ "(" _ Args _ ")" _ ) value:( _ ( ":=" / "=" ) _ ExprTerm )? { + return makeRuleHead(currentLocation(c), name, args, nil, value) +} + +RuleHead <- name:Var key:( _ "[" _ ExprTerm _ "]" _ )? value:( _ ( ":=" / "=" ) _ ExprTerm )? { + return makeRuleHead(currentLocation(c), name, nil, key, value) +} + +Args <- list:ExprTermList { + return makeArgs(list) +} + +Else <- "else" value:( _ "=" _ Term )? body:( _ NonEmptyBraceEnclosedBody ) { + return makeRuleExt(currentLocation(c), value, body) +} + +RuleDup <- b:NonEmptyBraceEnclosedBody { + return ruleExt{loc: currentLocation(c), body: b.(Body)}, nil +} + +RuleExt <- Else / RuleDup + +Body <- NonWhitespaceBody / BraceEnclosedBody + +NonEmptyBraceEnclosedBody <- "{" _ val:WhitespaceBody? _ "}" { + if val == nil { + return NewBody(), fmt.Errorf("found empty body") + } + return val, nil +} + +BraceEnclosedBody <- "{" _ val:WhitespaceBody? _ "}" { + return makeBraceEnclosedBody(currentLocation(c), val) +} + +WhitespaceBody <- head:Literal tail:(WhitespaceLiteralSeparator _ Literal)* { + return makeBody(head, tail, 2) +} + +NonWhitespaceBody <- head:Literal tail:( _ NonWhitespaceLiteralSeparator _ Literal)* { + return makeBody(head, tail, 3) +} + +WhitespaceLiteralSeparator <- [ \t]* ((NonWhitespaceLiteralSeparator Comment?) / (Comment? [\r\n])) + +NonWhitespaceLiteralSeparator <- ";" + +Literal <- TermExpr / SomeDecl + +SomeDecl <- "some" ws symbols:SomeDeclList { + return makeSomeDeclLiteral(currentLocation(c), symbols) +} + +SomeDeclList <- head:Var rest:( _ ',' _ Var)* { + return makeSomeDeclSymbols(head, rest) +} + +TermExpr <- negated:NotKeyword? value:LiteralExpr with:WithKeywordList? { + return makeLiteral(negated, value, with) +} + +LiteralExpr <- lhs:ExprTerm rest:( _ LiteralExprOperator _ ExprTerm)? { + return makeLiteralExpr(currentLocation(c), lhs, rest) +} + +LiteralExprOperator <- val:( ":=" / "=" ) { + return makeInfixOperator(currentLocation(c), c.text) +} + +NotKeyword <- val:("not" ws)? { + return val != nil, nil +} + +WithKeywordList <- ws head:WithKeyword rest:( ws WithKeyword )* { + return makeWithKeywordList(head, rest) +} + +WithKeyword <- "with" ws target:ExprTerm ws "as" ws value:ExprTerm { + return makeWithKeyword(currentLocation(c), target, value) +} + +ExprTerm <- lhs:RelationExpr rest:( _ RelationOperator _ RelationExpr )* { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +ExprTermPairList <- head:ExprTermPair? tail:( _ ',' _ ExprTermPair )* _ ","? { + return makeExprTermPairList(head, tail) +} + +ExprTermList <- head:ExprTerm? tail:( _ ',' _ ExprTerm )* _ ","? { + return makeExprTermList(head, tail) +} + +ExprTermPair <- key:ExprTerm _ ':' _ value:ExprTerm { + return makeExprTermPair(key, value) +} + +RelationOperator <- val:("==" / "!=" / "<=" / ">=" / ">" / "<") { + return makeInfixOperator(currentLocation(c), c.text) +} + +RelationExpr <- lhs:BitwiseOrExpr rest:( _ BitwiseOrOperator _ BitwiseOrExpr)* { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +BitwiseOrOperator <- val:"|" { + return makeInfixOperator(currentLocation(c), c.text) +} + +BitwiseOrExpr <- lhs:BitwiseAndExpr rest:( _ BitwiseAndOperator _ BitwiseAndExpr)* { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +BitwiseAndOperator <- val:"&" { + return makeInfixOperator(currentLocation(c), c.text) +} + +BitwiseAndExpr <- lhs:ArithExpr rest:( _ ArithOperator _ ArithExpr)* { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +ArithOperator <- val:("+" / "-") { + return makeInfixOperator(currentLocation(c), c.text) +} + +ArithExpr <- lhs:FactorExpr rest:( _ FactorOperator _ FactorExpr )* { + return makeExprTerm(currentLocation(c), lhs, rest) +} + +FactorOperator <- val:("*" / "/" / "%"){ + return makeInfixOperator(currentLocation(c), c.text) +} + +FactorExpr <- ( "(" _ expr:ExprTerm _ ")" ) { + return expr, nil +} / term:Term { + return term, nil +} + +Call <- operator:(Ref / Var) "(" _ args:ExprTermList _ ")" { + return makeCall(currentLocation(c), operator, args) +} + +Term <- val:( Comprehension / Composite / Scalar / Call / Var ) refs:RefOperand* { + return makeRef(currentLocation(c), val, refs) +} + +TermPair <- key:Term _ ":" _ value:Term { + return makeExprTermPair(key, value) +} + +Comprehension <- ArrayComprehension / ObjectComprehension / SetComprehension + +ArrayComprehension <- "[" _ head:Term _ "|" _ body:WhitespaceBody _ "]" { + return makeArrayComprehension(currentLocation(c), head, body) +} + +ObjectComprehension <- "{" _ head:TermPair _ "|" _ body:WhitespaceBody _ "}" { + return makeObjectComprehension(currentLocation(c), head, body) +} + +SetComprehension <- "{" _ head:Term _ "|" _ body:WhitespaceBody _ "}" { + return makeSetComprehension(currentLocation(c), head, body) +} + +Composite <- Object / Array / Set + +Scalar <- Number / String / Bool / Null + +Object <- '{' _ list:ExprTermPairList _ '}' { + return makeObject(currentLocation(c), list) +} + +Array <- '[' _ list:ExprTermList _ ']' { + return makeArray(currentLocation(c), list) +} + +Set <- SetEmpty / SetNonEmpty + +SetEmpty <- "set(" _ ")" { + var empty []*Term + return makeSet(currentLocation(c), empty) +} + +SetNonEmpty <- '{' _ list:ExprTermList _ '}' { + return makeSet(currentLocation(c), list) +} + +Ref <- head:(Composite / Var) rest:RefOperand+ { + return makeRef(currentLocation(c), head, rest) +} + +RefOperand <- RefOperandDot / RefOperandCanonical + +RefOperandDot <- "." val:Var { + return makeRefOperandDot(currentLocation(c), val) +} + +RefOperandCanonical <- "[" val:ExprTerm "]" { + return val, nil +} + +Var <- val:VarChecked { + return val.([]interface{})[0], nil +} + +VarChecked <- val:VarUnchecked !{ + return IsKeyword(string(val.(*Term).Value.(Var))), nil +} + +VarUnchecked <- VarStart VarChar* { + return makeVar(currentLocation(c), c.text) +} + +Number <- '-'? ( Float / Integer ) { + return makeNumber(currentLocation(c), c.text) +} + +Float <- ExponentFloat / PointFloat + +ExponentFloat <- ( PointFloat / Integer ) Exponent + +PointFloat <- Integer? Fraction + +Fraction <- '.' DecimalDigit+ + +Exponent <- 'e'i [+-]? DecimalDigit+ + +Integer <- '0' / ( NonZeroDecimalDigit DecimalDigit* ) + +String <- QuotedString / RawString + +QuotedString <- '"' Char* '"' { + return makeString(currentLocation(c), c.text) +} / '"' Char* !'"' { + return makeNonterminatedString(currentLocation(c), string(c.text)) +} + +RawString <- '`' [^`]* '`' { + return makeRawString(currentLocation(c), c.text) +} + +Bool <- val:("true" / "false") !VarChar { + return makeBool(currentLocation(c), c.text) +} + +Null <- "null" !VarChar { + return makeNull(currentLocation(c)) +} + +VarStart <- AsciiLetter + +VarChar <- AsciiLetter / DecimalDigit + +AsciiLetter <- [A-Za-z_] + +Char <- ( !EscapedChar . ) / ( '\\' EscapeSequence ) + +EscapedChar <- [\x00-\x1f"\\] + +EscapeSequence <- SingleCharEscape / UnicodeEscape + +SingleCharEscape <- [ " \\ / b f n r t ] + +UnicodeEscape <- 'u' HexDigit HexDigit HexDigit HexDigit + +DecimalDigit <- [0-9] + +NonZeroDecimalDigit <- [1-9] + +HexDigit <- [0-9a-fA-F] + +ws "whitespace" <- [ \t\r\n]+ + +_ "whitespace" <- ( [ \t\r\n] / Comment )* + +Comment <- [ \t]* "#" text:[^\r\n]* { + return makeComments(c, text) +} + +EOF <- !. diff --git a/vendor/github.com/open-policy-agent/opa/ast/strings.go b/vendor/github.com/open-policy-agent/opa/ast/strings.go new file mode 100644 index 000000000..8f9928017 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/strings.go @@ -0,0 +1,15 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "reflect" + "strings" +) + +// TypeName returns a human readable name for the AST element type. +func TypeName(x interface{}) string { + return strings.ToLower(reflect.Indirect(reflect.ValueOf(x)).Type().Name()) +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/term.go b/vendor/github.com/open-policy-agent/opa/ast/term.go new file mode 100644 index 000000000..b6137d2c0 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/term.go @@ -0,0 +1,2572 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "math/big" + "net/url" + "regexp" + "sort" + "strconv" + "strings" + + "github.com/OneOfOne/xxhash" + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/util" +) + +var errFindNotFound = fmt.Errorf("find: not found") + +// Location records a position in source code +type Location struct { + Text []byte `json:"-"` // The original text fragment from the source. + File string `json:"file"` // The name of the source file (which may be empty). + Row int `json:"row"` // The line in the source. + Col int `json:"col"` // The column in the row. +} + +// NewLocation returns a new Location object. +func NewLocation(text []byte, file string, row int, col int) *Location { + return &Location{Text: text, File: file, Row: row, Col: col} +} + +// Equal checks if two locations are equal to each other. +func (loc *Location) Equal(other *Location) bool { + return bytes.Equal(loc.Text, other.Text) && + loc.File == other.File && + loc.Row == other.Row && + loc.Col == other.Col +} + +// Errorf returns a new error value with a message formatted to include the location +// info (e.g., line, column, filename, etc.) +func (loc *Location) Errorf(f string, a ...interface{}) error { + return errors.New(loc.Format(f, a...)) +} + +// Wrapf returns a new error value that wraps an existing error with a message formatted +// to include the location info (e.g., line, column, filename, etc.) +func (loc *Location) Wrapf(err error, f string, a ...interface{}) error { + return errors.Wrap(err, loc.Format(f, a...)) +} + +// Format returns a formatted string prefixed with the location information. +func (loc *Location) Format(f string, a ...interface{}) string { + if len(loc.File) > 0 { + f = fmt.Sprintf("%v:%v: %v", loc.File, loc.Row, f) + } else { + f = fmt.Sprintf("%v:%v: %v", loc.Row, loc.Col, f) + } + return fmt.Sprintf(f, a...) +} + +func (loc *Location) String() string { + if len(loc.File) > 0 { + return fmt.Sprintf("%v:%v", loc.File, loc.Row) + } + if len(loc.Text) > 0 { + return string(loc.Text) + } + return fmt.Sprintf("%v:%v", loc.Row, loc.Col) +} + +// Compare returns -1, 0, or 1 to indicate if this loc is less than, equal to, +// or greater than the other. Comparison is performed on the file, row, and +// column of the Location (but not on the text.) Nil locations are greater than +// non-nil locations. +func (loc *Location) Compare(other *Location) int { + if loc == nil && other == nil { + return 0 + } else if loc == nil { + return 1 + } else if other == nil { + return -1 + } else if loc.File < other.File { + return -1 + } else if loc.File > other.File { + return 1 + } else if loc.Row < other.Row { + return -1 + } else if loc.Row > other.Row { + return 1 + } else if loc.Col < other.Col { + return -1 + } else if loc.Col > other.Col { + return 1 + } + return 0 +} + +// Value declares the common interface for all Term values. Every kind of Term value +// in the language is represented as a type that implements this interface: +// +// - Null, Boolean, Number, String +// - Object, Array, Set +// - Variables, References +// - Array, Set, and Object Comprehensions +// - Calls +type Value interface { + Compare(other Value) int // Compare returns <0, 0, or >0 if this Value is less than, equal to, or greater than other, respectively. + Find(path Ref) (Value, error) // Find returns value referred to by path or an error if path is not found. + Hash() int // Returns hash code of the value. + IsGround() bool // IsGround returns true if this value is not a variable or contains no variables. + String() string // String returns a human readable string representation of the value. +} + +// InterfaceToValue converts a native Go value x to a Value. +func InterfaceToValue(x interface{}) (Value, error) { + switch x := x.(type) { + case nil: + return Null{}, nil + case bool: + return Boolean(x), nil + case json.Number: + return Number(x), nil + case int64: + return int64Number(x), nil + case float64: + return floatNumber(x), nil + case int: + return intNumber(x), nil + case string: + return String(x), nil + case []interface{}: + r := make(Array, 0, len(x)) + for _, e := range x { + e, err := InterfaceToValue(e) + if err != nil { + return nil, err + } + r = append(r, &Term{Value: e}) + } + return r, nil + case map[string]interface{}: + r := newobject(len(x)) + for k, v := range x { + k, err := InterfaceToValue(k) + if err != nil { + return nil, err + } + v, err := InterfaceToValue(v) + if err != nil { + return nil, err + } + r.Insert(NewTerm(k), NewTerm(v)) + } + return r, nil + case map[string]string: + r := newobject(len(x)) + for k, v := range x { + k, err := InterfaceToValue(k) + if err != nil { + return nil, err + } + v, err := InterfaceToValue(v) + if err != nil { + return nil, err + } + r.Insert(NewTerm(k), NewTerm(v)) + } + return r, nil + default: + return nil, fmt.Errorf("ast: illegal value: %T", x) + } +} + +// ValueFromReader returns an AST value from a JSON serialized value in the reader. +func ValueFromReader(r io.Reader) (Value, error) { + var x interface{} + if err := util.NewJSONDecoder(r).Decode(&x); err != nil { + return nil, err + } + return InterfaceToValue(x) +} + +// As converts v into a Go native type referred to by x. +func As(v Value, x interface{}) error { + return util.NewJSONDecoder(bytes.NewBufferString(v.String())).Decode(x) +} + +// Resolver defines the interface for resolving references to native Go values. +type Resolver interface { + Resolve(ref Ref) (value interface{}, err error) +} + +// ValueResolver defines the interface for resolving references to AST values. +type ValueResolver interface { + Resolve(ref Ref) (value Value, err error) +} + +// UnknownValueErr indicates a ValueResolver was unable to resolve a reference +// because the reference refers to an unknown value. +type UnknownValueErr struct{} + +func (UnknownValueErr) Error() string { + return "unknown value" +} + +// IsUnknownValueErr returns true if the err is an UnknownValueErr. +func IsUnknownValueErr(err error) bool { + _, ok := err.(UnknownValueErr) + return ok +} + +type illegalResolver struct{} + +func (illegalResolver) Resolve(ref Ref) (interface{}, error) { + return nil, fmt.Errorf("illegal value: %v", ref) +} + +// ValueToInterface returns the Go representation of an AST value. The AST +// value should not contain any values that require evaluation (e.g., vars, +// comprehensions, etc.) +func ValueToInterface(v Value, resolver Resolver) (interface{}, error) { + switch v := v.(type) { + case Null: + return nil, nil + case Boolean: + return bool(v), nil + case Number: + return json.Number(v), nil + case String: + return string(v), nil + case Array: + buf := []interface{}{} + for _, x := range v { + x1, err := ValueToInterface(x.Value, resolver) + if err != nil { + return nil, err + } + buf = append(buf, x1) + } + return buf, nil + case Object: + buf := map[string]interface{}{} + err := v.Iter(func(k, v *Term) error { + ki, err := ValueToInterface(k.Value, resolver) + if err != nil { + return err + } + asStr, stringKey := ki.(string) + if !stringKey { + return fmt.Errorf("object value has non-string key (%T)", ki) + } + vi, err := ValueToInterface(v.Value, resolver) + if err != nil { + return err + } + buf[asStr] = vi + return nil + }) + if err != nil { + return nil, err + } + return buf, nil + case Set: + buf := []interface{}{} + err := v.Iter(func(x *Term) error { + x1, err := ValueToInterface(x.Value, resolver) + if err != nil { + return err + } + buf = append(buf, x1) + return nil + }) + if err != nil { + return nil, err + } + return buf, nil + case Ref: + return resolver.Resolve(v) + default: + return nil, fmt.Errorf("%v requires evaluation", TypeName(v)) + } +} + +// JSON returns the JSON representation of v. The value must not contain any +// refs or terms that require evaluation (e.g., vars, comprehensions, etc.) +func JSON(v Value) (interface{}, error) { + return ValueToInterface(v, illegalResolver{}) +} + +// MustInterfaceToValue converts a native Go value x to a Value. If the +// conversion fails, this function will panic. This function is mostly for test +// purposes. +func MustInterfaceToValue(x interface{}) Value { + v, err := InterfaceToValue(x) + if err != nil { + panic(err) + } + return v +} + +// Term is an argument to a function. +type Term struct { + Value Value `json:"value"` // the value of the Term as represented in Go + Location *Location `json:"-"` // the location of the Term in the source +} + +// NewTerm returns a new Term object. +func NewTerm(v Value) *Term { + return &Term{ + Value: v, + } +} + +// SetLocation updates the term's Location and returns the term itself. +func (term *Term) SetLocation(loc *Location) *Term { + term.Location = loc + return term +} + +// Loc returns the Location of term. +func (term *Term) Loc() *Location { + if term == nil { + return nil + } + return term.Location +} + +// SetLoc sets the location on term. +func (term *Term) SetLoc(loc *Location) { + term.SetLocation(loc) +} + +// Copy returns a deep copy of term. +func (term *Term) Copy() *Term { + + if term == nil { + return nil + } + + cpy := *term + + switch v := term.Value.(type) { + case Null, Boolean, Number, String, Var: + cpy.Value = v + case Ref: + cpy.Value = v.Copy() + case Array: + cpy.Value = v.Copy() + case Set: + cpy.Value = v.Copy() + case Object: + cpy.Value = v.Copy() + case *ArrayComprehension: + cpy.Value = v.Copy() + case *ObjectComprehension: + cpy.Value = v.Copy() + case *SetComprehension: + cpy.Value = v.Copy() + case Call: + cpy.Value = v.Copy() + } + + return &cpy +} + +// Equal returns true if this term equals the other term. Equality is +// defined for each kind of term. +func (term *Term) Equal(other *Term) bool { + if term == nil && other != nil { + return false + } + if term != nil && other == nil { + return false + } + if term == other { + return true + } + + // TODO(tsandall): This early-exit avoids allocations for types that have + // Equal() functions that just use == underneath. We should revisit the + // other types and implement Equal() functions that do not require + // allocations. + switch v := term.Value.(type) { + case Null: + return v.Equal(other.Value) + case Boolean: + return v.Equal(other.Value) + case Number: + return v.Equal(other.Value) + case String: + return v.Equal(other.Value) + case Var: + return v.Equal(other.Value) + } + + return term.Value.Compare(other.Value) == 0 +} + +// Get returns a value referred to by name from the term. +func (term *Term) Get(name *Term) *Term { + switch v := term.Value.(type) { + case Array: + return v.Get(name) + case Object: + return v.Get(name) + case Set: + if v.Contains(name) { + return name + } + } + return nil +} + +// Hash returns the hash code of the Term's value. +func (term *Term) Hash() int { + return term.Value.Hash() +} + +// IsGround returns true if this terms' Value is ground. +func (term *Term) IsGround() bool { + return term.Value.IsGround() +} + +// MarshalJSON returns the JSON encoding of the term. +// +// Specialized marshalling logic is required to include a type hint for Value. +func (term *Term) MarshalJSON() ([]byte, error) { + d := map[string]interface{}{ + "type": TypeName(term.Value), + "value": term.Value, + } + return json.Marshal(d) +} + +func (term *Term) String() string { + return term.Value.String() +} + +// UnmarshalJSON parses the byte array and stores the result in term. +// Specialized unmarshalling is required to handle Value. +func (term *Term) UnmarshalJSON(bs []byte) error { + v := map[string]interface{}{} + if err := util.UnmarshalJSON(bs, &v); err != nil { + return err + } + val, err := unmarshalValue(v) + if err != nil { + return err + } + term.Value = val + return nil +} + +// Vars returns a VarSet with variables contained in this term. +func (term *Term) Vars() VarSet { + vis := &VarVisitor{vars: VarSet{}} + vis.Walk(term) + return vis.vars +} + +// IsConstant returns true if the AST value is constant. +func IsConstant(v Value) bool { + found := false + vis := GenericVisitor{ + func(x interface{}) bool { + switch x.(type) { + case Var, Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension, Call: + found = true + return true + } + return false + }, + } + vis.Walk(v) + return !found +} + +// IsComprehension returns true if the supplied value is a comprehension. +func IsComprehension(x Value) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + } + return false +} + +// ContainsRefs returns true if the Value v contains refs. +func ContainsRefs(v interface{}) bool { + found := false + WalkRefs(v, func(r Ref) bool { + found = true + return found + }) + return found +} + +// ContainsComprehensions returns true if the Value v contains comprehensions. +func ContainsComprehensions(v interface{}) bool { + found := false + WalkClosures(v, func(x interface{}) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + found = true + return found + } + return found + }) + return found +} + +// IsScalar returns true if the AST value is a scalar. +func IsScalar(v Value) bool { + switch v.(type) { + case String: + return true + case Number: + return true + case Boolean: + return true + case Null: + return true + } + return false +} + +// Null represents the null value defined by JSON. +type Null struct{} + +// NullTerm creates a new Term with a Null value. +func NullTerm() *Term { + return &Term{Value: Null{}} +} + +// Equal returns true if the other term Value is also Null. +func (null Null) Equal(other Value) bool { + switch other.(type) { + case Null: + return true + default: + return false + } +} + +// Compare compares null to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (null Null) Compare(other Value) int { + return Compare(null, other) +} + +// Find returns the current value or a not found error. +func (null Null) Find(path Ref) (Value, error) { + if len(path) == 0 { + return null, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (null Null) Hash() int { + return 0 +} + +// IsGround always returns true. +func (null Null) IsGround() bool { + return true +} + +func (null Null) String() string { + return "null" +} + +// Boolean represents a boolean value defined by JSON. +type Boolean bool + +// BooleanTerm creates a new Term with a Boolean value. +func BooleanTerm(b bool) *Term { + return &Term{Value: Boolean(b)} +} + +// Equal returns true if the other Value is a Boolean and is equal. +func (bol Boolean) Equal(other Value) bool { + switch other := other.(type) { + case Boolean: + return bol == other + default: + return false + } +} + +// Compare compares bol to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (bol Boolean) Compare(other Value) int { + return Compare(bol, other) +} + +// Find returns the current value or a not found error. +func (bol Boolean) Find(path Ref) (Value, error) { + if len(path) == 0 { + return bol, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (bol Boolean) Hash() int { + if bol { + return 1 + } + return 0 +} + +// IsGround always returns true. +func (bol Boolean) IsGround() bool { + return true +} + +func (bol Boolean) String() string { + return strconv.FormatBool(bool(bol)) +} + +// Number represents a numeric value as defined by JSON. +type Number json.Number + +// NumberTerm creates a new Term with a Number value. +func NumberTerm(n json.Number) *Term { + return &Term{Value: Number(n)} +} + +// IntNumberTerm creates a new Term with an integer Number value. +func IntNumberTerm(i int) *Term { + return &Term{Value: Number(strconv.Itoa(i))} +} + +// FloatNumberTerm creates a new Term with a floating point Number value. +func FloatNumberTerm(f float64) *Term { + s := strconv.FormatFloat(f, 'g', -1, 64) + return &Term{Value: Number(s)} +} + +// Equal returns true if the other Value is a Number and is equal. +func (num Number) Equal(other Value) bool { + switch other := other.(type) { + case Number: + return Compare(num, other) == 0 + default: + return false + } +} + +// Compare compares num to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (num Number) Compare(other Value) int { + return Compare(num, other) +} + +// Find returns the current value or a not found error. +func (num Number) Find(path Ref) (Value, error) { + if len(path) == 0 { + return num, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (num Number) Hash() int { + f, err := json.Number(num).Float64() + if err != nil { + bs := []byte(num) + h := xxhash.Checksum64(bs) + return int(h) + } + return int(f) +} + +// Int returns the int representation of num if possible. +func (num Number) Int() (int, bool) { + i64, ok := num.Int64() + return int(i64), ok +} + +// Int64 returns the int64 representation of num if possible. +func (num Number) Int64() (int64, bool) { + i, err := json.Number(num).Int64() + if err != nil { + return 0, false + } + return i, true +} + +// Float64 returns the float64 representation of num if possible. +func (num Number) Float64() (float64, bool) { + f, err := json.Number(num).Float64() + if err != nil { + return 0, false + } + return f, true +} + +// IsGround always returns true. +func (num Number) IsGround() bool { + return true +} + +// MarshalJSON returns JSON encoded bytes representing num. +func (num Number) MarshalJSON() ([]byte, error) { + return json.Marshal(json.Number(num)) +} + +func (num Number) String() string { + return string(num) +} + +func intNumber(i int) Number { + return Number(strconv.Itoa(i)) +} + +func int64Number(i int64) Number { + return Number(strconv.FormatInt(i, 10)) +} + +func floatNumber(f float64) Number { + return Number(strconv.FormatFloat(f, 'g', -1, 64)) +} + +// String represents a string value as defined by JSON. +type String string + +// StringTerm creates a new Term with a String value. +func StringTerm(s string) *Term { + return &Term{Value: String(s)} +} + +// Equal returns true if the other Value is a String and is equal. +func (str String) Equal(other Value) bool { + switch other := other.(type) { + case String: + return str == other + default: + return false + } +} + +// Compare compares str to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (str String) Compare(other Value) int { + return Compare(str, other) +} + +// Find returns the current value or a not found error. +func (str String) Find(path Ref) (Value, error) { + if len(path) == 0 { + return str, nil + } + return nil, errFindNotFound +} + +// IsGround always returns true. +func (str String) IsGround() bool { + return true +} + +func (str String) String() string { + return strconv.Quote(string(str)) +} + +// Hash returns the hash code for the Value. +func (str String) Hash() int { + h := xxhash.ChecksumString64S(string(str), hashSeed0) + return int(h) +} + +// Var represents a variable as defined by the language. +type Var string + +// VarTerm creates a new Term with a Variable value. +func VarTerm(v string) *Term { + return &Term{Value: Var(v)} +} + +// Equal returns true if the other Value is a Variable and has the same value +// (name). +func (v Var) Equal(other Value) bool { + switch other := other.(type) { + case Var: + return v == other + default: + return false + } +} + +// Compare compares v to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (v Var) Compare(other Value) int { + return Compare(v, other) +} + +// Find returns the current value or a not found error. +func (v Var) Find(path Ref) (Value, error) { + if len(path) == 0 { + return v, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (v Var) Hash() int { + h := xxhash.ChecksumString64S(string(v), hashSeed0) + return int(h) +} + +// IsGround always returns false. +func (v Var) IsGround() bool { + return false +} + +// IsWildcard returns true if this is a wildcard variable. +func (v Var) IsWildcard() bool { + return strings.HasPrefix(string(v), WildcardPrefix) +} + +// IsGenerated returns true if this variable was generated during compilation. +func (v Var) IsGenerated() bool { + return strings.HasPrefix(string(v), "__local") +} + +func (v Var) String() string { + // Special case for wildcard so that string representation is parseable. The + // parser mangles wildcard variables to make their names unique and uses an + // illegal variable name character (WildcardPrefix) to avoid conflicts. When + // we serialize the variable here, we need to make sure it's parseable. + if v.IsWildcard() { + return Wildcard.String() + } + return string(v) +} + +// Ref represents a reference as defined by the language. +type Ref []*Term + +// EmptyRef returns a new, empty reference. +func EmptyRef() Ref { + return Ref([]*Term{}) +} + +// PtrRef returns a new reference against the head for the pointer +// s. Path components in the pointer are unescaped. +func PtrRef(head *Term, s string) (Ref, error) { + s = strings.Trim(s, "/") + if s == "" { + return Ref{head}, nil + } + parts := strings.Split(s, "/") + ref := make(Ref, len(parts)+1) + ref[0] = head + for i := 0; i < len(parts); i++ { + var err error + parts[i], err = url.PathUnescape(parts[i]) + if err != nil { + return nil, err + } + ref[i+1] = StringTerm(parts[i]) + } + return ref, nil +} + +// RefTerm creates a new Term with a Ref value. +func RefTerm(r ...*Term) *Term { + return &Term{Value: Ref(r)} +} + +// Append returns a copy of ref with the term appended to the end. +func (ref Ref) Append(term *Term) Ref { + n := len(ref) + dst := make(Ref, n+1) + copy(dst, ref) + dst[n] = term + return dst +} + +// Insert returns a copy of the ref with x inserted at pos. If pos < len(ref), +// existing elements are shifted to the right. If pos > len(ref)+1 this +// function panics. +func (ref Ref) Insert(x *Term, pos int) Ref { + if pos == len(ref) { + return ref.Append(x) + } else if pos > len(ref)+1 { + panic("illegal index") + } + cpy := make(Ref, len(ref)+1) + for i := 0; i < pos; i++ { + cpy[i] = ref[i] + } + cpy[pos] = x + for i := pos; i < len(ref); i++ { + cpy[i+1] = ref[i] + } + return cpy +} + +// Extend returns a copy of ref with the terms from other appended. The head of +// other will be converted to a string. +func (ref Ref) Extend(other Ref) Ref { + dst := make(Ref, len(ref)+len(other)) + for i := range ref { + dst[i] = ref[i] + } + head := other[0].Copy() + head.Value = String(head.Value.(Var)) + offset := len(ref) + dst[offset] = head + for i := range other[1:] { + dst[offset+i+1] = other[i+1] + } + return dst +} + +// Concat returns a ref with the terms appended. +func (ref Ref) Concat(terms []*Term) Ref { + if len(terms) == 0 { + return ref + } + cpy := make(Ref, len(ref)+len(terms)) + for i := range ref { + cpy[i] = ref[i] + } + for i := range terms { + cpy[len(ref)+i] = terms[i] + } + return cpy +} + +// Dynamic returns the offset of the first non-constant operand of ref. +func (ref Ref) Dynamic() int { + switch ref[0].Value.(type) { + case Call: + return 0 + } + for i := 1; i < len(ref); i++ { + if !IsConstant(ref[i].Value) { + return i + } + } + return -1 +} + +// Copy returns a deep copy of ref. +func (ref Ref) Copy() Ref { + return termSliceCopy(ref) +} + +// Equal returns true if ref is equal to other. +func (ref Ref) Equal(other Value) bool { + return Compare(ref, other) == 0 +} + +// Compare compares ref to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (ref Ref) Compare(other Value) int { + return Compare(ref, other) +} + +// Find returns the current value or a not found error. +func (ref Ref) Find(path Ref) (Value, error) { + if len(path) == 0 { + return ref, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (ref Ref) Hash() int { + return termSliceHash(ref) +} + +// HasPrefix returns true if the other ref is a prefix of this ref. +func (ref Ref) HasPrefix(other Ref) bool { + if len(other) > len(ref) { + return false + } + for i := range other { + if !ref[i].Equal(other[i]) { + return false + } + } + return true +} + +// ConstantPrefix returns the constant portion of the ref starting from the head. +func (ref Ref) ConstantPrefix() Ref { + ref = ref.Copy() + + i := ref.Dynamic() + if i < 0 { + return ref + } + return ref[:i] +} + +// GroundPrefix returns the ground portion of the ref starting from the head. By +// definition, the head of the reference is always ground. +func (ref Ref) GroundPrefix() Ref { + prefix := make(Ref, 0, len(ref)) + + for i, x := range ref { + if i > 0 && !x.IsGround() { + break + } + prefix = append(prefix, x) + } + + return prefix +} + +// IsGround returns true if all of the parts of the Ref are ground. +func (ref Ref) IsGround() bool { + if len(ref) == 0 { + return true + } + return termSliceIsGround(ref[1:]) +} + +// IsNested returns true if this ref contains other Refs. +func (ref Ref) IsNested() bool { + for _, x := range ref { + if _, ok := x.Value.(Ref); ok { + return true + } + } + return false +} + +// Ptr returns a slash-separated path string for this ref. If the ref +// contains non-string terms this function returns an error. Path +// components are escaped. +func (ref Ref) Ptr() (string, error) { + parts := make([]string, 0, len(ref)-1) + for _, term := range ref[1:] { + if str, ok := term.Value.(String); ok { + parts = append(parts, url.PathEscape(string(str))) + } else { + return "", fmt.Errorf("invalid path value type") + } + } + return strings.Join(parts, "/"), nil +} + +var varRegexp = regexp.MustCompile("^[[:alpha:]_][[:alpha:][:digit:]_]*$") + +func (ref Ref) String() string { + if len(ref) == 0 { + return "" + } + buf := []string{ref[0].Value.String()} + path := ref[1:] + for _, p := range path { + switch p := p.Value.(type) { + case String: + str := string(p) + if varRegexp.MatchString(str) && len(buf) > 0 && !IsKeyword(str) { + buf = append(buf, "."+str) + } else { + buf = append(buf, "["+p.String()+"]") + } + default: + buf = append(buf, "["+p.String()+"]") + } + } + return strings.Join(buf, "") +} + +// OutputVars returns a VarSet containing variables that would be bound by evaluating +// this expression in isolation. +func (ref Ref) OutputVars() VarSet { + vis := NewVarVisitor().WithParams(VarVisitorParams{SkipRefHead: true}) + vis.Walk(ref) + return vis.Vars() +} + +// QueryIterator defines the interface for querying AST documents with references. +type QueryIterator func(map[Var]Value, Value) error + +// Array represents an array as defined by the language. Arrays are similar to the +// same types as defined by JSON with the exception that they can contain Vars +// and References. +type Array []*Term + +// ArrayTerm creates a new Term with an Array value. +func ArrayTerm(a ...*Term) *Term { + return &Term{Value: Array(a)} +} + +// Copy returns a deep copy of arr. +func (arr Array) Copy() Array { + return termSliceCopy(arr) +} + +// Equal returns true if arr is equal to other. +func (arr Array) Equal(other Value) bool { + return Compare(arr, other) == 0 +} + +// Compare compares arr to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (arr Array) Compare(other Value) int { + return Compare(arr, other) +} + +// Find returns the value at the index or an out-of-range error. +func (arr Array) Find(path Ref) (Value, error) { + if len(path) == 0 { + return arr, nil + } + num, ok := path[0].Value.(Number) + if !ok { + return nil, errFindNotFound + } + i, ok := num.Int() + if !ok { + return nil, errFindNotFound + } + if i < 0 || i >= len(arr) { + return nil, errFindNotFound + } + return arr[i].Value.Find(path[1:]) +} + +// Get returns the element at pos or nil if not possible. +func (arr Array) Get(pos *Term) *Term { + num, ok := pos.Value.(Number) + if !ok { + return nil + } + + i, ok := num.Int() + if !ok { + return nil + } + + if i >= 0 && i < len(arr) { + return arr[i] + } + + return nil +} + +// Sorted returns a new Array that contains the sorted elements of arr. +func (arr Array) Sorted() Array { + cpy := make(Array, len(arr)) + for i := range cpy { + cpy[i] = arr[i] + } + sort.Sort(termSlice(cpy)) + return cpy +} + +// Hash returns the hash code for the Value. +func (arr Array) Hash() int { + return termSliceHash(arr) +} + +// IsGround returns true if all of the Array elements are ground. +func (arr Array) IsGround() bool { + return termSliceIsGround(arr) +} + +// MarshalJSON returns JSON encoded bytes representing arr. +func (arr Array) MarshalJSON() ([]byte, error) { + if len(arr) == 0 { + return json.Marshal([]interface{}{}) + } + return json.Marshal([]*Term(arr)) +} + +func (arr Array) String() string { + var buf []string + for _, e := range arr { + buf = append(buf, e.String()) + } + return "[" + strings.Join(buf, ", ") + "]" +} + +// Set represents a set as defined by the language. +type Set interface { + Value + Len() int + Copy() Set + Diff(Set) Set + Intersect(Set) Set + Union(Set) Set + Add(*Term) + Iter(func(*Term) error) error + Until(func(*Term) bool) bool + Foreach(func(*Term)) + Contains(*Term) bool + Map(func(*Term) (*Term, error)) (Set, error) + Reduce(*Term, func(*Term, *Term) (*Term, error)) (*Term, error) + Sorted() Array + Slice() []*Term +} + +// NewSet returns a new Set containing t. +func NewSet(t ...*Term) Set { + s := newset(len(t)) + for i := range t { + s.Add(t[i]) + } + return s +} + +func newset(n int) *set { + var keys []*Term + if n > 0 { + keys = make([]*Term, 0, n) + } + return &set{ + elems: make(map[int]*Term, n), + keys: keys, + } +} + +// SetTerm returns a new Term representing a set containing terms t. +func SetTerm(t ...*Term) *Term { + set := NewSet(t...) + return &Term{ + Value: set, + } +} + +type set struct { + elems map[int]*Term + keys []*Term +} + +// Copy returns a deep copy of s. +func (s *set) Copy() Set { + cpy := newset(s.Len()) + s.Foreach(func(x *Term) { + cpy.Add(x.Copy()) + }) + return cpy +} + +// IsGround returns true if all terms in s are ground. +func (s *set) IsGround() bool { + return !s.Until(func(x *Term) bool { + return !x.IsGround() + }) +} + +// Hash returns a hash code for s. +func (s *set) Hash() int { + var hash int + s.Foreach(func(x *Term) { + hash += x.Hash() + }) + return hash +} + +func (s *set) String() string { + if s.Len() == 0 { + return "set()" + } + buf := []string{} + s.Foreach(func(x *Term) { + buf = append(buf, fmt.Sprint(x)) + }) + return "{" + strings.Join(buf, ", ") + "}" +} + +// Compare compares s to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (s *set) Compare(other Value) int { + o1 := sortOrder(s) + o2 := sortOrder(other) + if o1 < o2 { + return -1 + } else if o1 > o2 { + return 1 + } + t := other.(*set) + sort.Sort(termSlice(s.keys)) + sort.Sort(termSlice(t.keys)) + return termSliceCompare(s.keys, t.keys) +} + +// Find returns the set or dereferences the element itself. +func (s *set) Find(path Ref) (Value, error) { + if len(path) == 0 { + return s, nil + } + if !s.Contains(path[0]) { + return nil, errFindNotFound + } + return path[0].Value.Find(path[1:]) +} + +// Diff returns elements in s that are not in other. +func (s *set) Diff(other Set) Set { + r := NewSet() + s.Foreach(func(x *Term) { + if !other.Contains(x) { + r.Add(x) + } + }) + return r +} + +// Intersect returns the set containing elements in both s and other. +func (s *set) Intersect(other Set) Set { + o := other.(*set) + n, m := s.Len(), o.Len() + ss := s + so := o + if m < n { + ss = o + so = s + n = m + } + + r := newset(n) + ss.Foreach(func(x *Term) { + if so.Contains(x) { + r.Add(x) + } + }) + return r +} + +// Union returns the set containing all elements of s and other. +func (s *set) Union(other Set) Set { + r := NewSet() + s.Foreach(func(x *Term) { + r.Add(x) + }) + other.Foreach(func(x *Term) { + r.Add(x) + }) + return r +} + +// Add updates s to include t. +func (s *set) Add(t *Term) { + s.insert(t) +} + +// Iter calls f on each element in s. If f returns an error, iteration stops +// and the return value is the error. +func (s *set) Iter(f func(*Term) error) error { + for i := range s.keys { + if err := f(s.keys[i]); err != nil { + return err + } + } + return nil +} + +var errStop = errors.New("stop") + +// Until calls f on each element in s. If f returns true, iteration stops. +func (s *set) Until(f func(*Term) bool) bool { + err := s.Iter(func(t *Term) error { + if f(t) { + return errStop + } + return nil + }) + return err != nil +} + +// Foreach calls f on each element in s. +func (s *set) Foreach(f func(*Term)) { + s.Iter(func(t *Term) error { + f(t) + return nil + }) +} + +// Map returns a new Set obtained by applying f to each value in s. +func (s *set) Map(f func(*Term) (*Term, error)) (Set, error) { + set := NewSet() + err := s.Iter(func(x *Term) error { + term, err := f(x) + if err != nil { + return err + } + set.Add(term) + return nil + }) + if err != nil { + return nil, err + } + return set, nil +} + +// Reduce returns a Term produced by applying f to each value in s. The first +// argument to f is the reduced value (starting with i) and the second argument +// to f is the element in s. +func (s *set) Reduce(i *Term, f func(*Term, *Term) (*Term, error)) (*Term, error) { + err := s.Iter(func(x *Term) error { + var err error + i, err = f(i, x) + if err != nil { + return err + } + return nil + }) + return i, err +} + +// Contains returns true if t is in s. +func (s *set) Contains(t *Term) bool { + return s.get(t) != nil +} + +// Len returns the number of elements in the set. +func (s *set) Len() int { + return len(s.keys) +} + +// MarshalJSON returns JSON encoded bytes representing s. +func (s *set) MarshalJSON() ([]byte, error) { + if s.keys == nil { + return json.Marshal([]interface{}{}) + } + return json.Marshal(s.keys) +} + +// Sorted returns an Array that contains the sorted elements of s. +func (s *set) Sorted() Array { + cpy := make(Array, len(s.keys)) + for i := range cpy { + cpy[i] = s.keys[i] + } + sort.Sort(termSlice(cpy)) + return cpy +} + +// Slice returns a slice of terms contained in the set. +func (s *set) Slice() []*Term { + return s.keys +} + +func (s *set) insert(x *Term) { + hash := x.Hash() + var equal func(v Value) bool + + switch x := x.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + a, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + + equal = func(b Value) bool { + if b, ok := b.(Number); ok { + b, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr, ok := s.elems[hash]; ok; { + if equal(curr.Value) { + return + } + + hash++ + curr, ok = s.elems[hash] + } + + s.elems[hash] = x + s.keys = append(s.keys, x) +} + +func (s *set) get(x *Term) *Term { + hash := x.Hash() + var equal func(v Value) bool + + switch x := x.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + a, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + + equal = func(b Value) bool { + if b, ok := b.(Number); ok { + b, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr, ok := s.elems[hash]; ok; { + if equal(curr.Value) { + return curr + } + + hash++ + curr, ok = s.elems[hash] + } + return nil +} + +// Object represents an object as defined by the language. +type Object interface { + Value + Len() int + Get(*Term) *Term + Copy() Object + Insert(*Term, *Term) + Iter(func(*Term, *Term) error) error + Until(func(*Term, *Term) bool) bool + Foreach(func(*Term, *Term)) + Map(func(*Term, *Term) (*Term, *Term, error)) (Object, error) + Diff(other Object) Object + Intersect(other Object) [][3]*Term + Merge(other Object) (Object, bool) + MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) + Filter(filter Object) (Object, error) + Keys() []*Term +} + +// NewObject creates a new Object with t. +func NewObject(t ...[2]*Term) Object { + obj := newobject(len(t)) + for i := range t { + obj.Insert(t[i][0], t[i][1]) + } + return obj +} + +// ObjectTerm creates a new Term with an Object value. +func ObjectTerm(o ...[2]*Term) *Term { + return &Term{Value: NewObject(o...)} +} + +type object struct { + elems map[int]*objectElem + keys []*Term + ground bool +} + +func newobject(n int) *object { + var keys []*Term + if n > 0 { + keys = make([]*Term, 0, n) + } + return &object{ + elems: make(map[int]*objectElem, n), + keys: keys, + ground: true, + } +} + +type objectElem struct { + key *Term + value *Term + next *objectElem +} + +// Item is a helper for constructing an tuple containing two Terms +// representing a key/value pair in an Object. +func Item(key, value *Term) [2]*Term { + return [2]*Term{key, value} +} + +// Compare compares obj to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (obj *object) Compare(other Value) int { + o1 := sortOrder(obj) + o2 := sortOrder(other) + if o1 < o2 { + return -1 + } else if o2 < o1 { + return 1 + } + a := obj + b := other.(*object) + keysA := a.Keys() + keysB := b.Keys() + sort.Sort(termSlice(keysA)) + sort.Sort(termSlice(keysB)) + minLen := a.Len() + if b.Len() < a.Len() { + minLen = b.Len() + } + for i := 0; i < minLen; i++ { + keysCmp := Compare(keysA[i], keysB[i]) + if keysCmp < 0 { + return -1 + } + if keysCmp > 0 { + return 1 + } + valA := a.Get(keysA[i]) + valB := b.Get(keysB[i]) + valCmp := Compare(valA, valB) + if valCmp != 0 { + return valCmp + } + } + if a.Len() < b.Len() { + return -1 + } + if b.Len() < a.Len() { + return 1 + } + return 0 +} + +// Find returns the value at the key or undefined. +func (obj *object) Find(path Ref) (Value, error) { + if len(path) == 0 { + return obj, nil + } + value := obj.Get(path[0]) + if value == nil { + return nil, errFindNotFound + } + return value.Value.Find(path[1:]) +} + +func (obj *object) Insert(k, v *Term) { + obj.insert(k, v) +} + +// Get returns the value of k in obj if k exists, otherwise nil. +func (obj *object) Get(k *Term) *Term { + if elem := obj.get(k); elem != nil { + return elem.value + } + return nil +} + +// Hash returns the hash code for the Value. +func (obj *object) Hash() int { + var hash int + obj.Foreach(func(k, v *Term) { + hash += k.Value.Hash() + hash += v.Value.Hash() + }) + return hash +} + +// IsGround returns true if all of the Object key/value pairs are ground. +func (obj *object) IsGround() bool { + return obj.ground +} + +// Copy returns a deep copy of obj. +func (obj *object) Copy() Object { + cpy, _ := obj.Map(func(k, v *Term) (*Term, *Term, error) { + return k.Copy(), v.Copy(), nil + }) + return cpy +} + +// Diff returns a new Object that contains only the key/value pairs that exist in obj. +func (obj *object) Diff(other Object) Object { + r := NewObject() + obj.Foreach(func(k, v *Term) { + if other.Get(k) == nil { + r.Insert(k, v) + } + }) + return r +} + +// Intersect returns a slice of term triplets that represent the intersection of keys +// between obj and other. For each intersecting key, the values from obj and other are included +// as the last two terms in the triplet (respectively). +func (obj *object) Intersect(other Object) [][3]*Term { + r := [][3]*Term{} + obj.Foreach(func(k, v *Term) { + if v2 := other.Get(k); v2 != nil { + r = append(r, [3]*Term{k, v, v2}) + } + }) + return r +} + +// Iter calls the function f for each key-value pair in the object. If f +// returns an error, iteration stops and the error is returned. +func (obj *object) Iter(f func(*Term, *Term) error) error { + for i := range obj.keys { + k := obj.keys[i] + node := obj.get(k) + if node == nil { + panic("corrupt object") + } + if err := f(k, node.value); err != nil { + return err + } + } + return nil +} + +// Until calls f for each key-value pair in the object. If f returns true, +// iteration stops. +func (obj *object) Until(f func(*Term, *Term) bool) bool { + err := obj.Iter(func(k, v *Term) error { + if f(k, v) { + return errStop + } + return nil + }) + return err != nil +} + +// Foreach calls f for each key-value pair in the object. +func (obj *object) Foreach(f func(*Term, *Term)) { + obj.Iter(func(k, v *Term) error { + f(k, v) + return nil + }) +} + +// Map returns a new Object constructed by mapping each element in the object +// using the function f. +func (obj *object) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { + cpy := newobject(obj.Len()) + err := obj.Iter(func(k, v *Term) error { + var err error + k, v, err = f(k, v) + if err != nil { + return err + } + cpy.insert(k, v) + return nil + }) + if err != nil { + return nil, err + } + return cpy, nil +} + +// Keys returns the keys of obj. +func (obj *object) Keys() []*Term { + return obj.keys +} + +// MarshalJSON returns JSON encoded bytes representing obj. +func (obj *object) MarshalJSON() ([]byte, error) { + sl := make([][2]*Term, obj.Len()) + for i := range obj.keys { + k := obj.keys[i] + sl[i] = Item(k, obj.get(k).value) + } + return json.Marshal(sl) +} + +// Merge returns a new Object containing the non-overlapping keys of obj and other. If there are +// overlapping keys between obj and other, the values of associated with the keys are merged. Only +// objects can be merged with other objects. If the values cannot be merged, the second turn value +// will be false. +func (obj object) Merge(other Object) (Object, bool) { + return obj.MergeWith(other, func(v1, v2 *Term) (*Term, bool) { + obj1, ok1 := v1.Value.(Object) + obj2, ok2 := v2.Value.(Object) + if !ok1 || !ok2 { + return nil, true + } + obj3, ok := obj1.Merge(obj2) + if !ok { + return nil, true + } + return NewTerm(obj3), false + }) +} + +// MergeWith returns a new Object containing the merged keys of obj and other. +// If there are overlapping keys between obj and other, the conflictResolver +// is called. The conflictResolver can return a merged value and a boolean +// indicating if the merge has failed and should stop. +func (obj object) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { + result := NewObject() + stop := obj.Until(func(k, v *Term) bool { + v2 := other.Get(k) + // The key didn't exist in other, keep the original value + if v2 == nil { + result.Insert(k, v) + return false + } + + // The key exists in both, resolve the conflict if possible + merged, stop := conflictResolver(v, v2) + if !stop { + result.Insert(k, merged) + } + return stop + }) + + if stop { + return nil, false + } + + // Copy in any values from other for keys that don't exist in obj + other.Foreach(func(k, v *Term) { + if v2 := obj.Get(k); v2 == nil { + result.Insert(k, v) + } + }) + return result, true +} + +// Filter returns a new object from values in obj where the keys are +// found in filter. Array indices for values can be specified as +// number strings. +func (obj *object) Filter(filter Object) (Object, error) { + filtered, err := filterObject(obj, filter) + if err != nil { + return nil, err + } + return filtered.(Object), nil +} + +// Len returns the number of elements in the object. +func (obj object) Len() int { + return len(obj.keys) +} + +func (obj object) String() string { + var buf []string + obj.Foreach(func(k, v *Term) { + buf = append(buf, fmt.Sprintf("%s: %s", k, v)) + }) + return "{" + strings.Join(buf, ", ") + "}" +} + +func (obj *object) get(k *Term) *objectElem { + hash := k.Hash() + + var equal func(v Value) bool + + switch x := k.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + a, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + + equal = func(b Value) bool { + if b, ok := b.(Number); ok { + b, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr := obj.elems[hash]; curr != nil; curr = curr.next { + if equal(curr.key.Value) { + return curr + } + } + return nil +} + +func (obj *object) insert(k, v *Term) { + hash := k.Hash() + head := obj.elems[hash] + var equal func(v Value) bool + + switch x := k.Value.(type) { + case Null, Boolean, String, Var: + equal = func(y Value) bool { return x == y } + case Number: + if xi, err := json.Number(x).Int64(); err == nil { + equal = func(y Value) bool { + if y, ok := y.(Number); ok { + if yi, err := json.Number(y).Int64(); err == nil { + return xi == yi + } + } + + return false + } + break + } + + a, ok := new(big.Float).SetString(string(x)) + if !ok { + panic("illegal value") + } + + equal = func(b Value) bool { + if b, ok := b.(Number); ok { + b, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + + return a.Cmp(b) == 0 + } + + return false + } + default: + equal = func(y Value) bool { return Compare(x, y) == 0 } + } + + for curr := head; curr != nil; curr = curr.next { + if equal(curr.key.Value) { + curr.value = v + return + } + } + obj.elems[hash] = &objectElem{ + key: k, + value: v, + next: head, + } + obj.keys = append(obj.keys, k) + obj.ground = obj.ground && k.IsGround() && v.IsGround() +} + +func filterObject(o Value, filter Value) (Value, error) { + if filter.Compare(Null{}) == 0 { + return o, nil + } + + filteredObj, ok := filter.(Object) + if !ok { + return nil, fmt.Errorf("invalid filter value %q, expected an object", filter) + } + + switch v := o.(type) { + case String, Number, Boolean, Null: + return o, nil + case Array: + var values Array + for i, t := range v { + subFilter := filteredObj.Get(StringTerm(strconv.Itoa(i))) + if subFilter != nil { + filteredValue, err := filterObject(t.Value, subFilter.Value) + if err != nil { + return nil, err + } + values = append(values, NewTerm(filteredValue)) + } + } + return values, nil + case Set: + values := NewSet() + err := v.Iter(func(t *Term) error { + if filteredObj.Get(t) != nil { + filteredValue, err := filterObject(t.Value, filteredObj.Get(t).Value) + if err != nil { + return err + } + values.Add(NewTerm(filteredValue)) + } + return nil + }) + return values, err + case Object: + values := NewObject() + + iterObj := v + other := filteredObj + if v.Len() < filteredObj.Len() { + iterObj = filteredObj + other = v + } + + err := iterObj.Iter(func(key *Term, value *Term) error { + if other.Get(key) != nil { + filteredValue, err := filterObject(v.Get(key).Value, filteredObj.Get(key).Value) + if err != nil { + return err + } + values.Insert(key, NewTerm(filteredValue)) + } + return nil + }) + return values, err + default: + return nil, fmt.Errorf("invalid object value type %q", v) + } +} + +// ArrayComprehension represents an array comprehension as defined in the language. +type ArrayComprehension struct { + Term *Term `json:"term"` + Body Body `json:"body"` +} + +// ArrayComprehensionTerm creates a new Term with an ArrayComprehension value. +func ArrayComprehensionTerm(term *Term, body Body) *Term { + return &Term{ + Value: &ArrayComprehension{ + Term: term, + Body: body, + }, + } +} + +// Copy returns a deep copy of ac. +func (ac *ArrayComprehension) Copy() *ArrayComprehension { + cpy := *ac + cpy.Body = ac.Body.Copy() + cpy.Term = ac.Term.Copy() + return &cpy +} + +// Equal returns true if ac is equal to other. +func (ac *ArrayComprehension) Equal(other Value) bool { + return Compare(ac, other) == 0 +} + +// Compare compares ac to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (ac *ArrayComprehension) Compare(other Value) int { + return Compare(ac, other) +} + +// Find returns the current value or a not found error. +func (ac *ArrayComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return ac, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (ac *ArrayComprehension) Hash() int { + return ac.Term.Hash() + ac.Body.Hash() +} + +// IsGround returns true if the Term and Body are ground. +func (ac *ArrayComprehension) IsGround() bool { + return ac.Term.IsGround() && ac.Body.IsGround() +} + +func (ac *ArrayComprehension) String() string { + return "[" + ac.Term.String() + " | " + ac.Body.String() + "]" +} + +// ObjectComprehension represents an object comprehension as defined in the language. +type ObjectComprehension struct { + Key *Term `json:"key"` + Value *Term `json:"value"` + Body Body `json:"body"` +} + +// ObjectComprehensionTerm creates a new Term with an ObjectComprehension value. +func ObjectComprehensionTerm(key, value *Term, body Body) *Term { + return &Term{ + Value: &ObjectComprehension{ + Key: key, + Value: value, + Body: body, + }, + } +} + +// Copy returns a deep copy of oc. +func (oc *ObjectComprehension) Copy() *ObjectComprehension { + cpy := *oc + cpy.Body = oc.Body.Copy() + cpy.Key = oc.Key.Copy() + cpy.Value = oc.Value.Copy() + return &cpy +} + +// Equal returns true if oc is equal to other. +func (oc *ObjectComprehension) Equal(other Value) bool { + return Compare(oc, other) == 0 +} + +// Compare compares oc to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (oc *ObjectComprehension) Compare(other Value) int { + return Compare(oc, other) +} + +// Find returns the current value or a not found error. +func (oc *ObjectComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return oc, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (oc *ObjectComprehension) Hash() int { + return oc.Key.Hash() + oc.Value.Hash() + oc.Body.Hash() +} + +// IsGround returns true if the Key, Value and Body are ground. +func (oc *ObjectComprehension) IsGround() bool { + return oc.Key.IsGround() && oc.Value.IsGround() && oc.Body.IsGround() +} + +func (oc *ObjectComprehension) String() string { + return "{" + oc.Key.String() + ": " + oc.Value.String() + " | " + oc.Body.String() + "}" +} + +// SetComprehension represents a set comprehension as defined in the language. +type SetComprehension struct { + Term *Term `json:"term"` + Body Body `json:"body"` +} + +// SetComprehensionTerm creates a new Term with an SetComprehension value. +func SetComprehensionTerm(term *Term, body Body) *Term { + return &Term{ + Value: &SetComprehension{ + Term: term, + Body: body, + }, + } +} + +// Copy returns a deep copy of sc. +func (sc *SetComprehension) Copy() *SetComprehension { + cpy := *sc + cpy.Body = sc.Body.Copy() + cpy.Term = sc.Term.Copy() + return &cpy +} + +// Equal returns true if sc is equal to other. +func (sc *SetComprehension) Equal(other Value) bool { + return Compare(sc, other) == 0 +} + +// Compare compares sc to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (sc *SetComprehension) Compare(other Value) int { + return Compare(sc, other) +} + +// Find returns the current value or a not found error. +func (sc *SetComprehension) Find(path Ref) (Value, error) { + if len(path) == 0 { + return sc, nil + } + return nil, errFindNotFound +} + +// Hash returns the hash code of the Value. +func (sc *SetComprehension) Hash() int { + return sc.Term.Hash() + sc.Body.Hash() +} + +// IsGround returns true if the Term and Body are ground. +func (sc *SetComprehension) IsGround() bool { + return sc.Term.IsGround() && sc.Body.IsGround() +} + +func (sc *SetComprehension) String() string { + return "{" + sc.Term.String() + " | " + sc.Body.String() + "}" +} + +// Call represents as function call in the language. +type Call []*Term + +// CallTerm returns a new Term with a Call value defined by terms. The first +// term is the operator and the rest are operands. +func CallTerm(terms ...*Term) *Term { + return NewTerm(Call(terms)) +} + +// Copy returns a deep copy of c. +func (c Call) Copy() Call { + return termSliceCopy(c) +} + +// Compare compares c to other, return <0, 0, or >0 if it is less than, equal to, +// or greater than other. +func (c Call) Compare(other Value) int { + return Compare(c, other) +} + +// Find returns the current value or a not found error. +func (c Call) Find(Ref) (Value, error) { + return nil, errFindNotFound +} + +// Hash returns the hash code for the Value. +func (c Call) Hash() int { + return termSliceHash(c) +} + +// IsGround returns true if the Value is ground. +func (c Call) IsGround() bool { + return termSliceIsGround(c) +} + +// MakeExpr returns an ew Expr from this call. +func (c Call) MakeExpr(output *Term) *Expr { + terms := []*Term(c) + return NewExpr(append(terms, output)) +} + +func (c Call) String() string { + args := make([]string, len(c)-1) + for i := 1; i < len(c); i++ { + args[i-1] = c[i].String() + } + return fmt.Sprintf("%v(%v)", c[0], strings.Join(args, ", ")) +} + +func termSliceCopy(a []*Term) []*Term { + cpy := make([]*Term, len(a)) + for i := range a { + cpy[i] = a[i].Copy() + } + return cpy +} + +func termSliceEqual(a, b []*Term) bool { + if len(a) == len(b) { + for i := range a { + if !a[i].Equal(b[i]) { + return false + } + } + return true + } + return false +} + +func termSliceHash(a []*Term) int { + var hash int + for _, v := range a { + hash += v.Value.Hash() + } + return hash +} + +func termSliceIsGround(a []*Term) bool { + for _, v := range a { + if !v.IsGround() { + return false + } + } + return true +} + +// NOTE(tsandall): The unmarshalling errors in these functions are not +// helpful for callers because they do not identify the source of the +// unmarshalling error. Because OPA doesn't accept JSON describing ASTs +// from callers, this is acceptable (for now). If that changes in the future, +// the error messages should be revisited. The current approach focuses +// on the happy path and treats all errors the same. If better error +// reporting is needed, the error paths will need to be fleshed out. + +func unmarshalBody(b []interface{}) (Body, error) { + buf := Body{} + for _, e := range b { + if m, ok := e.(map[string]interface{}); ok { + expr := &Expr{} + if err := unmarshalExpr(expr, m); err == nil { + buf = append(buf, expr) + continue + } + } + goto unmarshal_error + } + return buf, nil +unmarshal_error: + return nil, fmt.Errorf("ast: unable to unmarshal body") +} + +func unmarshalExpr(expr *Expr, v map[string]interface{}) error { + if x, ok := v["negated"]; ok { + if b, ok := x.(bool); ok { + expr.Negated = b + } else { + return fmt.Errorf("ast: unable to unmarshal negated field with type: %T (expected true or false)", v["negated"]) + } + } + if err := unmarshalExprIndex(expr, v); err != nil { + return err + } + switch ts := v["terms"].(type) { + case map[string]interface{}: + t, err := unmarshalTerm(ts) + if err != nil { + return err + } + expr.Terms = t + case []interface{}: + terms, err := unmarshalTermSlice(ts) + if err != nil { + return err + } + expr.Terms = terms + default: + return fmt.Errorf(`ast: unable to unmarshal terms field with type: %T (expected {"value": ..., "type": ...} or [{"value": ..., "type": ...}, ...])`, v["terms"]) + } + if x, ok := v["with"]; ok { + if sl, ok := x.([]interface{}); ok { + ws := make([]*With, len(sl)) + for i := range sl { + var err error + ws[i], err = unmarshalWith(sl[i]) + if err != nil { + return err + } + } + expr.With = ws + } + } + return nil +} + +func unmarshalExprIndex(expr *Expr, v map[string]interface{}) error { + if x, ok := v["index"]; ok { + if n, ok := x.(json.Number); ok { + i, err := n.Int64() + if err == nil { + expr.Index = int(i) + return nil + } + } + } + return fmt.Errorf("ast: unable to unmarshal index field with type: %T (expected integer)", v["index"]) +} + +func unmarshalTerm(m map[string]interface{}) (*Term, error) { + v, err := unmarshalValue(m) + if err != nil { + return nil, err + } + return &Term{Value: v}, nil +} + +func unmarshalTermSlice(s []interface{}) ([]*Term, error) { + buf := []*Term{} + for _, x := range s { + if m, ok := x.(map[string]interface{}); ok { + if t, err := unmarshalTerm(m); err == nil { + buf = append(buf, t) + continue + } else { + return nil, err + } + } + return nil, fmt.Errorf("ast: unable to unmarshal term") + } + return buf, nil +} + +func unmarshalTermSliceValue(d map[string]interface{}) ([]*Term, error) { + if s, ok := d["value"].([]interface{}); ok { + return unmarshalTermSlice(s) + } + return nil, fmt.Errorf(`ast: unable to unmarshal term (expected {"value": [...], "type": ...} where type is one of: ref, array, or set)`) +} + +func unmarshalWith(i interface{}) (*With, error) { + if m, ok := i.(map[string]interface{}); ok { + tgt, _ := m["target"].(map[string]interface{}) + target, err := unmarshalTerm(tgt) + if err == nil { + val, _ := m["value"].(map[string]interface{}) + value, err := unmarshalTerm(val) + if err == nil { + return &With{ + Target: target, + Value: value, + }, nil + } + return nil, err + } + return nil, err + } + return nil, fmt.Errorf(`ast: unable to unmarshal with modifier (expected {"target": {...}, "value": {...}})`) +} + +func unmarshalValue(d map[string]interface{}) (Value, error) { + v := d["value"] + switch d["type"] { + case "null": + return Null{}, nil + case "boolean": + if b, ok := v.(bool); ok { + return Boolean(b), nil + } + case "number": + if n, ok := v.(json.Number); ok { + return Number(n), nil + } + case "string": + if s, ok := v.(string); ok { + return String(s), nil + } + case "var": + if s, ok := v.(string); ok { + return Var(s), nil + } + case "ref": + if s, err := unmarshalTermSliceValue(d); err == nil { + return Ref(s), nil + } + case "array": + if s, err := unmarshalTermSliceValue(d); err == nil { + return Array(s), nil + } + case "set": + if s, err := unmarshalTermSliceValue(d); err == nil { + set := NewSet() + for _, x := range s { + set.Add(x) + } + return set, nil + } + case "object": + if s, ok := v.([]interface{}); ok { + buf := NewObject() + for _, x := range s { + if i, ok := x.([]interface{}); ok && len(i) == 2 { + p, err := unmarshalTermSlice(i) + if err == nil { + buf.Insert(p[0], p[1]) + continue + } + } + goto unmarshal_error + } + return buf, nil + } + case "arraycomprehension", "setcomprehension": + if m, ok := v.(map[string]interface{}); ok { + t, ok := m["term"].(map[string]interface{}) + if !ok { + goto unmarshal_error + } + + term, err := unmarshalTerm(t) + if err != nil { + goto unmarshal_error + } + + b, ok := m["body"].([]interface{}) + if !ok { + goto unmarshal_error + } + + body, err := unmarshalBody(b) + if err != nil { + goto unmarshal_error + } + + if d["type"] == "arraycomprehension" { + return &ArrayComprehension{Term: term, Body: body}, nil + } + return &SetComprehension{Term: term, Body: body}, nil + } + case "objectcomprehension": + if m, ok := v.(map[string]interface{}); ok { + k, ok := m["key"].(map[string]interface{}) + if !ok { + goto unmarshal_error + } + + key, err := unmarshalTerm(k) + if err != nil { + goto unmarshal_error + } + + v, ok := m["value"].(map[string]interface{}) + if !ok { + goto unmarshal_error + } + + value, err := unmarshalTerm(v) + if err != nil { + goto unmarshal_error + } + + b, ok := m["body"].([]interface{}) + if !ok { + goto unmarshal_error + } + + body, err := unmarshalBody(b) + if err != nil { + goto unmarshal_error + } + + return &ObjectComprehension{Key: key, Value: value, Body: body}, nil + } + case "call": + if s, err := unmarshalTermSliceValue(d); err == nil { + return Call(s), nil + } + } +unmarshal_error: + return nil, fmt.Errorf("ast: unable to unmarshal term") +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/transform.go b/vendor/github.com/open-policy-agent/opa/ast/transform.go new file mode 100644 index 000000000..8472d2b47 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/transform.go @@ -0,0 +1,382 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import "fmt" + +// Transformer defines the interface for transforming AST elements. If the +// transformer returns nil and does not indicate an error, the AST element will +// be set to nil and no transformations will be applied to children of the +// element. +type Transformer interface { + Transform(v interface{}) (interface{}, error) +} + +// Transform iterates the AST and calls the Transform function on the +// Transformer t for x before recursing. +func Transform(t Transformer, x interface{}) (interface{}, error) { + + if term, ok := x.(*Term); ok { + return Transform(t, term.Value) + } + + y, err := t.Transform(x) + if err != nil { + return x, err + } + + if y == nil { + return nil, nil + } + + var ok bool + switch y := y.(type) { + case *Module: + p, err := Transform(t, y.Package) + if err != nil { + return nil, err + } + if y.Package, ok = p.(*Package); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Package, p) + } + for i := range y.Imports { + imp, err := Transform(t, y.Imports[i]) + if err != nil { + return nil, err + } + if y.Imports[i], ok = imp.(*Import); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Imports[i], imp) + } + } + for i := range y.Rules { + rule, err := Transform(t, y.Rules[i]) + if err != nil { + return nil, err + } + if y.Rules[i], ok = rule.(*Rule); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Rules[i], rule) + } + } + for i := range y.Comments { + comment, err := Transform(t, y.Comments[i]) + if err != nil { + return nil, err + } + if y.Comments[i], ok = comment.(*Comment); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Comments[i], comment) + } + } + return y, nil + case *Package: + ref, err := Transform(t, y.Path) + if err != nil { + return nil, err + } + if y.Path, ok = ref.(Ref); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Path, ref) + } + return y, nil + case *Import: + y.Path, err = transformTerm(t, y.Path) + if err != nil { + return nil, err + } + if y.Alias, err = transformVar(t, y.Alias); err != nil { + return nil, err + } + return y, nil + case *Rule: + if y.Head, err = transformHead(t, y.Head); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + if y.Else != nil { + rule, err := Transform(t, y.Else) + if err != nil { + return nil, err + } + if y.Else, ok = rule.(*Rule); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.Else, rule) + } + } + return y, nil + case *Head: + if y.Name, err = transformVar(t, y.Name); err != nil { + return nil, err + } + if y.Args, err = transformArgs(t, y.Args); err != nil { + return nil, err + } + if y.Key != nil { + if y.Key, err = transformTerm(t, y.Key); err != nil { + return nil, err + } + } + if y.Value != nil { + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + } + return y, nil + case Args: + for i := range y { + if y[i], err = transformTerm(t, y[i]); err != nil { + return nil, err + } + } + return y, nil + case Body: + for i, e := range y { + e, err := Transform(t, e) + if err != nil { + return nil, err + } + if y[i], ok = e.(*Expr); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y[i], e) + } + } + return y, nil + case *Expr: + switch ts := y.Terms.(type) { + case *SomeDecl: + decl, err := Transform(t, ts) + if err != nil { + return nil, err + } + if y.Terms, ok = decl.(*SomeDecl); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y, decl) + } + return y, nil + case []*Term: + for i := range ts { + if ts[i], err = transformTerm(t, ts[i]); err != nil { + return nil, err + } + } + case *Term: + if y.Terms, err = transformTerm(t, ts); err != nil { + return nil, err + } + } + for i, w := range y.With { + w, err := Transform(t, w) + if err != nil { + return nil, err + } + if y.With[i], ok = w.(*With); !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", y.With[i], w) + } + } + return y, nil + case *With: + if y.Target, err = transformTerm(t, y.Target); err != nil { + return nil, err + } + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + return y, nil + case Ref: + for i, term := range y { + if y[i], err = transformTerm(t, term); err != nil { + return nil, err + } + } + return y, nil + case Object: + return y.Map(func(k, v *Term) (*Term, *Term, error) { + k, err := transformTerm(t, k) + if err != nil { + return nil, nil, err + } + v, err = transformTerm(t, v) + if err != nil { + return nil, nil, err + } + return k, v, nil + }) + case Array: + for i := range y { + if y[i], err = transformTerm(t, y[i]); err != nil { + return nil, err + } + } + return y, nil + case Set: + y, err = y.Map(func(term *Term) (*Term, error) { + return transformTerm(t, term) + }) + if err != nil { + return nil, err + } + return y, nil + case *ArrayComprehension: + if y.Term, err = transformTerm(t, y.Term); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case *ObjectComprehension: + if y.Key, err = transformTerm(t, y.Key); err != nil { + return nil, err + } + if y.Value, err = transformTerm(t, y.Value); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case *SetComprehension: + if y.Term, err = transformTerm(t, y.Term); err != nil { + return nil, err + } + if y.Body, err = transformBody(t, y.Body); err != nil { + return nil, err + } + return y, nil + case Call: + for i := range y { + if y[i], err = transformTerm(t, y[i]); err != nil { + return nil, err + } + } + return y, nil + default: + return y, nil + } +} + +// TransformRefs calls the function f on all references under x. +func TransformRefs(x interface{}, f func(Ref) (Value, error)) (interface{}, error) { + t := &GenericTransformer{func(x interface{}) (interface{}, error) { + if r, ok := x.(Ref); ok { + return f(r) + } + return x, nil + }} + return Transform(t, x) +} + +// TransformVars calls the function f on all vars under x. +func TransformVars(x interface{}, f func(Var) (Value, error)) (interface{}, error) { + t := &GenericTransformer{func(x interface{}) (interface{}, error) { + if v, ok := x.(Var); ok { + return f(v) + } + return x, nil + }} + return Transform(t, x) +} + +// TransformComprehensions calls the functio nf on all comprehensions under x. +func TransformComprehensions(x interface{}, f func(interface{}) (Value, error)) (interface{}, error) { + t := &GenericTransformer{func(x interface{}) (interface{}, error) { + switch x := x.(type) { + case *ArrayComprehension: + return f(x) + case *SetComprehension: + return f(x) + case *ObjectComprehension: + return f(x) + } + return x, nil + }} + return Transform(t, x) +} + +// GenericTransformer implements the Transformer interface to provide a utility +// to transform AST nodes using a closure. +type GenericTransformer struct { + f func(x interface{}) (interface{}, error) +} + +// NewGenericTransformer returns a new GenericTransformer that will transform +// AST nodes using the function f. +func NewGenericTransformer(f func(x interface{}) (interface{}, error)) *GenericTransformer { + return &GenericTransformer{ + f: f, + } +} + +// Transform calls the function f on the GenericTransformer. +func (t *GenericTransformer) Transform(x interface{}) (interface{}, error) { + return t.f(x) +} + +func transformHead(t Transformer, head *Head) (*Head, error) { + y, err := Transform(t, head) + if err != nil { + return nil, err + } + h, ok := y.(*Head) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", head, y) + } + return h, nil +} +func transformArgs(t Transformer, args Args) (Args, error) { + y, err := Transform(t, args) + if err != nil { + return nil, err + } + a, ok := y.(Args) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", args, y) + } + return a, nil +} + +func transformBody(t Transformer, body Body) (Body, error) { + y, err := Transform(t, body) + if err != nil { + return nil, err + } + r, ok := y.(Body) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", body, y) + } + return r, nil +} + +func transformTerm(t Transformer, term *Term) (*Term, error) { + v, err := transformValue(t, term.Value) + if err != nil { + return nil, err + } + r := &Term{ + Value: v, + Location: term.Location, + } + return r, nil +} + +func transformValue(t Transformer, v Value) (Value, error) { + v1, err := Transform(t, v) + if err != nil { + return nil, err + } + r, ok := v1.(Value) + if !ok { + return nil, fmt.Errorf("illegal transform: %T != %T", v, v1) + } + return r, nil +} + +func transformVar(t Transformer, v Var) (Var, error) { + v1, err := Transform(t, v) + if err != nil { + return "", err + } + r, ok := v1.(Var) + if !ok { + return "", fmt.Errorf("illegal transform: %T != %T", v, v1) + } + return r, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/unify.go b/vendor/github.com/open-policy-agent/opa/ast/unify.go new file mode 100644 index 000000000..511dbe4c5 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/unify.go @@ -0,0 +1,184 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +// Unify returns a set of variables that will be unified when the equality expression defined by +// terms a and b is evaluated. The unifier assumes that variables in the VarSet safe are already +// unified. +func Unify(safe VarSet, a *Term, b *Term) VarSet { + u := &unifier{ + safe: safe, + unified: VarSet{}, + unknown: map[Var]VarSet{}, + } + u.unify(a, b) + return u.unified +} + +type unifier struct { + safe VarSet + unified VarSet + unknown map[Var]VarSet +} + +func (u *unifier) isSafe(x Var) bool { + return u.safe.Contains(x) || u.unified.Contains(x) +} + +func (u *unifier) unify(a *Term, b *Term) { + + switch a := a.Value.(type) { + + case Var: + switch b := b.Value.(type) { + case Var: + if u.isSafe(b) { + u.markSafe(a) + } else if u.isSafe(a) { + u.markSafe(b) + } else { + u.markUnknown(a, b) + u.markUnknown(b, a) + } + case Array, Object: + u.unifyAll(a, b) + case Ref: + if u.isSafe(b[0].Value.(Var)) { + u.markSafe(a) + } + default: + u.markSafe(a) + } + + case Ref: + if u.isSafe(a[0].Value.(Var)) { + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case Array, Object: + u.markAllSafe(b) + } + } + + case *ArrayComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case Array: + u.markAllSafe(b) + } + case *ObjectComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + case Object: + u.markAllSafe(b) + } + case *SetComprehension: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + } + + case Array: + switch b := b.Value.(type) { + case Var: + u.unifyAll(b, a) + case Ref, *ArrayComprehension, *ObjectComprehension, *SetComprehension: + u.markAllSafe(a) + case Array: + if len(a) == len(b) { + for i := range a { + u.unify(a[i], b[i]) + } + } + } + + case Object: + switch b := b.Value.(type) { + case Var: + u.unifyAll(b, a) + case Ref: + u.markAllSafe(a) + case Object: + if a.Len() == b.Len() { + a.Iter(func(k, v *Term) error { + if v2 := b.Get(k); v2 != nil { + u.unify(v, v2) + } + return nil + }) + } + } + + default: + switch b := b.Value.(type) { + case Var: + u.markSafe(b) + } + } +} + +func (u *unifier) markAllSafe(x Value) { + vis := u.varVisitor() + vis.Walk(x) + for v := range vis.Vars() { + u.markSafe(v) + } +} + +func (u *unifier) markSafe(x Var) { + u.unified.Add(x) + + // Add dependencies of 'x' to safe set + vs := u.unknown[x] + delete(u.unknown, x) + for v := range vs { + u.markSafe(v) + } + + // Add dependants of 'x' to safe set if they have no more + // dependencies. + for v, deps := range u.unknown { + if deps.Contains(x) { + delete(deps, x) + if len(deps) == 0 { + u.markSafe(v) + } + } + } +} + +func (u *unifier) markUnknown(a, b Var) { + if _, ok := u.unknown[a]; !ok { + u.unknown[a] = NewVarSet() + } + u.unknown[a].Add(b) +} + +func (u *unifier) unifyAll(a Var, b Value) { + if u.isSafe(a) { + u.markAllSafe(b) + } else { + vis := u.varVisitor() + vis.Walk(b) + unsafe := vis.Vars().Diff(u.safe).Diff(u.unified) + if len(unsafe) == 0 { + u.markSafe(a) + } else { + for v := range unsafe { + u.markUnknown(a, v) + } + } + } +} + +func (u *unifier) varVisitor() *VarVisitor { + return NewVarVisitor().WithParams(VarVisitorParams{ + SkipRefHead: true, + SkipObjectKeys: true, + SkipClosures: true, + }) +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/varset.go b/vendor/github.com/open-policy-agent/opa/ast/varset.go new file mode 100644 index 000000000..16dc3f584 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/varset.go @@ -0,0 +1,100 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +import ( + "fmt" + "sort" +) + +// VarSet represents a set of variables. +type VarSet map[Var]struct{} + +// NewVarSet returns a new VarSet containing the specified variables. +func NewVarSet(vs ...Var) VarSet { + s := VarSet{} + for _, v := range vs { + s.Add(v) + } + return s +} + +// Add updates the set to include the variable "v". +func (s VarSet) Add(v Var) { + s[v] = struct{}{} +} + +// Contains returns true if the set contains the variable "v". +func (s VarSet) Contains(v Var) bool { + _, ok := s[v] + return ok +} + +// Copy returns a shallow copy of the VarSet. +func (s VarSet) Copy() VarSet { + cpy := VarSet{} + for v := range s { + cpy.Add(v) + } + return cpy +} + +// Diff returns a VarSet containing variables in s that are not in vs. +func (s VarSet) Diff(vs VarSet) VarSet { + r := VarSet{} + for v := range s { + if !vs.Contains(v) { + r.Add(v) + } + } + return r +} + +// Equal returns true if s contains exactly the same elements as vs. +func (s VarSet) Equal(vs VarSet) bool { + if len(s.Diff(vs)) > 0 { + return false + } + return len(vs.Diff(s)) == 0 +} + +// Intersect returns a VarSet containing variables in s that are in vs. +func (s VarSet) Intersect(vs VarSet) VarSet { + r := VarSet{} + for v := range s { + if vs.Contains(v) { + r.Add(v) + } + } + return r +} + +// Sorted returns a sorted slice of vars from s. +func (s VarSet) Sorted() []Var { + sorted := make([]Var, 0, len(s)) + for v := range s { + sorted = append(sorted, v) + } + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Compare(sorted[j]) < 0 + }) + return sorted +} + +// Update merges the other VarSet into this VarSet. +func (s VarSet) Update(vs VarSet) { + for v := range vs { + s.Add(v) + } +} + +func (s VarSet) String() string { + tmp := []string{} + for v := range s { + tmp = append(tmp, string(v)) + } + sort.Strings(tmp) + return fmt.Sprintf("%v", tmp) +} diff --git a/vendor/github.com/open-policy-agent/opa/ast/visit.go b/vendor/github.com/open-policy-agent/opa/ast/visit.go new file mode 100644 index 000000000..19bd14f36 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/ast/visit.go @@ -0,0 +1,686 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ast + +// Visitor defines the interface for iterating AST elements. The Visit function +// can return a Visitor w which will be used to visit the children of the AST +// element v. If the Visit function returns nil, the children will not be +// visited. This is deprecated. +type Visitor interface { + Visit(v interface{}) (w Visitor) +} + +// BeforeAndAfterVisitor wraps Visitor to provide hooks for being called before +// and after the AST has been visited. This is deprecated. +type BeforeAndAfterVisitor interface { + Visitor + Before(x interface{}) + After(x interface{}) +} + +// Walk iterates the AST by calling the Visit function on the Visitor +// v for x before recursing. This is deprecated. +func Walk(v Visitor, x interface{}) { + if bav, ok := v.(BeforeAndAfterVisitor); !ok { + walk(v, x) + } else { + bav.Before(x) + defer bav.After(x) + walk(bav, x) + } +} + +// WalkBeforeAndAfter iterates the AST by calling the Visit function on the +// Visitor v for x before recursing. This is deprecated. +func WalkBeforeAndAfter(v BeforeAndAfterVisitor, x interface{}) { + Walk(v, x) +} + +func walk(v Visitor, x interface{}) { + w := v.Visit(x) + if w == nil { + return + } + switch x := x.(type) { + case *Module: + Walk(w, x.Package) + for _, i := range x.Imports { + Walk(w, i) + } + for _, r := range x.Rules { + Walk(w, r) + } + for _, c := range x.Comments { + Walk(w, c) + } + case *Package: + Walk(w, x.Path) + case *Import: + Walk(w, x.Path) + Walk(w, x.Alias) + case *Rule: + Walk(w, x.Head) + Walk(w, x.Body) + if x.Else != nil { + Walk(w, x.Else) + } + case *Head: + Walk(w, x.Name) + Walk(w, x.Args) + if x.Key != nil { + Walk(w, x.Key) + } + if x.Value != nil { + Walk(w, x.Value) + } + case Body: + for _, e := range x { + Walk(w, e) + } + case Args: + for _, t := range x { + Walk(w, t) + } + case *Expr: + switch ts := x.Terms.(type) { + case *SomeDecl: + Walk(w, ts) + case []*Term: + for _, t := range ts { + Walk(w, t) + } + case *Term: + Walk(w, ts) + } + for i := range x.With { + Walk(w, x.With[i]) + } + case *With: + Walk(w, x.Target) + Walk(w, x.Value) + case *Term: + Walk(w, x.Value) + case Ref: + for _, t := range x { + Walk(w, t) + } + case Object: + x.Foreach(func(k, vv *Term) { + Walk(w, k) + Walk(w, vv) + }) + case Array: + for _, t := range x { + Walk(w, t) + } + case Set: + x.Foreach(func(t *Term) { + Walk(w, t) + }) + case *ArrayComprehension: + Walk(w, x.Term) + Walk(w, x.Body) + case *ObjectComprehension: + Walk(w, x.Key) + Walk(w, x.Value) + Walk(w, x.Body) + case *SetComprehension: + Walk(w, x.Term) + Walk(w, x.Body) + case Call: + for _, t := range x { + Walk(w, t) + } + } +} + +// WalkVars calls the function f on all vars under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkVars(x interface{}, f func(Var) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if v, ok := x.(Var); ok { + return f(v) + } + return false + }} + vis.Walk(x) +} + +// WalkClosures calls the function f on all closures under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkClosures(x interface{}, f func(interface{}) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + switch x.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return f(x) + } + return false + }} + vis.Walk(x) +} + +// WalkRefs calls the function f on all references under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRefs(x interface{}, f func(Ref) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if r, ok := x.(Ref); ok { + return f(r) + } + return false + }} + vis.Walk(x) +} + +// WalkTerms calls the function f on all terms under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkTerms(x interface{}, f func(*Term) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if term, ok := x.(*Term); ok { + return f(term) + } + return false + }} + vis.Walk(x) +} + +// WalkWiths calls the function f on all with modifiers under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkWiths(x interface{}, f func(*With) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if w, ok := x.(*With); ok { + return f(w) + } + return false + }} + vis.Walk(x) +} + +// WalkExprs calls the function f on all expressions under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkExprs(x interface{}, f func(*Expr) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if r, ok := x.(*Expr); ok { + return f(r) + } + return false + }} + vis.Walk(x) +} + +// WalkBodies calls the function f on all bodies under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkBodies(x interface{}, f func(Body) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if b, ok := x.(Body); ok { + return f(b) + } + return false + }} + vis.Walk(x) +} + +// WalkRules calls the function f on all rules under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkRules(x interface{}, f func(*Rule) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if r, ok := x.(*Rule); ok { + stop := f(r) + // NOTE(tsandall): since rules cannot be embedded inside of queries + // we can stop early if there is no else block. + if stop || r.Else == nil { + return true + } + } + return false + }} + vis.Walk(x) +} + +// WalkNodes calls the function f on all nodes under x. If the function f +// returns true, AST nodes under the last node will not be visited. +func WalkNodes(x interface{}, f func(Node) bool) { + vis := &GenericVisitor{func(x interface{}) bool { + if n, ok := x.(Node); ok { + return f(n) + } + return false + }} + vis.Walk(x) +} + +// GenericVisitor provides a utility to walk over AST nodes using a +// closure. If the closure returns true, the visitor will not walk +// over AST nodes under x. +type GenericVisitor struct { + f func(x interface{}) bool +} + +// NewGenericVisitor returns a new GenericVisitor that will invoke the function +// f on AST nodes. +func NewGenericVisitor(f func(x interface{}) bool) *GenericVisitor { + return &GenericVisitor{f} +} + +// Walk iterates the AST by calling the function f on the +// GenericVisitor before recursing. Contrary to the generic Walk, this +// does not require allocating the visitor from heap. +func (vis *GenericVisitor) Walk(x interface{}) { + if vis.f(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for _, i := range x.Imports { + vis.Walk(i) + } + for _, r := range x.Rules { + vis.Walk(r) + } + for _, c := range x.Comments { + vis.Walk(c) + } + case *Package: + vis.Walk(x.Path) + case *Import: + vis.Walk(x.Path) + vis.Walk(x.Alias) + case *Rule: + vis.Walk(x.Head) + vis.Walk(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + vis.Walk(x.Name) + vis.Walk(x.Args) + if x.Key != nil { + vis.Walk(x.Key) + } + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + for _, e := range x { + vis.Walk(e) + } + case Args: + for _, t := range x { + vis.Walk(t) + } + case *Expr: + switch ts := x.Terms.(type) { + case *SomeDecl: + vis.Walk(ts) + case []*Term: + for _, t := range ts { + vis.Walk(t) + } + case *Term: + vis.Walk(ts) + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target) + vis.Walk(x.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for _, t := range x { + vis.Walk(t) + } + case Object: + for _, k := range x.Keys() { + vis.Walk(k) + vis.Walk(x.Get(k)) + } + case Array: + for _, t := range x { + vis.Walk(t) + } + case Set: + for _, t := range x.Slice() { + vis.Walk(t) + } + case *ArrayComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Body) + case *SetComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case Call: + for _, t := range x { + vis.Walk(t) + } + } +} + +// BeforeAfterVisitor provides a utility to walk over AST nodes using +// closures. If the before closure returns true, the visitor will not +// walk over AST nodes under x. The after closure is invoked always +// after visiting a node. +type BeforeAfterVisitor struct { + before func(x interface{}) bool + after func(x interface{}) +} + +// NewBeforeAfterVisitor returns a new BeforeAndAfterVisitor that +// will invoke the functions before and after AST nodes. +func NewBeforeAfterVisitor(before func(x interface{}) bool, after func(x interface{})) *BeforeAfterVisitor { + return &BeforeAfterVisitor{before, after} +} + +// Walk iterates the AST by calling the functions on the +// BeforeAndAfterVisitor before and after recursing. Contrary to the +// generic Walk, this does not require allocating the visitor from +// heap. +func (vis *BeforeAfterVisitor) Walk(x interface{}) { + defer vis.after(x) + if vis.before(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for _, i := range x.Imports { + vis.Walk(i) + } + for _, r := range x.Rules { + vis.Walk(r) + } + for _, c := range x.Comments { + vis.Walk(c) + } + case *Package: + vis.Walk(x.Path) + case *Import: + vis.Walk(x.Path) + vis.Walk(x.Alias) + case *Rule: + vis.Walk(x.Head) + vis.Walk(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + vis.Walk(x.Name) + vis.Walk(x.Args) + if x.Key != nil { + vis.Walk(x.Key) + } + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + for _, e := range x { + vis.Walk(e) + } + case Args: + for _, t := range x { + vis.Walk(t) + } + case *Expr: + switch ts := x.Terms.(type) { + case *SomeDecl: + vis.Walk(ts) + case []*Term: + for _, t := range ts { + vis.Walk(t) + } + case *Term: + vis.Walk(ts) + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target) + vis.Walk(x.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for _, t := range x { + vis.Walk(t) + } + case Object: + for _, k := range x.Keys() { + vis.Walk(k) + vis.Walk(x.Get(k)) + } + case Array: + for _, t := range x { + vis.Walk(t) + } + case Set: + for _, t := range x.Slice() { + vis.Walk(t) + } + case *ArrayComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Body) + case *SetComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case Call: + for _, t := range x { + vis.Walk(t) + } + } +} + +// VarVisitor walks AST nodes under a given node and collects all encountered +// variables. The collected variables can be controlled by specifying +// VarVisitorParams when creating the visitor. +type VarVisitor struct { + params VarVisitorParams + vars VarSet +} + +// VarVisitorParams contains settings for a VarVisitor. +type VarVisitorParams struct { + SkipRefHead bool + SkipRefCallHead bool + SkipObjectKeys bool + SkipClosures bool + SkipWithTarget bool + SkipSets bool +} + +// NewVarVisitor returns a new VarVisitor object. +func NewVarVisitor() *VarVisitor { + return &VarVisitor{ + vars: NewVarSet(), + } +} + +// WithParams sets the parameters in params on vis. +func (vis *VarVisitor) WithParams(params VarVisitorParams) *VarVisitor { + vis.params = params + return vis +} + +// Vars returns a VarSet that contains collected vars. +func (vis *VarVisitor) Vars() VarSet { + return vis.vars +} + +func (vis *VarVisitor) visit(v interface{}) bool { + if vis.params.SkipObjectKeys { + if o, ok := v.(Object); ok { + for _, k := range o.Keys() { + vis.Walk(o.Get(k)) + } + return true + } + } + if vis.params.SkipRefHead { + if r, ok := v.(Ref); ok { + for _, t := range r[1:] { + vis.Walk(t) + } + return true + } + } + if vis.params.SkipClosures { + switch v.(type) { + case *ArrayComprehension, *ObjectComprehension, *SetComprehension: + return true + } + } + if vis.params.SkipWithTarget { + if v, ok := v.(*With); ok { + vis.Walk(v.Value) + return true + } + } + if vis.params.SkipSets { + if _, ok := v.(Set); ok { + return true + } + } + if vis.params.SkipRefCallHead { + switch v := v.(type) { + case *Expr: + if terms, ok := v.Terms.([]*Term); ok { + for _, t := range terms[0].Value.(Ref)[1:] { + vis.Walk(t) + } + for i := 1; i < len(terms); i++ { + vis.Walk(terms[i]) + } + for _, w := range v.With { + vis.Walk(w) + } + return true + } + case Call: + operator := v[0].Value.(Ref) + for i := 1; i < len(operator); i++ { + vis.Walk(operator[i]) + } + for i := 1; i < len(v); i++ { + vis.Walk(v[i]) + } + return true + } + } + if v, ok := v.(Var); ok { + vis.vars.Add(v) + } + return false +} + +// Walk iterates the AST by calling the function f on the +// GenericVisitor before recursing. Contrary to the generic Walk, this +// does not require allocating the visitor from heap. +func (vis *VarVisitor) Walk(x interface{}) { + if vis.visit(x) { + return + } + + switch x := x.(type) { + case *Module: + vis.Walk(x.Package) + for _, i := range x.Imports { + vis.Walk(i) + } + for _, r := range x.Rules { + vis.Walk(r) + } + for _, c := range x.Comments { + vis.Walk(c) + } + case *Package: + vis.Walk(x.Path) + case *Import: + vis.Walk(x.Path) + vis.Walk(x.Alias) + case *Rule: + vis.Walk(x.Head) + vis.Walk(x.Body) + if x.Else != nil { + vis.Walk(x.Else) + } + case *Head: + vis.Walk(x.Name) + vis.Walk(x.Args) + if x.Key != nil { + vis.Walk(x.Key) + } + if x.Value != nil { + vis.Walk(x.Value) + } + case Body: + for _, e := range x { + vis.Walk(e) + } + case Args: + for _, t := range x { + vis.Walk(t) + } + case *Expr: + switch ts := x.Terms.(type) { + case *SomeDecl: + vis.Walk(ts) + case []*Term: + for _, t := range ts { + vis.Walk(t) + } + case *Term: + vis.Walk(ts) + } + for i := range x.With { + vis.Walk(x.With[i]) + } + case *With: + vis.Walk(x.Target) + vis.Walk(x.Value) + case *Term: + vis.Walk(x.Value) + case Ref: + for _, t := range x { + vis.Walk(t) + } + case Object: + for _, k := range x.Keys() { + vis.Walk(k) + vis.Walk(x.Get(k)) + } + case Array: + for _, t := range x { + vis.Walk(t) + } + case Set: + for _, t := range x.Slice() { + vis.Walk(t) + } + case *ArrayComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case *ObjectComprehension: + vis.Walk(x.Key) + vis.Walk(x.Value) + vis.Walk(x.Body) + case *SetComprehension: + vis.Walk(x.Term) + vis.Walk(x.Body) + case Call: + for _, t := range x { + vis.Walk(t) + } + } +} diff --git a/vendor/github.com/open-policy-agent/opa/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go new file mode 100644 index 000000000..7e82a8127 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go @@ -0,0 +1,493 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package bundle implements bundle loading. +package bundle + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io" + "net/url" + "path/filepath" + "reflect" + "strings" + + "github.com/open-policy-agent/opa/internal/file/archive" + "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/metrics" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/util" +) + +// Common file extensions and file names. +const ( + RegoExt = ".rego" + WasmFile = "/policy.wasm" + manifestExt = ".manifest" + dataFile = "data.json" + yamlDataFile = "data.yaml" +) + +const bundleLimitBytes = (1024 * 1024 * 1024) + 1 // limit bundle reads to 1GB to protect against gzip bombs + +// Bundle represents a loaded bundle. The bundle can contain data and policies. +type Bundle struct { + Manifest Manifest + Data map[string]interface{} + Modules []ModuleFile + Wasm []byte +} + +// Manifest represents the manifest from a bundle. The manifest may contain +// metadata such as the bundle revision. +type Manifest struct { + Revision string `json:"revision"` + Roots *[]string `json:"roots,omitempty"` +} + +// Init initializes the manifest. If you instantiate a manifest +// manually, call Init to ensure that the roots are set properly. +func (m *Manifest) Init() { + if m.Roots == nil { + defaultRoots := []string{""} + m.Roots = &defaultRoots + } +} + +func (m *Manifest) validateAndInjectDefaults(b Bundle) error { + + m.Init() + + // Validate roots in bundle. + roots := *m.Roots + + // Standardize the roots (no starting or trailing slash) + for i := range roots { + roots[i] = strings.Trim(roots[i], "/") + } + + for i := 0; i < len(roots)-1; i++ { + for j := i + 1; j < len(roots); j++ { + if RootPathsOverlap(roots[i], roots[j]) { + return fmt.Errorf("manifest has overlapped roots: %v and %v", roots[i], roots[j]) + } + } + } + + // Validate modules in bundle. + for _, module := range b.Modules { + found := false + if path, err := module.Parsed.Package.Path.Ptr(); err == nil { + for i := range roots { + if strings.HasPrefix(path, roots[i]) { + found = true + break + } + } + } + if !found { + return fmt.Errorf("manifest roots %v do not permit '%v' in module '%v'", roots, module.Parsed.Package, module.Path) + } + } + + // Validate data in bundle. + return dfs(b.Data, "", func(path string, node interface{}) (bool, error) { + path = strings.Trim(path, "/") + for i := range roots { + if strings.HasPrefix(path, roots[i]) { + return true, nil + } + } + if _, ok := node.(map[string]interface{}); ok { + for i := range roots { + if strings.HasPrefix(roots[i], path) { + return false, nil + } + } + } + return false, fmt.Errorf("manifest roots %v do not permit data at path '/%s' (hint: check bundle directory structure)", roots, path) + }) +} + +// ModuleFile represents a single module contained a bundle. +type ModuleFile struct { + Path string + Raw []byte + Parsed *ast.Module +} + +// Reader contains the reader to load the bundle from. +type Reader struct { + loader DirectoryLoader + includeManifestInData bool + metrics metrics.Metrics + baseDir string +} + +// NewReader returns a new Reader which is configured for reading tarballs. +func NewReader(r io.Reader) *Reader { + return NewCustomReader(NewTarballLoader(r)) +} + +// NewCustomReader returns a new Reader configured to use the +// specified DirectoryLoader. +func NewCustomReader(loader DirectoryLoader) *Reader { + nr := Reader{ + loader: loader, + metrics: metrics.New(), + } + return &nr +} + +// IncludeManifestInData sets whether the manifest metadata should be +// included in the bundle's data. +func (r *Reader) IncludeManifestInData(includeManifestInData bool) *Reader { + r.includeManifestInData = includeManifestInData + return r +} + +// WithMetrics sets the metrics object to be used while loading bundles +func (r *Reader) WithMetrics(m metrics.Metrics) *Reader { + r.metrics = m + return r +} + +// WithBaseDir sets a base directory for file paths of loaded Rego +// modules. This will *NOT* affect the loaded path of data files. +func (r *Reader) WithBaseDir(dir string) *Reader { + r.baseDir = dir + return r +} + +// Read returns a new Bundle loaded from the reader. +func (r *Reader) Read() (Bundle, error) { + + var bundle Bundle + + bundle.Data = map[string]interface{}{} + + for { + f, err := r.loader.NextFile() + if err == io.EOF { + break + } + if err != nil { + return bundle, errors.Wrap(err, "bundle read failed") + } + + var buf bytes.Buffer + n, err := f.Read(&buf, bundleLimitBytes) + f.Close() // always close, even on error + if err != nil && err != io.EOF { + return bundle, err + } else if err == nil && n >= bundleLimitBytes { + return bundle, fmt.Errorf("bundle exceeded max size (%v bytes)", bundleLimitBytes-1) + } + + // Normalize the paths to use `/` separators + path := filepath.ToSlash(f.Path()) + + if strings.HasSuffix(path, RegoExt) { + fullPath := r.fullPath(path) + r.metrics.Timer(metrics.RegoModuleParse).Start() + module, err := ast.ParseModule(fullPath, buf.String()) + r.metrics.Timer(metrics.RegoModuleParse).Stop() + if err != nil { + return bundle, err + } + + mf := ModuleFile{ + Path: fullPath, + Raw: buf.Bytes(), + Parsed: module, + } + bundle.Modules = append(bundle.Modules, mf) + + } else if path == WasmFile { + bundle.Wasm = buf.Bytes() + + } else if filepath.Base(path) == dataFile { + var value interface{} + + r.metrics.Timer(metrics.RegoDataParse).Start() + err := util.NewJSONDecoder(&buf).Decode(&value) + r.metrics.Timer(metrics.RegoDataParse).Stop() + + if err != nil { + return bundle, errors.Wrapf(err, "bundle load failed on %v", r.fullPath(path)) + } + + if err := insertValue(&bundle, path, value); err != nil { + return bundle, err + } + + } else if filepath.Base(path) == yamlDataFile { + + var value interface{} + + r.metrics.Timer(metrics.RegoDataParse).Start() + err := util.Unmarshal(buf.Bytes(), &value) + r.metrics.Timer(metrics.RegoDataParse).Stop() + + if err != nil { + return bundle, errors.Wrapf(err, "bundle load failed on %v", r.fullPath(path)) + } + + if err := insertValue(&bundle, path, value); err != nil { + return bundle, err + } + + } else if strings.HasSuffix(path, manifestExt) { + if err := util.NewJSONDecoder(&buf).Decode(&bundle.Manifest); err != nil { + return bundle, errors.Wrap(err, "bundle load failed on manifest decode") + } + } + } + + if err := bundle.Manifest.validateAndInjectDefaults(bundle); err != nil { + return bundle, err + } + + if r.includeManifestInData { + var metadata map[string]interface{} + + b, err := json.Marshal(&bundle.Manifest) + if err != nil { + return bundle, errors.Wrap(err, "bundle load failed on manifest marshal") + } + + err = util.UnmarshalJSON(b, &metadata) + if err != nil { + return bundle, errors.Wrap(err, "bundle load failed on manifest unmarshal") + } + + // For backwards compatibility always write to the old unnamed manifest path + // This will *not* be correct if >1 bundle is in use... + if err := bundle.insert(legacyManifestStoragePath, metadata); err != nil { + return bundle, errors.Wrapf(err, "bundle load failed on %v", legacyRevisionStoragePath) + } + } + + return bundle, nil +} + +func (r *Reader) fullPath(path string) string { + if r.baseDir != "" { + path = filepath.Join(r.baseDir, path) + } + return path +} + +// Write serializes the Bundle and writes it to w. +func Write(w io.Writer, bundle Bundle) error { + gw := gzip.NewWriter(w) + tw := tar.NewWriter(gw) + + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(bundle.Data); err != nil { + return err + } + + if err := archive.WriteFile(tw, "data.json", buf.Bytes()); err != nil { + return err + } + + for _, module := range bundle.Modules { + if err := archive.WriteFile(tw, module.Path, module.Raw); err != nil { + return err + } + } + + if err := writeWasm(tw, bundle); err != nil { + return err + } + + if err := writeManifest(tw, bundle); err != nil { + return err + } + + if err := tw.Close(); err != nil { + return err + } + + return gw.Close() +} + +func writeWasm(tw *tar.Writer, bundle Bundle) error { + if len(bundle.Wasm) == 0 { + return nil + } + + return archive.WriteFile(tw, WasmFile, bundle.Wasm) +} + +func writeManifest(tw *tar.Writer, bundle Bundle) error { + + var buf bytes.Buffer + + if err := json.NewEncoder(&buf).Encode(bundle.Manifest); err != nil { + return err + } + + return archive.WriteFile(tw, manifestExt, buf.Bytes()) +} + +// ParsedModules returns a map of parsed modules with names that are +// unique and human readable for the given a bundle name. +func (b *Bundle) ParsedModules(bundleName string) map[string]*ast.Module { + + mods := make(map[string]*ast.Module, len(b.Modules)) + + for _, mf := range b.Modules { + mods[modulePathWithPrefix(bundleName, mf.Path)] = mf.Parsed + } + + return mods +} + +// Equal returns true if this bundle's contents equal the other bundle's +// contents. +func (b Bundle) Equal(other Bundle) bool { + if !reflect.DeepEqual(b.Data, other.Data) { + return false + } + if len(b.Modules) != len(other.Modules) { + return false + } + for i := range b.Modules { + if b.Modules[i].Path != other.Modules[i].Path { + return false + } + if !b.Modules[i].Parsed.Equal(other.Modules[i].Parsed) { + return false + } + if !bytes.Equal(b.Modules[i].Raw, other.Modules[i].Raw) { + return false + } + } + if (b.Wasm == nil && other.Wasm != nil) || (b.Wasm != nil && other.Wasm == nil) { + return false + } + + return bytes.Equal(b.Wasm, other.Wasm) +} + +func (b *Bundle) insert(key []string, value interface{}) error { + // Build an object with the full structure for the value + obj, err := mktree(key, value) + if err != nil { + return err + } + + // Merge the new data in with the current bundle data object + merged, ok := merge.InterfaceMaps(b.Data, obj) + if !ok { + return fmt.Errorf("failed to insert data file from path %s", filepath.Join(key...)) + } + + b.Data = merged + + return nil +} + +func mktree(path []string, value interface{}) (map[string]interface{}, error) { + if len(path) == 0 { + // For 0 length path the value is the full tree. + obj, ok := value.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("root value must be object") + } + return obj, nil + } + + dir := map[string]interface{}{} + for i := len(path) - 1; i > 0; i-- { + dir[path[i]] = value + value = dir + dir = map[string]interface{}{} + } + dir[path[0]] = value + + return dir, nil +} + +// RootPathsOverlap takes in two bundle root paths and returns +// true if they overlap. +func RootPathsOverlap(pathA string, pathB string) bool { + + // Special case for empty prefixes, they always overlap + if pathA == "" || pathB == "" { + return true + } + + aParts := strings.Split(pathA, "/") + bParts := strings.Split(pathB, "/") + + for i := 0; i < len(aParts) && i < len(bParts); i++ { + if aParts[i] != bParts[i] { + // Found diverging path segments, no overlap + return false + } + } + return true +} + +func insertValue(b *Bundle, path string, value interface{}) error { + + // Remove leading / and . characters from the directory path. If the bundle + // was written with OPA then the paths will contain a leading slash. On the + // other hand, if the path is empty, filepath.Dir will return '.'. + // Note: filepath.Dir can return paths with '\' separators, always use + // filepath.ToSlash to keep them normalized. + dirpath := strings.TrimLeft(filepath.ToSlash(filepath.Dir(path)), "/.") + var key []string + if dirpath != "" { + key = strings.Split(dirpath, "/") + } + if err := b.insert(key, value); err != nil { + return errors.Wrapf(err, "bundle load failed on %v", path) + } + + return nil +} + +func dfs(value interface{}, path string, fn func(string, interface{}) (bool, error)) error { + if stop, err := fn(path, value); err != nil { + return err + } else if stop { + return nil + } + obj, ok := value.(map[string]interface{}) + if !ok { + return nil + } + for key := range obj { + if err := dfs(obj[key], path+"/"+key, fn); err != nil { + return err + } + } + return nil +} + +func modulePathWithPrefix(bundleName string, modulePath string) string { + // Default prefix is just the bundle name + prefix := bundleName + + // Bundle names are sometimes just file paths, some of which + // are full urls (file:///foo/). Parse these and only use the path. + parsed, err := url.Parse(bundleName) + if err == nil { + prefix = filepath.Join(parsed.Host, parsed.Path) + } + + return filepath.Join(prefix, modulePath) +} diff --git a/vendor/github.com/open-policy-agent/opa/bundle/file.go b/vendor/github.com/open-policy-agent/opa/bundle/file.go new file mode 100644 index 000000000..ab1d1c72a --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/bundle/file.go @@ -0,0 +1,166 @@ +package bundle + +import ( + "archive/tar" + "compress/gzip" + "io" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/pkg/errors" +) + +// Descriptor contains information about a file and +// can be used to read the file contents. +type Descriptor struct { + path string + reader io.Reader + closer io.Closer + closeOnce *sync.Once +} + +func newDescriptor(path string, reader io.Reader) *Descriptor { + return &Descriptor{ + path: path, + reader: reader, + } +} + +func (d *Descriptor) withCloser(closer io.Closer) *Descriptor { + d.closer = closer + d.closeOnce = new(sync.Once) + return d +} + +// Path returns the path of the file. +func (d *Descriptor) Path() string { + return d.path +} + +// Read will read all the contents from the file the Descriptor refers to +// into the dest writer up n bytes. Will return an io.EOF error +// if EOF is encountered before n bytes are read. +func (d *Descriptor) Read(dest io.Writer, n int64) (int64, error) { + n, err := io.CopyN(dest, d.reader, n) + return n, err +} + +// Close the file, on some Loader implementations this might be a no-op. +// It should *always* be called regardless of file. +func (d *Descriptor) Close() error { + var err error + if d.closer != nil { + d.closeOnce.Do(func() { + err = d.closer.Close() + }) + } + return err +} + +// DirectoryLoader defines an interface which can be used to load +// files from a directory by iterating over each one in the tree. +type DirectoryLoader interface { + // NextFile must return io.EOF if there is no next value. The returned + // descriptor should *always* be closed when no longer needed. + NextFile() (*Descriptor, error) +} + +type dirLoader struct { + root string + files []string + idx int +} + +// NewDirectoryLoader returns a basic DirectoryLoader implementation +// that will load files from a given root directory path. +func NewDirectoryLoader(root string) DirectoryLoader { + d := dirLoader{ + root: root, + } + return &d +} + +// NextFile iterates to the next file in the directory tree +// and returns a file Descriptor for the file. +func (d *dirLoader) NextFile() (*Descriptor, error) { + // build a list of all files we will iterate over and read, but only one time + if d.files == nil { + d.files = []string{} + err := filepath.Walk(d.root, func(path string, info os.FileInfo, err error) error { + if info != nil && info.Mode().IsRegular() { + d.files = append(d.files, filepath.ToSlash(path)) + } + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "failed to list files") + } + } + + // If done reading files then just return io.EOF + // errors for each NextFile() call + if d.idx >= len(d.files) { + return nil, io.EOF + } + + fileName := d.files[d.idx] + d.idx++ + fh, err := os.Open(fileName) + if err != nil { + return nil, errors.Wrapf(err, "failed to open file %s", fileName) + } + + // Trim off the root directory and return path as if chrooted + cleanedPath := strings.TrimPrefix(fileName, d.root) + if !strings.HasPrefix(cleanedPath, "/") { + cleanedPath = "/" + cleanedPath + } + + f := newDescriptor(cleanedPath, fh).withCloser(fh) + return f, nil +} + +type tarballLoader struct { + r io.Reader + tr *tar.Reader +} + +// NewTarballLoader returns a new DirectoryLoader that reads +// files out of a gzipped tar archive. +func NewTarballLoader(r io.Reader) DirectoryLoader { + l := tarballLoader{ + r: r, + } + return &l +} + +// NextFile iterates to the next file in the directory tree +// and returns a file Descriptor for the file. +func (t *tarballLoader) NextFile() (*Descriptor, error) { + if t.tr == nil { + gr, err := gzip.NewReader(t.r) + if err != nil { + return nil, errors.Wrap(err, "archive read failed") + } + + t.tr = tar.NewReader(gr) + } + + for { + header, err := t.tr.Next() + // Eventually we will get an io.EOF error when finished + // iterating through the archive + if err != nil { + return nil, err + } + + // Keep iterating on the archive until we find a normal file + if header.Typeflag == tar.TypeReg { + // no need to close this descriptor after reading + f := newDescriptor(header.Name, t.tr) + return f, nil + } + } +} diff --git a/vendor/github.com/open-policy-agent/opa/bundle/store.go b/vendor/github.com/open-policy-agent/opa/bundle/store.go new file mode 100644 index 000000000..481ff6ef3 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/bundle/store.go @@ -0,0 +1,526 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package bundle + +import ( + "context" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/metrics" + + "github.com/open-policy-agent/opa/ast" + + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/util" +) + +var bundlesBasePath = storage.MustParsePath("/system/bundles") + +// Note: As needed these helpers could be memoized. + +// ManifestStoragePath is the storage path used for the given named bundle manifest. +func ManifestStoragePath(name string) storage.Path { + return append(bundlesBasePath, name, "manifest") +} + +func namedBundlePath(name string) storage.Path { + return append(bundlesBasePath, name) +} + +func rootsPath(name string) storage.Path { + return append(bundlesBasePath, name, "manifest", "roots") +} + +func revisionPath(name string) storage.Path { + return append(bundlesBasePath, name, "manifest", "revision") +} + +// ReadBundleNamesFromStore will return a list of bundle names which have had their metadata stored. +func ReadBundleNamesFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) ([]string, error) { + value, err := store.Read(ctx, txn, bundlesBasePath) + if err != nil { + return nil, err + } + + bundleMap, ok := value.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("corrupt manifest roots") + } + + bundles := make([]string, len(bundleMap)) + idx := 0 + for name := range bundleMap { + bundles[idx] = name + idx++ + } + return bundles, nil +} + +// WriteManifestToStore will write the manifest into the storage. This function is called when +// the bundle is activated. +func WriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string, manifest Manifest) error { + return write(ctx, store, txn, ManifestStoragePath(name), manifest) +} + +func write(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path, manifest Manifest) error { + var value interface{} = manifest + if err := util.RoundTrip(&value); err != nil { + return err + } + + var dir []string + if len(path) > 1 { + dir = path[:len(path)-1] + } + + if err := storage.MakeDir(ctx, store, txn, dir); err != nil { + return err + } + + return store.Write(ctx, txn, storage.AddOp, path, value) +} + +// EraseManifestFromStore will remove the manifest from storage. This function is called +// when the bundle is deactivated. +func EraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) error { + path := namedBundlePath(name) + err := store.Write(ctx, txn, storage.RemoveOp, path, nil) + if err != nil && !storage.IsNotFound(err) { + return err + } + return nil +} + +// ReadBundleRootsFromStore returns the roots in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRootsFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) ([]string, error) { + value, err := store.Read(ctx, txn, rootsPath(name)) + if err != nil { + return nil, err + } + + sl, ok := value.([]interface{}) + if !ok { + return nil, fmt.Errorf("corrupt manifest roots") + } + + roots := make([]string, len(sl)) + + for i := range sl { + roots[i], ok = sl[i].(string) + if !ok { + return nil, fmt.Errorf("corrupt manifest root") + } + } + + return roots, nil +} + +// ReadBundleRevisionFromStore returns the revision in the specified bundle. +// If the bundle is not activated, this function will return +// storage NotFound error. +func ReadBundleRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, name string) (string, error) { + return readRevisionFromStore(ctx, store, txn, revisionPath(name)) +} + +func readRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction, path storage.Path) (string, error) { + value, err := store.Read(ctx, txn, path) + if err != nil { + return "", err + } + + str, ok := value.(string) + if !ok { + return "", fmt.Errorf("corrupt manifest revision") + } + + return str, nil +} + +// ActivateOpts defines options for the Activate API call. +type ActivateOpts struct { + Ctx context.Context + Store storage.Store + Txn storage.Transaction + Compiler *ast.Compiler + Metrics metrics.Metrics + Bundles map[string]*Bundle // Optional + ExtraModules map[string]*ast.Module // Optional + + legacy bool +} + +// Activate the bundle(s) by loading into the given Store. This will load policies, data, and record +// the manifest in storage. The compiler provided will have had the polices compiled on it. +func Activate(opts *ActivateOpts) error { + opts.legacy = false + return activateBundles(opts) +} + +// DeactivateOpts defines options for the Deactivate API call +type DeactivateOpts struct { + Ctx context.Context + Store storage.Store + Txn storage.Transaction + BundleNames map[string]struct{} +} + +// Deactivate the bundle(s). This will erase associated data, policies, and the manifest entry from the store. +func Deactivate(opts *DeactivateOpts) error { + erase := map[string]struct{}{} + for name := range opts.BundleNames { + if roots, err := ReadBundleRootsFromStore(opts.Ctx, opts.Store, opts.Txn, name); err == nil { + for _, root := range roots { + erase[root] = struct{}{} + } + } else if !storage.IsNotFound(err) { + return err + } + } + _, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, opts.BundleNames, erase) + return err +} + +func activateBundles(opts *ActivateOpts) error { + + // Build collections of bundle names, modules, and roots to erase + erase := map[string]struct{}{} + names := map[string]struct{}{} + + for name, b := range opts.Bundles { + names[name] = struct{}{} + + if roots, err := ReadBundleRootsFromStore(opts.Ctx, opts.Store, opts.Txn, name); err == nil { + for _, root := range roots { + erase[root] = struct{}{} + } + } else if !storage.IsNotFound(err) { + return err + } + + // Erase data at new roots to prepare for writing the new data + for _, root := range *b.Manifest.Roots { + erase[root] = struct{}{} + } + } + + // Before changing anything make sure the roots don't collide with any + // other bundles that already are activated or other bundles being activated. + err := hasRootsOverlap(opts.Ctx, opts.Store, opts.Txn, opts.Bundles) + if err != nil { + return err + } + + // Erase data and policies at new + old roots, and remove the old + // manifests before activating a new bundles. + remaining, err := eraseBundles(opts.Ctx, opts.Store, opts.Txn, names, erase) + if err != nil { + return err + } + + for _, b := range opts.Bundles { + // Write data from each new bundle into the store. Only write under the + // roots contained in their manifest. This should be done *before* the + // policies so that path conflict checks can occur. + if err := writeData(opts.Ctx, opts.Store, opts.Txn, *b.Manifest.Roots, b.Data); err != nil { + return err + } + } + + // Write and compile the modules all at once to avoid having to re-do work. + remainingAndExtra := make(map[string]*ast.Module) + for name, mod := range remaining { + remainingAndExtra[name] = mod + } + for name, mod := range opts.ExtraModules { + remainingAndExtra[name] = mod + } + + err = writeModules(opts.Ctx, opts.Store, opts.Txn, opts.Compiler, opts.Metrics, opts.Bundles, remainingAndExtra, opts.legacy) + if err != nil { + return err + } + + for name, b := range opts.Bundles { + // Always write manifests to the named location. If the plugin is in the older style config + // then also write to the old legacy unnamed location. + if err := WriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, name, b.Manifest); err != nil { + return err + } + if opts.legacy { + if err := LegacyWriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, b.Manifest); err != nil { + return err + } + } + } + + return nil +} + +// erase bundles by name and roots. This will clear all policies and data at its roots and remove its +// manifest from storage. +func eraseBundles(ctx context.Context, store storage.Store, txn storage.Transaction, names map[string]struct{}, roots map[string]struct{}) (map[string]*ast.Module, error) { + + if err := eraseData(ctx, store, txn, roots); err != nil { + return nil, err + } + + remaining, err := erasePolicies(ctx, store, txn, roots) + if err != nil { + return nil, err + } + + for name := range names { + if err := EraseManifestFromStore(ctx, store, txn, name); err != nil && !storage.IsNotFound(err) { + return nil, err + } + + if err := LegacyEraseManifestFromStore(ctx, store, txn); err != nil && !storage.IsNotFound(err) { + return nil, err + } + } + + return remaining, nil +} + +func eraseData(ctx context.Context, store storage.Store, txn storage.Transaction, roots map[string]struct{}) error { + for root := range roots { + path, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("manifest root path invalid: %v", root) + } + if len(path) > 0 { + if err := store.Write(ctx, txn, storage.RemoveOp, path, nil); err != nil { + if !storage.IsNotFound(err) { + return err + } + } + } + } + return nil +} + +func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transaction, roots map[string]struct{}) (map[string]*ast.Module, error) { + + ids, err := store.ListPolicies(ctx, txn) + if err != nil { + return nil, err + } + + remaining := map[string]*ast.Module{} + + for _, id := range ids { + bs, err := store.GetPolicy(ctx, txn, id) + if err != nil { + return nil, err + } + module, err := ast.ParseModule(id, string(bs)) + if err != nil { + return nil, err + } + path, err := module.Package.Path.Ptr() + if err != nil { + return nil, err + } + deleted := false + for root := range roots { + if strings.HasPrefix(path, root) { + if err := store.DeletePolicy(ctx, txn, id); err != nil { + return nil, err + } + deleted = true + break + } + } + if !deleted { + remaining[id] = module + } + } + + return remaining, nil +} + +func writeData(ctx context.Context, store storage.Store, txn storage.Transaction, roots []string, data map[string]interface{}) error { + for _, root := range roots { + path, ok := storage.ParsePathEscaped("/" + root) + if !ok { + return fmt.Errorf("manifest root path invalid: %v", root) + } + if value, ok := lookup(path, data); ok { + if len(path) > 0 { + if err := storage.MakeDir(ctx, store, txn, path[:len(path)-1]); err != nil { + return err + } + } + if err := store.Write(ctx, txn, storage.AddOp, path, value); err != nil { + return err + } + } + } + return nil +} + +func writeModules(ctx context.Context, store storage.Store, txn storage.Transaction, compiler *ast.Compiler, m metrics.Metrics, bundles map[string]*Bundle, extraModules map[string]*ast.Module, legacy bool) error { + + m.Timer(metrics.RegoModuleCompile).Start() + defer m.Timer(metrics.RegoModuleCompile).Stop() + + modules := map[string]*ast.Module{} + + // preserve any modules already on the compiler + for name, module := range compiler.Modules { + modules[name] = module + } + + // preserve any modules passed in from the store + for name, module := range extraModules { + modules[name] = module + } + + // include all the new bundle modules + for bundleName, b := range bundles { + if legacy { + for _, mf := range b.Modules { + modules[mf.Path] = mf.Parsed + } + } else { + for name, module := range b.ParsedModules(bundleName) { + modules[name] = module + } + } + } + + if compiler.Compile(modules); compiler.Failed() { + return compiler.Errors + } + for bundleName, b := range bundles { + for _, mf := range b.Modules { + var path string + + // For backwards compatibility, in legacy mode, upsert policies to + // the unprefixed path. + if legacy { + path = mf.Path + } else { + path = modulePathWithPrefix(bundleName, mf.Path) + } + + if err := store.UpsertPolicy(ctx, txn, path, mf.Raw); err != nil { + return err + } + } + } + return nil +} + +func lookup(path storage.Path, data map[string]interface{}) (interface{}, bool) { + if len(path) == 0 { + return data, true + } + for i := 0; i < len(path)-1; i++ { + value, ok := data[path[i]] + if !ok { + return nil, false + } + obj, ok := value.(map[string]interface{}) + if !ok { + return nil, false + } + data = obj + } + value, ok := data[path[len(path)-1]] + return value, ok +} + +func hasRootsOverlap(ctx context.Context, store storage.Store, txn storage.Transaction, bundles map[string]*Bundle) error { + collisions := map[string][]string{} + allBundles, err := ReadBundleNamesFromStore(ctx, store, txn) + if err != nil && !storage.IsNotFound(err) { + return err + } + + allRoots := map[string][]string{} + + // Build a map of roots for existing bundles already in the system + for _, name := range allBundles { + roots, err := ReadBundleRootsFromStore(ctx, store, txn, name) + if err != nil && !storage.IsNotFound(err) { + return err + } + allRoots[name] = roots + } + + // Add in any bundles that are being activated, overwrite existing roots + // with new ones where bundles are in both groups. + for name, bundle := range bundles { + allRoots[name] = *bundle.Manifest.Roots + } + + // Now check for each new bundle if it conflicts with any of the others + for name, bundle := range bundles { + for otherBundle, otherRoots := range allRoots { + if name == otherBundle { + // Skip the current bundle being checked + continue + } + + // Compare the "new" roots with other existing (or a different bundles new roots) + for _, newRoot := range *bundle.Manifest.Roots { + for _, otherRoot := range otherRoots { + if RootPathsOverlap(newRoot, otherRoot) { + collisions[otherBundle] = append(collisions[otherBundle], newRoot) + } + } + } + } + } + + if len(collisions) > 0 { + var bundleNames []string + for name := range collisions { + bundleNames = append(bundleNames, name) + } + return fmt.Errorf("detected overlapping roots in bundle manifest with: %s", bundleNames) + } + return nil +} + +// Helpers for the older single (unnamed) bundle style manifest storage. + +// LegacyManifestStoragePath is the older unnamed bundle path for manifests to be stored. +// Deprecated: Use ManifestStoragePath and named bundles instead. +var legacyManifestStoragePath = storage.MustParsePath("/system/bundle/manifest") +var legacyRevisionStoragePath = append(legacyManifestStoragePath, "revision") + +// LegacyWriteManifestToStore will write the bundle manifest to the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyWriteManifestToStore(ctx context.Context, store storage.Store, txn storage.Transaction, manifest Manifest) error { + return write(ctx, store, txn, legacyManifestStoragePath, manifest) +} + +// LegacyEraseManifestFromStore will erase the bundle manifest from the older single (unnamed) bundle manifest location. +// Deprecated: Use WriteManifestToStore and named bundles instead. +func LegacyEraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) error { + err := store.Write(ctx, txn, storage.RemoveOp, legacyManifestStoragePath, nil) + if err != nil { + return err + } + return nil +} + +// LegacyReadRevisionFromStore will read the bundle manifest revision from the older single (unnamed) bundle manifest location. +// Deprecated: Use ReadBundleRevisionFromStore and named bundles instead. +func LegacyReadRevisionFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) (string, error) { + return readRevisionFromStore(ctx, store, txn, legacyRevisionStoragePath) +} + +// ActivateLegacy calls Activate for the bundles but will also write their manifest to the older unnamed store location. +// Deprecated: Use Activate with named bundles instead. +func ActivateLegacy(opts *ActivateOpts) error { + opts.legacy = true + return activateBundles(opts) +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.go b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.go new file mode 100644 index 000000000..4861409a7 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.go @@ -0,0 +1,25 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// THIS FILE IS GENERATED. DO NOT EDIT. + +// Package opa contains bytecode for the OPA-WASM library. +package opa + +import ( + "bytes" + "compress/gzip" + "io/ioutil" +) + +// Bytes returns the OPA-WASM bytecode. +func Bytes() ([]byte, error) { + gr, err := gzip.NewReader(bytes.NewBuffer(gzipped)) + if err != nil { + return nil, err + } + return ioutil.ReadAll(gr) +} + +var gzipped = []byte("\x1F\x8B\x08\x00\x00\x00\x00\x00\x00\xFF\xD4\xBD\x09\x90\x65\xD7\x59\x18\x7C\x96\xBB\x9F\x7B\xBB\x6F\x8F\x34\x92\xAC\x96\xAD\xEF\x5E\xDB\x30\xB2\x34\x42\xB6\xE5\x91\x35\x2C\xD3\xA7\xAD\x99\x51\x6B\x10\x63\xB0\x0C\x48\x80\x7B\x5A\x3D\x6F\x46\xFD\xBA\xA7\x7B\xFA\xF5\x9B\x91\xC4\x3F\x9E\xD7\xDA\x2C\x2F\xFC\x05\x55\x54\x12\x23\x42\x90\xE5\x24\x22\x4E\x54\x09\x90\x20\x0C\x45\x0C\x45\xC5\x24\x54\x19\x5C\x31\x14\x2E\x70\xC0\x86\xD8\x09\xC1\x18\x8A\xAD\x5C\xA1\x40\xA9\xEF\xFB\xCE\x5D\xDE\xD6\x33\xC6\x14\x4B\xAB\x34\xEF\xBD\x73\xCF\xF2\xED\xDF\x77\xCE\x3D\xE7\x7C\x62\x65\xE7\x9C\x14\x42\xC8\x1F\x94\xD7\x9D\x52\x83\x81\x1C\x9C\xF2\x06\x83\xC1\x40\x9C\xD2\x03\xFA\x25\x07\x82\x4B\xE4\xE0\x94\x3F\x70\x5F\x82\x41\xF5\x4D\x88\x53\x82\x6A\xC9\xC1\x29\x35\xB8\x8C\xFF\x5E\xBA\xC4\x05\x97\xA8\x0B\x71\x4A\xD2\x6F\x7A\x84\x3F\xD5\xE0\xB2\x38\x15\x61\xF3\x4B\xDC\x85\x19\x34\x7F\x72\x70\x4A\x5F\x1E\x70\x67\x97\xE4\x40\x5D\x0E\x75\x67\xF3\x62\x70\xAE\x73\x6E\xAB\xF7\xB8\x12\x0A\x7F\xC5\x5B\xE7\x57\x96\x57\x1E\xDE\xEA\xF5\x85\xC6\xDF\x29\xFE\x7E\xF8\xC2\xDA\x46\x7F\x6D\xF3\x0E\x21\x46\x8B\xDE\xC8\xAD\xDA\x45\x6F\x12\xDE\x68\xD1\x9B\x85\x3F\x5A\x74\xA7\x08\xF4\xD3\xF2\x49\x19\x46\x91\xD6\xB1\x8E\x05\xFE\x25\x82\xFF\x62\xFC\x33\x22\xC5\xAF\x5A\x68\xAD\xB5\x10\x59\x1C\xC7\x33\x62\x36\x16\xB1\x8A\xE3\x3C\x8E\xE2\x58\xE8\x28\x8E\xA2\x7C\x2E\xCF\x45\x2C\x94\xE7\x49\xB9\x6F\xDF\x35\x5E\xAC\xA8\xF9\xB5\x4A\x45\x79\x1E\x87\x71\x1C\x7B\x1A\x0B\x32\x25\x62\x11\x0B\x1D\xC7\x0A\xBF\xED\x4F\x84\x50\xC2\x57\x2A\xF6\x7C\x79\x5E\x06\x41\x50\xF8\x03\x69\x9F\xFF\x80\x67\x06\xA2\xFA\xD8\x8D\xF0\xDF\x7F\x6E\xF0\xDF\x0F\x1A\x13\xFE\xF0\xFE\xC1\xDC\xF2\xF2\xA3\x2B\x3B\xE7\x96\x57\x57\x36\x36\x96\x57\xFB\x5B\xBD\x1D\x11\x98\xE5\xE5\x47\x3A\x2B\xE7\x97\x1F\x5E\xD9\xE9\x68\x99\x2E\x2F\x9F\xDE\xD9\x5A\x7E\x64\x65\xF3\xF4\x46\x47\xAB\x64\x79\xF9\xF4\x4A\x7F\x65\xB9\xB3\x79\x5A\xEB\x1C\x29\x40\x75\xCF\xF7\x7B\xCB\x67\x3B\x7D\x11\x36\x45\xFD\xAD\xF3\x54\x14\x0D\xD7\xDA\xE9\xF4\x45\x3C\x5C\x0B\x8B\x92\x04\x8B\xCE\xAD\x6C\x6C\x6C\xAD\x0A\x13\xE1\x8F\x33\xBD\x4E\x47\xA4\x33\xF8\xF5\xE2\xCA\xC6\x85\xCE\x72\xFF\xF1\xF3\x1D\x91\x5D\xDB\x14\x9C\xED\xF4\x97\xB7\x1E\xEE\x76\x56\xFB\x62\x66\xAE\x29\x5E\xDD\x3A\x77\x7E\xA5\xD7\x11\xB3\xD4\x96\x2B\x10\x28\xF9\xDC\x70\x5B\x1C\x78\xCE\x60\xD9\x4E\x87\x6B\xEC\xBB\x71\xB8\xC6\x4A\xAF\xB7\xF2\xF8\xF2\xE6\x4A\x7F\xED\x62\x47\x5C\x73\xCD\x84\x87\xE2\x5A\x82\x7C\xA5\xBF\xB5\x76\xE8\x4E\xB1\xBC\x0F\x7F\x6C\x5E\x38\xF7\x70\xA7\xB7\xDC\xEF\x3D\xBE\xBC\xB6\xD9\x17\xFB\xB3\xA1\x76\xE2\xBA\xFD\xCD\xEF\xB5\x7E\xA7\x57\xE1\x70\xFD\xBE\x91\x72\x84\xEF\x86\x6B\x47\x0A\x79\xD4\x57\xCD\x0C\x17\x8B\x1B\xAF\x6F\x0A\x36\x3A\x9B\x67\xFB\x8F\x54\xDD\xCE\x5F\x3B\xF6\x04\x3B\xBE\xE9\xBA\xB1\x62\xEE\xFA\xD5\xE3\x3D\xED\xF4\x7B\x6B\x9B\x67\xC5\x6B\xF2\xD1\x27\xE2\xE6\xEB\xC7\xE8\xBE\x7C\x66\x63\x6B\xA5\x2F\xE0\x86\xF1\x27\x4C\x18\x51\x54\x24\x3B\x73\xE8\x4E\x71\xEA\x9A\x16\xC9\x56\x76\x5C\xEB\x72\x42\x6B\x07\xC5\x6B\x99\x63\xFD\xDE\xE6\xEA\xB9\xF3\xE2\x3B\x27\x00\xC0\x78\xBC\x6E\x42\x17\x8E\x24\xAF\x9F\x6D\x49\xC6\x7A\xE7\xF1\x1D\xF1\x35\x44\x4F\x66\x37\x09\xDE\xD7\xEE\x9F\x00\x40\xA7\x2F\x0E\xB4\x08\x4F\x15\x6F\x99\x75\xE0\xAC\x6D\x9E\xE5\x92\x37\xB4\x7B\xA7\x92\x5B\xD3\x4A\xC8\xE8\xE7\x6D\xB3\x4D\x1F\xE7\x3A\xBD\xB3\x1D\x71\xB0\xDD\x84\xF8\x79\xFB\x5C\xBB\x64\x73\xA7\xD3\xEB\x8B\xAF\x7B\x4D\xD3\x6E\xE7\x11\xD4\x95\x47\x97\x57\xB7\xCE\x3F\xBE\xFC\xF0\xD6\xD6\x46\x67\x65\x53\xDC\x41\xA4\xA9\x7E\xBD\xF1\xD5\x53\xAA\x3B\x36\xBC\x29\x6F\x51\x9E\xC9\xFE\xE6\x99\x56\x51\xAF\x73\x46\xDC\xD9\x2E\x40\x61\x7E\xCB\xB4\x4E\x1D\x77\x0E\x25\x0D\x39\xC4\x5D\x37\x4D\xA9\xCC\x1C\x7A\xEB\xB5\x0D\xD1\x1F\x5D\xEB\x3F\xB2\xDC\xD9\xE8\x9C\xDB\x11\x77\x4F\x1B\xC2\x71\xEF\xF0\x8D\xD3\x40\xE8\xF4\xC5\xD7\xD7\xFA\xBC\x72\xFA\xB4\xF8\x86\xEB\x26\x57\x15\xDF\x18\x31\x5E\x1B\x1B\xE2\x9B\x66\x5B\x28\xEE\xAC\x7D\x6F\x47\x1C\xD9\xDF\x62\x69\xBF\xD3\x3B\xB7\xB6\xB9\xD2\xEF\x9C\x16\x0B\x15\x6E\x1B\x9D\x4D\xF1\x1D\xD7\xB6\x2A\x91\xDD\xA2\x3A\x36\x5F\x5E\x6E\xB0\x3A\xDB\xDB\x7A\x54\x2C\xC6\x75\x81\x78\xDB\xBE\x11\x94\x57\x57\xCE\x8B\x7B\x92\x86\xD7\xE2\x68\xE8\x10\x10\xC7\x5A\x22\xE8\x78\x4A\xE5\xC7\x5B\x82\x5D\x81\xDD\xE9\x13\x77\xEE\xCD\x9B\xEE\x57\xCE\x9F\xEF\x6C\x9E\x16\x4B\x2D\xC9\xDE\x41\x47\x78\xDF\xF5\x0C\xA1\x93\x2D\xA4\x39\xC3\x2F\x4E\x5C\xCB\x4F\xB0\xB7\x56\xF1\x37\xD7\xD2\x4B\x92\x79\x3F\xBA\x58\x84\xE3\x5B\xE2\x9D\xCD\xF3\xBD\xB5\xCD\xFE\x99\x65\x71\x32\xB9\xD8\xFC\xF8\xF6\x8A\x4E\xA8\xA0\x0F\x12\x43\xD6\x76\x4E\xAF\x9D\x5D\xEB\x8B\x87\xDC\xAF\x9D\xF3\x2B\xAB\x1D\xF1\x5D\x31\xFF\x7A\xA4\xF3\x98\xF8\x6E\x7A\xD2\xEB\x5C\xEC\xF4\x76\x3A\xE2\x7B\x88\x3F\x6B\xFD\xAD\x15\xF1\xAE\x80\x9A\xEE\x68\x8F\x90\xEB\x5C\x5C\x41\x57\xF5\xD8\xF2\x66\xE7\x51\xB1\x72\xDD\x50\x11\x93\xE1\xFC\x85\xBE\x78\x78\xFF\xD8\x03\xF4\x5B\x62\xF5\xFA\xA1\x72\x34\xE3\xBD\xCE\xCE\x85\x8D\xBE\x38\x3D\xBF\xBC\x7C\x66\xAB\xB7\xDA\x59\x5E\x3B\x77\x7E\xAB\xD7\x5F\x6E\xB9\xF7\x1D\xD1\x21\x2B\xD5\xDD\xD9\xDA\x5C\xDE\xE8\x3C\xB6\xBC\x75\xE6\x0C\x52\xE0\xCC\x75\x43\xA5\xBD\xCE\xB9\x95\xB5\x4D\x14\xFC\xB3\xF9\xD0\x83\xCE\xD6\x19\xF1\xC8\x68\xDD\x95\xD3\x68\x02\xCF\x89\xB5\x1B\xC6\x1F\x30\xC6\xA2\xFB\xAA\xF1\x47\x17\x36\xD7\x56\xB7\x4E\x77\xC4\xFA\x84\x66\x4E\xB1\x37\x26\x3C\x72\x1A\x79\x6E\x6E\xEC\x91\xD8\x1C\x2E\x5B\xDB\x5C\xEB\x8B\xAD\xFD\x75\xD9\xF9\x95\xDE\x4E\x6D\x7C\xCF\x8F\x96\xBB\x21\xB7\xAF\x1D\x29\x67\x71\xEF\x8D\x56\x77\x42\xDE\x9F\x19\x2E\x17\x17\x9A\xF6\x8F\xF6\xC8\xCF\x11\x18\x17\xAF\x1B\x2D\x76\x94\x7F\x74\xFF\xE8\x03\x96\xA8\xC7\xC6\xFA\x21\x15\x7C\xFC\xC6\xD1\xE2\xCE\xB9\xB5\xFE\xF2\xEA\x23\x2B\xBD\x1D\xF1\xBD\xAF\x9A\xFA\x50\xFC\x7F\x93\x9F\x91\xE1\xB8\x74\xD3\xC4\x67\x95\xF9\x7D\xF7\xE4\x31\xD9\xD0\x5E\x9E\xDC\x76\x6D\xB3\xDF\x39\xDB\xE9\x89\xC1\xFC\x94\x61\x89\xD8\xBB\x72\xF2\x63\xC7\xA3\x27\x64\x39\xF1\x31\xDB\x00\x54\xEC\xCE\x66\x5F\x3C\x29\x27\xC3\x47\x76\x45\x3C\x25\x6F\x9E\x4C\x94\xAD\x8D\x8D\xCE\x6A\x7F\x6D\x6B\x53\x3C\x2D\x5F\x3B\xB1\x4A\xCB\xAE\xE0\x38\xCF\x48\x98\x0C\x6C\xA7\xA9\xF3\x1E\x99\xD5\x75\x4E\x5F\x38\x77\x5E\x3C\x2B\xE3\x19\x29\xAC\x34\xFE\xB7\x7E\xDB\x93\xF2\x19\xF9\x1E\x99\x7C\xFC\x57\xE4\x93\x52\x09\x63\x84\x15\x07\xD4\xEE\x7B\x77\x77\xAB\xEF\x4F\xD3\xF7\x4C\x58\x01\xE2\x90\x7B\x52\xFD\xE2\x67\x3F\x22\xA5\x1E\xA8\x05\xB5\x60\xC5\x41\xF1\x5E\x2C\x3A\x9A\x35\x4D\x0B\x99\x4A\x63\x31\x4C\x7E\x7A\xB7\xEA\xC0\x8A\x05\x51\x48\x2B\xAC\x3C\xCC\x0D\xAC\x00\x69\xF3\x7E\x29\xAB\x3E\x6D\x03\x47\xA9\x40\x74\x4B\xED\x9A\xAA\x05\xD0\x20\x97\x32\x01\xC2\xE6\x17\xAD\xEC\x96\x62\x41\xDC\x68\x9B\xF1\xB0\xBC\xDF\xAD\xFA\x01\x65\x94\x30\xA1\x00\x71\x50\x08\x73\x48\x20\x98\x20\x0E\x28\xAF\x44\x20\x35\x7D\x17\x20\xF3\xEF\xDF\x25\xB0\x15\xFE\x8E\x4A\x91\x09\x63\xAC\x98\x35\x54\xD5\xFC\xB0\xE7\x05\x03\x9C\x37\xA9\xCB\x76\x00\x02\x64\xCF\x8A\xE3\x20\xE7\x0B\x85\x9D\x1D\xCD\x04\x48\xC4\x78\x50\x28\x1A\xA6\xD4\x20\xF1\xC3\x5B\xCA\x84\x95\x85\x02\x0F\x34\x7E\x15\x58\xBF\xF9\x0F\xB4\x1D\x74\x4B\x6D\x83\x13\x19\x61\x35\x13\x86\x42\x69\x2F\xF0\x43\x1E\x16\xE4\x01\xE5\xAD\xCF\x1A\xFB\x03\x88\x49\xBE\xBB\x4B\x74\x42\x98\x40\xE6\x1F\xC2\x5F\x0C\x5F\x84\x15\xE9\x1F\xAF\x54\x8C\x9B\x06\x05\x7A\x69\x3E\xFF\xF4\x2E\xD1\x2F\xD3\x0C\x9B\xF5\xBA\x07\x94\x28\x91\xD6\xFC\x4D\x2E\x65\x1A\xBB\x3B\x31\x6B\xD4\x02\x75\x53\xFA\x4C\x81\x00\x7C\x08\x96\xCA\x70\xBE\x54\x88\x9F\x7A\xA9\x88\x40\xDA\xA8\x5B\xC4\xD6\x2B\xF4\xA2\x2C\x12\x08\x0A\x4F\x2F\xB4\x91\x82\x64\x71\x70\xA9\x34\xE0\xBD\xF4\x10\xB1\x87\xC6\x00\x8D\xFF\x16\x0A\x0C\xC4\x6F\x51\xE2\x81\x4C\xA6\xCA\xB8\x1F\x0F\x65\xD2\x80\xAA\xA0\xE1\x9A\xCC\x09\x1C\x55\xA6\x81\x01\x65\xEB\x92\xCC\x37\x90\x40\xF4\x50\x26\x41\x23\x24\x1A\x92\x45\x79\xA9\x48\x40\xD8\x88\xC6\xF0\x52\xE4\xDA\x00\x42\x08\xC0\x5F\x9A\x77\xDC\x2B\x04\xA3\xE6\x95\xC8\x24\x18\x62\x7B\x6B\x3C\x24\x04\xD5\xAE\xEB\x4A\x60\x31\x40\x0E\x8A\x4C\xDA\x01\x0A\xE9\xBC\xA3\xFF\xBF\x72\xF4\x57\xE6\xCD\x5F\xB9\x4C\x5D\xA5\x1C\x7A\xC3\x72\x28\xAE\x72\xA8\xA1\x66\xE6\x80\x94\x03\x66\x12\x88\xB7\xA0\x98\x7C\x07\x12\x81\xC4\xEB\x45\xAB\xFB\x5D\xC4\x59\x21\x1E\x9F\x90\x4A\x0F\xE4\xE5\xD7\xB2\x9C\xE5\xEB\xA5\x7A\x9D\x13\xB9\x42\x23\x05\x0F\x0A\x61\xF5\xF1\xCC\xFD\x26\x40\xE4\x41\x21\x4B\xCF\xEA\x63\x99\x00\xCF\xAA\x63\x99\x06\xCF\xCA\xE3\x99\x44\xA6\xDE\xA2\xA3\xD2\xBF\x4B\x47\xE0\x2F\x8A\x77\x64\x1A\xB9\x5E\xC9\x69\x0A\xCA\x46\xDD\xFC\x77\xB0\xFB\x4C\x81\xA2\xAA\x8B\xE2\x41\x92\x0D\xFB\xCB\x8D\xB8\xA7\xD2\x80\x4F\x70\x3F\x58\x41\xED\xD7\x50\x4B\x50\x36\xEF\x32\x90\x20\x91\x17\xCD\x4F\x6D\xBE\x53\xCA\x81\x03\x53\x20\x98\x8A\xC1\x54\x08\xA6\x02\xC5\x60\x4A\x10\xB7\xE8\xE8\x2E\x2D\x6C\xAD\x4B\xC2\xA6\x5D\xA6\xE9\xEF\x30\x8B\xDB\xF0\x18\x3B\x30\x5F\x52\x7B\xD2\x09\xF9\xD2\xD6\x72\x1E\x5F\x94\x9E\x0D\x99\x4C\xC1\xB1\x4C\x82\x67\xFD\xE3\x59\x58\x13\x35\xC4\xC6\x2D\x72\x7A\x4C\xCE\x90\xC9\x19\x8C\x92\xF3\xC1\xCC\x4F\x9D\x91\x40\x39\x08\xF6\x90\x83\x34\x34\x4D\x45\x7F\x0F\xD9\x4C\x03\xAE\x28\x0A\xD1\x26\xA4\xA8\xB5\x68\xB4\xF4\x0A\xCC\x7C\x47\xA6\x1A\xDE\xC9\x51\xDE\x8D\xF6\x36\x44\xE4\x61\x46\xBE\x4B\xAA\x41\xAD\xC8\x95\xE0\x1D\x65\x73\x8A\xD4\xD6\x23\x48\x15\xA4\xC8\x51\x21\x40\x13\x7E\x99\x48\x3D\x44\x2D\x93\xCC\xE4\xA3\x68\x74\x08\x51\x12\xFB\xBF\x59\xF7\xDE\xD5\x76\xFF\xBE\x29\xD2\xD2\x12\x0F\x37\x9A\xAE\xA5\x41\x8F\x48\x83\x1C\x52\x2E\xAF\x2D\x0D\xA9\x32\x6C\x10\xA2\xA3\x4E\x41\x70\xE4\x3D\x34\xC3\x4E\x65\x9D\x24\xD6\x15\x3E\x5A\x64\x7F\x51\x5E\x6A\x29\xAF\x98\xA6\x84\xA2\xD0\xC3\xDC\x1A\xE6\xE4\xD0\xB8\xE6\xB7\xF5\x57\xA8\x38\x7B\x2A\x4F\x8C\x74\x6B\x68\x16\x8F\xD0\x2C\x60\x9A\xC5\x4C\xB3\x68\x88\x66\x61\x2D\xD2\x6C\xE5\x8F\x66\xD1\x28\x93\xBD\xAA\x86\x57\x31\x39\x36\xED\x56\x24\x1A\xE1\xC4\x56\x11\xB7\xF2\xA8\x55\x64\x48\x24\x82\xE9\x9A\x15\x1D\xCD\xFC\x9A\x71\xE3\x15\xA8\xA3\x69\x8F\xF4\x94\x47\x7B\xA8\xE6\x44\xFE\xCA\x69\x8A\x3A\xDE\xF7\x1E\x8A\x7A\x90\x2C\x2E\x1B\x1B\x27\xD3\xE4\xA1\xAC\xEC\xB2\x57\x8D\x4A\x99\x09\x43\xBD\x88\x2B\xD7\xF6\xDA\xB5\x43\x12\xBC\xC8\x7D\x7A\xE6\x09\x29\x15\xBA\x35\xE9\x24\xC4\x5E\xEA\xA2\x85\x3F\xD1\x88\x10\xA8\x19\x4F\x48\xA5\x05\xB3\xCD\x11\x7B\xB6\x31\x86\x12\x9B\xEB\x05\x90\x38\xA2\x6C\xEC\xA0\x6A\x19\x4C\x31\x56\xC7\x39\x57\x90\x66\x56\x70\x2C\x78\x1A\xFF\x59\x9D\x37\x9F\xF7\xB4\x1A\xA8\xCB\xEA\x92\x13\x70\x98\xA0\xEB\x93\xFE\x63\xF7\xA4\xD9\x3D\x69\x36\xFB\x1A\x85\x56\x81\x72\xEE\x29\x67\xC1\xD6\x28\xE8\x32\xD5\x23\x9E\x0A\x99\xE0\x78\xEC\xD7\x15\x8F\xA1\xF9\xA5\xDE\x3C\xEE\x4D\x57\x2A\xE0\xDD\xA5\x23\x3B\x40\xC6\xDE\xA2\xF3\xD2\x07\xEF\xC1\x4C\xA5\xE9\xB0\xDB\xD5\x95\x0C\xD5\x43\x34\x62\xD4\x6E\x4B\xC6\xDE\x7B\x47\x96\xA0\x28\x40\x25\x0A\x3E\x78\xEF\x1C\x95\x14\xE4\x2B\x85\xC6\x56\x1E\xA3\x00\x0E\x21\x44\x00\x8F\x67\x88\xCF\xAD\x3A\x2A\x02\x06\x5E\x60\x0D\x9D\x7A\x86\x90\xFF\x68\xAB\xF8\x38\x45\x11\xCE\xDE\xB5\xD0\xBF\xA1\x9B\x7F\x76\xD7\xCD\x3A\xFE\x60\x48\x3C\x6F\xD5\x37\x0C\xB5\x57\x38\x15\xF9\xBE\xF7\xD5\x55\xEE\x71\xEF\x10\xC4\x50\x2D\x69\x88\x52\x1F\x2D\xC2\x54\x1A\x66\x11\x73\x47\x20\xB8\x12\x24\x42\x1B\x32\x28\x23\x64\xDA\x1B\x94\x70\xEA\xF0\x61\x9B\x80\x76\x00\x01\x84\xA7\xF1\x9F\xD5\xF9\xD9\x21\xD2\x0A\xF3\x71\xA9\xD5\x40\x5E\x6A\xDB\x51\xD9\x16\xB3\x2A\xDA\x91\x1C\xED\x90\xFB\x50\x08\xB7\x62\x2A\x6B\x9C\x68\xB5\xEC\x35\x51\xB9\xF0\xDA\xA5\xDE\x47\x67\xC7\xA8\x8C\xB1\xFF\x14\xD4\xE4\xA4\x6E\xDB\x68\xB6\x1E\xD5\x18\x9B\x77\x48\x35\xA8\x06\xB9\xE2\xFC\x45\x4C\x9F\xBF\x08\x9A\xBF\xB0\xB3\xFD\x82\x0C\x3C\x9A\xA7\xC9\xCB\x0A\xFF\x77\x53\x1A\x17\xE4\x70\xA7\xA5\x37\x5F\xFA\xE8\x2C\xFD\x97\x90\xE9\x38\x91\x08\xAD\x57\xF8\x8B\xB2\x88\x40\x17\xB1\x5A\xA0\x49\x4D\x52\xAB\x67\xC4\x93\x9A\xF8\xA5\x07\x32\x01\x06\xC2\x66\x12\x53\xCD\x70\x7C\xB2\x94\x89\x7B\x48\x93\x9A\xA4\x9E\xD4\xF8\xAD\x49\x4D\x72\x94\xD5\x37\xA9\x27\x35\x09\xAA\x69\x04\xC1\x43\x99\x02\x1F\x61\xF1\x21\xC2\x49\x4D\x54\x4F\x6A\x62\x9C\xD4\x40\x32\x8B\x13\x1B\x0F\x34\xA8\xA5\x79\xF3\x39\x4F\x66\x03\x10\xF9\xBF\x26\xAF\xA3\xAA\x38\x82\x7F\x97\x1A\x91\xF5\x40\xF1\x6C\xCE\xC3\xA9\x50\x19\xD0\x24\xCE\x7A\x88\xAC\x28\xD0\xDD\x85\x08\x5C\x64\x75\xBF\x8C\x31\x22\x03\x6D\xBD\x6E\x99\x60\x21\xFD\x76\xF0\x99\x4C\x59\x51\xA4\x56\x14\x59\x63\xAF\x1C\xF1\x0D\xF6\x19\xD6\x2D\x8A\x19\xBD\x00\x06\x7F\xCE\x34\xD1\xA5\x41\x20\x4C\x86\xF3\xF9\x22\x33\x35\xDF\x8C\x9B\x05\x62\xB3\xAC\x29\xA5\xD8\x29\x69\xF5\x88\xCC\x18\xE9\x53\xD6\x7D\x92\xA5\xA6\x2E\x52\x03\x19\xA4\x0D\xC8\x10\x59\xD9\x2D\x23\x08\x96\xC8\x56\x7B\xE0\xD3\x9C\xA4\x42\xDF\xA0\x74\x23\xE0\xE5\x0C\xC5\x1C\x64\xDE\xCA\xF8\x80\xA2\xA9\xFC\x0C\xF2\xEB\xFB\x58\xA1\x00\x0B\xAD\x5A\xAA\x88\x17\xD9\xA8\x98\xB1\xB2\x88\xF5\x02\x44\x04\x56\xB7\xAE\x0D\x33\xC8\xBF\x19\x88\x71\xEC\x98\xFA\x3C\xA0\x04\x43\x80\x83\x15\x33\x06\x66\xB8\xAA\x01\x55\x8F\xA0\x51\xDE\x1D\x1C\xFA\x2A\xE0\xD0\x5F\x21\x1C\xBA\x0D\x87\x66\x4A\x0F\x81\xA2\x5D\x33\x83\x22\x66\x25\x52\xEB\xC4\xBC\xF9\x6B\x5F\x1A\x72\xB0\xD5\xDC\x91\xD4\xD2\xC5\xC5\x1A\x49\x26\xBB\x85\x02\x92\x35\x4D\xD3\xCD\x3C\xFF\x00\xD1\xDF\xB3\xFE\x61\x21\xC0\x03\x75\x48\xA5\xE0\x2D\x8A\xBB\x94\x57\xB9\xE4\xA3\x14\xC4\xA5\x5D\x6C\xDE\x3F\x44\x16\xD3\xEB\x73\x3B\x72\xE5\x1E\x12\xC0\x67\x02\x50\x00\xEE\x21\xB6\x01\xC6\x76\xE8\x85\x41\x75\x21\x20\x43\xDB\xBD\x45\x89\xBB\x9C\x63\x42\x30\x10\x57\x0D\x7E\x8D\x28\xB6\x2B\x55\x43\x13\x05\x12\x87\xF3\x29\x2E\xB7\x1E\x06\xF1\x4E\xE8\x42\x9E\x74\x8E\x15\x93\x2C\x22\xAC\x45\x44\x60\x15\xA8\x4A\xBA\x5B\x04\x4E\x77\xD0\x52\x80\x22\xEA\x97\x1A\x19\x13\x21\x14\x7D\x9B\x80\x9E\x2F\x15\xE3\xA6\x2B\xDC\x90\x85\x2D\xB4\x1C\x3E\xE2\xEA\xF1\x09\x1A\x54\x02\x10\x0D\x2A\x38\xC5\x42\x56\x1C\x52\xA2\x66\x80\x06\xF5\x12\x86\x76\xDA\x3E\xA5\xBE\x5E\x0A\x6A\x5B\x4A\x02\xA7\x5B\x2A\xD0\x04\x1B\xC4\x87\x30\xCE\xE3\xA5\x99\x10\xB9\x18\xE2\x40\xA4\x0C\x24\x67\x44\x79\x61\xA3\x22\xB1\x92\x0C\x86\x64\x6B\xA1\x98\x41\x85\xA1\x02\x5A\xC0\xE2\x39\x1D\x76\xE6\x95\xB1\x0B\x85\x29\x58\x12\x18\x22\x74\x8B\x80\xCD\x93\x95\xF7\x66\x02\x92\x42\x41\x44\x41\x55\x80\xB0\x77\xED\x63\x15\xEE\x82\x29\x61\x29\xF0\x84\xAA\xA3\xB0\xEA\x88\xC6\x29\x02\x50\xF6\x31\x24\x91\xB4\x03\x0C\xC9\x34\x7E\x68\x08\xAD\xB8\x0F\xA3\x45\x1E\x2F\x95\x06\xC7\x30\x10\x80\x21\x5A\x81\xC4\xC6\x8C\xB3\xB3\x0D\x1E\xEB\xC5\xC9\x4C\x42\x82\x44\x4F\x38\x96\x96\x64\x6E\x3D\xF3\x7D\x52\x7A\x43\xF1\x69\x6B\x9A\x25\xDB\x9A\x89\x76\x9A\x24\x8D\xC9\x86\xF2\x53\x68\x1B\x15\xD2\xCA\xC2\xD3\xA4\xDF\x02\x64\x4B\x3D\xC9\xE5\xE0\x24\x46\x76\xD9\x4A\x3B\x16\x3B\xC9\x43\xB0\x65\xA5\x9B\xAE\x8D\x79\x58\xCA\xE1\x49\xEA\xD5\x2C\x68\xA9\x3D\x17\xB4\x48\xBE\x15\xE9\xBC\x00\x39\x6F\x7E\x4D\x39\x84\x31\x9A\x1E\x74\x4B\xE9\xD6\x25\x47\x82\x55\x39\x13\x86\x61\x88\x81\x35\xCE\xA2\x0E\x0A\xE9\x26\x37\x11\x43\x9A\x06\xA6\x26\x99\xFF\xF7\x40\xB2\xD4\x6F\xC6\xF7\xD8\xFB\x7B\x47\x33\x0A\xE1\x5D\x5F\x5E\xA9\x70\x7E\x4C\x4F\xB2\xC6\x12\x53\xBC\xE9\x1A\x6A\x17\x8B\x0C\x37\x54\xA5\x87\x30\xD0\x93\x0C\x43\xE4\x66\x50\x65\x40\x16\x9E\x01\x6F\x94\x6F\x06\x84\x8B\xC8\xDC\xF4\x3C\x1A\xAD\xF1\xAD\xB4\x74\xDE\x16\xB3\x3A\x6A\x61\xBE\x36\xC3\x6B\x14\x77\x7A\xE2\x7C\x9F\xC4\xD9\xB7\xBE\xDA\x1E\xBD\xA9\x3D\x7A\x57\xE8\xF1\x09\x5F\x86\x83\x6A\xA1\x97\x05\x24\x38\x8E\x4C\xAD\xBE\xD9\xC8\x99\x1E\x65\x05\xAA\x98\xB2\xC1\x61\x21\xDA\x5E\x83\xDD\xA7\x47\x66\x04\xD9\x9B\xFF\x53\x52\x6E\x9F\x86\x67\x03\x31\x22\x68\x14\xA3\x05\xE0\xE7\x2F\x51\xCF\xC1\xD1\x2C\xA5\x96\x45\x88\x3C\x10\x65\xC4\xDC\x89\xC8\x1A\x41\x58\x85\x07\x64\x86\xA2\x32\x72\x1C\x1A\x69\xE1\x4D\x68\x11\x36\x2D\x02\x83\x8E\xC0\x37\x36\x75\xF8\x44\x10\x1E\xA2\x30\x49\x1C\x52\x11\xC6\x69\x64\x34\x8F\xD2\x84\x39\xC2\x22\xE7\xFF\x82\x34\x44\xDB\x43\x8F\x5D\x91\x8F\xAA\xE0\x61\x2D\x51\x15\x79\xA9\xFF\x37\xEC\x5F\x61\xD4\x38\xDC\x3F\x05\x50\xC3\xFD\xD3\xBC\x74\x98\xE2\x52\x13\xCB\x5A\x34\xC7\x47\xED\x2E\x98\x01\x8E\x50\x8D\x9A\x3B\x82\x89\x09\x04\xD3\x0D\xC1\xD4\xDF\x94\x60\xD3\x10\x92\x63\x08\x29\x9A\xFF\xEF\x63\x23\x40\x1A\x0F\x72\xFE\x80\x12\xE6\x02\xAF\x00\x54\x6B\xD1\x24\x6A\x1E\x43\xEC\x95\x7A\x64\x95\x53\x57\xFA\xDE\x80\xEA\x71\x48\xE0\x31\xA8\x14\xAE\x78\xA0\x11\x54\x0D\xDE\x21\x5A\x2A\xD0\x54\x38\xCB\x86\x06\x0B\x85\x79\x6D\x6D\x89\x65\x2D\xF5\x02\x7B\xF2\x70\x76\x88\x81\x8F\x30\x37\x49\x39\xA8\x1F\x4A\x20\x95\x90\xFC\x50\x9A\xE7\xA4\xA2\xF9\x49\x6B\xB2\x26\x79\xB2\x26\x79\xB2\x26\xDD\x64\x0D\xA7\x65\xAA\x76\xEA\x02\x14\xFA\x74\x61\x9F\xF2\xD0\xA7\x8B\x6A\xE2\x36\xA9\x86\x9A\x56\xC3\x3E\x15\xD0\x23\x57\x73\xD6\xD8\x7F\xD1\x7E\xB5\x64\x5E\x8D\x80\xE7\x0D\xE0\x77\x6B\x9C\xDB\xF2\x88\xD2\xBC\xAE\xFD\x54\x01\x91\x4D\xB9\x2E\x29\xBE\x4A\x41\x8D\x75\x71\x17\x77\xA1\xB8\x8B\xD7\x4A\x79\x19\xC4\x2D\x44\xBD\xB4\x86\x8A\x31\x02\x79\x17\x12\x91\x28\x5C\x3F\x54\x4C\x5A\xC5\x75\xAA\x41\xCD\x0F\x49\x19\x10\x1B\xD2\x26\xC6\x92\x95\x18\xA4\xF5\xEA\x2D\x39\x16\x9A\xC2\x16\x1E\xCA\x69\x15\x43\xEA\x49\x31\x97\x8F\xDE\xC5\x67\x53\x81\x13\x07\x27\xDC\xEC\xD0\xA2\xC2\x6F\xA1\xEE\x33\xEA\x14\xE7\x2A\x14\xE2\x14\x2A\x40\xCD\x1B\xDA\x04\xD0\x58\x8A\x91\x18\x55\xD5\x1C\x12\x6B\x96\x08\x6D\x7E\x0F\xB1\x68\x04\xC5\x3A\x41\xA9\x6C\xA7\x15\xF9\x73\xBB\xEE\x5D\x18\x4F\x79\xAB\xA8\xD1\x59\x4F\xAF\x79\x93\x48\x21\x12\x0B\xBF\x5F\x72\x48\xD9\xA8\x6B\x40\x33\xC1\x16\x46\x35\x79\x7D\xA0\x48\xD5\x67\x1D\xF0\x51\xC4\x3D\x08\x50\x07\x02\x46\x12\xFB\x79\xAE\x8A\x27\x50\x67\x03\xAE\xD3\x2E\x67\x33\x82\xF5\xC5\x50\x39\x2F\x93\xFD\xAA\x44\xE7\x31\x86\x64\xB8\x27\x92\xC3\x88\x79\xCC\x50\x9F\x34\xBB\x8D\x5C\x49\xEB\xAC\x1E\xC6\x7E\x0A\x82\xC2\x47\x4C\x3D\x52\x73\x2B\x68\x81\x06\x3F\x10\x63\x0C\x08\x83\x22\x04\x1F\x91\x8F\x6A\x03\xA0\x6A\x03\xE0\x41\x80\x2A\xAE\x21\x98\x27\x3D\x07\x0F\x0D\x19\xC6\x04\x63\x08\x7D\x00\x23\xC3\x2A\x8A\x1F\x36\x3D\x08\x24\x4D\x59\x87\x0D\x10\x3E\xD3\x0C\xA6\x04\xED\x9C\x5E\x05\xA6\x42\xD8\x34\xCA\x5F\xA1\xDB\xB0\xC9\x16\x6C\x38\x6D\x63\x1B\xA4\x6B\xF0\xD0\x32\x19\xF3\xB3\x5A\xAB\x81\xBC\xEC\x0D\x1A\xD7\x3C\x35\x76\x6B\x22\xB8\x54\x48\xAD\x3C\x3F\x75\x2B\xDA\xAA\x65\xAC\x54\xDB\x58\xCD\x9A\x96\x81\x0A\xD8\x40\x85\x6C\xA0\x22\x36\x2E\xBA\xA5\xEB\xFA\xAE\xB6\xB9\xE0\x95\xD6\x96\x7E\x7A\x4E\x37\xB5\xA5\xD9\x01\x72\xDA\x67\xDD\x0C\xDC\x9C\xD2\x03\xD9\xA5\x29\x09\xC8\x4A\x37\x5D\xE4\x87\x53\x9C\x52\xF1\x4C\x26\x44\xDD\xF4\x31\xB4\x42\x3B\xA2\x6B\x59\x96\x6C\x23\x10\x0E\x44\x01\xC7\xCF\x7F\x66\xD7\xBD\x25\xCF\x7F\x96\xBF\x59\x5A\x6A\x23\x85\x56\x2D\xD0\xD5\x21\x5A\xC5\x23\x2D\x95\xC8\xF9\x14\x24\x0B\xBA\xAC\xCC\xE8\x24\x4C\x55\x8D\xE9\x48\x0D\x67\x15\xAB\x9A\xC3\x86\x16\x81\xC0\xE8\xCA\xEC\x47\x53\x21\x6B\x33\x28\xD9\x7B\xBC\x66\xC4\x82\xBE\x34\x6C\x42\x0F\xA2\xDB\x1B\xC1\x59\xE4\xBF\xC1\x2B\x41\x20\xD9\x42\x56\x7C\x64\x9B\x3D\x66\x4C\x9F\x56\x43\xD6\xF4\x59\x94\x68\xA0\xF8\x34\x45\x5E\xE3\x24\x56\xCE\x97\x64\xD1\x65\xC3\x40\xD5\xC4\xF3\x0D\x0B\x91\x9B\x18\x55\x29\xE4\x9D\x9E\xC4\x3B\x17\xB5\x8B\xF6\x3A\x04\x36\x2D\x65\x2B\xC0\x27\x55\x64\xD3\xDF\x78\x2B\x62\x87\x40\x15\x48\x41\xD0\x3A\x02\x08\xF3\xA2\x94\x7E\x8B\x3E\x15\xCF\xA8\x92\x74\x8B\x0D\x6E\x4F\x86\x4D\x51\x6B\x78\xA1\x41\x34\x0B\x0D\x8A\x26\xA3\x51\x97\x74\xD4\xBD\x79\x60\xCB\x53\x39\x09\x05\xA2\x4B\x8E\x00\x44\x3D\x39\x45\x64\x44\xE5\x24\x74\x8D\x09\xB6\x2B\x45\x83\x89\x60\x4C\x40\x0E\x47\x01\x82\x15\x59\x90\x71\x1F\x0D\x11\xEA\x87\x21\x3F\x8C\xC9\x1B\xA2\x7E\xE7\xEC\x17\xC9\xAD\xCB\xC3\x42\xA2\xCB\xF0\xAB\x65\x53\x5E\xD7\x4C\x79\xF5\x01\x15\x69\x8F\x05\x08\xAF\x9A\x76\xB5\x17\x57\x44\x7B\x71\xC5\xBB\xFA\xC5\x08\x31\xB6\xB8\xE2\xB5\x57\x24\x70\x9C\x2B\x2E\x4A\x54\x6B\xA7\xD5\xAA\x04\x4B\x26\x2D\x4E\x98\xEF\x57\xD2\x54\x48\xB6\xA6\x88\x64\x70\xDD\x14\x51\xF1\x9A\x80\x67\x75\xBF\x0C\xBA\x25\x2D\xC8\x90\x03\x43\xF3\x17\x57\x2B\x12\x21\xDA\xD9\x04\xE4\x1C\xE9\x1D\x4E\x37\xA8\x5D\x08\x41\xB5\x28\xE1\xF3\xA2\x04\x1A\x65\xAF\x30\xB4\x28\x11\x82\x5F\x13\x80\x17\x70\xED\x25\x02\xB5\xD5\x51\x5A\x75\x14\xBB\x45\x09\xDF\x3E\x86\xE6\xCC\xD8\x41\xB7\x30\x10\xE3\x47\x0C\xA9\x5B\x94\xC0\x1E\x91\x94\xE4\x86\xBC\xC2\xA0\x17\xA5\x30\x37\x04\x43\xEB\x12\x09\x1B\x79\xDA\xD3\x32\x41\x5D\x46\x43\x22\xC1\xCC\x65\xED\xAD\xE2\x8E\xF1\xA0\x13\x85\x9A\xBD\x2D\x87\xCF\xDE\x50\xF8\x7C\x1B\xBF\x7F\x23\xB7\xCB\x12\xA0\x81\x2C\x10\xB2\xBB\xD0\xA0\x90\x92\xEC\xF3\x84\xB9\x5B\xCA\x41\xF3\xBE\xC1\xAB\xDE\x13\x20\xBB\x52\x20\xF5\x05\x9D\x7F\x72\x97\x5F\x8F\x7B\xED\x97\x82\xBF\x79\x93\x97\xBA\xAD\x53\xCD\x7B\x31\xD7\x81\x7D\x82\xFE\x7D\x92\x15\x67\xBE\xF0\xD1\x55\x83\xB2\xAA\x8B\x33\x11\x50\x07\x45\x35\x07\x51\x0B\x10\xD9\xD7\x63\x90\x1C\xD9\x1B\xFA\xF6\x86\x0B\x80\x73\x43\x09\xFE\x1C\x75\x21\x20\x44\xA0\x43\x08\xF0\x23\x70\xEE\x82\x9A\x57\xAB\x7B\xB4\xAE\x0F\x21\x22\x95\xDC\x86\x0F\xEC\xA9\x6E\x69\x6C\x7E\x22\x93\x56\xB6\xF7\x65\x81\x99\x99\x13\x41\x20\x03\xFE\x53\x81\x0E\x02\x4F\x18\x1B\x15\x18\xFF\xD8\xBC\x50\xD8\xA1\x47\x7E\xDB\x2A\x94\x77\x37\x76\x0C\xAA\xC7\xAB\xF5\xAD\xCE\x22\xFB\xF6\xAE\x7D\x45\x6E\xDB\xF8\x7E\xA7\x74\x91\x7D\xC3\xF1\x4C\x42\x6C\x55\x0F\x62\x8E\x6F\x22\x2B\xEE\x9D\x2F\x62\x88\x20\xB2\x37\x5F\x28\x55\x17\xD4\x0E\xE9\x9D\xB3\xAF\x06\x42\xC2\x85\x3A\xBA\xFD\x18\x05\x62\xF5\x32\x6C\xB2\x51\xD1\xA4\x6B\xDF\x8E\xEA\x9A\x1C\x14\xB2\x88\x20\x41\x1A\x84\x45\x32\x0C\x82\xA1\xEE\x5C\x4F\xFC\x3E\x0C\x12\x5E\x14\x6F\x4A\xA5\xC3\x29\x81\xD8\xEE\x46\x3D\xA4\x9D\x5A\x40\x18\x64\x39\xD4\x99\x6B\x41\xF8\x54\x88\xE0\xD4\xEF\xBE\x79\x04\x19\xD9\x98\x3A\x24\xB0\xB1\x2A\x22\x86\x1B\xD5\xCC\x8C\xC2\x6D\x86\xE0\x4E\x47\xE1\xA6\xC9\x7C\x52\xA4\x08\x6F\x58\xA4\xA6\x45\x60\xEA\xC3\x7E\x70\xD0\xB5\x37\x3F\x5A\x86\x36\x6E\xBF\x03\x0E\x67\x12\xA9\xB4\xE7\x79\xC2\xF3\x84\x34\x90\x22\x5A\x29\xA2\x45\xBC\xD2\x06\x52\xC4\x2A\xB4\xFF\x5B\x1C\xCF\x34\xA4\x0C\x73\x6C\x7F\x5E\xE2\x63\xD5\x6E\xE0\x61\x89\xAC\x1B\x7C\x51\xE0\x24\xAD\x6E\xB0\x1B\xF4\x8A\x98\x1E\x72\xE0\xD9\x6A\x28\xAB\x86\x43\x63\x9B\xBD\x5F\x0A\x37\x18\x30\x7E\x65\x62\xBF\xAB\x5B\x46\xF6\x53\x62\x62\x60\x07\xD1\xCC\x03\x51\xBC\xF7\x9F\x54\x72\xB4\x48\x34\xDF\x8C\xC0\xE7\x5C\x20\xD2\x38\xCE\xF0\xBB\x88\x90\x8F\xAF\xC8\xED\x32\xB2\x3F\x86\x9A\x63\xE1\x44\xA6\x6C\x54\x84\x36\x2F\x32\xB5\x00\xC9\x4C\xE1\xE9\xEA\x4F\xE8\xD6\x9F\x5F\x7D\xF1\x3C\x54\x93\x30\xF5\xE8\xA5\x8B\x85\x1E\xF1\xF4\x7F\x89\xE3\x99\x40\x5A\xE4\x4C\x0B\x14\x17\x7E\xF2\x4B\xEE\x09\x10\x41\x7D\xA7\xE8\xDA\x86\x5D\xFB\xD8\x76\x19\xDE\xAA\x05\x18\x50\x10\xE7\x9F\x22\x73\x13\x40\xC8\x76\x33\x25\x9D\x27\xDB\xE1\x2C\x47\x76\x63\x3A\x3F\xDE\x43\x54\xF7\x60\x01\x35\x90\xF0\xB3\x1F\x17\xC7\xE6\xF3\x5F\x73\x3D\x46\x53\x7B\x7C\x55\x7A\x23\x5A\xF7\xAD\xED\x22\xB6\x49\x91\x99\xAA\x93\xC8\x7E\x5A\x1C\x23\x15\xB6\xFF\x53\xE0\x24\x3E\x2B\x42\x88\xEC\xEF\x0B\x7A\x0D\x1F\xDB\x9D\x6D\x96\x81\x0C\xA3\xCF\xD8\x5E\xDE\x86\x98\x74\x6B\x7B\xBE\x79\xD1\x48\xC2\x8C\x2D\x04\xF5\x42\x6A\x98\xD8\x5D\x6F\xDB\x69\x17\xE9\xC6\xAE\xDA\xCE\x14\x24\xF6\xE7\xC5\x76\x86\x2A\x92\xF7\x6D\x7E\x01\xE8\x89\xDC\x9E\x2F\x23\x7B\xF3\xC5\x62\x06\x12\x48\xEC\xD6\x36\x44\xF3\xC5\x2C\x44\x47\x33\x93\xA6\x55\x57\x6A\xB8\xAB\xCC\x75\xD5\x34\xB1\xAF\xBC\xA2\xB7\x5B\x1D\xBA\x2E\x66\xD3\xDC\x54\x75\x2A\x4A\xEA\x5B\xB4\x28\xF3\x45\xF1\x6D\x58\x2B\x83\x7C\xF1\xC8\x7B\x5F\x2C\xE6\x18\x11\x32\x76\x11\x64\x88\x23\xD9\x93\x97\x8A\x7D\x90\x63\x9D\x67\xCB\x6B\x2E\xC1\x35\xCF\x14\x39\x64\x16\xB6\xED\xE7\xC4\x8E\xFD\x0B\xD9\x2D\x62\xF7\x5A\xD2\x83\xA8\x6B\xEF\x80\x18\x2B\xC3\xBE\xDD\xF2\x1A\xD8\x77\xF9\xDD\x2F\x96\x09\xAA\xC1\xEE\xEE\xEE\x27\xC5\xBD\xF3\x90\x74\xD1\x4F\xF1\x3A\xBE\xBD\xF9\x44\x26\xB1\xEA\x43\x45\x02\xD7\x14\x39\x24\xEE\x45\x17\xB1\xB0\x62\xBE\x07\x11\xCC\xA1\xBB\x05\x05\x59\xFE\xEB\x8E\xCF\x13\x18\x7C\x5D\x7A\x3D\x1A\xC2\x51\x2A\xB6\x90\x4A\x60\xB6\x42\xCA\xD9\xEB\xA4\x0B\xC9\x4E\x11\xC1\x6C\x1B\x9F\x39\xAC\x4A\xF8\x24\x88\xCF\x1C\x56\x86\x70\xB3\xCC\x20\xDC\x58\x2F\xE3\x36\x3A\x31\xA1\x43\x76\x2F\x61\x74\x22\x08\x4F\x16\x31\x64\x45\x04\x71\xFD\xDE\x6E\x08\x9D\x04\x66\x1C\x3A\xB3\x7B\xA1\xB3\x3F\xBD\xEE\x0A\x6C\xDB\x83\x5D\x7F\xB7\xFC\xB1\xE2\x6A\xF8\x73\x6D\xBA\xDF\xD8\xD7\x8F\x78\x7F\x72\x62\x23\x65\xC6\xC5\x02\x69\x6E\xAC\x44\x25\x43\x8F\xBB\x5D\xC6\x99\xB0\xAA\x48\xAA\x97\x5C\x18\xE4\x14\x11\xBF\xCC\x4C\x70\x6E\xA8\x17\x2C\x50\x4F\x61\xB7\x1D\x5E\x44\xE4\x10\xCB\xF0\x38\xBD\x31\x80\xA0\x8B\xED\x02\x03\xFA\x36\x0C\xD8\x87\x43\x11\x17\x83\x38\x96\xC5\x47\xB3\x59\x48\x40\x9D\xCC\x66\x41\x41\xB2\x8E\x41\x0A\xA8\x2E\xD2\xD2\x8D\x34\xDE\x94\xA2\x94\x10\x87\x8A\xB0\xC5\x44\x42\xE4\xE9\x1C\x5A\x92\xA2\x57\x86\x10\x12\x7B\x29\x20\x75\xF6\x04\x42\xC7\xD1\xC4\x71\x74\x88\x7B\xF6\x2E\x08\xED\xEC\x76\xA9\x6C\xB2\x34\x0F\x6A\x8C\x5B\xA1\x9D\x3D\x51\x28\x08\xAD\x77\x11\x07\x9C\x2C\x81\x91\x15\x36\x07\x63\x23\x48\xF6\x62\x18\x19\x0E\x82\x6D\x96\x3C\xC6\x2C\x16\x27\x38\x03\x91\x18\x2D\x83\x99\x47\x92\xCC\x12\x74\x08\x43\x91\x41\x78\x94\x60\xA0\xA0\x2A\xC2\xA0\x60\x8E\x34\x61\x0E\x0D\x35\xC1\x92\xC1\xEC\x7A\x89\xC2\x12\x82\x59\x9A\x87\x90\x4D\x69\x99\xD9\xE4\xE2\x3C\xAA\x0C\x33\x3A\x81\x08\xC9\x1E\x83\x82\x68\x1D\x23\xFE\xAB\x61\x70\x17\x50\x2E\x0C\x4B\x44\x04\x49\x11\x38\x88\x69\x70\x32\xA4\x2E\xE2\x98\x68\x6B\x99\xD2\xFF\xA8\x4C\x44\x92\x1A\x73\x65\x53\xF7\x77\x0A\x78\x6D\x0A\xF6\x04\x3C\x4E\x13\xD3\x72\x5B\x18\xA4\x4C\xE6\xC1\x3F\x40\xD8\xE9\xE5\x10\x89\x7B\x84\x9E\xF9\x15\xB9\x7D\x34\x93\x18\x06\x65\x38\x1D\x9B\x25\xD9\xC7\x28\xF9\x28\x39\xFB\x89\x33\x1E\x67\x2B\xDC\x8C\x13\x27\x08\x45\x52\x69\x4D\xE2\x5E\xE7\xD5\x1D\xED\xDD\xC7\x58\xE3\x06\xB3\xF8\x28\xED\x6C\x50\x27\xD1\x58\x42\x54\x9B\x2F\x75\x55\xE6\x8B\x5A\x4C\x24\x80\xAC\x62\xE4\x91\x72\x3A\xB7\xC0\xFD\xDA\x41\x17\x3F\x96\xE6\xDB\x43\x78\xCD\x56\xB8\xC0\x5C\x47\x7B\x77\x40\x23\x70\xB4\xAE\x2F\x0E\x0B\x9C\x7E\x09\xF3\x73\x51\x28\x69\x93\x9C\xBC\x84\x9F\xFE\xA0\x99\x7B\x46\x43\x9B\xE5\xBC\x7B\x5E\xA1\xBF\x3F\x7A\x65\x35\xC3\x09\xAD\xF7\x30\x7E\x54\x85\x83\xD3\x56\xEE\xD0\x9E\x95\xE7\xF0\x59\xBD\x79\x6D\x75\xBE\x8C\x7F\xAE\x4C\x16\xB9\xDA\x2B\xE1\x53\x8B\xBB\xFC\xF7\xE5\x23\x4F\x7F\xCC\xD5\xFA\xF2\xC7\x9E\xBF\x67\xE5\x6D\x5B\xAB\x2F\x3E\xFB\xDF\x8F\xBC\x00\xC9\xE2\x9D\xEF\x7D\xD1\xBE\x32\xBB\x6D\x9F\x78\xAC\xFB\xF2\x3D\x7F\xF9\xF8\x8F\xBE\xFD\x9A\x7B\x3E\x75\xE4\x85\x7B\x7E\xF2\xBF\x9E\xFA\xC0\x81\xF7\x7F\xFC\xC8\xF3\xCF\x97\xE6\x87\x5C\xD3\xCF\xDA\xD5\x4C\xA2\x18\xEE\x3E\x56\xA4\xB4\xD1\x8C\x67\xD8\xE0\x81\x8F\x96\xCB\x05\xAA\x3E\x44\xAD\x3D\x97\xB3\x06\xCC\x47\x8A\xD4\x40\xFA\x72\x69\xEE\xB9\xEE\x9D\xFF\xE9\x67\x7F\x7C\x5D\x2D\xBC\xE0\x3C\xBC\xB9\x67\xFB\xC3\x8F\xC7\x27\x7F\x30\x59\x78\xA1\x1A\xE4\xC8\xC8\x88\xC2\x8D\x38\x83\xEE\xD4\x7C\xA4\x98\xC1\xD8\x1F\xED\xEA\xAC\x4D\x2E\x16\x39\x7A\x50\xC8\x60\xE6\xE5\x7B\xFE\xE8\xEE\xFF\xFB\xD7\x8B\xB7\x7F\xE1\x63\x2F\x3C\x5F\x1A\x30\xCF\x57\x54\x59\x00\xF3\x21\x30\x60\x5E\xC0\x52\x30\xAE\xF8\xB6\x85\x0F\xBB\x6F\xAF\x5B\x78\xBE\xFA\x7A\xC3\xC2\xF3\x1F\xAE\x7F\xFC\xF1\x91\xE7\x61\xC6\xBE\x12\x76\x5F\x5A\xBC\xF3\x3D\x1F\x7B\xA1\x34\xAB\x56\xEE\x64\xA2\xEE\xE1\x75\x0B\x1F\x2E\x50\x56\x06\x5D\xC4\xCE\xB7\x01\xE4\xF3\xC5\x8C\xF5\xAC\x0F\xA9\xFD\x3D\xD1\xB5\xBF\x24\x97\xE6\x31\xEE\x44\x70\xF3\x6D\xDE\xAA\x11\xDF\x73\xF0\x6D\x37\xFD\xC1\xEF\x1E\xBA\xF1\xC8\x19\xEE\x2D\xA6\xDE\x76\x9F\xBE\xDD\xBA\xEE\x67\x20\xBD\xAF\xC0\x2E\x06\x3B\x45\x6E\x45\x91\xC2\x0C\xE4\x5D\x2B\xC0\x9F\x2F\x66\x08\xF3\x1E\xCD\x8B\xE7\x68\x0F\x28\xCC\xD0\x96\x2C\x98\xB5\xE2\xF8\x3A\x91\xC8\x8A\x62\x86\x5E\x7E\xD6\xDC\x89\xC1\x7C\x18\x62\x48\xE7\x4B\xF3\x1C\x98\x21\x71\x81\x19\x8B\x32\x3D\x07\x73\x10\x2C\xCD\xC3\xDC\x7A\x99\xC3\xDC\x3C\xE4\x38\xED\xD9\x2E\xF7\x59\x79\x71\x1E\x27\x22\x5B\xDB\x8E\xB5\xB3\x6A\x01\xE6\x68\x7B\x1D\xDA\xAE\xCF\x8B\x1D\x90\x30\x0B\x1A\x44\xA5\x07\x29\xA4\xE8\x57\xC2\x2E\x84\x3B\x45\x48\x6B\x1B\x11\xF8\x5D\xC0\x60\x20\xD9\x2C\x67\x6C\xB2\xB1\x6E\xEF\xE8\xA1\x01\xE3\x25\xCB\xCA\xB3\xC7\x27\x8A\x1C\x66\x8A\x10\x72\x52\x73\xDF\xC2\x12\x2D\xB6\xF8\x30\x67\x2F\x77\xCB\x19\xD4\x25\xAA\x2C\xA8\x43\x7B\x87\x15\x56\xC1\xDC\x3A\xF8\xDD\x32\x04\xDF\x9E\xEA\x96\x39\x84\x90\x9F\x98\x5F\xCF\x7F\x05\x61\xB9\x51\x2F\x54\x43\x60\x57\xE0\xC3\xCC\xC9\x4C\x52\xC7\xA4\xCD\x50\xCC\x20\x6B\x8E\x56\x1D\xDE\x6A\x0F\x22\xD1\xEF\x9B\xAF\x60\x43\x41\x43\x43\x55\xE6\x85\x8F\x90\x11\x2A\x21\x46\x58\xB7\xD1\xCA\xAE\xDF\xC6\x9B\x1A\xF8\x2D\x13\x83\x4C\x2B\x66\x61\x1F\xD9\x4D\x50\xEB\x10\x9C\xA4\xC3\x89\x6A\xBD\xF0\xC9\x46\x8D\x10\xCE\xE7\xB1\x66\xBB\xBC\x39\x90\xAD\xD9\xAC\x69\xAB\xD2\xAC\x79\x5F\xAE\x83\x81\xBE\xA4\xA6\x99\x0D\x34\x1C\x23\xA6\xE2\x15\x12\x2B\x49\x0C\x2D\x62\x50\x85\xAF\x16\x20\xB0\x3E\x45\x99\x04\x31\x06\x2F\xF4\x35\xCE\xD0\x54\x5E\xEA\x62\x18\x5A\x03\x39\x09\xCB\x84\x56\xAB\x2A\x28\xB1\x85\x6F\xEC\xC1\x91\xCA\xF6\xF7\x89\x48\x56\x76\xDB\x85\x5F\xE2\x42\x35\x54\xF8\x05\x2E\xD4\xDD\xE1\xB1\xE8\x45\x4C\x4C\xEF\xF5\x1D\x01\x35\x13\x30\x99\x04\x5B\xE2\xB8\xCD\x04\xA4\x75\x99\x11\x9C\xBD\xA9\x38\xBF\xFB\x2B\xC6\xF9\xDD\x84\xF3\x97\x46\xE5\xC0\x7E\xEE\x6A\x91\x26\x7C\x19\x3F\x55\xE3\xA7\xAE\x1E\x3F\x45\x7B\xE2\x86\x3D\x42\x26\xAC\x67\x35\xA2\x7D\xD1\xCA\xED\x32\x76\xCB\x5D\xDB\x45\x0A\x8A\x82\xDC\x1A\xF1\x0C\x52\xF7\x69\x48\x30\x21\x00\xB3\xFE\x15\xD2\x00\x5B\x64\xC6\xEE\xBE\x1F\x25\xF1\x19\xFC\x17\xE2\x79\x8A\x42\x7C\x30\x14\xCB\xC4\x18\xCB\xEC\xA5\x2E\x4D\x87\x19\x18\x2C\x48\xBF\x1A\x6A\x38\xC7\x89\x96\xAD\xF3\x89\x4F\xD8\xD3\xA4\x06\xEE\xD7\x2F\xAC\xB2\xC7\xAC\x6D\x1F\x78\x1F\x1A\x71\x9D\xC5\x0C\x6F\xAF\x26\x53\x4E\x1E\x66\xBE\x34\x36\xA1\x2D\x94\x64\x6E\x62\x30\xEB\xA5\x6F\x4F\xA1\xB5\x69\xCC\x8C\x95\xEE\xDD\xAA\xCF\x4B\x93\x18\xF8\x70\x3B\x36\x36\x45\x0C\x09\x0B\x1A\xC6\xB3\xE8\x2D\x12\x03\x33\x93\x5C\xAA\x3F\xC9\xA5\x7E\x6A\xB2\x4B\x9D\xF9\x48\xE1\xF3\x11\x80\x19\xF0\x5F\xFE\x10\xAD\xD7\xDB\xFF\xF0\xBE\xDD\x5D\xD1\xBD\x55\x8B\x72\xF6\x85\x32\xAF\xDC\x98\x5D\x85\xBC\x46\xF3\xCC\x36\x45\xD0\xB4\xE8\x93\xFF\xDB\xB1\xE5\x3E\xC8\x21\xFE\xE9\x0F\xD5\x6D\x3F\x7B\x84\xC5\x0A\x66\xDD\xAE\xDD\x9F\xEE\xB0\x35\x61\xFE\x61\x47\x60\xF8\xFD\x7B\xD3\x64\x95\x96\xC9\xE4\x36\xC4\x47\x7B\x10\x77\x8B\xD8\x00\x6F\xA0\x8E\x20\xE1\x38\x37\xC3\xC8\x0F\xB2\x6E\x99\xA2\x41\x57\x30\x07\x59\x97\x16\x9E\x92\xCD\x32\x6D\x79\x88\x8C\xE7\x5D\x31\x3A\x87\x7D\x90\x16\x31\xEC\x43\x59\xA1\xA6\x31\x92\x76\x1F\x4D\x9F\xC9\x29\x18\xC8\x8E\x65\x82\x9E\x15\x29\xF5\x58\xEC\xC3\xC2\x75\xA4\xF8\x1C\x73\x56\x2F\xC0\x3E\x0A\xAF\xAB\xF8\xB9\x88\x21\xC5\x02\x89\x1C\x23\x47\x34\x07\xC9\x71\x1C\xB2\x48\x20\x23\x8E\x19\x1A\x07\x52\x87\xC9\x3E\x14\xD0\x08\xE2\xAE\xBD\x1D\x3B\x89\x89\xE7\xBC\x67\xC8\x67\x56\x34\x74\xB0\x72\x67\xBB\xCB\x5C\x4D\x8B\x04\x29\x1E\x17\x89\x21\x30\xEA\xB8\x3D\xE9\x62\x4B\x9B\x9C\x2B\x63\x46\xBC\x8E\xED\x0B\x14\xF1\x98\xF0\x2F\x7C\xC8\xEC\xBE\x13\x6E\x1F\x6C\x68\xF5\xB6\x95\xC7\xDD\x06\x3A\xDA\x40\x9F\x40\xD0\x16\x65\x08\x6D\xBA\x6D\xC5\xF1\xDE\x7A\x19\x2C\x55\x2F\x1A\x02\x9C\x03\x9C\xE4\xC5\x3B\xE7\x46\x13\x72\xA3\xD8\x7A\xBD\x44\x65\x3C\xD5\x2D\x63\xF0\x21\x1E\xF2\xA1\x3C\xCF\x40\x7D\x94\x90\x38\xE7\x49\x86\x84\x97\xDF\x09\x08\xEE\xB1\xD6\x3E\x02\x82\x05\x87\x06\x39\xD8\xC2\x89\xEC\x51\xC8\xB6\xF7\x28\x89\x0E\xA1\xE4\x6D\x53\x50\x11\xE1\x7C\x83\x1B\xC1\x9E\x8D\xB8\xCE\xAD\x4D\x1D\x33\x54\x89\xF6\x90\xA2\xA5\x77\x66\x3E\x71\x56\x8E\xBD\xC1\x5E\x26\x8E\xDE\x91\x95\xF1\xF1\xDA\xCA\x25\xEB\x58\x4C\x02\xEC\x93\x2E\x67\x14\x08\x64\x8D\x65\x8B\xDB\xFD\xC4\x2C\x56\x19\xF6\x92\xF1\xCE\xDF\x04\x7D\x05\xAF\x1E\x54\xA6\x4D\x5E\xB5\x69\x33\x2E\x14\xF0\x87\x42\x01\xDF\xFC\x85\xCF\xDB\x62\x13\x0C\xD4\x4C\x46\x64\xD8\x2E\x52\x54\x08\x94\x88\x14\xFF\x89\x21\xB1\xE9\x36\x04\x3D\x0A\x0E\x69\x39\xDB\x87\xA8\x15\x49\x79\x2E\x92\xC2\xE2\xF5\x32\xE3\x30\x6A\x06\xA3\xE9\x09\x61\x14\xB6\x6C\xE2\x27\x1E\xC1\x87\xB8\xE9\x8E\x66\xAF\xBE\x53\x2F\xD7\x26\x6E\xB7\x69\xC9\x8B\x8B\x87\x5B\x96\x87\x57\x84\x04\xEF\xAF\xF4\x21\x3A\x46\xE4\x8A\x8F\xF1\x5B\x9F\xFC\x38\x4F\xCD\x7C\x7B\xB9\x0B\x3E\x12\x37\x82\x68\x9E\x96\x47\xF2\x63\xF3\xE4\xD3\x73\x57\x95\x8E\x42\xB9\xB8\x92\xE0\xF9\x5D\xD1\x0E\x37\xEF\xCF\x14\xAF\xF9\x27\x16\xB6\x71\x66\x37\x44\x8C\x2F\x4F\xA8\x0B\xB4\x53\xB8\x5D\xEB\xD3\x75\x2D\xE4\x0B\x3D\xA1\x4D\x8F\x6D\xE4\xEB\x27\xA2\xD2\x52\x7A\x7E\xB0\x79\x0E\x0A\x67\xB5\x16\xE7\xD8\x4E\x0F\x12\xD6\x83\xC4\xE9\x01\x35\x80\xA9\x0D\xF8\xF9\xAD\xED\x01\xAB\x0A\x2C\xFF\x21\x18\x92\xFF\xB0\xE2\x53\x0C\x3E\x7B\xF9\xC8\x09\x5E\xD4\x16\xBC\xC8\xAD\x2D\xD5\x4E\x39\x46\xF9\xF7\xD7\x8B\xD0\x54\xFB\x5E\xC3\x22\x02\xBF\x59\xA7\xA8\x34\x60\xEF\x6E\x42\xF0\xBB\xDC\x07\x9F\xCC\x41\x1D\x88\xEB\x68\xB8\x02\x25\x1C\xEA\xA3\x5E\xF6\x8A\x97\x2A\x48\x8A\xD0\x40\xE8\x76\x1E\x34\xEF\x75\xCD\xCD\xB4\xF1\x91\x36\xDD\xBB\x03\x91\xA5\x3C\x28\x04\xB6\x92\x20\xD6\xCD\x37\xD3\x79\xAE\xFA\x98\x87\x5E\x18\xBD\x0A\x83\xBC\xED\x89\x4C\xD7\x87\x29\xF9\xA4\x67\xF3\x9E\xD9\x8A\x59\x63\x07\xB3\xC6\x4A\xF3\x21\x29\x43\x37\x5A\x75\xD8\x84\xDE\xE7\xD2\x68\xB4\xE3\x5F\x2F\xB8\x7D\x09\x5C\x5A\x6D\x17\x03\xB9\x5E\x7A\xA0\x40\xAC\xBB\xA3\x4F\x65\x30\x5F\x86\xCD\xB1\x15\x01\xAA\xEB\xC0\x72\xDF\x22\x82\xCC\xAD\x8F\xB8\xB5\xBD\xFB\x32\x5D\x6D\x39\x0A\x79\x16\x33\xA0\xD3\x0A\xDE\xD2\x7C\x0D\xE2\x8C\x00\x51\xBD\xA1\x4C\x96\xCC\x2D\xEE\x38\xAB\xBD\xD8\x2D\xA5\xBD\xEE\x04\x2D\x70\xCA\xBE\x7D\xDF\xEE\xAE\xDA\xA6\xE5\x4E\xDA\x46\x1A\x1F\xC3\xD0\x43\xD8\xDF\x91\xDB\xF6\x63\x03\x6E\x1D\x2C\xB5\x7B\xEA\x99\x33\xD2\x9F\x42\x69\xB5\x80\xB4\xB6\x83\x9D\x6E\x75\x64\xB6\xC2\x89\xE8\x50\x78\x40\xB7\x79\x74\x4B\xFF\xA0\x60\xD5\x01\x8F\xF6\x28\x32\x2E\x92\xF6\x86\xB9\x3D\x1C\xE6\xD7\xA4\xD2\x97\x79\xDF\xCF\xE2\x91\x67\x4B\x7D\x09\xF4\x33\x85\x06\xF5\xEF\x0A\x0F\xA4\x23\xBB\x38\xA0\x3E\xF8\x01\x7A\x91\x8F\xCC\x1B\x20\x4D\x2F\xBF\xFB\xC5\xAE\xEB\xDD\x9D\x01\xF2\x0B\x0D\xFE\xA2\x78\x27\x03\x28\x16\x07\xEF\xA4\x95\xF5\x83\x4D\x15\xBA\xC6\x83\xF6\xE9\x4C\xE2\x28\x2D\xE2\x48\xC2\x4A\x31\x56\x01\x6D\xD4\x0C\xBA\x25\xAD\x4C\x45\x10\x12\xB7\xCA\xD8\x8D\x1B\x43\x74\xD8\x2D\x37\x95\x81\x13\x20\xB7\x1B\xC7\x3C\x21\x95\xBC\xCC\xB7\xD1\xB8\x13\x27\x8B\xEE\x78\xF9\x41\x21\xEC\xC1\x63\xA5\x07\xF2\x5B\xB8\x10\x3C\xDA\xEC\x23\xC0\x27\x41\x08\xEA\xF7\xCD\x28\xA4\x7A\x31\xB9\x0C\xC1\x4B\x8B\x6F\x7D\xCF\xE2\x5B\x9F\xBD\xB4\xF8\xF6\x4B\x88\x24\xDB\x5A\xEC\xD4\x18\x54\x8D\xC5\xC1\xA2\x04\x6F\xFE\xB2\xBB\x40\x82\x24\xE3\xB2\xF9\x09\x2F\x94\x03\x45\x0B\x4F\xF8\x7F\xA5\x16\x2D\x90\xAA\x90\xF1\x63\xF5\x12\x48\x0D\x9E\x9E\x2F\x1A\xE7\x4E\x13\x2A\x4D\x00\xB7\xCA\x08\x66\x8D\x26\xC1\xBE\xBD\x5B\x06\x15\xCC\x12\xFC\x7A\xC5\xE4\x05\x08\x5E\x7E\xBE\xF0\xAB\xAD\x3B\x0E\x62\x0F\xFC\x17\x0A\xD4\x7F\xC9\x56\x1E\xE1\xE5\xDD\xE1\x8E\x00\xB7\x1F\x6F\x0F\x14\xAB\x85\xBA\x03\x84\xE0\xB9\x1F\xC2\xBF\x8F\x1E\x29\xFC\x56\x9D\xA9\xC0\x60\x84\x13\xBC\xFC\xC2\xF3\x45\xDC\x82\xEB\xC3\x63\x30\x85\xE0\x41\xFC\xC2\xF3\x0C\x16\xFA\x22\x07\x4D\x11\x18\x7B\xB9\x88\x20\xA8\xDE\x18\x7F\x5E\x1C\xCF\x14\xBF\xCA\x51\x6E\x4F\x3B\xC5\x93\x0E\xF4\x5B\x8F\xB1\x03\xB0\x07\x8F\xF3\x31\xEA\x6E\x61\xEC\xA0\x48\x38\x24\x33\x38\x5C\x1D\xA0\xE1\xD3\xD2\x20\x52\xB4\x8C\x43\xD7\x67\x80\x41\x1C\x4A\xDD\x12\x02\x09\x81\x4D\x36\x40\xD3\xFE\x84\x00\x0C\x0D\x57\xC1\x6D\x90\x88\x08\x8F\xA6\x51\xB1\x14\x7F\x90\x59\x4A\x36\x0A\x0D\x01\x8A\x65\xC0\x3B\x80\x50\x58\x10\xC1\x64\xE3\xE5\x17\xEE\xD6\xF4\xBE\x4A\x41\x78\xB7\xA6\xED\x18\x91\x81\x88\x77\xEF\xA4\xED\xFD\x60\x11\x6F\x79\x6B\xEF\x07\x6B\xEF\x0C\xAB\x0E\xC4\xDF\x2F\xEC\xC0\x8A\xFC\x09\x9E\x23\x0D\xAC\xB0\x22\x7F\xB2\xF5\xC3\x8A\xFC\xA9\xA1\x9F\x56\xE4\x4F\x8F\x14\x58\x91\x3F\x43\x45\x86\x0F\xA9\xE4\x74\x98\x68\xDD\x5C\xE3\x6E\x7B\xC0\xA2\x75\x2A\xEB\xBA\x32\x57\x83\x1E\x9F\x34\xF7\xBA\xAB\x56\x9A\xDA\xA5\xE7\xEA\x83\xBA\x97\x16\x67\x3D\x50\x7C\x98\xF7\x28\xD1\x9C\x6C\x61\xDE\x2D\xEB\x43\x5F\xB4\xE9\xDF\x3C\x54\x1F\x5D\xC9\x4B\xD9\x1E\x82\x6F\x7B\xEA\xD6\x7B\xBA\xD1\x3E\xDC\x26\x44\xFE\x9B\xAE\x4B\x50\xB4\x21\x8B\x4C\x25\x6D\x7C\x72\x8D\xEB\x7D\x7A\xE6\x19\xBA\x30\x40\xF2\x85\x01\x35\x98\xCA\x81\x69\xBD\x7B\xE9\x8C\xB1\x04\x85\xDD\x7E\x66\xD7\x1D\x94\x20\x20\xB1\xE7\xDB\x84\x6C\x4A\xB9\x40\x8D\x54\x13\x54\xAA\x5B\xA5\xD4\xB7\xF5\x68\x1F\x1C\x1F\x52\x32\x7F\xEE\x91\x63\x73\x28\x1E\x52\x91\x72\xF7\xE8\x88\x52\xB1\xE8\x62\x5F\xCE\xE8\x73\x3B\x05\xC3\xA4\x90\x54\xBD\x50\x3C\x2D\x55\x24\xAD\x77\x8C\xB7\x74\x94\xAA\x4E\xB0\x2B\xF6\x6C\x15\xC5\x44\xB3\x39\xDE\x61\x38\x42\x70\x47\x69\x6F\x84\xD2\x6A\x1A\xA5\x3D\xA6\x74\xAA\x4D\xC3\x5E\x3E\x9A\xC5\x4E\x8B\xF6\xEB\xAD\xBB\xFD\xDC\x27\x49\x57\x1D\xD6\xB7\x1F\xAF\x09\xDD\x40\x5F\x99\x4B\xF0\xD7\x4B\x0D\x4E\x00\x3C\x3E\x7D\x79\x15\xF0\x94\xD4\x8C\xB6\x0B\x7B\xEE\xE4\x2B\x78\x4B\xEE\xEE\x04\x86\xAC\x6C\x80\xAA\xCE\x7C\x22\xDA\xA6\xA2\x2E\xA8\xB6\xE5\x69\x53\x57\x57\x7C\xD1\x08\x5D\x80\xD0\xF1\xD9\x03\xF4\xC1\x65\x68\x0F\x1E\xA3\x39\xD6\xAD\xB4\x83\xC3\x35\x53\xDC\xAC\xD5\xC4\xF1\x2F\x80\x46\xBA\xDD\x96\x5A\xF4\x8F\x7A\x08\x47\x49\xE7\x71\xAA\xB1\x41\x3B\x9A\x37\x07\xE1\x34\x5D\xB2\xD6\x22\x3A\x9B\x89\xD4\xFA\x7C\x30\xFE\xB3\x9A\x4F\x70\xC9\x4A\xBB\xD0\xF5\xDA\x92\xF0\x72\xBE\x98\x2C\x0E\x72\x41\xF3\xB6\x78\xC5\xE4\x61\x45\xF6\x4A\xFF\x24\x7B\x66\x04\x9A\xF6\xE3\xA1\xA2\x94\xA1\xFD\x8C\x60\x6C\xCB\x63\x6E\x63\x77\x68\xE1\xDE\xCC\x83\xD0\xBE\x22\xE8\xEE\x89\xBA\x7B\xDA\x27\xBA\x0E\xFE\x52\x75\x60\x80\x1E\x84\x4E\x39\xD0\x11\xAC\x83\x7F\x32\xD3\x10\xA2\x33\x79\x57\xB7\x0C\x69\xB7\x93\x76\x1B\xB9\x1E\x10\x7E\xEB\x6F\xE8\xC7\x95\xFE\x44\xFD\xAF\x68\x95\x08\x5F\x48\xC1\x70\x28\x06\xD0\xCA\x22\x18\x07\xD2\x3D\xC4\x58\xAA\x0B\x6A\x1D\x4D\x85\x1E\x32\x13\x9A\xA4\xBA\x65\x21\xAA\x02\x35\x5A\xA0\x5B\x05\x54\x44\x1B\x69\x9B\x91\x6B\x8A\x3B\x81\xF4\x97\x9A\x8B\xBC\xF8\xEC\x08\x89\x53\xB5\xB7\x36\xB4\x01\x04\xF3\x6C\x58\x5E\x46\xC3\xD2\xD8\xB7\xBC\x74\xBD\x69\xC7\xBF\x46\xCA\x7C\xA7\x49\x93\x37\x9C\x35\xBC\x95\xCC\x83\xDF\x12\x38\x1D\x0B\x67\xBE\x5B\x57\x4E\x5F\x78\xA2\xFD\xE7\x8B\x2B\xFC\x05\x22\xAC\xBE\x46\xEE\x73\xE8\x06\x6F\xC3\xF7\x8D\xB0\xB8\xBB\xDD\xD9\xA4\x43\x0A\x5D\x47\xFE\x5B\x4E\x0B\x10\x39\xF0\x69\x97\x71\x4B\xA2\x2A\x24\x2B\x5D\x97\x69\x86\xD1\x6D\xFE\xC5\x6A\xBF\x3F\xED\xFE\xFF\xC3\xEA\x1C\x40\x9B\x7C\xE9\x68\x41\x36\x5A\x90\x8C\x16\x18\x3A\x41\x20\x51\x2E\xD6\xC1\xEB\x5A\xFF\xDE\xCC\xB3\xFF\xF2\xFD\x7C\xD5\x91\xCF\x1E\x2E\xF3\x58\x8F\x9C\xD2\xFA\xD4\xD0\x1B\x69\x88\x42\x64\x9F\x73\x0D\x3D\xD7\x50\x0F\x35\x64\xBF\xA1\xC6\x1B\x2A\xFB\xA3\x23\x0D\xD5\x84\x86\x7A\x14\xF6\x68\xB4\x20\x9E\x45\xC1\x79\x8D\xA8\xF7\x10\xBB\x8D\xC5\xF8\x91\x83\xC2\xE8\x23\x32\x3F\xA5\xDD\xB1\x60\x77\xE4\x13\x54\xFE\x32\x13\xB3\x9A\xEC\x71\xEC\x23\x68\xA7\xAA\xE0\x6D\xC4\x20\x30\x2A\x43\xDB\x50\xB2\x93\x41\xC8\x25\xBA\x7B\x0C\x07\x28\x74\x5E\x77\x1B\xD3\x7D\xEE\x40\xD2\x26\x7C\x9F\x0F\x38\xE1\x9C\xC1\x9B\x74\x72\x85\xFB\x2D\x03\xFB\x19\xC1\xC0\x78\x14\xB4\x05\x28\xA9\x1E\x59\x8B\x48\x2D\x80\x37\xF3\x80\x98\xBC\xCB\xF1\x2A\xFE\xB8\xB2\x8A\xE3\x58\xBB\x12\x2F\x8E\x63\x3F\x0E\x22\xB2\x15\xC1\x61\x41\x67\x9C\xF9\x5B\xB5\x57\xDA\x63\xCC\xA2\x94\x77\x36\x61\x1D\x0F\xBF\xA5\xF8\x4D\xE3\xB7\x04\xBF\xD1\x8D\x3C\xD9\x61\xDE\xB0\xA4\x6D\x7C\x58\x60\x9F\xDC\x85\x40\x3B\x43\xBD\xA8\xD4\x37\xF6\x23\xEF\x6F\x1D\x17\xF9\xE5\xE6\x07\x30\x09\xE9\xB5\x8B\xAA\x48\x68\xE8\xEC\x10\xAD\x6E\xF9\xEB\xF9\x2F\x50\xA3\x94\x22\xC5\xFC\x3F\xD2\x8F\x47\xF8\xC4\x92\xCC\x7F\xB1\x52\x31\x74\xA3\xD5\xE9\xA5\xFC\x4B\xBB\x7C\x6F\x43\xCA\x77\x6C\x19\xBE\x64\x88\xC5\x8E\xB7\x25\x08\xF0\xF2\x3F\xE5\x9D\xE5\x14\x00\x81\xCC\xFF\x1B\xBD\x19\x93\xE4\x78\x68\x37\x32\xFA\x99\x3F\x94\x52\x57\xB7\x0D\x4A\x7B\xB9\x5B\x6A\x1B\x8D\x6D\x58\x05\x3D\x13\x4B\xA5\x3D\x21\xFC\x30\x90\x06\x64\x75\x3D\x8B\x38\xA0\x52\x94\x8C\x3F\x66\x01\xCB\xFF\xB3\xD3\x61\x99\xFF\x78\xA5\xCD\xD5\x37\x6E\x22\xB9\x89\x5C\x67\x3C\x49\xD5\xFF\x8C\x51\xE4\x37\xB4\x35\xC6\x84\xAF\x37\x84\xAF\x66\x7C\xF5\x10\xBE\x5E\x85\xAF\xBE\x02\xBE\x74\xF5\x63\x75\x14\x4C\xE6\xFF\x65\x88\xB0\x53\x06\x8A\xF7\x1A\x88\xCE\xEC\xBA\x26\x36\x63\xCF\xEC\x7E\x0E\x83\xA2\x41\xE6\xFF\x7E\x02\x30\xDF\xDE\xEC\xD1\x6F\xAE\x9E\xAA\x0F\x66\xBA\xB3\x3E\xF8\x71\x03\xE9\xB6\xCE\xE9\x7C\x07\x5F\xED\xD4\x1A\x86\x96\x64\x5A\xF7\x0D\xE5\xEE\x08\x4E\x35\x1F\x31\x59\x33\x2B\xA8\xE7\x08\x11\x15\x55\x73\x84\x0D\xBE\xDA\xA7\x3A\xD9\xA4\xDD\x25\x2B\x3C\xB7\xA7\x03\xFE\xE8\x4A\x29\xF2\xF7\xEA\xB5\x04\x77\xF0\xE1\x38\xAF\x4E\xF0\xB9\x49\xBA\x91\xC1\x43\x22\xF1\xD9\x42\xBE\x7A\x0F\xBC\xF5\xEE\x21\xE5\xF1\x8C\xDB\x7C\x96\x2F\x6A\xA8\x0F\x9B\x57\xB1\x11\x1D\xA2\xA0\x83\x8F\xF7\x93\x54\xCB\x7E\x19\x34\x00\x49\xB7\xD2\x36\x1D\x20\x7F\x18\xA0\x61\x48\x90\x70\x41\x15\x64\x97\xA4\x78\xD4\x78\x9D\x23\x34\xD3\x42\xD7\x9D\x74\xAB\x06\x91\xAD\x11\xE8\xFE\xBC\x51\xCC\xF9\x18\xA2\xE2\x49\x03\x61\xF8\x93\xC3\x18\xB2\xCF\x5B\xE7\x59\x34\x61\xE8\xDD\x4F\xE2\x25\xFB\xA5\x3F\x8C\xA1\xD7\x2C\x12\x69\xDA\x1F\xC3\x93\x2D\x37\xBE\xE7\x42\xB3\x16\x86\x6A\x08\x43\xBF\xC6\x10\xE3\x38\x3A\x0F\xA5\xD6\xD9\xF3\xD2\xB5\xAD\x7C\x3C\x0B\x43\x98\x2A\x06\x6F\x80\xFE\xF5\x61\xA0\xDD\x34\x81\x2B\xB3\xBA\xDF\x4F\xAB\x44\xB2\x5F\x7A\xD5\xC1\x16\x06\x5A\x37\xD7\xAE\xD1\xA1\x32\xBE\xD8\xA2\x02\x5A\xBB\x39\x42\x0B\x68\xE9\x0E\x5D\x31\xD0\x5E\x0D\x34\xDD\xF7\x44\x8D\xD7\x79\x72\x60\x40\xDA\x2F\x89\x1A\x6A\x72\x96\x7F\x2E\x0E\x0B\xE7\x37\xBF\x28\x0E\x0B\xD5\x7C\xD5\x30\xE4\x4F\x19\xAD\x9F\xD2\x52\x0F\x86\xF8\xD0\x5C\x86\x7A\x40\x79\xD5\x35\x3E\x5E\xCD\x1B\x59\xF3\xC6\x6B\x78\xA3\x5A\x77\x4C\x4E\xE6\x8D\xBC\x12\x6F\x1A\x34\x31\x36\x90\x23\xBC\xB1\x7F\xD6\xA0\x49\xAC\xF9\x13\x42\x53\x55\x18\x3B\x86\x7D\x9E\xD0\x74\x3F\x6A\x34\x09\x03\xFF\xEF\x1B\x83\x2F\x8C\x60\xF0\xB9\x06\x83\x2F\x36\x18\xFC\x29\x63\x50\x21\xE3\x55\xC8\xF8\x0D\xCF\x88\x6F\x3F\xA1\x64\x50\x59\xC7\x3B\x86\xAC\xE3\xDD\x9A\x6F\xD0\xB3\x60\xFF\x0F\x87\x55\xF9\xAF\xD2\x62\x88\x8B\x6E\xF2\x2E\x1F\xAE\x64\x72\x78\xA5\x33\x7C\xDD\xFA\xA2\xE4\xFB\xE9\xA5\x85\xEC\x97\x61\x75\x76\x97\xE5\x38\x68\x9D\x5A\x06\xDF\x91\xC7\xAF\xC8\x13\xB8\xD5\xC4\x16\x79\x7C\x74\xB3\x35\x79\xC2\x9A\x3C\x01\x78\x7C\x30\xCF\x5F\xEF\x96\x9E\x33\x2F\x4E\x4B\xFC\x66\x5A\x4C\x83\x20\xBC\xAD\x41\xD8\x6A\xE9\xE1\xB1\xE8\x2E\x03\xD0\x5D\x3E\x45\x76\x47\xFB\x62\xD0\xD6\xCF\x81\xF9\xD1\x16\xCD\x3E\x29\x1A\xA2\x21\x67\x93\xFC\xB7\x5B\x44\xFA\x47\x45\xA1\xAF\x8C\x3C\x9F\x14\xC3\xF4\x69\x7E\x0F\xCC\xCF\x28\x3A\x8C\x59\xE9\xFF\xF4\x7B\xF7\xE8\x8A\xBC\xFC\xAF\xAB\x18\x85\x6E\x17\xCC\x5F\x71\x3F\xE5\x01\x15\xF1\x0A\x75\xCB\xAA\x54\x57\x0A\x96\xA4\x1E\x8D\x1F\x93\x6D\x3F\xA6\x58\x0F\xFD\x3D\x0D\xA6\x7F\x25\x83\xD9\xF6\x63\xCA\x1D\xBF\xAE\xF4\xB0\xF1\x21\xB2\xF1\x63\x7C\xD4\xB7\x35\x02\xEB\xB6\x37\x3C\x10\x92\x51\x41\x63\x3B\xFF\xF0\xFD\x43\xB7\x28\xFF\x89\x2F\xE3\xF6\xC2\xF8\x3F\x28\xD7\x66\xCB\xDA\xF6\x0C\x4F\x38\x1B\x33\x4F\x0B\xC9\x7C\x7B\x20\x4D\x99\xE9\x1E\xBC\x80\xDF\x28\xD1\xC2\x0A\xBD\xD5\x68\xA3\x63\xA3\x6E\x49\xB7\x85\x35\x38\xC5\x0D\x4E\xE1\x57\x85\x53\x04\x31\x2F\x51\xB8\x40\xA4\x8D\xCB\x67\x9C\x18\x0C\x4D\x9C\x1B\x70\x8B\xC8\x4C\x04\x35\x1E\x06\x35\x69\x40\xF5\xBE\x2A\x50\x63\x48\xA6\x81\xCA\xAF\x75\x46\x20\x75\x6F\x79\xDC\x1D\x81\x4B\xD5\x25\x65\xA3\xF0\x0E\x43\xDB\x12\x16\xFD\x77\x28\x2C\xE3\x71\x50\xFB\x7F\x73\xAD\x7B\xA7\xCA\xD7\x77\xE4\x4F\x3D\x81\xBA\xF0\x67\xCA\x25\x4F\x18\x9E\xEA\xE6\xFF\x3F\x1B\x99\x6E\xA9\xAA\x04\x01\x6A\x86\xF2\x03\xF8\x41\x28\xCC\x84\xB8\xCA\x69\x8C\x9A\xE4\xAE\xA3\x26\x20\x95\x3C\x45\x1F\x35\x13\x6A\xB2\x99\x98\xE4\xAE\x71\x0A\xA2\xFE\x56\xE3\xAA\xCB\xEE\x52\xFD\xBF\x6A\x2C\x64\xBE\xFB\x44\xF3\xFD\x89\xE6\xBB\xFD\x2D\x61\xFF\x87\xB0\xF4\x4E\x23\x7F\xBA\x55\xFE\x97\xC2\xFE\x95\xB0\x4F\x8F\x95\x73\xFD\x67\xDA\xE5\x76\x60\x3E\xEE\xC9\x60\x30\x6C\x7C\xFC\x46\xA4\xF8\x46\xB3\x32\xBC\x9F\x76\x2D\xCA\x7E\x59\x9D\xAB\x0E\xD8\xF8\x84\x8D\xE3\x0A\xC6\x1D\x57\x38\xD9\x71\x05\x35\x29\xA3\x9A\x94\x21\x04\xAC\x8A\xE8\xB8\x7C\x22\xA5\x0F\xAA\xA6\x64\x44\xD7\x0B\x56\xF2\x44\xAE\x2C\xEA\xBA\xA3\xCC\x12\xFC\xE6\xE6\x94\x6A\x3A\xA3\xF8\x3A\xC8\x61\x5C\x48\x93\x1B\x5C\x92\x06\x17\xFF\xAB\xC2\xC5\x69\x72\x04\x01\xDF\x41\xD9\xC2\xC1\xDE\x46\x67\x8A\x5A\xE0\xF3\x3E\x21\xDE\x52\xE0\xCD\xD1\x1B\x2A\xBA\x64\x51\x18\x08\xF9\x2D\x64\x15\xE9\x8D\x30\xC2\xE9\xF6\x44\x5E\xE8\xBF\x4D\x5E\xD0\xCD\x8A\xA3\xBC\xD0\xAC\x1B\xD3\xD4\xDA\x7C\xBA\xBA\x26\x10\x14\xEB\x33\xCF\xE3\xF5\xAC\x19\x9B\x92\xBA\x73\xFD\x93\xA7\xA4\xDE\xD0\x94\x94\xEF\x63\xD0\x0E\x19\xDD\xB8\x72\x7E\x77\xDA\x42\x46\x0F\x45\x44\x6D\x57\xCE\x97\x3B\x82\xAE\xA6\xA4\xA0\xED\xE1\x5A\xB0\xDC\xEA\x33\x61\x34\x6E\x93\x08\xB3\x54\x34\x48\x99\x7F\xA3\xA4\x37\x18\xBB\x70\x98\xEF\x17\x88\xE8\xDA\x0D\x2D\xD4\x82\xDD\x8D\x9A\xC8\x44\xB8\x3B\x36\xEC\x6E\xC4\xEF\x65\xF8\x46\x12\x0E\x3C\x24\xB8\xA1\xB2\x3A\x39\x07\x5B\x08\x47\x25\x7A\x4D\x24\x6A\x2A\x79\xC3\xBE\x5F\x8C\x98\x73\x39\x64\xCE\xC5\x90\x39\x97\xB5\xED\x92\x6C\xD8\xA5\xBB\x13\x91\x4D\x9B\xA4\x1D\x13\xCE\x9A\x7B\xCD\x3E\x05\xAA\x46\xF4\xF1\xEA\x86\xCD\xB5\x06\x8A\xC3\xB6\xE6\x7A\xC2\xD6\x2D\xF7\xE6\xC7\xB4\xA6\x64\x90\x71\xB3\x02\x6E\x7F\x20\x32\xCF\xEA\xB5\x8D\x8D\xCE\xD9\x95\x0D\xE0\x14\x40\x7B\x26\x8F\x3B\x0C\x55\xE5\x5E\xE7\x7C\x4F\x8C\x67\x3D\x9C\x5E\xA1\xCA\xF1\xD7\xAE\x71\xE6\x4A\x15\xCE\xF7\x6A\x58\xFF\xF8\x48\xF5\xB2\x9E\x3F\x1F\xE4\xCF\x85\xEF\xE7\xCF\xF7\xFE\x22\x7F\x76\xBF\x4C\x9F\xBB\x4F\xDF\x6E\xF1\xF3\x93\xFB\x56\xE9\xF3\xE9\xDF\xF8\x67\xF4\xD9\xF9\xC4\x27\xEC\x99\xCD\xB5\x5B\xC5\x99\xCD\x35\x71\xC7\x1B\xDF\xF4\xE6\x3B\xDF\x72\xE8\xAE\xB7\xDE\xBD\xF2\xF0\xEA\xE9\xCE\x19\x41\xF9\x9D\xFA\xBD\x0B\x1D\x71\x66\x65\x63\xA7\x23\x36\xB7\xFA\xB0\x76\xEE\x3C\xA7\x2B\xEA\x9C\x3E\x0C\xEF\x7C\xE0\xD8\xC1\x37\x1E\x82\xF3\x2B\xBD\x9D\xB5\xCD\xB3\xA2\x02\x94\x33\x30\x41\x67\x67\x75\xE5\x7C\x07\x56\x1F\x59\xE9\xAD\xAC\xF6\x3B\x3D\xF1\xFA\xB3\x62\x8F\x84\x4E\xA3\x88\xDA\x0F\x1A\xE3\xC5\xBE\x10\xE2\x47\xF6\x7B\x9B\x2B\xE7\x3A\xF2\x9F\xEC\x7F\xAF\x14\x4D\x32\x53\x39\x94\xC7\x54\x0D\xA5\x30\xD5\x43\xD9\x4B\xBD\xA1\xC4\xA5\xFE\x50\xCE\xD2\x60\x3C\xFB\x67\x38\x96\xD2\x33\x1A\xCB\xE8\x19\x8F\x25\xF4\x4C\xC6\xF2\x79\x9A\x56\x3A\xCF\xB4\xCE\xE6\x99\x8D\x24\xF3\x9C\x99\x98\xCB\x73\x76\x3C\x95\x67\x3E\x92\xC9\x73\x6E\x3C\x91\xE7\xBE\x76\x1E\xCF\x6B\xF6\x48\xE3\x79\xED\xA4\x2C\x9E\xFB\x27\xE4\xED\xBC\x6E\x38\x6D\xE7\xF5\x93\xB3\x76\xDE\x30\x21\x69\xE7\xAB\x26\xE6\xEC\xBC\x71\x24\x65\xE7\xFC\xB4\x8C\x9D\x37\x4D\x4C\xD8\xF9\xEA\x29\xF9\x3A\x5F\x33\x2D\x5D\xE7\xCD\x63\xD9\x3A\x61\x5A\xB2\xCE\x62\x6A\xAE\xCE\x72\x52\x76\xCE\xD7\x4E\x4D\xCE\xF9\xBA\x69\xD9\x38\x5F\x3F\x35\x19\xE7\xD7\x8C\xE6\xE2\xFC\xDA\x91\x54\x9C\x07\x26\x67\xE2\xBC\x65\x24\x11\xE7\x1B\x46\xF3\x70\xDE\x3A\x9A\x86\xF3\xB6\xA1\x2C\x9C\x07\x47\x93\x70\xDE\x3E\x9A\x83\xF3\xEB\xC6\x53\x70\xDE\x71\x85\x0C\x9C\x6F\x6C\x27\xE0\x7C\xD3\xDE\xF9\x37\xDF\x3C\x96\x7E\xF3\xCE\x91\xEC\x9B\x6F\x19\x49\xBE\x79\x68\xEF\xDC\x9B\x77\xB5\x52\x6F\xBE\x75\xCF\xCC\x9B\x77\x4F\x4C\xBC\x79\x78\xEF\xBC\x9B\x5F\xBF\x47\xDA\xCD\x6F\x68\x67\xDD\xFC\xC6\x29\x49\x37\xBF\xA9\xCE\xB9\x79\x64\x34\xE5\xE6\xC2\xE4\x8C\x9B\x76\x62\x8E\xCD\xC5\xB1\x14\x9B\x6F\x6B\x32\x6C\xDE\x33\x21\xC1\xE6\xD1\x56\x7E\xCD\x63\x55\x7A\xCD\xE3\x93\xB3\x6B\xDE\x3B\x35\xB9\xE6\xD2\x58\x6E\xCD\xFB\x46\x52\x6B\x9E\x98\x96\x59\xF3\x9B\x27\x26\xD6\xBC\x7F\x28\xAF\xE6\xB7\xB8\xB4\x9A\x27\x9B\xAC\x9A\x6F\x4F\x96\xEB\xAC\x9A\xDF\x6A\x96\xB7\x2E\xF4\x97\x1F\xBE\x70\xE6\x4C\xA7\xF7\x6D\x31\xFD\x40\x52\xBE\xC3\x5F\xEE\xF4\xB7\x56\x1E\xF0\x97\xCF\xF4\xB7\x56\xDE\x99\x2E\x6F\xF6\xB7\x56\x96\xCF\x6C\xF5\xCE\xAD\xF4\xBF\xBD\x95\x94\xF3\x3B\x5A\xB9\x4B\xBF\xB3\x9D\x41\xF7\xC1\x56\xB2\xCE\x87\xDA\xB9\x3A\xBF\xAB\x9D\xAA\xF3\xBB\x9B\x4C\x9D\xDF\xD3\x4E\xD4\xF9\xAE\x3A\x4F\xE7\x72\x2B\x0F\xF2\xA9\x56\x82\xDF\x95\xB1\xAC\x9D\x0F\x4F\x49\xDA\xB9\x3A\x39\x67\xE7\xE9\x69\x29\x3B\x3B\x7B\x65\xEC\x3C\x33\x29\x61\xE7\xD9\x29\xF9\x3A\x1F\x19\x4B\xD7\xB9\x36\x25\x5B\x67\x77\x6A\xB2\xCE\xF5\xE9\xB9\x3A\x27\xE5\xE3\x64\xD9\x3A\x37\x35\x53\xE7\xE6\x78\xA2\xCE\xAD\xF1\x3C\x9D\x63\xE9\x38\xB9\xF1\xF6\xE4\x2C\x9D\xBD\x89\x49\x3A\x77\x46\x4B\xFB\x5B\xEB\x9D\xCD\xFE\xE4\xCC\x9D\x17\x46\x12\x77\x5E\x9C\x98\xB7\xF3\xD1\x29\x69\x3B\x1F\x9B\x9C\xB5\xF3\xF1\x89\x49\x3B\xBF\x77\x8F\x9C\x9D\x53\xD2\x72\xE2\xB3\x4B\xD3\x33\x76\xBE\x7B\xCF\x84\x9D\x97\xF7\xC8\xD7\x39\xD8\x33\x5D\xE7\xB4\x7C\x9C\x4C\xF5\x27\xF6\xCC\xD6\xF9\xE4\xD5\x24\xEB\x7C\x6A\xAF\x5C\x9D\x4F\x5F\x31\x55\xE7\x33\x57\x95\xA9\xF3\x3D\x57\x4E\xD4\xF9\xEC\x48\x9E\xCE\xFF\x17\x00\x00\xFF\xFF\x78\x9C\x11\x2A\x12\x80\x00\x00") diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm new file mode 100644 index 000000000..6b9592b50 Binary files /dev/null and b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/opa/opa.wasm differ diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go new file mode 100644 index 000000000..51b2ed16b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go @@ -0,0 +1,1013 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package wasm contains an IR->WASM compiler backend. +package wasm + +import ( + "bytes" + "fmt" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/internal/compiler/wasm/opa" + "github.com/open-policy-agent/opa/internal/ir" + "github.com/open-policy-agent/opa/internal/wasm/encoding" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +const ( + opaTypeNull int32 = iota + 1 + opaTypeBoolean + opaTypeNumber + opaTypeString + opaTypeArray + opaTypeObject +) + +const ( + opaFuncPrefix = "opa_" + opaAbort = "opa_abort" + opaJSONParse = "opa_json_parse" + opaNull = "opa_null" + opaBoolean = "opa_boolean" + opaNumberInt = "opa_number_int" + opaNumberFloat = "opa_number_float" + opaNumberRef = "opa_number_ref" + opaNumberSize = "opa_number_size" + opaArrayWithCap = "opa_array_with_cap" + opaArrayAppend = "opa_array_append" + opaObject = "opa_object" + opaObjectInsert = "opa_object_insert" + opaSet = "opa_set" + opaSetAdd = "opa_set_add" + opaStringTerminated = "opa_string_terminated" + opaValueBooleanSet = "opa_value_boolean_set" + opaValueNumberSetInt = "opa_value_number_set_int" + opaValueCompare = "opa_value_compare" + opaValueGet = "opa_value_get" + opaValueIter = "opa_value_iter" + opaValueLength = "opa_value_length" + opaValueMerge = "opa_value_merge" + opaValueShallowCopy = "opa_value_shallow_copy" + opaValueType = "opa_value_type" +) + +var builtins = [...]string{ + "opa_builtin0", + "opa_builtin1", + "opa_builtin2", + "opa_builtin3", + "opa_builtin4", +} + +// Compiler implements an IR->WASM compiler backend. +type Compiler struct { + stages []func() error // compiler stages to execute + errors []error // compilation errors encountered + + policy *ir.Policy // input policy to compile + module *module.Module // output WASM module + code *module.CodeEntry // output WASM code + + builtinStringAddrs map[int]uint32 // addresses of built-in string constants + builtinFuncNameAddrs map[string]int32 // addresses of built-in function names for listing + builtinFuncs map[string]int32 // built-in function ids + stringOffset int32 // null-terminated string data base offset + stringAddrs []uint32 // null-terminated string constant addresses + funcs map[string]uint32 // maps imported and exported function names to function indices + + nextLocal uint32 + locals map[ir.Local]uint32 + lctx uint32 // local pointing to eval context + lrs uint32 // local pointing to result set +} + +const ( + errVarAssignConflict int = iota + errObjectInsertConflict + errObjectMergeConflict + errWithConflict +) + +var errorMessages = [...]struct { + id int + message string +}{ + {errVarAssignConflict, "var assignment conflict"}, + {errObjectInsertConflict, "object insert conflict"}, + {errObjectMergeConflict, "object merge conflict"}, + {errWithConflict, "with target conflict"}, +} + +// New returns a new compiler object. +func New() *Compiler { + c := &Compiler{} + c.stages = []func() error{ + c.initModule, + c.compileStrings, + c.compileBuiltinDecls, + c.compileFuncs, + c.compilePlan, + } + return c +} + +// WithPolicy sets the policy to compile. +func (c *Compiler) WithPolicy(p *ir.Policy) *Compiler { + c.policy = p + return c +} + +// Compile returns a compiled WASM module. +func (c *Compiler) Compile() (*module.Module, error) { + + for _, stage := range c.stages { + if err := stage(); err != nil { + return nil, err + } else if len(c.errors) > 0 { + return nil, c.errors[0] // TODO(tsandall) return all errors. + } + } + + return c.module, nil +} + +// initModule instantiates the module from the pre-compiled OPA binary. The +// module is then updated to include declarations for all of the functions that +// are about to be compiled. +func (c *Compiler) initModule() error { + + bs, err := opa.Bytes() + if err != nil { + return err + } + + c.module, err = encoding.ReadModule(bytes.NewReader(bs)) + if err != nil { + return err + } + + c.funcs = make(map[string]uint32) + var funcidx uint32 + + for _, imp := range c.module.Import.Imports { + if imp.Descriptor.Kind() == module.FunctionImportType { + c.funcs[imp.Name] = funcidx + funcidx++ + } + } + + for i := range c.module.Export.Exports { + exp := &c.module.Export.Exports[i] + if exp.Descriptor.Type == module.FunctionExportType { + c.funcs[exp.Name] = exp.Descriptor.Index + } + } + + for _, fn := range c.policy.Funcs.Funcs { + + params := make([]types.ValueType, len(fn.Params)) + for i := 0; i < len(params); i++ { + params[i] = types.I32 + } + + tpe := module.FunctionType{ + Params: params, + Results: []types.ValueType{types.I32}, + } + + c.emitFunctionDecl(fn.Name, tpe, false) + } + + c.emitFunctionDecl("eval", module.FunctionType{ + Params: []types.ValueType{types.I32}, + Results: []types.ValueType{types.I32}, + }, true) + + c.emitFunctionDecl("builtins", module.FunctionType{ + Params: nil, + Results: []types.ValueType{types.I32}, + }, true) + + return nil +} + +// compileStrings compiles string constants into the data section of the module. +// The strings are indexed for lookups in later stages. +func (c *Compiler) compileStrings() error { + + c.stringOffset = 2048 + c.stringAddrs = make([]uint32, len(c.policy.Static.Strings)) + var buf bytes.Buffer + + for i, s := range c.policy.Static.Strings { + addr := uint32(buf.Len()) + uint32(c.stringOffset) + buf.WriteString(s.Value) + buf.WriteByte(0) + c.stringAddrs[i] = addr + } + + c.builtinFuncNameAddrs = make(map[string]int32, len(c.policy.Static.BuiltinFuncs)) + + for _, decl := range c.policy.Static.BuiltinFuncs { + addr := int32(buf.Len()) + int32(c.stringOffset) + buf.WriteString(decl.Name) + buf.WriteByte(0) + c.builtinFuncNameAddrs[decl.Name] = addr + } + + c.builtinStringAddrs = make(map[int]uint32, len(errorMessages)) + + for i := range errorMessages { + addr := uint32(buf.Len()) + uint32(c.stringOffset) + buf.WriteString(errorMessages[i].message) + buf.WriteByte(0) + c.builtinStringAddrs[errorMessages[i].id] = addr + } + + c.module.Data.Segments = append(c.module.Data.Segments, module.DataSegment{ + Index: 0, + Offset: module.Expr{ + Instrs: []instruction.Instruction{ + instruction.I32Const{ + Value: c.stringOffset, + }, + }, + }, + Init: buf.Bytes(), + }) + + return nil +} + +// compileBuiltinDecls generates a function that lists the built-ins required by +// the policy. The host environment should invoke this function obtain the list +// of built-in function identifiers (represented as integers) that will be used +// when calling out. +func (c *Compiler) compileBuiltinDecls() error { + + c.code = &module.CodeEntry{} + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + + lobj := c.genLocal() + + c.appendInstr(instruction.Call{Index: c.function(opaObject)}) + c.appendInstr(instruction.SetLocal{Index: lobj}) + c.builtinFuncs = make(map[string]int32, len(c.policy.Static.BuiltinFuncs)) + + for index, decl := range c.policy.Static.BuiltinFuncs { + c.appendInstr(instruction.GetLocal{Index: lobj}) + c.appendInstr(instruction.I32Const{Value: c.builtinFuncNameAddrs[decl.Name]}) + c.appendInstr(instruction.Call{Index: c.function(opaStringTerminated)}) + c.appendInstr(instruction.I64Const{Value: int64(index)}) + c.appendInstr(instruction.Call{Index: c.function(opaNumberInt)}) + c.appendInstr(instruction.Call{Index: c.function(opaObjectInsert)}) + c.builtinFuncs[decl.Name] = int32(index) + } + + c.appendInstr(instruction.GetLocal{Index: lobj}) + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.emitFunction("builtins", c.code) +} + +// compileFuncs compiles the policy functions and emits them into the module. +func (c *Compiler) compileFuncs() error { + + for _, fn := range c.policy.Funcs.Funcs { + if err := c.compileFunc(fn); err != nil { + return errors.Wrapf(err, "func %v", fn.Name) + } + } + + return nil +} + +// compilePlan compiles the policy plan and emits the resulting function into +// the module. +func (c *Compiler) compilePlan() error { + + c.code = &module.CodeEntry{} + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + c.lctx = c.genLocal() + c.lrs = c.genLocal() + + // Initialize the input and data locals. + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.I32Load{Offset: 0, Align: 2}) + c.appendInstr(instruction.SetLocal{Index: c.local(ir.Input)}) + + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.I32Load{Offset: 4, Align: 2}) + c.appendInstr(instruction.SetLocal{Index: c.local(ir.Data)}) + + // Initialize the result set. + c.appendInstr(instruction.Call{Index: c.function(opaSet)}) + c.appendInstr(instruction.SetLocal{Index: c.lrs}) + c.appendInstr(instruction.GetLocal{Index: c.lctx}) + c.appendInstr(instruction.GetLocal{Index: c.lrs}) + c.appendInstr(instruction.I32Store{Offset: 8, Align: 2}) + + for i := range c.policy.Plan.Blocks { + + instrs, err := c.compileBlock(c.policy.Plan.Blocks[i]) + if err != nil { + return errors.Wrapf(err, "block %d", i) + } + + c.appendInstr(instruction.Block{Instrs: instrs}) + } + + c.appendInstr(instruction.I32Const{Value: int32(0)}) + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + return c.emitFunction("eval", c.code) +} + +func (c *Compiler) compileFunc(fn *ir.Func) error { + + if len(fn.Params) == 0 { + return fmt.Errorf("illegal function: zero args") + } + + c.nextLocal = 0 + c.locals = map[ir.Local]uint32{} + + for _, a := range fn.Params { + _ = c.local(a) + } + + _ = c.local(fn.Return) + + c.code = &module.CodeEntry{} + + for i := range fn.Blocks { + instrs, err := c.compileBlock(fn.Blocks[i]) + if err != nil { + return errors.Wrapf(err, "block %d", i) + } + if i < len(fn.Blocks)-1 { + c.appendInstr(instruction.Block{Instrs: instrs}) + } else { + c.appendInstrs(instrs) + } + } + + c.code.Func.Locals = []module.LocalDeclaration{ + { + Count: c.nextLocal, + Type: types.I32, + }, + } + + var params []types.ValueType + + for i := 0; i < len(fn.Params); i++ { + params = append(params, types.I32) + } + + return c.emitFunction(fn.Name, c.code) +} + +func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, error) { + + var instrs []instruction.Instruction + + for _, stmt := range block.Stmts { + switch stmt := stmt.(type) { + case *ir.ResultSetAdd: + instrs = append(instrs, instruction.GetLocal{Index: c.lrs}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaSetAdd)}) + case *ir.ReturnLocalStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.Return{}) + case *ir.BlockStmt: + for i := range stmt.Blocks { + block, err := c.compileBlock(stmt.Blocks[i]) + if err != nil { + return nil, err + } + instrs = append(instrs, instruction.Block{Instrs: block}) + } + case *ir.BreakStmt: + instrs = append(instrs, instruction.Br{Index: stmt.Index}) + case *ir.CallStmt: + if err := c.compileCallStmt(stmt, &instrs); err != nil { + return nil, err + } + case *ir.WithStmt: + if err := c.compileWithStmt(stmt, &instrs); err != nil { + return instrs, err + } + case *ir.AssignVarStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.AssignVarOnceStmt: + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: c.local(stmt.Target)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: c.local(stmt.Target)}, + instruction.GetLocal{Index: c.local(stmt.Source)}, + instruction.Call{Index: c.function(opaValueCompare)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 1}, + instruction.I32Const{Value: c.builtinStringAddr(errVarAssignConflict)}, + instruction.Call{Index: c.function(opaAbort)}, + instruction.Unreachable{}, + }, + }, + instruction.GetLocal{Index: c.local(stmt.Source)}, + instruction.SetLocal{Index: c.local(stmt.Target)}, + }, + }) + case *ir.AssignBooleanStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Target)}) + if stmt.Value { + instrs = append(instrs, instruction.I32Const{Value: 1}) + } else { + instrs = append(instrs, instruction.I32Const{Value: 0}) + } + instrs = append(instrs, instruction.Call{Index: c.function(opaValueBooleanSet)}) + case *ir.AssignIntStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.I64Const{Value: stmt.Value}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueNumberSetInt)}) + case *ir.ScanStmt: + if err := c.compileScan(stmt, &instrs); err != nil { + return nil, err + } + case *ir.NotStmt: + if err := c.compileNot(stmt, &instrs); err != nil { + return nil, err + } + case *ir.DotStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Key)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.LenStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueLength)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberSize)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.EqualStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.LessThanStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.I32GeS{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.LessThanEqualStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.I32GtS{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.GreaterThanStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.I32LeS{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.GreaterThanEqualStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.I32LtS{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.NotEqualStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueCompare)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.MakeNullStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaNull)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeBooleanStmt: + instr := instruction.I32Const{} + if stmt.Value { + instr.Value = 1 + } else { + instr.Value = 0 + } + instrs = append(instrs, instr) + instrs = append(instrs, instruction.Call{Index: c.function(opaBoolean)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeNumberFloatStmt: + instrs = append(instrs, instruction.F64Const{Value: stmt.Value}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberFloat)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeNumberIntStmt: + instrs = append(instrs, instruction.I64Const{Value: stmt.Value}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberInt)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeNumberRefStmt: + instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(stmt.Index)}) + instrs = append(instrs, instruction.I32Const{Value: int32(len(c.policy.Static.Strings[stmt.Index].Value))}) + instrs = append(instrs, instruction.Call{Index: c.function(opaNumberRef)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeStringStmt: + instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(stmt.Index)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaStringTerminated)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeArrayStmt: + instrs = append(instrs, instruction.I32Const{Value: stmt.Capacity}) + instrs = append(instrs, instruction.Call{Index: c.function(opaArrayWithCap)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeObjectStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaObject)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.MakeSetStmt: + instrs = append(instrs, instruction.Call{Index: c.function(opaSet)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + case *ir.IsArrayStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)}) + instrs = append(instrs, instruction.I32Const{Value: opaTypeArray}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.IsObjectStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)}) + instrs = append(instrs, instruction.I32Const{Value: opaTypeObject}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.IsUndefinedStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.IsDefinedStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + case *ir.ArrayAppendStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Array)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaArrayAppend)}) + case *ir.ObjectInsertStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Object)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Key)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)}) + case *ir.ObjectInsertOnceStmt: + tmp := c.genLocal() + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: c.local(stmt.Object)}, + instruction.GetLocal{Index: c.local(stmt.Key)}, + instruction.Call{Index: c.function(opaValueGet)}, + instruction.SetLocal{Index: tmp}, + instruction.GetLocal{Index: tmp}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: tmp}, + instruction.GetLocal{Index: c.local(stmt.Value)}, + instruction.Call{Index: c.function(opaValueCompare)}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 1}, + instruction.I32Const{Value: c.builtinStringAddr(errObjectInsertConflict)}, + instruction.Call{Index: c.function(opaAbort)}, + instruction.Unreachable{}, + }, + }, + instruction.GetLocal{Index: c.local(stmt.Object)}, + instruction.GetLocal{Index: c.local(stmt.Key)}, + instruction.GetLocal{Index: c.local(stmt.Value)}, + instruction.Call{Index: c.function(opaObjectInsert)}, + }, + }) + case *ir.ObjectMergeStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.A)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.B)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueMerge)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Target)}) + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: c.local(stmt.Target)}, + instruction.BrIf{Index: 0}, + instruction.I32Const{Value: c.builtinStringAddr(errObjectMergeConflict)}, + instruction.Call{Index: c.function(opaAbort)}, + instruction.Unreachable{}, + }, + }) + case *ir.SetAddStmt: + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Set)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Value)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaSetAdd)}) + default: + var buf bytes.Buffer + ir.Pretty(&buf, stmt) + return instrs, fmt.Errorf("illegal statement: %v", buf.String()) + } + } + + return instrs, nil +} + +func (c *Compiler) compileScan(scan *ir.ScanStmt, result *[]instruction.Instruction) error { + var instrs = *result + instrs = append(instrs, instruction.I32Const{Value: 0}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Key)}) + body, err := c.compileScanBlock(scan) + if err != nil { + return err + } + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Loop{Instrs: body}, + }, + }) + *result = instrs + return nil +} + +func (c *Compiler) compileScanBlock(scan *ir.ScanStmt) ([]instruction.Instruction, error) { + var instrs []instruction.Instruction + + // Execute iterator. + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueIter)}) + + // Check for emptiness. + instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 1}) + + // Load value. + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Source)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(scan.Key)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueGet)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(scan.Value)}) + + // Loop body. + nested, err := c.compileBlock(scan.Block) + if err != nil { + return nil, err + } + + // Continue. + instrs = append(instrs, nested...) + instrs = append(instrs, instruction.Br{Index: 0}) + + return instrs, nil +} + +func (c *Compiler) compileNot(not *ir.NotStmt, result *[]instruction.Instruction) error { + var instrs = *result + + // generate and initialize condition variable + cond := c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: 1}) + instrs = append(instrs, instruction.SetLocal{Index: cond}) + + nested, err := c.compileBlock(not.Block) + if err != nil { + return err + } + + // unset condition variable if end of block is reached + nested = append(nested, instruction.I32Const{Value: 0}) + nested = append(nested, instruction.SetLocal{Index: cond}) + instrs = append(instrs, instruction.Block{Instrs: nested}) + + // break out of block if condition variable was unset + instrs = append(instrs, instruction.GetLocal{Index: cond}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + + *result = instrs + return nil +} + +func (c *Compiler) compileWithStmt(with *ir.WithStmt, result *[]instruction.Instruction) error { + + var instrs = *result + save := c.genLocal() + instrs = append(instrs, instruction.GetLocal{Index: c.local(with.Local)}) + instrs = append(instrs, instruction.SetLocal{Index: save}) + + if len(with.Path) == 0 { + instrs = append(instrs, instruction.GetLocal{Index: c.local(with.Value)}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)}) + } else { + instrs = c.compileUpsert(with.Local, with.Path, with.Value, instrs) + } + + undefined := c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: 1}) + instrs = append(instrs, instruction.SetLocal{Index: undefined}) + + nested, err := c.compileBlock(with.Block) + if err != nil { + return err + } + + nested = append(nested, instruction.I32Const{Value: 0}) + nested = append(nested, instruction.SetLocal{Index: undefined}) + instrs = append(instrs, instruction.Block{Instrs: nested}) + instrs = append(instrs, instruction.GetLocal{Index: save}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(with.Local)}) + instrs = append(instrs, instruction.GetLocal{Index: undefined}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + + *result = instrs + + return nil +} + +func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Local, instrs []instruction.Instruction) []instruction.Instruction { + + lcopy := c.genLocal() // holds copy of local + instrs = append(instrs, instruction.GetLocal{Index: c.local(local)}) + instrs = append(instrs, instruction.SetLocal{Index: lcopy}) + + // Shallow copy the local if defined otherwise initialize to an empty object. + instrs = append(instrs, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.Block{Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: lcopy}, + instruction.I32Eqz{}, + instruction.BrIf{Index: 0}, + instruction.GetLocal{Index: lcopy}, + instruction.Call{Index: c.function(opaValueShallowCopy)}, + instruction.SetLocal{Index: lcopy}, + instruction.GetLocal{Index: lcopy}, + instruction.SetLocal{Index: c.local(local)}, + instruction.Br{Index: 1}, + }}, + instruction.Call{Index: c.function(opaObject)}, + instruction.SetLocal{Index: lcopy}, + instruction.GetLocal{Index: lcopy}, + instruction.SetLocal{Index: c.local(local)}, + }, + }) + + // Initialize the locals that specify the path of the upsert operation. + lpath := make(map[int]uint32, len(path)) + + for i := 0; i < len(path); i++ { + lpath[i] = c.genLocal() + instrs = append(instrs, instruction.I32Const{Value: c.stringAddr(path[i])}) + instrs = append(instrs, instruction.Call{Index: c.function(opaStringTerminated)}) + instrs = append(instrs, instruction.SetLocal{Index: lpath[i]}) + } + + // Generate a block that traverses the path of the upsert operation, + // shallowing copying values at each step as needed. Stop before the final + // segment that will only be inserted. + var inner []instruction.Instruction + ltemp := c.genLocal() + + for i := 0; i < len(path)-1; i++ { + + // Lookup the next part of the path. + inner = append(inner, instruction.GetLocal{Index: lcopy}) + inner = append(inner, instruction.GetLocal{Index: lpath[i]}) + inner = append(inner, instruction.Call{Index: c.function(opaValueGet)}) + inner = append(inner, instruction.SetLocal{Index: ltemp}) + + // If the next node is missing, break. + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.I32Eqz{}) + inner = append(inner, instruction.BrIf{Index: uint32(i)}) + + // If the next node is not an object, generate a conflict error. + inner = append(inner, instruction.Block{ + Instrs: []instruction.Instruction{ + instruction.GetLocal{Index: ltemp}, + instruction.Call{Index: c.function(opaValueType)}, + instruction.I32Const{Value: opaTypeObject}, + instruction.I32Eq{}, + instruction.BrIf{Index: 0}, + instruction.I32Const{Value: c.builtinStringAddr(errWithConflict)}, + instruction.Call{Index: c.function(opaAbort)}, + }, + }) + + // Otherwise, shallow copy the next node node and insert into the copy + // before continuing. + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.Call{Index: c.function(opaValueShallowCopy)}) + inner = append(inner, instruction.SetLocal{Index: ltemp}) + inner = append(inner, instruction.GetLocal{Index: lcopy}) + inner = append(inner, instruction.GetLocal{Index: lpath[i]}) + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.Call{Index: c.function(opaObjectInsert)}) + inner = append(inner, instruction.GetLocal{Index: ltemp}) + inner = append(inner, instruction.SetLocal{Index: lcopy}) + } + + inner = append(inner, instruction.Br{Index: uint32(len(path) - 1)}) + + // Generate blocks that handle missing nodes during traversal. + var block []instruction.Instruction + lval := c.genLocal() + + for i := 0; i < len(path)-1; i++ { + block = append(block, instruction.Block{Instrs: inner}) + block = append(block, instruction.Call{Index: c.function(opaObject)}) + block = append(block, instruction.SetLocal{Index: lval}) + block = append(block, instruction.GetLocal{Index: lcopy}) + block = append(block, instruction.GetLocal{Index: lpath[i]}) + block = append(block, instruction.GetLocal{Index: lval}) + block = append(block, instruction.Call{Index: c.function(opaObjectInsert)}) + block = append(block, instruction.GetLocal{Index: lval}) + block = append(block, instruction.SetLocal{Index: lcopy}) + inner = block + block = nil + } + + // Finish by inserting the statement's value into the shallow copied node. + instrs = append(instrs, instruction.Block{Instrs: inner}) + instrs = append(instrs, instruction.GetLocal{Index: lcopy}) + instrs = append(instrs, instruction.GetLocal{Index: lpath[len(path)-1]}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(value)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaObjectInsert)}) + + return instrs +} + +func (c *Compiler) compileCallStmt(stmt *ir.CallStmt, result *[]instruction.Instruction) error { + + if index, ok := c.funcs[stmt.Func]; ok { + return c.compileInternalCall(stmt, index, result) + } + + if id, ok := c.builtinFuncs[stmt.Func]; ok { + return c.compileBuiltinCall(stmt, id, result) + } + + c.errors = append(c.errors, fmt.Errorf("undefined function: %q", stmt.Func)) + + return nil +} + +func (c *Compiler) compileInternalCall(stmt *ir.CallStmt, index uint32, result *[]instruction.Instruction) error { + instrs := *result + + for _, arg := range stmt.Args { + instrs = append(instrs, instruction.GetLocal{Index: c.local(arg)}) + } + + instrs = append(instrs, instruction.Call{Index: index}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Result)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Result)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + *result = instrs + return nil +} + +func (c *Compiler) compileBuiltinCall(stmt *ir.CallStmt, id int32, result *[]instruction.Instruction) error { + + if len(stmt.Args) >= len(builtins) { + c.errors = append(c.errors, fmt.Errorf("too many built-in call arguments: %q", stmt.Func)) + return nil + } + + instrs := *result + instrs = append(instrs, instruction.I32Const{Value: id}) + instrs = append(instrs, instruction.I32Const{Value: 0}) // unused context parameter + + for _, arg := range stmt.Args { + instrs = append(instrs, instruction.GetLocal{Index: c.local(arg)}) + } + + instrs = append(instrs, instruction.Call{Index: c.funcs[builtins[len(stmt.Args)]]}) + instrs = append(instrs, instruction.SetLocal{Index: c.local(stmt.Result)}) + instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Result)}) + instrs = append(instrs, instruction.I32Eqz{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + *result = instrs + return nil +} + +func (c *Compiler) emitFunctionDecl(name string, tpe module.FunctionType, export bool) { + + typeIndex := c.emitFunctionType(tpe) + c.module.Function.TypeIndices = append(c.module.Function.TypeIndices, typeIndex) + c.module.Code.Segments = append(c.module.Code.Segments, module.RawCodeSegment{}) + c.funcs[name] = uint32((len(c.module.Function.TypeIndices) - 1) + c.functionImportCount()) + + if export { + c.module.Export.Exports = append(c.module.Export.Exports, module.Export{ + Name: name, + Descriptor: module.ExportDescriptor{ + Type: module.FunctionExportType, + Index: c.funcs[name], + }, + }) + } + +} + +func (c *Compiler) emitFunctionType(tpe module.FunctionType) uint32 { + for i, other := range c.module.Type.Functions { + if tpe.Equal(other) { + return uint32(i) + } + } + c.module.Type.Functions = append(c.module.Type.Functions, tpe) + return uint32(len(c.module.Type.Functions) - 1) +} + +func (c *Compiler) emitFunction(name string, entry *module.CodeEntry) error { + var buf bytes.Buffer + if err := encoding.WriteCodeEntry(&buf, entry); err != nil { + return err + } + index := c.function(name) - uint32(c.functionImportCount()) + c.module.Code.Segments[index].Code = buf.Bytes() + return nil +} + +func (c *Compiler) functionImportCount() int { + var count int + + for _, imp := range c.module.Import.Imports { + if imp.Descriptor.Kind() == module.FunctionImportType { + count++ + } + } + + return count +} + +func (c *Compiler) stringAddr(index int) int32 { + return int32(c.stringAddrs[index]) +} + +func (c *Compiler) builtinStringAddr(code int) int32 { + return int32(c.builtinStringAddrs[code]) +} + +func (c *Compiler) local(l ir.Local) uint32 { + var u32 uint32 + var exist bool + if u32, exist = c.locals[l]; !exist { + u32 = c.nextLocal + c.locals[l] = u32 + c.nextLocal++ + } + return u32 +} + +func (c *Compiler) genLocal() uint32 { + l := c.nextLocal + c.nextLocal++ + return l +} + +func (c *Compiler) function(name string) uint32 { + return c.funcs[name] +} + +func (c *Compiler) appendInstr(instr instruction.Instruction) { + c.code.Func.Expr.Instrs = append(c.code.Func.Expr.Instrs, instr) +} + +func (c *Compiler) appendInstrs(instrs []instruction.Instruction) { + for _, instr := range instrs { + c.appendInstr(instr) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go b/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go new file mode 100644 index 000000000..6b8ba48d1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go @@ -0,0 +1,42 @@ +package archive + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "strings" +) + +// MustWriteTarGz write the list of file names and content +// into a tarball. +func MustWriteTarGz(files [][2]string) *bytes.Buffer { + var buf bytes.Buffer + gw := gzip.NewWriter(&buf) + defer gw.Close() + tw := tar.NewWriter(gw) + defer tw.Close() + for _, file := range files { + if err := WriteFile(tw, file[0], []byte(file[1])); err != nil { + panic(err) + } + } + return &buf +} + +// WriteFile adds a file header with content to the given tar writer +func WriteFile(tw *tar.Writer, path string, bs []byte) error { + + hdr := &tar.Header{ + Name: "/" + strings.TrimLeft(path, "/"), + Mode: 0600, + Typeflag: tar.TypeReg, + Size: int64(len(bs)), + } + + if err := tw.WriteHeader(hdr); err != nil { + return err + } + + _, err := tw.Write(bs) + return err +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/file/url/url.go b/vendor/github.com/open-policy-agent/opa/internal/file/url/url.go new file mode 100644 index 000000000..aacc57183 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/file/url/url.go @@ -0,0 +1,42 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package url contains helpers for dealing with file paths and URLs. +package url + +import ( + "fmt" + "net/url" + "runtime" + "strings" +) + +var goos = runtime.GOOS + +// Clean returns a cleaned file path that may or may not be a URL. +func Clean(path string) (string, error) { + + if strings.Contains(path, "://") { + + url, err := url.Parse(path) + if err != nil { + return "", err + } + + if url.Scheme != "file" { + return "", fmt.Errorf("unsupported URL scheme: %v", path) + } + + path = url.Path + + // Trim leading slash on Windows if present. The url.Path field returned + // by url.Parse has leading slash that causes CreateFile() calls to fail + // on Windows. See https://github.com/golang/go/issues/6027 for details. + if goos == "windows" && len(path) >= 1 && path[0] == '/' { + path = path[1:] + } + } + + return path, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/ir/ir.go b/vendor/github.com/open-policy-agent/opa/internal/ir/ir.go new file mode 100644 index 000000000..14d67022f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/ir/ir.go @@ -0,0 +1,395 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package ir defines an intermediate representation (IR) for Rego. +// +// The IR specifies an imperative execution model for Rego policies similar to a +// query plan in traditional databases. +package ir + +import ( + "fmt" +) + +type ( + // Policy represents a planned policy query. + Policy struct { + Static *Static + Plan *Plan + Funcs *Funcs + } + + // Static represents a static data segment that is indexed into by the policy. + Static struct { + Strings []*StringConst + BuiltinFuncs []*BuiltinFunc + } + + // BuiltinFunc represents a built-in function that may be required by the + // policy. + BuiltinFunc struct { + Name string + } + + // Funcs represents a collection of planned functions to include in the + // policy. + Funcs struct { + Funcs []*Func + } + + // Func represents a named plan (function) that can be invoked. Functions + // accept one or more parameters and return a value. By convention, the + // input document and data documents are always passed as the first and + // second arguments (respectively). + Func struct { + Name string + Params []Local + Return Local + Blocks []*Block // TODO(tsandall): should this be a plan? + } + + // Plan represents an ordered series of blocks to execute. Plan execution + // stops when a return statement is reached. Blocks are executed in-order. + Plan struct { + Blocks []*Block + } + + // Block represents an ordered sequence of statements to execute. Blocks are + // executed until a return statement is encountered, a statement is undefined, + // or there are no more statements. If all statements are defined but no return + // statement is encountered, the block is undefined. + Block struct { + Stmts []Stmt + } + + // Stmt represents an operation (e.g., comparison, loop, dot, etc.) to execute. + Stmt interface { + } + + // Local represents a plan-scoped variable. + // + // TODO(tsandall): should this be int32 for safety? + Local int + + // Const represents a constant value from the policy. + Const interface { + typeMarker() + } + + // NullConst represents a null value. + NullConst struct{} + + // BooleanConst represents a boolean value. + BooleanConst struct { + Value bool + } + + // StringConst represents a string value. + StringConst struct { + Value string + } + + // IntConst represents an integer constant. + IntConst struct { + Value int64 + } + + // FloatConst represents a floating-point constant. + FloatConst struct { + Value float64 + } +) + +const ( + // Input is the local variable that refers to the global input document. + Input Local = iota + + // Data is the local variable that refers to the global data document. + Data + + // Unused is the free local variable that can be allocated in a plan. + Unused +) + +func (a *Policy) String() string { + return "Policy" +} + +func (a *Static) String() string { + return fmt.Sprintf("Static (%d strings)", len(a.Strings)) +} + +func (a *Funcs) String() string { + return fmt.Sprintf("Funcs (%d funcs)", len(a.Funcs)) +} + +func (a *Func) String() string { + return fmt.Sprintf("%v (%d params: %v, %d blocks)", a.Name, len(a.Params), a.Params, len(a.Blocks)) +} + +func (a *Plan) String() string { + return fmt.Sprintf("Plan (%d blocks)", len(a.Blocks)) +} + +func (a *Block) String() string { + return fmt.Sprintf("Block (%d statements)", len(a.Stmts)) +} + +func (a *BooleanConst) typeMarker() {} +func (a *NullConst) typeMarker() {} +func (a *IntConst) typeMarker() {} +func (a *FloatConst) typeMarker() {} +func (a *StringConst) typeMarker() {} + +// ReturnLocalStmt represents a return statement that yields a local value. +type ReturnLocalStmt struct { + Source Local +} + +// CallStmt represents a named function call. The result should be stored in the +// result local. +type CallStmt struct { + Func string + Args []Local + Result Local +} + +// BlockStmt represents a nested block. Nested blocks and break statements can +// be used to short-circuit execution. +type BlockStmt struct { + Blocks []*Block +} + +func (a *BlockStmt) String() string { + return fmt.Sprintf("BlockStmt (%d blocks)", len(a.Blocks)) +} + +// BreakStmt represents a jump out of the current block. The index specifies how +// many blocks to jump starting from zero (the current block). Execution will +// continue from the end of the block that is jumped to. +type BreakStmt struct { + Index uint32 +} + +// DotStmt represents a lookup operation on a value (e.g., array, object, etc.) +// The source of a DotStmt may be a scalar value in which case the statement +// will be undefined. +type DotStmt struct { + Source Local + Key Local + Target Local +} + +// LenStmt represents a length() operation on a local variable. The +// result is stored in the target local variable. +type LenStmt struct { + Source Local + Target Local +} + +// ScanStmt represents a linear scan over a composite value. The +// source may be a scalar in which case the block will never execute. +type ScanStmt struct { + Source Local + Key Local + Value Local + Block *Block +} + +// NotStmt represents a negated statement. +type NotStmt struct { + Block *Block +} + +// AssignBooleanStmt represents an assignment of a boolean value to a local variable. +type AssignBooleanStmt struct { + Value bool + Target Local +} + +// AssignIntStmt represents an assignment of an integer value to a +// local variable. +type AssignIntStmt struct { + Value int64 + Target Local +} + +// AssignVarStmt represents an assignment of one local variable to another. +type AssignVarStmt struct { + Source Local + Target Local +} + +// AssignVarOnceStmt represents an assignment of one local variable to another. +// If the target is defined, execution aborts with a conflict error. +// +// TODO(tsandall): is there a better name for this? +type AssignVarOnceStmt struct { + Target Local + Source Local +} + +// MakeStringStmt constructs a local variable that refers to a string constant. +type MakeStringStmt struct { + Index int + Target Local +} + +// MakeNullStmt constructs a local variable that refers to a null value. +type MakeNullStmt struct { + Target Local +} + +// MakeBooleanStmt constructs a local variable that refers to a boolean value. +type MakeBooleanStmt struct { + Value bool + Target Local +} + +// MakeNumberFloatStmt constructs a local variable that refers to a +// floating-point number value. +type MakeNumberFloatStmt struct { + Value float64 + Target Local +} + +// MakeNumberIntStmt constructs a local variable that refers to an integer value. +type MakeNumberIntStmt struct { + Value int64 + Target Local +} + +// MakeNumberRefStmt constructs a local variable that refers to a number stored as a string. +type MakeNumberRefStmt struct { + Index int + Target Local +} + +// MakeArrayStmt constructs a local variable that refers to an array value. +type MakeArrayStmt struct { + Capacity int32 + Target Local +} + +// MakeObjectStmt constructs a local variable that refers to an object value. +type MakeObjectStmt struct { + Target Local +} + +// MakeSetStmt constructs a local variable that refers to a set value. +type MakeSetStmt struct { + Target Local +} + +// EqualStmt represents an value-equality check of two local variables. +type EqualStmt struct { + A Local + B Local +} + +// LessThanStmt represents a < check of two local variables. +type LessThanStmt struct { + A Local + B Local +} + +// LessThanEqualStmt represents a <= check of two local variables. +type LessThanEqualStmt struct { + A Local + B Local +} + +// GreaterThanStmt represents a > check of two local variables. +type GreaterThanStmt struct { + A Local + B Local +} + +// GreaterThanEqualStmt represents a >= check of two local variables. +type GreaterThanEqualStmt struct { + A Local + B Local +} + +// NotEqualStmt represents a != check of two local variables. +type NotEqualStmt struct { + A Local + B Local +} + +// IsArrayStmt represents a dynamic type check on a local variable. +type IsArrayStmt struct { + Source Local +} + +// IsObjectStmt represents a dynamic type check on a local variable. +type IsObjectStmt struct { + Source Local +} + +// IsDefinedStmt represents a check of whether a local variable is defined. +type IsDefinedStmt struct { + Source Local +} + +// IsUndefinedStmt represents a check of whether local variable is undefined. +type IsUndefinedStmt struct { + Source Local +} + +// ArrayAppendStmt represents a dynamic append operation of a value +// onto an array. +type ArrayAppendStmt struct { + Value Local + Array Local +} + +// ObjectInsertStmt represents a dynamic insert operation of a +// key/value pair into an object. +type ObjectInsertStmt struct { + Key Local + Value Local + Object Local +} + +// ObjectInsertOnceStmt represents a dynamic insert operation of a key/value +// pair into an object. If the key already exists and the value differs, +// execution aborts with a conflict error. +type ObjectInsertOnceStmt struct { + Key Local + Value Local + Object Local +} + +// ObjectMergeStmt performs a recursive merge of two object values. If either of +// the locals refer to non-object values this operation will abort with a +// conflict error. Overlapping object keys are merged recursively. +type ObjectMergeStmt struct { + A Local + B Local + Target Local +} + +// SetAddStmt represents a dynamic add operation of an element into a set. +type SetAddStmt struct { + Value Local + Set Local +} + +// WithStmt replaces the Local or a portion of the document referred to by the +// Local with the Value and executes the contained block. If the Path is +// non-empty, the Value is upserted into the Local. If the intermediate nodes in +// the Local referred to by the Path do not exist, they will be created. When +// the WithStmt finishes the Local is reset to it's original value. +type WithStmt struct { + Local Local + Path []int + Value Local + Block *Block +} + +// ResultSetAdd adds a value into the result set returned by the query plan. +type ResultSetAdd struct { + Value Local +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/ir/pretty.go b/vendor/github.com/open-policy-agent/opa/internal/ir/pretty.go new file mode 100644 index 000000000..a23bad729 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/ir/pretty.go @@ -0,0 +1,44 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +import ( + "fmt" + "io" + "strings" +) + +// Pretty writes a human-readable representation of an IR object to w. +func Pretty(w io.Writer, x interface{}) { + + pp := &prettyPrinter{ + depth: -1, + w: w, + } + Walk(pp, x) +} + +type prettyPrinter struct { + depth int + w io.Writer +} + +func (pp *prettyPrinter) Before(x interface{}) { + pp.depth++ +} + +func (pp *prettyPrinter) After(x interface{}) { + pp.depth-- +} + +func (pp *prettyPrinter) Visit(x interface{}) (Visitor, error) { + pp.writeIndent("%T %+v", x, x) + return pp, nil +} + +func (pp *prettyPrinter) writeIndent(f string, a ...interface{}) { + pad := strings.Repeat("| ", pp.depth) + fmt.Fprintf(pp.w, pad+f+"\n", a...) +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/ir/walk.go b/vendor/github.com/open-policy-agent/opa/internal/ir/walk.go new file mode 100644 index 000000000..0e97099c5 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/ir/walk.go @@ -0,0 +1,84 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package ir + +// Visitor defines the interface for visiting IR nodes. +type Visitor interface { + Before(x interface{}) + Visit(x interface{}) (Visitor, error) + After(x interface{}) +} + +// Walk invokes the visitor for nodes under x. +func Walk(vis Visitor, x interface{}) error { + impl := walkerImpl{ + vis: vis, + } + impl.walk(x) + return impl.err +} + +type walkerImpl struct { + vis Visitor + err error +} + +func (w *walkerImpl) walk(x interface{}) { + + if x == nil { + return + } + + prev := w.vis + w.vis.Before(x) + defer w.vis.After(x) + w.vis, w.err = w.vis.Visit(x) + if w.err != nil { + return + } else if w.vis == nil { + w.vis = prev + return + } + + switch x := x.(type) { + case *Policy: + w.walk(x.Static) + w.walk(x.Plan) + w.walk(x.Funcs) + case *Static: + for _, s := range x.Strings { + w.walk(s) + } + for _, f := range x.BuiltinFuncs { + w.walk(f) + } + case *Funcs: + for _, fn := range x.Funcs { + w.walk(fn) + } + case *Func: + for _, b := range x.Blocks { + w.walk(b) + } + case *Plan: + for _, b := range x.Blocks { + w.walk(b) + } + case *Block: + for _, s := range x.Stmts { + w.walk(s) + } + case *BlockStmt: + for _, b := range x.Blocks { + w.walk(b) + } + case *ScanStmt: + w.walk(x.Block) + case *NotStmt: + w.walk(x.Block) + case *WithStmt: + w.walk(x.Block) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go b/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go new file mode 100644 index 000000000..24ddc9095 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go @@ -0,0 +1,170 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package leb128 implements LEB128 integer encoding. +package leb128 + +import ( + "io" +) + +// MustReadVarInt32 returns an int32 from r or panics. +func MustReadVarInt32(r io.Reader) int32 { + i32, err := ReadVarInt32(r) + if err != nil { + panic(err) + } + return i32 +} + +// MustReadVarInt64 returns an int64 from r or panics. +func MustReadVarInt64(r io.Reader) int64 { + i64, err := ReadVarInt64(r) + if err != nil { + panic(err) + } + return i64 +} + +// MustReadVarUint32 returns an uint32 from r or panics. +func MustReadVarUint32(r io.Reader) uint32 { + u32, err := ReadVarUint32(r) + if err != nil { + panic(err) + } + return u32 +} + +// MustReadVarUint64 returns an uint64 from r or panics. +func MustReadVarUint64(r io.Reader) uint64 { + u64, err := ReadVarUint64(r) + if err != nil { + panic(err) + } + return u64 +} + +// Copied rom http://dwarfstd.org/doc/Dwarf3.pdf. + +// ReadVarUint32 tries to read a uint32 from r. +func ReadVarUint32(r io.Reader) (uint32, error) { + u64, err := ReadVarUint64(r) + if err != nil { + return 0, err + } + return uint32(u64), nil +} + +// ReadVarUint64 tries to read a uint64 from r. +func ReadVarUint64(r io.Reader) (uint64, error) { + var result uint64 + var shift uint64 + buf := make([]byte, 1) + for { + if _, err := r.Read(buf); err != nil { + return 0, err + } + v := uint64(buf[0]) + result |= (v & 0x7F) << shift + if v&0x80 == 0 { + return result, nil + } + shift += 7 + } + +} + +// ReadVarInt32 tries to read a int32 from r. +func ReadVarInt32(r io.Reader) (int32, error) { + i64, err := ReadVarInt64(r) + if err != nil { + return 0, err + } + return int32(i64), nil +} + +// ReadVarInt64 tries to read a int64 from r. +func ReadVarInt64(r io.Reader) (int64, error) { + var result int64 + var shift uint64 + size := uint64(32) + buf := make([]byte, 1) + for { + if _, err := r.Read(buf); err != nil { + return 0, err + } + v := int64(buf[0]) + result |= (v & 0x7F) << shift + shift += 7 + if v&0x80 == 0 { + if (shift < size) && (v&0x40 != 0) { + result |= (^0 << shift) + } + return result, nil + } + } +} + +// WriteVarUint32 writes u to w. +func WriteVarUint32(w io.Writer, u uint32) error { + var b []byte + _, err := w.Write(appendUleb128(b, uint64(u))) + return err +} + +// WriteVarUint64 writes u to w. +func WriteVarUint64(w io.Writer, u uint64) error { + var b []byte + _, err := w.Write(appendUleb128(b, u)) + return err +} + +// WriteVarInt32 writes u to w. +func WriteVarInt32(w io.Writer, i int32) error { + var b []byte + _, err := w.Write(appendSleb128(b, int64(i))) + return err +} + +// WriteVarInt64 writes u to w. +func WriteVarInt64(w io.Writer, i int64) error { + var b []byte + _, err := w.Write(appendSleb128(b, i)) + return err +} + +// Copied from https://github.com/golang/go/blob/master/src/cmd/internal/dwarf/dwarf.go. + +// appendUleb128 appends v to b using DWARF's unsigned LEB128 encoding. +func appendUleb128(b []byte, v uint64) []byte { + for { + c := uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + b = append(b, c) + if c&0x80 == 0 { + break + } + } + return b +} + +// appendSleb128 appends v to b using DWARF's signed LEB128 encoding. +func appendSleb128(b []byte, v int64) []byte { + for { + c := uint8(v & 0x7f) + s := uint8(v & 0x40) + v >>= 7 + if (v != -1 || s == 0) && (v != 0 || s != 0) { + c |= 0x80 + } + b = append(b, c) + if c&0x80 == 0 { + break + } + } + return b +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/merge/merge.go b/vendor/github.com/open-policy-agent/opa/internal/merge/merge.go new file mode 100644 index 000000000..fa53236d0 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/merge/merge.go @@ -0,0 +1,40 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package merge contains helpers to merge data structures +// frequently encountered in OPA. +package merge + +// InterfaceMaps returns the result of merging a and b. If a and b cannot be +// merged because of conflicting key-value pairs, ok is false. +func InterfaceMaps(a map[string]interface{}, b map[string]interface{}) (c map[string]interface{}, ok bool) { + + c = map[string]interface{}{} + for k := range a { + c[k] = a[k] + } + + for k := range b { + + add := b[k] + exist, ok := c[k] + if !ok { + c[k] = add + continue + } + + existObj, existOk := exist.(map[string]interface{}) + addObj, addOk := add.(map[string]interface{}) + if !existOk || !addOk { + return nil, false + } + + c[k], ok = InterfaceMaps(existObj, addObj) + if !ok { + return nil, false + } + } + + return c, true +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go new file mode 100644 index 000000000..de9bfeec7 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go @@ -0,0 +1,1758 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package planner contains a query planner for Rego queries. +package planner + +import ( + "errors" + "fmt" + "sort" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/internal/ir" +) + +type planiter func() error +type binaryiter func(ir.Local, ir.Local) error + +// Planner implements a query planner for Rego queries. +type Planner struct { + policy *ir.Policy // result of planning + queries []ast.Body // input query to plan + modules []*ast.Module // input modules to support queries + rewritten map[ast.Var]ast.Var // rewritten query vars + strings map[string]int // global string constant indices + externs map[string]struct{} // built-in functions that are required in execution environment + decls map[string]*ast.Builtin // built-in functions that may be provided in execution environment + rules *ruletrie // rules that may be planned + funcs *funcstack // functions that have been planned + curr *ir.Block // in-progress query block + vars *varstack // in-scope variables + ltarget ir.Local // target variable of last planned statement + lnext ir.Local // next variable to use +} + +// New returns a new Planner object. +func New() *Planner { + return &Planner{ + policy: &ir.Policy{ + Static: &ir.Static{}, + Plan: &ir.Plan{}, + Funcs: &ir.Funcs{}, + }, + strings: map[string]int{}, + externs: map[string]struct{}{}, + lnext: ir.Unused, + vars: newVarstack(map[ast.Var]ir.Local{ + ast.InputRootDocument.Value.(ast.Var): ir.Input, + ast.DefaultRootDocument.Value.(ast.Var): ir.Data, + }), + rules: newRuletrie(), + funcs: newFuncstack(), + } +} + +// WithBuiltinDecls tells the planner what built-in function may be available +// inside the execution environment. +func (p *Planner) WithBuiltinDecls(decls map[string]*ast.Builtin) *Planner { + p.decls = decls + return p +} + +// WithQueries sets the query set to generate a plan for. +func (p *Planner) WithQueries(queries []ast.Body) *Planner { + p.queries = queries + return p +} + +// WithModules sets the module set that contains query dependencies. +func (p *Planner) WithModules(modules []*ast.Module) *Planner { + p.modules = modules + return p +} + +// WithRewrittenVars sets a mapping of rewritten query vars on the planner. The +// plan will use the rewritten variable name but the result set key will be the +// original variable name. +func (p *Planner) WithRewrittenVars(vs map[ast.Var]ast.Var) *Planner { + p.rewritten = vs + return p +} + +// Plan returns a IR plan for the policy query. +func (p *Planner) Plan() (*ir.Policy, error) { + + if err := p.buildFunctrie(); err != nil { + return nil, err + } + + if err := p.planQueries(); err != nil { + return nil, err + } + + if err := p.planExterns(); err != nil { + return nil, err + } + + return p.policy, nil +} + +func (p *Planner) buildFunctrie() error { + + for _, module := range p.modules { + + // Create functrie node for empty packages so that extent queries return + // empty objects. For example: + // + // package x.y + // + // Query: data.x + // + // Expected result: {"y": {}} + if len(module.Rules) == 0 { + _ = p.rules.LookupOrInsert(module.Package.Path) + continue + } + + for _, rule := range module.Rules { + val := p.rules.LookupOrInsert(rule.Path()) + val.rules = append(val.rules, rule) + } + } + + return nil +} + +func (p *Planner) planRules(rules []*ast.Rule) (string, error) { + + path := rules[0].Path().String() + + if funcName, ok := p.funcs.Get(path); ok { + return funcName, nil + } + + // Save current state of planner. + // + // TODO(tsandall): perhaps we would be better off using stacks here or + // splitting block planner into separate struct that could be instantiated + // for rule and comprehension bodies. + pvars := p.vars + pcurr := p.curr + pltarget := p.ltarget + plnext := p.lnext + + // Reset the variable counter for the function plan. + p.lnext = ir.Input + + // Create function definition for rules. + fn := &ir.Func{ + Name: fmt.Sprintf("g%d.%s", p.funcs.gen, path), + Params: []ir.Local{ + p.newLocal(), // input document + p.newLocal(), // data document + }, + Return: p.newLocal(), + } + + // Initialize parameters for functions. + for i := 0; i < len(rules[0].Head.Args); i++ { + fn.Params = append(fn.Params, p.newLocal()) + } + + params := fn.Params[2:] + + // Initialize return value for partial set/object rules. Complete docs do + // not require their return value to be initialized. + if rules[0].Head.DocKind() == ast.PartialObjectDoc { + fn.Blocks = append(fn.Blocks, &ir.Block{ + Stmts: []ir.Stmt{ + &ir.MakeObjectStmt{ + Target: fn.Return, + }, + }, + }) + } else if rules[0].Head.DocKind() == ast.PartialSetDoc { + fn.Blocks = append(fn.Blocks, &ir.Block{ + Stmts: []ir.Stmt{ + &ir.MakeSetStmt{ + Target: fn.Return, + }, + }, + }) + } + + // At this point the locals for the params and return value have been + // allocated. This will be the first local that can be used in each block. + lnext := p.lnext + + var defaultRule *ast.Rule + + // Generate function blocks for rules. + for i := range rules { + + // Save default rule for the end. + if rules[i].Default { + defaultRule = rules[i] + continue + } + + // Ordered rules are nested inside an additional block so that execution + // can short-circuit. For unordered rules blocks can be added directly + // to the function. + var blocks *[]*ir.Block + + if rules[i].Else == nil { + blocks = &fn.Blocks + } else { + stmt := &ir.BlockStmt{} + block := &ir.Block{Stmts: []ir.Stmt{stmt}} + fn.Blocks = append(fn.Blocks, block) + blocks = &stmt.Blocks + } + + // Unordered rules are treated as a special case of ordered rules. + for rule := rules[i]; rule != nil; rule = rule.Else { + + // Setup planner for block. + p.lnext = lnext + p.vars = newVarstack(map[ast.Var]ir.Local{ + ast.InputRootDocument.Value.(ast.Var): fn.Params[0], + ast.DefaultRootDocument.Value.(ast.Var): fn.Params[1], + }) + + curr := &ir.Block{} + *blocks = append(*blocks, curr) + p.curr = curr + + // Complete and partial rules are treated as special cases of + // functions. If there are args, the first step is a no-op. + err := p.planFuncParams(params, rule.Head.Args, 0, func() error { + + // Run planner on the rule body. + err := p.planQuery(rule.Body, 0, func() error { + + // Run planner on the result. + switch rule.Head.DocKind() { + case ast.CompleteDoc: + return p.planTerm(rule.Head.Value, func() error { + p.appendStmt(&ir.AssignVarOnceStmt{ + Target: fn.Return, + Source: p.ltarget, + }) + return nil + }) + case ast.PartialSetDoc: + return p.planTerm(rule.Head.Key, func() error { + p.appendStmt(&ir.SetAddStmt{ + Set: fn.Return, + Value: p.ltarget, + }) + return nil + }) + case ast.PartialObjectDoc: + return p.planTerm(rule.Head.Key, func() error { + key := p.ltarget + return p.planTerm(rule.Head.Value, func() error { + value := p.ltarget + p.appendStmt(&ir.ObjectInsertOnceStmt{ + Object: fn.Return, + Key: key, + Value: value, + }) + return nil + }) + }) + default: + return fmt.Errorf("illegal rule kind") + } + }) + + if err != nil { + return err + } + + // Ordered rules are handled by short circuiting execution. The + // plan will jump out to the extra block that was planned above. + if rule.Else != nil { + p.appendStmt(&ir.IsDefinedStmt{Source: fn.Return}) + p.appendStmt(&ir.BreakStmt{Index: 1}) + } + + return nil + }) + + if err != nil { + return "", err + } + } + } + + // Default rules execute if the return is undefined. + if defaultRule != nil { + + fn.Blocks = append(fn.Blocks, &ir.Block{ + Stmts: []ir.Stmt{ + &ir.IsUndefinedStmt{Source: fn.Return}, + }, + }) + + p.curr = fn.Blocks[len(fn.Blocks)-1] + + err := p.planQuery(defaultRule.Body, 0, func() error { + return p.planTerm(defaultRule.Head.Value, func() error { + p.appendStmt(&ir.AssignVarOnceStmt{ + Target: fn.Return, + Source: p.ltarget, + }) + return nil + }) + }) + + if err != nil { + return "", err + } + } + + // All rules return a value. + fn.Blocks = append(fn.Blocks, &ir.Block{ + Stmts: []ir.Stmt{ + &ir.ReturnLocalStmt{ + Source: fn.Return, + }, + }, + }) + + p.appendFunc(fn) + p.funcs.Add(path, fn.Name) + + // Restore the state of the planner. + p.lnext = plnext + p.ltarget = pltarget + p.vars = pvars + p.curr = pcurr + + return fn.Name, nil +} + +func (p *Planner) planFuncParams(params []ir.Local, args ast.Args, idx int, iter planiter) error { + if idx >= len(args) { + return iter() + } + return p.planUnifyLocal(params[idx], args[idx], func() error { + return p.planFuncParams(params, args, idx+1, iter) + }) +} + +func (p *Planner) planQueries() error { + + // Initialize the plan with a block that prepares the query result. + p.curr = &ir.Block{} + + // Build a set of variables appearing in the query and allocate strings for + // each one. The strings will be used in the result set objects. + qvs := ast.NewVarSet() + + for _, q := range p.queries { + vs := q.Vars(ast.VarVisitorParams{SkipRefCallHead: true, SkipClosures: true}).Diff(ast.ReservedVars) + qvs.Update(vs) + } + + lvarnames := make(map[ast.Var]ir.Local, len(qvs)) + + for _, qv := range qvs.Sorted() { + qv = p.rewrittenVar(qv) + if !qv.IsGenerated() && !qv.IsWildcard() { + stmt := &ir.MakeStringStmt{ + Index: p.getStringConst(string(qv)), + Target: p.newLocal(), + } + p.appendStmt(stmt) + lvarnames[qv] = stmt.Target + } + } + + if len(p.curr.Stmts) > 0 { + p.appendBlock(p.curr) + } + + lnext := p.lnext + + for _, q := range p.queries { + p.lnext = lnext + p.vars.Push(map[ast.Var]ir.Local{}) + p.curr = &ir.Block{} + defined := false + qvs := q.Vars(ast.VarVisitorParams{SkipRefCallHead: true, SkipClosures: true}).Diff(ast.ReservedVars).Sorted() + + if err := p.planQuery(q, 0, func() error { + + // Add an object containing variable bindings into the result set. + lr := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lr, + }) + + for _, qv := range qvs { + rw := p.rewrittenVar(qv) + if !rw.IsGenerated() && !rw.IsWildcard() { + p.appendStmt(&ir.ObjectInsertStmt{ + Object: lr, + Key: lvarnames[rw], + Value: p.vars.GetOrEmpty(qv), + }) + } + } + + p.appendStmt(&ir.ResultSetAdd{ + Value: lr, + }) + + defined = true + return nil + }); err != nil { + return err + } + + p.vars.Pop() + + if defined { + p.appendBlock(p.curr) + } + } + + return nil +} + +func (p *Planner) planQuery(q ast.Body, index int, iter planiter) error { + + if index >= len(q) { + return iter() + } + + return p.planExpr(q[index], func() error { + return p.planQuery(q, index+1, iter) + }) +} + +// TODO(tsandall): improve errors to include location information. +func (p *Planner) planExpr(e *ast.Expr, iter planiter) error { + if e.Negated { + return p.planNot(e, iter) + } + + if len(e.With) > 0 { + return p.planWith(e, iter) + } + + if e.IsCall() { + return p.planExprCall(e, iter) + } + + return p.planExprTerm(e, iter) +} + +func (p *Planner) planNot(e *ast.Expr, iter planiter) error { + + not := &ir.NotStmt{ + Block: &ir.Block{}, + } + + prev := p.curr + p.curr = not.Block + + if err := p.planExpr(e.Complement(), func() error { + return nil + }); err != nil { + return err + } + + p.curr = prev + p.appendStmt(not) + + return iter() +} + +func (p *Planner) planWith(e *ast.Expr, iter planiter) error { + + // Plan the values that will be applied by the with modifiers. All values + // must be defined for the overall expression to evaluate. + values := make([]*ast.Term, len(e.With)) + + for i := range e.With { + values[i] = e.With[i].Value + } + + return p.planTermSlice(values, func(locals []ir.Local) error { + + paths := make([][]int, len(e.With)) + saveVars := ast.NewVarSet() + dataRefs := []ast.Ref{} + + for i := range e.With { + + target := e.With[i].Target.Value.(ast.Ref) + paths[i] = make([]int, len(target)-1) + + for j := 1; j < len(target); j++ { + if s, ok := target[j].Value.(ast.String); ok { + paths[i][j-1] = p.getStringConst(string(s)) + } else { + return errors.New("invalid with target") + } + } + + head := target[0].Value.(ast.Var) + saveVars.Add(head) + + if head.Equal(ast.DefaultRootDocument.Value) { + dataRefs = append(dataRefs, target) + } + } + + restore := make([][2]ir.Local, len(saveVars)) + + for i, v := range saveVars.Sorted() { + lorig := p.vars.GetOrEmpty(v) + lsave := p.newLocal() + p.appendStmt(&ir.AssignVarStmt{Source: lorig, Target: lsave}) + restore[i] = [2]ir.Local{lorig, lsave} + } + + // If any of the with statements targeted the data document we shadow + // the existing planned functions during expression planning. This + // causes the planner to re-plan any rules that may be required during + // planning of this expression (transitively). + if len(dataRefs) > 0 { + p.funcs.Push(map[string]string{}) + for _, ref := range dataRefs { + p.rules.Push(ref) + } + } + + return p.planWithRec(e, paths, locals, 0, func() error { + + if len(dataRefs) > 0 { + p.funcs.Pop() + for i := len(dataRefs) - 1; i >= 0; i-- { + p.rules.Pop(dataRefs[i]) + } + } + + return p.planWithUndoRec(restore, 0, iter) + }) + }) +} + +func (p *Planner) planWithRec(e *ast.Expr, targets [][]int, values []ir.Local, index int, iter planiter) error { + + if index >= len(e.With) { + return p.planExpr(e.NoWith(), iter) + } + + prev := p.curr + p.curr = &ir.Block{} + + err := p.planWithRec(e, targets, values, index+1, iter) + if err != nil { + return err + } + + block := p.curr + p.curr = prev + target := e.With[index].Target.Value.(ast.Ref) + head := target[0].Value.(ast.Var) + + stmt := &ir.WithStmt{ + Local: p.vars.GetOrEmpty(head), + Path: targets[index], + Value: values[index], + Block: block, + } + + p.appendStmt(stmt) + + return nil +} + +func (p *Planner) planWithUndoRec(restore [][2]ir.Local, index int, iter planiter) error { + + if index >= len(restore) { + return iter() + } + + prev := p.curr + p.curr = &ir.Block{} + + if err := p.planWithUndoRec(restore, index+1, iter); err != nil { + return err + } + + block := p.curr + p.curr = prev + lorig := restore[index][0] + lsave := restore[index][1] + + p.appendStmt(&ir.WithStmt{ + Local: lorig, + Value: lsave, + Block: block, + }) + + return nil +} + +func (p *Planner) planExprTerm(e *ast.Expr, iter planiter) error { + return p.planTerm(e.Terms.(*ast.Term), func() error { + falsy := p.newLocal() + p.appendStmt(&ir.MakeBooleanStmt{ + Value: false, + Target: falsy, + }) + p.appendStmt(&ir.NotEqualStmt{ + A: p.ltarget, + B: falsy, + }) + return iter() + }) +} + +func (p *Planner) planExprCall(e *ast.Expr, iter planiter) error { + operator := e.Operator().String() + switch operator { + case ast.Equality.Name: + return p.planUnify(e.Operand(0), e.Operand(1), iter) + case ast.Equal.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.EqualStmt{ + A: a, + B: b, + }) + return iter() + }) + case ast.LessThan.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.LessThanStmt{ + A: a, + B: b, + }) + return iter() + }) + case ast.LessThanEq.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.LessThanEqualStmt{ + A: a, + B: b, + }) + return iter() + }) + case ast.GreaterThan.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.GreaterThanStmt{ + A: a, + B: b, + }) + return iter() + }) + case ast.GreaterThanEq.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.GreaterThanEqualStmt{ + A: a, + B: b, + }) + return iter() + }) + case ast.NotEqual.Name: + return p.planBinaryExpr(e, func(a, b ir.Local) error { + p.appendStmt(&ir.NotEqualStmt{ + A: a, + B: b, + }) + return iter() + }) + default: + + var name string + var arity int + var args []ir.Local + + node := p.rules.Lookup(e.Operator()) + + if node != nil { + var err error + name, err = p.planRules(node.Rules()) + if err != nil { + return err + } + arity = node.Arity() + args = []ir.Local{ + p.vars.GetOrEmpty(ast.InputRootDocument.Value.(ast.Var)), + p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var)), + } + } else if decl, ok := p.decls[operator]; ok { + arity = len(decl.Decl.Args()) + name = operator + p.externs[operator] = struct{}{} + } else { + return fmt.Errorf("illegal call: unknown operator %q", operator) + } + + operands := e.Operands() + + if len(operands) == arity { + // rule: f(x) = x { ... } + // call: f(x) # result not captured + return p.planCallArgs(operands, 0, args, func(args []ir.Local) error { + p.ltarget = p.newLocal() + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: p.ltarget, + }) + + falsy := p.newLocal() + + p.appendStmt(&ir.MakeBooleanStmt{ + Value: false, + Target: falsy, + }) + + p.appendStmt(&ir.NotEqualStmt{ + A: p.ltarget, + B: falsy, + }) + + return iter() + }) + } else if len(operands) == arity+1 { + // rule: f(x) = x { ... } + // call: f(x, 1) # caller captures result + return p.planCallArgs(operands[:len(operands)-1], 0, args, func(args []ir.Local) error { + result := p.newLocal() + p.appendStmt(&ir.CallStmt{ + Func: name, + Args: args, + Result: result, + }) + return p.planUnifyLocal(result, operands[len(operands)-1], iter) + }) + } + + return fmt.Errorf("illegal call: wrong number of operands: got %v, want %v)", len(operands), arity) + } +} + +func (p *Planner) planCallArgs(terms []*ast.Term, idx int, args []ir.Local, iter func([]ir.Local) error) error { + if idx >= len(terms) { + return iter(args) + } + return p.planTerm(terms[idx], func() error { + args = append(args, p.ltarget) + return p.planCallArgs(terms, idx+1, args, iter) + }) +} + +func (p *Planner) planUnify(a, b *ast.Term, iter planiter) error { + + switch va := a.Value.(type) { + case ast.Null, ast.Boolean, ast.Number, ast.String, ast.Ref, ast.Set, *ast.SetComprehension, *ast.ArrayComprehension, *ast.ObjectComprehension: + return p.planTerm(a, func() error { + return p.planUnifyLocal(p.ltarget, b, iter) + }) + case ast.Var: + return p.planUnifyVar(va, b, iter) + case ast.Array: + switch vb := b.Value.(type) { + case ast.Var: + return p.planUnifyVar(vb, a, iter) + case ast.Ref: + return p.planTerm(b, func() error { + return p.planUnifyLocalArray(p.ltarget, va, iter) + }) + case ast.Array: + if len(va) == len(vb) { + return p.planUnifyArraysRec(va, vb, 0, iter) + } + return nil + } + case ast.Object: + switch vb := b.Value.(type) { + case ast.Var: + return p.planUnifyVar(vb, a, iter) + case ast.Ref: + return p.planTerm(b, func() error { + return p.planUnifyLocalObject(p.ltarget, va, iter) + }) + case ast.Object: + if va.Len() == vb.Len() { + return p.planUnifyObjectsRec(va, vb, va.Keys(), 0, iter) + } + return nil + } + } + + return fmt.Errorf("not implemented: unify(%v, %v)", a, b) +} + +func (p *Planner) planUnifyVar(a ast.Var, b *ast.Term, iter planiter) error { + + if la, ok := p.vars.Get(a); ok { + return p.planUnifyLocal(la, b, iter) + } + + return p.planTerm(b, func() error { + target := p.newLocal() + p.vars.Put(a, target) + p.appendStmt(&ir.AssignVarStmt{ + Source: p.ltarget, + Target: target, + }) + return iter() + }) +} + +func (p *Planner) planUnifyLocal(a ir.Local, b *ast.Term, iter planiter) error { + switch vb := b.Value.(type) { + case ast.Null, ast.Boolean, ast.Number, ast.String, ast.Ref, ast.Set, *ast.SetComprehension, *ast.ArrayComprehension, *ast.ObjectComprehension: + return p.planTerm(b, func() error { + p.appendStmt(&ir.EqualStmt{ + A: a, + B: p.ltarget, + }) + return iter() + }) + case ast.Var: + if lv, ok := p.vars.Get(vb); ok { + p.appendStmt(&ir.EqualStmt{ + A: a, + B: lv, + }) + return iter() + } + lv := p.newLocal() + p.vars.Put(vb, lv) + p.appendStmt(&ir.AssignVarStmt{ + Source: a, + Target: lv, + }) + return iter() + case ast.Array: + return p.planUnifyLocalArray(a, vb, iter) + case ast.Object: + return p.planUnifyLocalObject(a, vb, iter) + } + + return fmt.Errorf("not implemented: unifyLocal(%v, %v)", a, b) +} + +func (p *Planner) planUnifyLocalArray(a ir.Local, b ast.Array, iter planiter) error { + p.appendStmt(&ir.IsArrayStmt{ + Source: a, + }) + + blen := p.newLocal() + alen := p.newLocal() + + p.appendStmt(&ir.LenStmt{ + Source: a, + Target: alen, + }) + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: int64(len(b)), + Target: blen, + }) + + p.appendStmt(&ir.EqualStmt{ + A: alen, + B: blen, + }) + + lkey := p.newLocal() + + p.appendStmt(&ir.MakeNumberIntStmt{ + Target: lkey, + }) + + lval := p.newLocal() + + return p.planUnifyLocalArrayRec(a, 0, b, lkey, lval, iter) +} + +func (p *Planner) planUnifyLocalArrayRec(a ir.Local, index int, b ast.Array, lkey, lval ir.Local, iter planiter) error { + if len(b) == index { + return iter() + } + + p.appendStmt(&ir.AssignIntStmt{ + Value: int64(index), + Target: lkey, + }) + + p.appendStmt(&ir.DotStmt{ + Source: a, + Key: lkey, + Target: lval, + }) + + return p.planUnifyLocal(lval, b[index], func() error { + return p.planUnifyLocalArrayRec(a, index+1, b, lkey, lval, iter) + }) +} + +func (p *Planner) planUnifyLocalObject(a ir.Local, b ast.Object, iter planiter) error { + p.appendStmt(&ir.IsObjectStmt{ + Source: a, + }) + + blen := p.newLocal() + alen := p.newLocal() + + p.appendStmt(&ir.LenStmt{ + Source: a, + Target: alen, + }) + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: int64(b.Len()), + Target: blen, + }) + + p.appendStmt(&ir.EqualStmt{ + A: alen, + B: blen, + }) + + lkey := p.newLocal() + lval := p.newLocal() + bkeys := b.Keys() + + return p.planUnifyLocalObjectRec(a, 0, bkeys, b, lkey, lval, iter) +} + +func (p *Planner) planUnifyLocalObjectRec(a ir.Local, index int, keys []*ast.Term, b ast.Object, lkey, lval ir.Local, iter planiter) error { + + if index == len(keys) { + return iter() + } + + return p.planTerm(keys[index], func() error { + p.appendStmt(&ir.AssignVarStmt{ + Source: p.ltarget, + Target: lkey, + }) + p.appendStmt(&ir.DotStmt{ + Source: a, + Key: lkey, + Target: lval, + }) + return p.planUnifyLocal(lval, b.Get(keys[index]), func() error { + return p.planUnifyLocalObjectRec(a, index+1, keys, b, lkey, lval, iter) + }) + }) +} + +func (p *Planner) planUnifyArraysRec(a, b ast.Array, index int, iter planiter) error { + if index == len(a) { + return iter() + } + return p.planUnify(a[index], b[index], func() error { + return p.planUnifyArraysRec(a, b, index+1, iter) + }) +} + +func (p *Planner) planUnifyObjectsRec(a, b ast.Object, keys []*ast.Term, index int, iter planiter) error { + if index == len(keys) { + return iter() + } + + aval := a.Get(keys[index]) + bval := b.Get(keys[index]) + if aval == nil || bval == nil { + return nil + } + + return p.planUnify(aval, bval, func() error { + return p.planUnifyObjectsRec(a, b, keys, index+1, iter) + }) +} + +func (p *Planner) planBinaryExpr(e *ast.Expr, iter binaryiter) error { + return p.planTerm(e.Operand(0), func() error { + a := p.ltarget + return p.planTerm(e.Operand(1), func() error { + b := p.ltarget + return iter(a, b) + }) + }) +} + +func (p *Planner) planTerm(t *ast.Term, iter planiter) error { + + switch v := t.Value.(type) { + case ast.Null: + return p.planNull(v, iter) + case ast.Boolean: + return p.planBoolean(v, iter) + case ast.Number: + return p.planNumber(v, iter) + case ast.String: + return p.planString(v, iter) + case ast.Var: + return p.planVar(v, iter) + case ast.Ref: + return p.planRef(v, iter) + case ast.Array: + return p.planArray(v, iter) + case ast.Object: + return p.planObject(v, iter) + case ast.Set: + return p.planSet(v, iter) + case *ast.SetComprehension: + return p.planSetComprehension(v, iter) + case *ast.ArrayComprehension: + return p.planArrayComprehension(v, iter) + case *ast.ObjectComprehension: + return p.planObjectComprehension(v, iter) + default: + return fmt.Errorf("%v term not implemented", ast.TypeName(v)) + } +} + +func (p *Planner) planNull(null ast.Null, iter planiter) error { + + target := p.newLocal() + + p.appendStmt(&ir.MakeNullStmt{ + Target: target, + }) + + p.ltarget = target + + return iter() +} + +func (p *Planner) planBoolean(b ast.Boolean, iter planiter) error { + + target := p.newLocal() + + p.appendStmt(&ir.MakeBooleanStmt{ + Value: bool(b), + Target: target, + }) + + p.ltarget = target + + return iter() +} + +func (p *Planner) planNumber(num ast.Number, iter planiter) error { + + index := p.getStringConst(string(num)) + target := p.newLocal() + + p.appendStmt(&ir.MakeNumberRefStmt{ + Index: index, + Target: target, + }) + + p.ltarget = target + return iter() +} + +func (p *Planner) planNumberFloat(f float64, iter planiter) error { + + target := p.newLocal() + + p.appendStmt(&ir.MakeNumberFloatStmt{ + Value: f, + Target: target, + }) + + p.ltarget = target + + return iter() +} + +func (p *Planner) planNumberInt(i int64, iter planiter) error { + + target := p.newLocal() + + p.appendStmt(&ir.MakeNumberIntStmt{ + Value: i, + Target: target, + }) + + p.ltarget = target + + return iter() +} + +func (p *Planner) planString(str ast.String, iter planiter) error { + + index := p.getStringConst(string(str)) + target := p.newLocal() + + p.appendStmt(&ir.MakeStringStmt{ + Index: index, + Target: target, + }) + + p.ltarget = target + + return iter() +} + +func (p *Planner) planVar(v ast.Var, iter planiter) error { + p.ltarget = p.vars.GetOrElse(v, func() ir.Local { + return p.newLocal() + }) + return iter() +} + +func (p *Planner) planArray(arr ast.Array, iter planiter) error { + + larr := p.newLocal() + + p.appendStmt(&ir.MakeArrayStmt{ + Capacity: int32(len(arr)), + Target: larr, + }) + + return p.planArrayRec(arr, 0, larr, iter) +} + +func (p *Planner) planArrayRec(arr ast.Array, index int, larr ir.Local, iter planiter) error { + if index == len(arr) { + p.ltarget = larr + return iter() + } + + return p.planTerm(arr[index], func() error { + + p.appendStmt(&ir.ArrayAppendStmt{ + Value: p.ltarget, + Array: larr, + }) + + return p.planArrayRec(arr, index+1, larr, iter) + }) +} + +func (p *Planner) planObject(obj ast.Object, iter planiter) error { + + lobj := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lobj, + }) + + return p.planObjectRec(obj, 0, obj.Keys(), lobj, iter) +} + +func (p *Planner) planObjectRec(obj ast.Object, index int, keys []*ast.Term, lobj ir.Local, iter planiter) error { + if index == len(keys) { + p.ltarget = lobj + return iter() + } + + return p.planTerm(keys[index], func() error { + lkey := p.ltarget + + return p.planTerm(obj.Get(keys[index]), func() error { + lval := p.ltarget + p.appendStmt(&ir.ObjectInsertStmt{ + Key: lkey, + Value: lval, + Object: lobj, + }) + + return p.planObjectRec(obj, index+1, keys, lobj, iter) + }) + }) +} + +func (p *Planner) planSet(set ast.Set, iter planiter) error { + lset := p.newLocal() + + p.appendStmt(&ir.MakeSetStmt{ + Target: lset, + }) + + return p.planSetRec(set, 0, set.Slice(), lset, iter) +} + +func (p *Planner) planSetRec(set ast.Set, index int, elems []*ast.Term, lset ir.Local, iter planiter) error { + if index == len(elems) { + p.ltarget = lset + return iter() + } + + return p.planTerm(elems[index], func() error { + p.appendStmt(&ir.SetAddStmt{ + Value: p.ltarget, + Set: lset, + }) + return p.planSetRec(set, index+1, elems, lset, iter) + }) +} + +func (p *Planner) planSetComprehension(sc *ast.SetComprehension, iter planiter) error { + + lset := p.newLocal() + + p.appendStmt(&ir.MakeSetStmt{ + Target: lset, + }) + + return p.planComprehension(sc.Body, func() error { + return p.planTerm(sc.Term, func() error { + p.appendStmt(&ir.SetAddStmt{ + Value: p.ltarget, + Set: lset, + }) + return nil + }) + }, lset, iter) +} + +func (p *Planner) planArrayComprehension(ac *ast.ArrayComprehension, iter planiter) error { + + larr := p.newLocal() + + p.appendStmt(&ir.MakeArrayStmt{ + Target: larr, + }) + + return p.planComprehension(ac.Body, func() error { + return p.planTerm(ac.Term, func() error { + p.appendStmt(&ir.ArrayAppendStmt{ + Value: p.ltarget, + Array: larr, + }) + return nil + }) + }, larr, iter) +} + +func (p *Planner) planObjectComprehension(oc *ast.ObjectComprehension, iter planiter) error { + + lobj := p.newLocal() + + p.appendStmt(&ir.MakeObjectStmt{ + Target: lobj, + }) + + return p.planComprehension(oc.Body, func() error { + return p.planTerm(oc.Key, func() error { + lkey := p.ltarget + return p.planTerm(oc.Value, func() error { + p.appendStmt(&ir.ObjectInsertOnceStmt{ + Key: lkey, + Value: p.ltarget, + Object: lobj, + }) + return nil + }) + }) + }, lobj, iter) +} + +func (p *Planner) planComprehension(body ast.Body, closureIter planiter, target ir.Local, iter planiter) error { + + prev := p.curr + p.curr = &ir.Block{} + + if err := p.planQuery(body, 0, func() error { + return closureIter() + }); err != nil { + return err + } + + block := p.curr + p.curr = prev + + p.appendStmt(&ir.BlockStmt{ + Blocks: []*ir.Block{ + block, + }, + }) + + p.ltarget = target + return iter() +} + +func (p *Planner) planRef(ref ast.Ref, iter planiter) error { + + head, ok := ref[0].Value.(ast.Var) + if !ok { + return fmt.Errorf("illegal ref: non-var head") + } + + if head.Compare(ast.DefaultRootDocument.Value) == 0 { + virtual := p.rules.Get(ref[0].Value) + base := &baseptr{local: p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var))} + return p.planRefData(virtual, base, ref, 1, iter) + } + + p.ltarget, ok = p.vars.Get(head) + if !ok { + return fmt.Errorf("illegal ref: unsafe head") + } + + return p.planRefRec(ref, 1, iter) +} + +func (p *Planner) planRefRec(ref ast.Ref, index int, iter planiter) error { + + if len(ref) == index { + return iter() + } + + scan := false + + ast.WalkVars(ref[index], func(v ast.Var) bool { + if !scan { + _, exists := p.vars.Get(v) + if !exists { + scan = true + } + } + return scan + }) + + if !scan { + return p.planDot(ref[index], func() error { + return p.planRefRec(ref, index+1, iter) + }) + } + + return p.planScan(ref[index], func(lkey ir.Local) error { + return p.planRefRec(ref, index+1, iter) + }) +} + +type baseptr struct { + local ir.Local + path ast.Ref +} + +// planRefData implements the virtual document model by generating the value of +// the ref parameter and invoking the iterator with the planner target set to +// the virtual document and all variables in the reference assigned. +func (p *Planner) planRefData(virtual *ruletrie, base *baseptr, ref ast.Ref, index int, iter planiter) error { + + // Early-exit if the end of the reference has been reached. In this case the + // plan has to materialize the full extent of the referenced value. + if index >= len(ref) { + return p.planRefDataExtent(virtual, base, iter) + } + + // If the reference operand is ground then either continue to the next + // operand or invoke the function for the rule referred to by this operand. + if ref[index].IsGround() { + + var vchild *ruletrie + + if virtual != nil { + vchild = virtual.Get(ref[index].Value) + } + + rules := vchild.Rules() + + if len(rules) > 0 { + p.ltarget = p.newLocal() + + funcName, err := p.planRules(rules) + if err != nil { + return err + } + + p.appendStmt(&ir.CallStmt{ + Func: funcName, + Args: []ir.Local{ + p.vars.GetOrEmpty(ast.InputRootDocument.Value.(ast.Var)), + p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var)), + }, + Result: p.ltarget, + }) + + return p.planRefRec(ref, index+1, iter) + } + + bchild := *base + bchild.path = append(bchild.path, ref[index]) + + return p.planRefData(vchild, &bchild, ref, index+1, iter) + } + + exclude := ast.NewSet() + + // The planner does not support dynamic dispatch so generate blocks to + // evaluate each of the rulesets on the child nodes. + if virtual != nil { + + stmt := &ir.BlockStmt{} + + for _, child := range virtual.Children() { + + block := &ir.Block{} + prev := p.curr + p.curr = block + key := ast.NewTerm(child) + exclude.Add(key) + + // Assignments in each block due to local unification must be undone + // so create a new frame that will be popped after this key is + // processed. + p.vars.Push(map[ast.Var]ir.Local{}) + + if err := p.planTerm(key, func() error { + return p.planUnifyLocal(p.ltarget, ref[index], func() error { + // Create a copy of the reference with this operand plugged. + // This will result in evaluation of the rulesets on the + // child node. + cpy := ref.Copy() + cpy[index] = key + return p.planRefData(virtual, base, cpy, index, iter) + }) + }); err != nil { + return err + } + + p.vars.Pop() + p.curr = prev + stmt.Blocks = append(stmt.Blocks, block) + } + + p.appendStmt(stmt) + } + + // If the virtual tree was enumerated then we do not want to enumerate base + // trees that are rooted at the same key as any of the virtual sub trees. To + // prevent this we build a set of keys that are to be excluded and check + // below during the base scan. + var lexclude *ir.Local + + if exclude.Len() > 0 { + if err := p.planSet(exclude, func() error { + v := p.ltarget + lexclude = &v + return nil + }); err != nil { + return err + } + } + + p.ltarget = base.local + + // Perform a scan of the base documents starting from the location referred + // to by the data pointer. Use the set we built above to avoid revisiting + // sub trees. + return p.planRefRec(base.path, 0, func() error { + return p.planScan(ref[index], func(lkey ir.Local) error { + if lexclude != nil { + lignore := p.newLocal() + p.appendStmt(&ir.NotStmt{ + Block: &ir.Block{ + Stmts: []ir.Stmt{ + &ir.DotStmt{ + Source: *lexclude, + Key: lkey, + Target: lignore, + }, + }, + }, + }) + } + + // Assume that virtual sub trees have been visited already so + // recurse without the virtual node. + return p.planRefData(nil, &baseptr{local: p.ltarget}, ref, index+1, iter) + }) + }) +} + +// planRefDataExtent generates the full extent (combined) of the base and +// virtual nodes and then invokes the iterator with the planner target set to +// the full extent. +func (p *Planner) planRefDataExtent(virtual *ruletrie, base *baseptr, iter planiter) error { + + vtarget := p.newLocal() + + // Generate the virtual document out of rules contained under the virtual + // node (recursively). This document will _ONLY_ contain values generated by + // rules. No base document values will be included. + if virtual != nil { + + p.appendStmt(&ir.MakeObjectStmt{ + Target: vtarget, + }) + + for _, key := range virtual.Children() { + child := virtual.Get(key) + + // Skip functions. + if child.Arity() > 0 { + continue + } + + lkey := p.newLocal() + idx := p.getStringConst(string(key.(ast.String))) + p.appendStmt(&ir.MakeStringStmt{ + Index: idx, + Target: lkey, + }) + + rules := child.Rules() + + // Build object hierarchy depth-first. + if len(rules) == 0 { + err := p.planRefDataExtent(child, nil, func() error { + p.appendStmt(&ir.ObjectInsertStmt{ + Object: vtarget, + Key: lkey, + Value: p.ltarget, + }) + return nil + }) + if err != nil { + return err + } + continue + } + + // Generate virtual document for leaf. + lvalue := p.newLocal() + + funcName, err := p.planRules(rules) + if err != nil { + return err + } + + // Add leaf to object if defined. + p.appendStmt(&ir.BlockStmt{ + Blocks: []*ir.Block{ + &ir.Block{ + Stmts: []ir.Stmt{ + &ir.CallStmt{ + Func: funcName, + Args: []ir.Local{ + p.vars.GetOrEmpty(ast.InputRootDocument.Value.(ast.Var)), + p.vars.GetOrEmpty(ast.DefaultRootDocument.Value.(ast.Var)), + }, + Result: lvalue, + }, + &ir.ObjectInsertStmt{ + Object: vtarget, + Key: lkey, + Value: lvalue, + }, + }, + }, + }, + }) + } + + // At this point vtarget refers to the full extent of the virtual + // document at ref. If the base pointer is unset, no further processing + // is required. + if base == nil { + p.ltarget = vtarget + return iter() + } + } + + // Obtain the base document value and merge (recursively) with the virtual + // document value above if needed. + prev := p.curr + p.curr = &ir.Block{} + p.ltarget = base.local + target := p.newLocal() + + err := p.planRefRec(base.path, 0, func() error { + + if virtual == nil { + target = p.ltarget + } else { + stmt := &ir.ObjectMergeStmt{ + A: p.ltarget, + B: vtarget, + Target: target, + } + p.appendStmt(stmt) + } + + p.appendStmt(&ir.BreakStmt{Index: 1}) + return nil + }) + + if err != nil { + return err + } + + inner := p.curr + + // Fallback to virtual document value if base document is undefined. + // Otherwise, this block is undefined. + p.curr = &ir.Block{} + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{inner}}) + + if virtual != nil { + p.appendStmt(&ir.AssignVarStmt{ + Source: vtarget, + Target: target, + }) + } else { + p.appendStmt(&ir.BreakStmt{Index: 1}) + } + + outer := p.curr + p.curr = prev + p.appendStmt(&ir.BlockStmt{Blocks: []*ir.Block{outer}}) + + // At this point, target refers to either the full extent of the base and + // virtual documents at ref or just the base document at ref. + p.ltarget = target + + return iter() +} + +func (p *Planner) planDot(key *ast.Term, iter planiter) error { + + source := p.ltarget + + return p.planTerm(key, func() error { + + target := p.newLocal() + + p.appendStmt(&ir.DotStmt{ + Source: source, + Key: p.ltarget, + Target: target, + }) + + p.ltarget = target + + return iter() + }) +} + +type scaniter func(ir.Local) error + +func (p *Planner) planScan(key *ast.Term, iter scaniter) error { + + scan := &ir.ScanStmt{ + Source: p.ltarget, + Key: p.newLocal(), + Value: p.newLocal(), + Block: &ir.Block{}, + } + + prev := p.curr + p.curr = scan.Block + + if err := p.planUnifyLocal(scan.Key, key, func() error { + p.ltarget = scan.Value + return iter(scan.Key) + }); err != nil { + return err + } + + p.curr = prev + p.appendStmt(scan) + + return nil + +} + +// planSaveLocals returns a slice of locals holding temporary variables that +// have been assigned from the supplied vars. +func (p *Planner) planSaveLocals(vars ...ir.Local) []ir.Local { + + lsaved := make([]ir.Local, len(vars)) + + for i := range vars { + + lsaved[i] = p.newLocal() + + p.appendStmt(&ir.AssignVarStmt{ + Source: vars[i], + Target: lsaved[i], + }) + } + + return lsaved +} + +type termsliceiter func([]ir.Local) error + +func (p *Planner) planTermSlice(terms []*ast.Term, iter termsliceiter) error { + return p.planTermSliceRec(terms, make([]ir.Local, len(terms)), 0, iter) +} + +func (p *Planner) planTermSliceRec(terms []*ast.Term, locals []ir.Local, index int, iter termsliceiter) error { + if index >= len(terms) { + return iter(locals) + } + + return p.planTerm(terms[index], func() error { + locals[index] = p.ltarget + return p.planTermSliceRec(terms, locals, index+1, iter) + }) +} + +func (p *Planner) planExterns() error { + + p.policy.Static.BuiltinFuncs = make([]*ir.BuiltinFunc, 0, len(p.externs)) + + for name := range p.externs { + p.policy.Static.BuiltinFuncs = append(p.policy.Static.BuiltinFuncs, &ir.BuiltinFunc{Name: name}) + } + + sort.Slice(p.policy.Static.BuiltinFuncs, func(i, j int) bool { + return p.policy.Static.BuiltinFuncs[i].Name < p.policy.Static.BuiltinFuncs[j].Name + }) + + return nil +} + +func (p *Planner) getStringConst(s string) int { + index, ok := p.strings[s] + if !ok { + index = len(p.policy.Static.Strings) + p.policy.Static.Strings = append(p.policy.Static.Strings, &ir.StringConst{ + Value: s, + }) + p.strings[s] = index + } + return index +} + +func (p *Planner) appendStmt(s ir.Stmt) { + p.curr.Stmts = append(p.curr.Stmts, s) +} + +func (p *Planner) appendFunc(f *ir.Func) { + p.policy.Funcs.Funcs = append(p.policy.Funcs.Funcs, f) +} + +func (p *Planner) appendBlock(b *ir.Block) { + p.policy.Plan.Blocks = append(p.policy.Plan.Blocks, b) +} + +func (p *Planner) newLocal() ir.Local { + x := p.lnext + p.lnext++ + return x +} + +func (p *Planner) rewrittenVar(k ast.Var) ast.Var { + rw, ok := p.rewritten[k] + if !ok { + return k + } + return rw +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go new file mode 100644 index 000000000..e2d9d1561 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go @@ -0,0 +1,156 @@ +package planner + +import ( + "sort" + + "github.com/open-policy-agent/opa/ast" +) + +// funcstack implements a simple map structure used to keep track of virtual +// document => planned function names. The structure supports Push and Pop +// operations so that the planner can shadow planned functions when 'with' +// statements are found. +type funcstack struct { + stack []map[string]string + gen int +} + +func newFuncstack() *funcstack { + return &funcstack{ + stack: []map[string]string{ + map[string]string{}, + }, + gen: 0, + } +} + +func (p funcstack) Add(key, value string) { + p.stack[len(p.stack)-1][key] = value +} + +func (p funcstack) Get(key string) (string, bool) { + value, ok := p.stack[len(p.stack)-1][key] + return value, ok +} + +func (p *funcstack) Push(funcs map[string]string) { + p.stack = append(p.stack, funcs) + p.gen++ +} + +func (p *funcstack) Pop() map[string]string { + last := p.stack[len(p.stack)-1] + p.stack = p.stack[:len(p.stack)-1] + p.gen++ + return last +} + +// ruletrie implements a simple trie structure for organizing rules that may be +// planned. The trie nodes are keyed by the rule path. The ruletrie supports +// Push and Pop operations that allow the planner to shadow subtrees when 'with' +// statements are found. +type ruletrie struct { + children map[ast.Value][]*ruletrie + rules []*ast.Rule +} + +func newRuletrie() *ruletrie { + return &ruletrie{ + children: map[ast.Value][]*ruletrie{}, + } +} + +func (t *ruletrie) Arity() int { + rules := t.Rules() + if len(rules) > 0 { + return len(rules[0].Head.Args) + } + return 0 +} + +func (t *ruletrie) Rules() []*ast.Rule { + if t != nil { + return t.rules + } + return nil +} + +func (t *ruletrie) Push(key ast.Ref) { + node := t + for i := 0; i < len(key)-1; i++ { + node = node.Get(key[i].Value) + if node == nil { + return + } + } + elem := key[len(key)-1] + node.children[elem.Value] = append(node.children[elem.Value], nil) +} + +func (t *ruletrie) Pop(key ast.Ref) { + node := t + for i := 0; i < len(key)-1; i++ { + node = node.Get(key[i].Value) + if node == nil { + return + } + } + elem := key[len(key)-1] + sl := node.children[elem.Value] + node.children[elem.Value] = sl[:len(sl)-1] +} + +func (t *ruletrie) Insert(key ast.Ref) *ruletrie { + node := t + for _, elem := range key { + child := node.Get(elem.Value) + if child == nil { + child = newRuletrie() + node.children[elem.Value] = append(node.children[elem.Value], child) + } + node = child + } + return node +} + +func (t *ruletrie) Lookup(key ast.Ref) *ruletrie { + node := t + for _, elem := range key { + node = node.Get(elem.Value) + if node == nil { + return nil + } + } + return node +} + +func (t *ruletrie) LookupOrInsert(key ast.Ref) *ruletrie { + if val := t.Lookup(key); val != nil { + return val + } + return t.Insert(key) +} + +func (t *ruletrie) Children() []ast.Value { + sorted := make([]ast.Value, 0, len(t.children)) + for key := range t.children { + if t.Get(key) != nil { + sorted = append(sorted, key) + } + } + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Compare(sorted[j]) < 0 + }) + return sorted +} + +func (t *ruletrie) Get(k ast.Value) *ruletrie { + if t == nil { + return nil + } + nodes := t.children[k] + if len(nodes) == 0 { + return nil + } + return nodes[len(nodes)-1] +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go b/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go new file mode 100644 index 000000000..7dbf8f43c --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go @@ -0,0 +1,58 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package planner + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/internal/ir" +) + +type varstack []map[ast.Var]ir.Local + +func newVarstack(frames ...map[ast.Var]ir.Local) *varstack { + vs := &varstack{} + for _, f := range frames { + vs.Push(f) + } + return vs +} + +func (vs varstack) GetOrElse(k ast.Var, orElse func() ir.Local) ir.Local { + l, ok := vs.Get(k) + if !ok { + l = orElse() + vs.Put(k, l) + } + return l +} + +func (vs varstack) GetOrEmpty(k ast.Var) ir.Local { + l, _ := vs.Get(k) + return l +} + +func (vs varstack) Get(k ast.Var) (ir.Local, bool) { + for i := len(vs) - 1; i >= 0; i-- { + if l, ok := vs[i][k]; ok { + return l, true + } + } + return 0, false +} + +func (vs varstack) Put(k ast.Var, v ir.Local) { + vs[len(vs)-1][k] = v +} + +func (vs *varstack) Push(frame map[ast.Var]ir.Local) { + *vs = append(*vs, frame) +} + +func (vs *varstack) Pop() map[ast.Var]ir.Local { + sl := *vs + last := sl[len(sl)-1] + *vs = sl[:len(sl)-1] + return last +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/version/version.go b/vendor/github.com/open-policy-agent/opa/internal/version/version.go new file mode 100644 index 000000000..02f1c1b88 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/version/version.go @@ -0,0 +1,40 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package version implements helper functions for the stored version. +package version + +import ( + "context" + "fmt" + "runtime" + + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/version" +) + +var versionPath = storage.MustParsePath("/system/version") + +// Write the build version information into storage. This makes the +// version information available to the REPL and the HTTP server. +func Write(ctx context.Context, store storage.Store, txn storage.Transaction) error { + + if err := storage.MakeDir(ctx, store, txn, versionPath); err != nil { + return err + } + + if err := store.Write(ctx, txn, storage.AddOp, versionPath, map[string]interface{}{ + "version": version.Version, + "build_commit": version.Vcs, + "build_timestamp": version.Timestamp, + "build_hostname": version.Hostname, + }); err != nil { + return err + } + + return nil +} + +// UserAgent defines the current OPA instances User-Agent default header value. +var UserAgent = fmt.Sprintf("Open Policy Agent/%s (%s, %s)", version.Version, runtime.GOOS, runtime.GOARCH) diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/constant/constant.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/constant/constant.go new file mode 100644 index 000000000..84e4d4746 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/constant/constant.go @@ -0,0 +1,67 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package constant contains WASM constant definitions. +package constant + +// Magic bytes at the beginning of every WASM file ("\0asm"). +const Magic = uint32(0x6D736100) + +// Version defines the WASM version. +const Version = uint32(1) + +// WASM module section IDs. +const ( + CustomSectionID uint8 = iota + TypeSectionID + ImportSectionID + FunctionSectionID + TableSectionID + MemorySectionID + GlobalSectionID + ExportSectionID + StartSectionID + ElementSectionID + CodeSectionID + DataSectionID +) + +// FunctionTypeID indicates the start of a function type definition. +const FunctionTypeID = byte(0x60) + +// ValueType represents an intrinsic value type in WASM. +const ( + ValueTypeF64 byte = iota + 0x7C + ValueTypeF32 + ValueTypeI64 + ValueTypeI32 +) + +// WASM import descriptor types. +const ( + ImportDescType byte = iota + ImportDescTable + ImportDescMem + ImportDescGlobal +) + +// WASM export descriptor types. +const ( + ExportDescType byte = iota + ExportDescTable + ExportDescMem + ExportDescGlobal +) + +// ElementTypeAnyFunc indicates the type of a table import. +const ElementTypeAnyFunc byte = 0x70 + +// BlockTypeEmpty represents a block type. +const BlockTypeEmpty byte = 0x40 + +// WASM global varialbe mutability flag. +const ( + Const byte = iota + Mutable +) diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/doc.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/doc.go new file mode 100644 index 000000000..b25236968 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/doc.go @@ -0,0 +1,6 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package encoding implements WASM module reading and writing. +package encoding diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go new file mode 100644 index 000000000..9ecb8a43f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go @@ -0,0 +1,809 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package encoding + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/internal/leb128" + "github.com/open-policy-agent/opa/internal/wasm/constant" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/internal/wasm/opcode" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +// ReadModule reads a binary-encoded WASM module from r. +func ReadModule(r io.Reader) (*module.Module, error) { + + wr := &reader{r: r, n: 0} + module, err := readModule(wr) + if err != nil { + return nil, errors.Wrapf(err, "offset 0x%x", wr.n) + } + + return module, nil +} + +// ReadCodeEntry reads a binary-encoded WASM code entry from r. +func ReadCodeEntry(r io.Reader) (*module.CodeEntry, error) { + + wr := &reader{r: r, n: 0} + entry, err := readCodeEntry(wr) + if err != nil { + return nil, errors.Wrapf(err, "offset 0x%x", wr.n) + } + + return entry, nil +} + +// CodeEntries returns the WASM code entries contained in r. +func CodeEntries(m *module.Module) ([]*module.CodeEntry, error) { + + entries := make([]*module.CodeEntry, len(m.Code.Segments)) + + for i, s := range m.Code.Segments { + buf := bytes.NewBuffer(s.Code) + entry, err := ReadCodeEntry(buf) + if err != nil { + return nil, err + } + entries[i] = entry + } + + return entries, nil +} + +type reader struct { + r io.Reader + n int +} + +func (r *reader) Read(bs []byte) (int, error) { + n, err := r.r.Read(bs) + r.n += n + return n, err +} + +func readModule(r io.Reader) (*module.Module, error) { + + if err := readMagic(r); err != nil { + return nil, err + } + + if err := readVersion(r); err != nil { + return nil, err + } + + var m module.Module + + if err := readSections(r, &m); err != nil && err != io.EOF { + return nil, err + } + + return &m, nil +} + +func readCodeEntry(r io.Reader) (*module.CodeEntry, error) { + + var entry module.CodeEntry + + if err := readLocals(r, &entry.Func.Locals); err != nil { + return nil, errors.Wrapf(err, "local declarations") + } + + return &entry, readExpr(r, &entry.Func.Expr) +} + +func readMagic(r io.Reader) error { + var v uint32 + if err := binary.Read(r, binary.LittleEndian, &v); err != nil { + return err + } else if v != constant.Magic { + return fmt.Errorf("illegal magic value") + } + return nil +} + +func readVersion(r io.Reader) error { + var v uint32 + if err := binary.Read(r, binary.LittleEndian, &v); err != nil { + return err + } else if v != constant.Version { + return fmt.Errorf("illegal wasm version") + } + return nil +} + +func readSections(r io.Reader, m *module.Module) error { + for { + id, err := readByte(r) + if err != nil { + return err + } + + size, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + buf := make([]byte, size) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + + bufr := bytes.NewReader(buf) + + switch id { + case constant.CustomSectionID, constant.StartSectionID, constant.MemorySectionID: + continue + case constant.TypeSectionID: + if err := readTypeSection(bufr, &m.Type); err != nil { + return errors.Wrap(err, "type section") + } + case constant.ImportSectionID: + if err := readImportSection(bufr, &m.Import); err != nil { + return errors.Wrap(err, "import section") + } + case constant.GlobalSectionID: + if err := readGlobalSection(bufr, &m.Global); err != nil { + return errors.Wrap(err, "global section") + } + case constant.TableSectionID: + if err := readTableSection(bufr, &m.Table); err != nil { + return errors.Wrap(err, "table section") + } + case constant.FunctionSectionID: + if err := readFunctionSection(bufr, &m.Function); err != nil { + return errors.Wrap(err, "function section") + } + case constant.ExportSectionID: + if err := readExportSection(bufr, &m.Export); err != nil { + return errors.Wrap(err, "export section") + } + case constant.ElementSectionID: + if err := readElementSection(bufr, &m.Element); err != nil { + return errors.Wrap(err, "element section") + } + case constant.DataSectionID: + if err := readDataSection(bufr, &m.Data); err != nil { + return errors.Wrap(err, "data section") + } + case constant.CodeSectionID: + if err := readRawCodeSection(bufr, &m.Code); err != nil { + return errors.Wrap(err, "code section") + } + default: + return fmt.Errorf("illegal section id") + } + } +} + +func readTypeSection(r io.Reader, s *module.TypeSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var ftype module.FunctionType + if err := readFunctionType(r, &ftype); err != nil { + return err + } + + s.Functions = append(s.Functions, ftype) + } + + return nil +} + +func readImportSection(r io.Reader, s *module.ImportSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var imp module.Import + + if err := readImport(r, &imp); err != nil { + return err + } + + s.Imports = append(s.Imports, imp) + } + + return nil +} + +func readTableSection(r io.Reader, s *module.TableSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var table module.Table + + if elem, err := readByte(r); err != nil { + return err + } else if elem != constant.ElementTypeAnyFunc { + return fmt.Errorf("illegal element type") + } else { + table.Type = types.Anyfunc + } + + if err := readLimits(r, &table.Lim); err != nil { + return err + } + + s.Tables = append(s.Tables, table) + } + + return nil +} + +func readGlobalSection(r io.Reader, s *module.GlobalSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var global module.Global + + if err := readGlobal(r, &global); err != nil { + return err + } + + s.Globals = append(s.Globals, global) + } + + return nil +} + +func readFunctionSection(r io.Reader, s *module.FunctionSection) error { + return readVarUint32Vector(r, &s.TypeIndices) +} + +func readExportSection(r io.Reader, s *module.ExportSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var exp module.Export + + if err := readExport(r, &exp); err != nil { + return err + } + + s.Exports = append(s.Exports, exp) + } + + return nil +} + +func readElementSection(r io.Reader, s *module.ElementSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var seg module.ElementSegment + + if err := readElementSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readDataSection(r io.Reader, s *module.DataSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + + var seg module.DataSegment + + if err := readDataSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readRawCodeSection(r io.Reader, s *module.RawCodeSection) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + for i := uint32(0); i < n; i++ { + var seg module.RawCodeSegment + + if err := readRawCodeSegment(r, &seg); err != nil { + return err + } + + s.Segments = append(s.Segments, seg) + } + + return nil +} + +func readFunctionType(r io.Reader, ftype *module.FunctionType) error { + + if b, err := readByte(r); err != nil { + return err + } else if b != constant.FunctionTypeID { + return fmt.Errorf("illegal function type id 0x%x", b) + } + + if err := readValueTypeVector(r, &ftype.Params); err != nil { + return err + } + + return readValueTypeVector(r, &ftype.Results) +} + +func readGlobal(r io.Reader, global *module.Global) error { + + if err := readValueType(r, &global.Type); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + } + + if b == 1 { + global.Mutable = true + } else if b != 0 { + return fmt.Errorf("illegal mutability flag") + } + + if err := readConstantExpr(r, &global.Init); err != nil { + return err + } + + return nil +} + +func readImport(r io.Reader, imp *module.Import) error { + + if err := readByteVectorString(r, &imp.Module); err != nil { + return err + } + + if err := readByteVectorString(r, &imp.Name); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + + } + + if b == constant.ImportDescType { + index, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + imp.Descriptor = module.FunctionImport{ + Func: index, + } + return nil + } + + if b == constant.ImportDescTable { + if elem, err := readByte(r); err != nil { + return err + } else if elem != constant.ElementTypeAnyFunc { + return fmt.Errorf("illegal element type") + } + desc := module.TableImport{ + Type: types.Anyfunc, + } + if err := readLimits(r, &desc.Lim); err != nil { + return err + } + imp.Descriptor = desc + return nil + } + + if b == constant.ImportDescMem { + desc := module.MemoryImport{} + if err := readLimits(r, &desc.Mem.Lim); err != nil { + return err + } + imp.Descriptor = desc + return nil + } + + if b == constant.ImportDescGlobal { + desc := module.GlobalImport{} + if err := readValueType(r, &desc.Type); err != nil { + return err + } + b, err := readByte(r) + if err != nil { + return err + } + if b == 1 { + desc.Mutable = true + } else if b != 0 { + return fmt.Errorf("illegal mutability flag") + } + return nil + } + + return fmt.Errorf("illegal import descriptor type") +} + +func readExport(r io.Reader, exp *module.Export) error { + + if err := readByteVectorString(r, &exp.Name); err != nil { + return err + } + + b, err := readByte(r) + if err != nil { + return err + } + + switch b { + case constant.ExportDescType: + exp.Descriptor.Type = module.FunctionExportType + case constant.ExportDescTable: + exp.Descriptor.Type = module.TableExportType + case constant.ExportDescMem: + exp.Descriptor.Type = module.MemoryExportType + case constant.ExportDescGlobal: + exp.Descriptor.Type = module.GlobalExportType + default: + return fmt.Errorf("illegal export descriptor type") + } + + exp.Descriptor.Index, err = leb128.ReadVarUint32(r) + if err != nil { + return err + } + + return nil +} + +func readElementSegment(r io.Reader, seg *module.ElementSegment) error { + + if err := readVarUint32(r, &seg.Index); err != nil { + return err + } + + if err := readConstantExpr(r, &seg.Offset); err != nil { + return err + } + + if err := readVarUint32Vector(r, &seg.Indices); err != nil { + return err + } + + return nil +} + +func readDataSegment(r io.Reader, seg *module.DataSegment) error { + + if err := readVarUint32(r, &seg.Index); err != nil { + return err + } + + if err := readConstantExpr(r, &seg.Offset); err != nil { + return err + } + + if err := readByteVector(r, &seg.Init); err != nil { + return err + } + + return nil +} + +func readRawCodeSegment(r io.Reader, seg *module.RawCodeSegment) error { + return readByteVector(r, &seg.Code) +} + +func readConstantExpr(r io.Reader, expr *module.Expr) error { + + instrs := make([]instruction.Instruction, 0) + + for { + b, err := readByte(r) + if err != nil { + return err + } + + switch opcode.Opcode(b) { + case opcode.I32Const: + i32, err := leb128.ReadVarInt32(r) + if err != nil { + return err + } + instrs = append(instrs, instruction.I32Const{Value: i32}) + case opcode.I64Const: + i64, err := leb128.ReadVarInt64(r) + if err != nil { + return err + } + instrs = append(instrs, instruction.I64Const{Value: i64}) + case opcode.End: + expr.Instrs = instrs + return nil + default: + return fmt.Errorf("illegal constant expr opcode 0x%x", b) + } + } +} + +func readExpr(r io.Reader, expr *module.Expr) (err error) { + + defer func() { + if r := recover(); r != nil { + switch r := r.(type) { + case error: + err = r + default: + err = fmt.Errorf("unknown panic") + } + } + }() + + return readInstructions(r, &expr.Instrs) +} + +func readInstructions(r io.Reader, instrs *[]instruction.Instruction) error { + + ret := make([]instruction.Instruction, 0) + + for { + b, err := readByte(r) + if err != nil { + return err + } + + switch opcode.Opcode(b) { + case opcode.I32Const: + ret = append(ret, instruction.I32Const{Value: leb128.MustReadVarInt32(r)}) + case opcode.I64Const: + ret = append(ret, instruction.I64Const{Value: leb128.MustReadVarInt64(r)}) + case opcode.I32Eqz: + ret = append(ret, instruction.I32Eqz{}) + case opcode.GetLocal: + ret = append(ret, instruction.GetLocal{Index: leb128.MustReadVarUint32(r)}) + case opcode.SetLocal: + ret = append(ret, instruction.SetLocal{Index: leb128.MustReadVarUint32(r)}) + case opcode.Call: + ret = append(ret, instruction.Call{Index: leb128.MustReadVarUint32(r)}) + case opcode.BrIf: + ret = append(ret, instruction.BrIf{Index: leb128.MustReadVarUint32(r)}) + case opcode.Return: + ret = append(ret, instruction.Return{}) + case opcode.Block: + block := instruction.Block{} + if err := readBlockValueType(r, block.Type); err != nil { + return err + } + if err := readInstructions(r, &block.Instrs); err != nil { + return err + } + ret = append(ret, block) + case opcode.Loop: + loop := instruction.Loop{} + if err := readBlockValueType(r, loop.Type); err != nil { + return err + } + if err := readInstructions(r, &loop.Instrs); err != nil { + return err + } + ret = append(ret, loop) + case opcode.End: + *instrs = ret + return nil + default: + return fmt.Errorf("illegal opcode 0x%x", b) + } + } +} + +func readLimits(r io.Reader, l *module.Limit) error { + + b, err := readByte(r) + if err != nil { + return err + } + + min, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + l.Min = min + + if b == 1 { + max, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + l.Max = &max + } else if b != 0 { + return fmt.Errorf("illegal limit flag") + } + + return nil +} + +func readLocals(r io.Reader, locals *[]module.LocalDeclaration) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]module.LocalDeclaration, n) + + for i := uint32(0); i < n; i++ { + if err := readVarUint32(r, &ret[i].Count); err != nil { + return err + } + if err := readValueType(r, &ret[i].Type); err != nil { + return err + } + } + + *locals = ret + return nil +} + +func readByteVector(r io.Reader, v *[]byte) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + buf := make([]byte, n) + if _, err := io.ReadFull(r, buf); err != nil { + return err + } + + *v = buf + return nil +} + +func readByteVectorString(r io.Reader, v *string) error { + + var buf []byte + + if err := readByteVector(r, &buf); err != nil { + return err + } + + *v = string(buf) + return nil +} + +func readVarUint32Vector(r io.Reader, v *[]uint32) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]uint32, n) + + for i := uint32(0); i < n; i++ { + if err := readVarUint32(r, &ret[i]); err != nil { + return err + } + } + + *v = ret + return nil +} + +func readValueTypeVector(r io.Reader, v *[]types.ValueType) error { + + n, err := leb128.ReadVarUint32(r) + if err != nil { + return err + } + + ret := make([]types.ValueType, n) + + for i := uint32(0); i < n; i++ { + if err := readValueType(r, &ret[i]); err != nil { + return err + } + } + + *v = ret + return nil +} + +func readVarUint32(r io.Reader, v *uint32) error { + var err error + *v, err = leb128.ReadVarUint32(r) + return err +} + +func readValueType(r io.Reader, v *types.ValueType) error { + if b, err := readByte(r); err != nil { + return err + } else if b == constant.ValueTypeI32 { + *v = types.I32 + } else if b == constant.ValueTypeI64 { + *v = types.I64 + } else if b == constant.ValueTypeF32 { + *v = types.F32 + } else if b == constant.ValueTypeF64 { + *v = types.F64 + } else { + return fmt.Errorf("illegal value type: 0x%x", b) + } + return nil +} + +func readBlockValueType(r io.Reader, v *types.ValueType) error { + if b, err := readByte(r); err != nil { + return err + } else if b == constant.ValueTypeI32 { + *v = types.I32 + } else if b == constant.ValueTypeI64 { + *v = types.I64 + } else if b == constant.ValueTypeF32 { + *v = types.F32 + } else if b == constant.ValueTypeF64 { + *v = types.F64 + } else if b != constant.BlockTypeEmpty { + return fmt.Errorf("illegal value type: 0x%x", b) + } + return nil +} + +func readByte(r io.Reader) (byte, error) { + buf := make([]byte, 1) + _, err := r.Read(buf) + return buf[0], err +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/writer.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/writer.go new file mode 100644 index 000000000..c95045b05 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/writer.go @@ -0,0 +1,615 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package encoding + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "math" + + "github.com/open-policy-agent/opa/internal/leb128" + "github.com/open-policy-agent/opa/internal/wasm/constant" + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/module" + "github.com/open-policy-agent/opa/internal/wasm/opcode" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +// WriteModule writes a binary-encoded representation of module to w. +func WriteModule(w io.Writer, module *module.Module) error { + + if err := writeMagic(w); err != nil { + return err + } + + if err := writeVersion(w); err != nil { + return err + } + + if module == nil { + return nil + } + + if err := writeTypeSection(w, module.Type); err != nil { + return err + } + + if err := writeImportSection(w, module.Import); err != nil { + return err + } + + if err := writeFunctionSection(w, module.Function); err != nil { + return err + } + + if err := writeTableSection(w, module.Table); err != nil { + return err + } + + if err := writeGlobalSection(w, module.Global); err != nil { + return err + } + + if err := writeExportSection(w, module.Export); err != nil { + return err + } + + if err := writeElementSection(w, module.Element); err != nil { + return err + } + + if err := writeRawCodeSection(w, module.Code); err != nil { + return err + } + + if err := writeDataSection(w, module.Data); err != nil { + return err + } + + return nil +} + +// WriteCodeEntry writes a binary encoded representation of entry to w. +func WriteCodeEntry(w io.Writer, entry *module.CodeEntry) error { + + if err := leb128.WriteVarUint32(w, uint32(len(entry.Func.Locals))); err != nil { + return err + } + + for _, local := range entry.Func.Locals { + + if err := leb128.WriteVarUint32(w, local.Count); err != nil { + return err + } + + if err := writeValueType(w, local.Type); err != nil { + return err + } + } + + return writeInstructions(w, entry.Func.Expr.Instrs) +} + +func writeMagic(w io.Writer) error { + return binary.Write(w, binary.LittleEndian, constant.Magic) +} + +func writeVersion(w io.Writer) error { + return binary.Write(w, binary.LittleEndian, constant.Version) +} + +func writeTypeSection(w io.Writer, s module.TypeSection) error { + + if len(s.Functions) == 0 { + return nil + } + + if err := writeByte(w, constant.TypeSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Functions))); err != nil { + return err + } + + for _, fsig := range s.Functions { + if err := writeFunctionType(&buf, fsig); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeImportSection(w io.Writer, s module.ImportSection) error { + + if len(s.Imports) == 0 { + return nil + } + + if err := writeByte(w, constant.ImportSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Imports))); err != nil { + return err + } + + for _, imp := range s.Imports { + if err := writeImport(&buf, imp); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeGlobalSection(w io.Writer, s module.GlobalSection) error { + + if len(s.Globals) == 0 { + return nil + } + + if err := writeByte(w, constant.GlobalSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Globals))); err != nil { + return err + } + + for _, global := range s.Globals { + if err := writeGlobal(&buf, global); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeFunctionSection(w io.Writer, s module.FunctionSection) error { + + if len(s.TypeIndices) == 0 { + return nil + } + + if err := writeByte(w, constant.FunctionSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.TypeIndices))); err != nil { + return err + } + + for _, idx := range s.TypeIndices { + if err := leb128.WriteVarUint32(&buf, uint32(idx)); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeTableSection(w io.Writer, s module.TableSection) error { + + if len(s.Tables) == 0 { + return nil + } + + if err := writeByte(w, constant.TableSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Tables))); err != nil { + return err + } + + for _, table := range s.Tables { + switch table.Type { + case types.Anyfunc: + if err := writeByte(&buf, constant.ElementTypeAnyFunc); err != nil { + return err + } + default: + return fmt.Errorf("illegal table element type") + } + if err := writeLimits(&buf, table.Lim); err != nil { + return err + } + } + + return writeRawSection(w, &buf) + +} + +func writeExportSection(w io.Writer, s module.ExportSection) error { + + if len(s.Exports) == 0 { + return nil + } + + if err := writeByte(w, constant.ExportSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Exports))); err != nil { + return err + } + + for _, exp := range s.Exports { + if err := writeByteVector(&buf, []byte(exp.Name)); err != nil { + return err + } + var tpe byte + switch exp.Descriptor.Type { + case module.FunctionExportType: + tpe = constant.ExportDescType + case module.TableExportType: + tpe = constant.ExportDescTable + case module.MemoryExportType: + tpe = constant.ExportDescMem + case module.GlobalExportType: + tpe = constant.ExportDescGlobal + default: + return fmt.Errorf("illegal export descriptor type 0x%x", exp.Descriptor.Type) + } + if err := writeByte(&buf, tpe); err != nil { + return err + } + if err := leb128.WriteVarUint32(&buf, exp.Descriptor.Index); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeElementSection(w io.Writer, s module.ElementSection) error { + + if len(s.Segments) == 0 { + return nil + } + + if err := writeByte(w, constant.ElementSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil { + return err + } + + for _, seg := range s.Segments { + if err := leb128.WriteVarUint32(&buf, seg.Index); err != nil { + return err + } + if err := writeInstructions(&buf, seg.Offset.Instrs); err != nil { + return err + } + if err := writeVarUint32Vector(&buf, seg.Indices); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeRawCodeSection(w io.Writer, s module.RawCodeSection) error { + + if len(s.Segments) == 0 { + return nil + } + + if err := writeByte(w, constant.CodeSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil { + return err + } + + for _, seg := range s.Segments { + if err := leb128.WriteVarUint32(&buf, uint32(len(seg.Code))); err != nil { + return err + } + if _, err := buf.Write(seg.Code); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeDataSection(w io.Writer, s module.DataSection) error { + + if len(s.Segments) == 0 { + return nil + } + + if err := writeByte(w, constant.DataSectionID); err != nil { + return err + } + + var buf bytes.Buffer + + if err := leb128.WriteVarUint32(&buf, uint32(len(s.Segments))); err != nil { + return err + } + + for _, seg := range s.Segments { + if err := leb128.WriteVarUint32(&buf, seg.Index); err != nil { + return err + } + if err := writeInstructions(&buf, seg.Offset.Instrs); err != nil { + return err + } + if err := writeByteVector(&buf, seg.Init); err != nil { + return err + } + } + + return writeRawSection(w, &buf) +} + +func writeFunctionType(w io.Writer, fsig module.FunctionType) error { + + if err := writeByte(w, constant.FunctionTypeID); err != nil { + return err + } + + if err := writeValueTypeVector(w, fsig.Params); err != nil { + return err + } + + return writeValueTypeVector(w, fsig.Results) +} + +func writeImport(w io.Writer, imp module.Import) error { + + if err := writeByteVector(w, []byte(imp.Module)); err != nil { + return err + } + + if err := writeByteVector(w, []byte(imp.Name)); err != nil { + return err + } + + switch desc := imp.Descriptor.(type) { + case module.FunctionImport: + if err := writeByte(w, constant.ImportDescType); err != nil { + return err + } + return leb128.WriteVarUint32(w, desc.Func) + case module.TableImport: + if err := writeByte(w, constant.ImportDescTable); err != nil { + return err + } + if err := writeByte(w, constant.ElementTypeAnyFunc); err != nil { + return err + } + return writeLimits(w, desc.Lim) + case module.MemoryImport: + if err := writeByte(w, constant.ImportDescMem); err != nil { + return err + } + return writeLimits(w, desc.Mem.Lim) + case module.GlobalImport: + if err := writeByte(w, constant.ImportDescGlobal); err != nil { + return err + } + if err := writeValueType(w, desc.Type); err != nil { + return err + } + if desc.Mutable { + return writeByte(w, constant.Mutable) + } + return writeByte(w, constant.Const) + default: + return fmt.Errorf("illegal import descriptor type") + } +} + +func writeGlobal(w io.Writer, global module.Global) error { + + if err := writeValueType(w, global.Type); err != nil { + return err + } + + var err error + + if global.Mutable { + err = writeByte(w, constant.Mutable) + } else { + err = writeByte(w, constant.Const) + } + + if err != nil { + return err + } + + if err := writeInstructions(w, global.Init.Instrs); err != nil { + return err + } + + return nil +} + +func writeInstructions(w io.Writer, instrs []instruction.Instruction) error { + + for i, instr := range instrs { + + _, err := w.Write([]byte{byte(instr.Op())}) + if err != nil { + return err + } + + for _, arg := range instr.ImmediateArgs() { + var err error + switch arg := arg.(type) { + case int32: + err = leb128.WriteVarInt32(w, arg) + case int64: + err = leb128.WriteVarInt64(w, arg) + case uint32: + err = leb128.WriteVarUint32(w, arg) + case uint64: + err = leb128.WriteVarUint64(w, arg) + case float32: + u32 := math.Float32bits(arg) + err = binary.Write(w, binary.LittleEndian, u32) + case float64: + u64 := math.Float64bits(arg) + err = binary.Write(w, binary.LittleEndian, u64) + default: + return fmt.Errorf("illegal immediate argument type on instruction %d", i) + } + if err != nil { + return err + } + } + + if si, ok := instr.(instruction.StructuredInstruction); ok { + if err := writeBlockValueType(w, si.BlockType()); err != nil { + return err + } + if err := writeInstructions(w, si.Instructions()); err != nil { + return err + } + } + + } + + _, err := w.Write([]byte{byte(opcode.End)}) + return err +} + +func writeLimits(w io.Writer, lim module.Limit) error { + if lim.Max == nil { + if err := writeByte(w, 0); err != nil { + return err + } + } else { + if err := writeByte(w, 1); err != nil { + return err + } + } + if err := leb128.WriteVarUint32(w, lim.Min); err != nil { + return err + } + if lim.Max != nil { + return leb128.WriteVarUint32(w, *lim.Max) + } + return nil +} + +func writeVarUint32Vector(w io.Writer, v []uint32) error { + + if err := leb128.WriteVarUint32(w, uint32(len(v))); err != nil { + return err + } + + for i := range v { + if err := leb128.WriteVarUint32(w, v[i]); err != nil { + return err + } + } + + return nil +} + +func writeValueTypeVector(w io.Writer, v []types.ValueType) error { + + if err := leb128.WriteVarUint32(w, uint32(len(v))); err != nil { + return err + } + + for i := range v { + if err := writeValueType(w, v[i]); err != nil { + return err + } + } + + return nil +} + +func writeBlockValueType(w io.Writer, v *types.ValueType) error { + var b byte + if v != nil { + switch *v { + case types.I32: + b = constant.ValueTypeI32 + case types.I64: + b = constant.ValueTypeI64 + case types.F32: + b = constant.ValueTypeF32 + case types.F64: + b = constant.ValueTypeF64 + } + } else { + b = constant.BlockTypeEmpty + } + return writeByte(w, b) +} + +func writeValueType(w io.Writer, v types.ValueType) error { + var b byte + switch v { + case types.I32: + b = constant.ValueTypeI32 + case types.I64: + b = constant.ValueTypeI64 + case types.F32: + b = constant.ValueTypeF32 + case types.F64: + b = constant.ValueTypeF64 + } + return writeByte(w, b) +} + +func writeRawSection(w io.Writer, buf *bytes.Buffer) error { + + size := buf.Len() + + if err := leb128.WriteVarUint32(w, uint32(size)); err != nil { + return err + } + + _, err := io.Copy(w, buf) + return err +} + +func writeByteVector(w io.Writer, bs []byte) error { + + if err := leb128.WriteVarUint32(w, uint32(len(bs))); err != nil { + return err + } + + _, err := w.Write(bs) + return err +} + +func writeByte(w io.Writer, b byte) error { + buf := make([]byte, 1) + buf[0] = b + _, err := w.Write(buf) + return err +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/control.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/control.go new file mode 100644 index 000000000..51567153d --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/control.go @@ -0,0 +1,139 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package instruction + +import ( + "github.com/open-policy-agent/opa/internal/wasm/opcode" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +// Unreachable reprsents an unreachable opcode. +type Unreachable struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (Unreachable) Op() opcode.Opcode { + return opcode.Unreachable +} + +// Nop represents a WASM no-op instruction. +type Nop struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (Nop) Op() opcode.Opcode { + return opcode.Nop +} + +// Block represents a WASM block instruction. +type Block struct { + NoImmediateArgs + Type *types.ValueType + Instrs []Instruction +} + +// Op returns the opcode of the instruction +func (Block) Op() opcode.Opcode { + return opcode.Block +} + +// BlockType returns the type of the block's return value. +func (i Block) BlockType() *types.ValueType { + return i.Type +} + +// Instructions returns the instructions contained in the block. +func (i Block) Instructions() []Instruction { + return i.Instrs +} + +// Loop represents a WASM loop instruction. +type Loop struct { + NoImmediateArgs + Type *types.ValueType + Instrs []Instruction +} + +// Op returns the opcode of the instruction. +func (Loop) Op() opcode.Opcode { + return opcode.Loop +} + +// BlockType returns the type of the loop's return value. +func (i Loop) BlockType() *types.ValueType { + return i.Type +} + +// Instructions represents the instructions contained in the loop. +func (i Loop) Instructions() []Instruction { + return i.Instrs +} + +// Br represents a WASM br instruction. +type Br struct { + Index uint32 +} + +// Op returns the opcode of the instruction. +func (Br) Op() opcode.Opcode { + return opcode.Br +} + +// ImmediateArgs returns the block index to break to. +func (i Br) ImmediateArgs() []interface{} { + return []interface{}{i.Index} +} + +// BrIf represents a WASM br_if instruction. +type BrIf struct { + Index uint32 +} + +// Op returns the opcode of the instruction. +func (BrIf) Op() opcode.Opcode { + return opcode.BrIf +} + +// ImmediateArgs returns the block index to break to. +func (i BrIf) ImmediateArgs() []interface{} { + return []interface{}{i.Index} +} + +// Call represents a WASM call instruction. +type Call struct { + Index uint32 +} + +// Op returns the opcode of the instruction. +func (Call) Op() opcode.Opcode { + return opcode.Call +} + +// ImmediateArgs returns the function index. +func (i Call) ImmediateArgs() []interface{} { + return []interface{}{i.Index} +} + +// Return represents a WASM return instruction. +type Return struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (Return) Op() opcode.Opcode { + return opcode.Return +} + +// End represents the special WASM end instruction. +type End struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (End) Op() opcode.Opcode { + return opcode.End +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/instruction.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/instruction.go new file mode 100644 index 000000000..066be77c4 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/instruction.go @@ -0,0 +1,33 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package instruction defines WASM instruction types. +package instruction + +import ( + "github.com/open-policy-agent/opa/internal/wasm/opcode" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +// NoImmediateArgs indicates the instruction has no immediate arguments. +type NoImmediateArgs struct { +} + +// ImmediateArgs returns the immedate arguments of an instruction. +func (NoImmediateArgs) ImmediateArgs() []interface{} { + return nil +} + +// Instruction represents a single WASM instruction. +type Instruction interface { + Op() opcode.Opcode + ImmediateArgs() []interface{} +} + +// StructuredInstruction represents a structured control instruction like br_if. +type StructuredInstruction interface { + Instruction + BlockType() *types.ValueType + Instructions() []Instruction +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/memory.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/memory.go new file mode 100644 index 000000000..c449cb1b6 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/memory.go @@ -0,0 +1,39 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package instruction + +import "github.com/open-policy-agent/opa/internal/wasm/opcode" + +// I32Load represents the WASM i32.load instruction. +type I32Load struct { + Offset int32 + Align int32 // expressed as a power of two +} + +// Op returns the opcode of the instruction. +func (I32Load) Op() opcode.Opcode { + return opcode.I32Load +} + +// ImmediateArgs returns the static offset and alignment operands. +func (i I32Load) ImmediateArgs() []interface{} { + return []interface{}{i.Align, i.Offset} +} + +// I32Store represents the WASM i32.store instruction. +type I32Store struct { + Offset int32 + Align int32 // expressed as a power of two +} + +// Op returns the opcode of the instruction. +func (I32Store) Op() opcode.Opcode { + return opcode.I32Store +} + +// ImmediateArgs returns the static offset and alignment operands. +func (i I32Store) ImmediateArgs() []interface{} { + return []interface{}{i.Align, i.Offset} +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/numeric.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/numeric.go new file mode 100644 index 000000000..f1acb31fc --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/numeric.go @@ -0,0 +1,139 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package instruction + +import ( + "github.com/open-policy-agent/opa/internal/wasm/opcode" +) + +// I32Const represents the WASM i32.const instruction. +type I32Const struct { + Value int32 +} + +// Op returns the opcode of the instruction. +func (I32Const) Op() opcode.Opcode { + return opcode.I32Const +} + +// ImmediateArgs returns the i32 value to push onto the stack. +func (i I32Const) ImmediateArgs() []interface{} { + return []interface{}{i.Value} +} + +// I64Const represents the WASM i64.const instruction. +type I64Const struct { + Value int64 +} + +// Op returns the opcode of the instruction. +func (I64Const) Op() opcode.Opcode { + return opcode.I64Const +} + +// ImmediateArgs returns the i64 value to push onto the stack. +func (i I64Const) ImmediateArgs() []interface{} { + return []interface{}{i.Value} +} + +// F32Const represents the WASM f32.const instruction. +type F32Const struct { + Value int32 +} + +// Op returns the opcode of the instruction. +func (F32Const) Op() opcode.Opcode { + return opcode.F32Const +} + +// ImmediateArgs returns the f32 value to push onto the stack. +func (i F32Const) ImmediateArgs() []interface{} { + return []interface{}{i.Value} +} + +// F64Const represents the WASM f64.const instruction. +type F64Const struct { + Value float64 +} + +// Op returns the opcode of the instruction. +func (F64Const) Op() opcode.Opcode { + return opcode.F64Const +} + +// ImmediateArgs returns the f64 value to push onto the stack. +func (i F64Const) ImmediateArgs() []interface{} { + return []interface{}{i.Value} +} + +// I32Eqz represents the WASM i32.eqz instruction. +type I32Eqz struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32Eqz) Op() opcode.Opcode { + return opcode.I32Eqz +} + +// I32Eq represents the WASM i32.eq instruction. +type I32Eq struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32Eq) Op() opcode.Opcode { + return opcode.I32Eq +} + +// I32Ne represents the WASM i32.ne instruction. +type I32Ne struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32Ne) Op() opcode.Opcode { + return opcode.I32Ne +} + +// I32GtS represents the WASM i32.gt_s instruction. +type I32GtS struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32GtS) Op() opcode.Opcode { + return opcode.I32GtS +} + +// I32GeS represents the WASM i32.ge_s instruction. +type I32GeS struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32GeS) Op() opcode.Opcode { + return opcode.I32GeS +} + +// I32LtS represents the WASM i32.lt_s instruction. +type I32LtS struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32LtS) Op() opcode.Opcode { + return opcode.I32LtS +} + +// I32LeS represents the WASM i32.le_s instruction. +type I32LeS struct { + NoImmediateArgs +} + +// Op returns the opcode of the instruction. +func (I32LeS) Op() opcode.Opcode { + return opcode.I32LeS +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/variable.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/variable.go new file mode 100644 index 000000000..ac57e5048 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/instruction/variable.go @@ -0,0 +1,38 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package instruction + +import "github.com/open-policy-agent/opa/internal/wasm/opcode" + +// GetLocal represents the WASM get_local instruction. +type GetLocal struct { + Index uint32 +} + +// Op returns the opcode of the instruction. +func (GetLocal) Op() opcode.Opcode { + return opcode.GetLocal +} + +// ImmediateArgs returns the index of the local variable to push onto the stack. +func (i GetLocal) ImmediateArgs() []interface{} { + return []interface{}{i.Index} +} + +// SetLocal represents the WASM set_local instruction. +type SetLocal struct { + Index uint32 +} + +// Op returns the opcode of the instruction. +func (SetLocal) Op() opcode.Opcode { + return opcode.SetLocal +} + +// ImmediateArgs returns the index of the local variable to set with the top of +// the stack. +func (i SetLocal) ImmediateArgs() []interface{} { + return []interface{}{i.Index} +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/module/module.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/module/module.go new file mode 100644 index 000000000..b55094c8c --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/module/module.go @@ -0,0 +1,340 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package module + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/internal/wasm/instruction" + "github.com/open-policy-agent/opa/internal/wasm/types" +) + +type ( + // Module represents a WASM module. + Module struct { + Version uint32 + Type TypeSection + Import ImportSection + Function FunctionSection + Table TableSection + Element ElementSection + Global GlobalSection + Export ExportSection + Code RawCodeSection + Data DataSection + } + + // TypeSection represents a WASM type section. + TypeSection struct { + Functions []FunctionType + } + + // ImportSection represents a WASM import section. + ImportSection struct { + Imports []Import + } + + // FunctionSection represents a WASM function section. + FunctionSection struct { + TypeIndices []uint32 + } + + // TableSection represents a WASM table section. + TableSection struct { + Tables []Table + } + + // ElementSection represents a WASM element section. + ElementSection struct { + Segments []ElementSegment + } + + // GlobalSection represents a WASM global section. + GlobalSection struct { + Globals []Global + } + + // ExportSection represents a WASM export section. + ExportSection struct { + Exports []Export + } + + // RawCodeSection represents a WASM code section. The code section is left as a + // raw byte sequence. See CodeSection for the decoded version. + RawCodeSection struct { + Segments []RawCodeSegment + } + + // DataSection represents a WASM data section. + DataSection struct { + Segments []DataSegment + } + + // FunctionType represents a WASM function type definition. + FunctionType struct { + Params []types.ValueType + Results []types.ValueType + } + + // Import represents a WASM import statement. + Import struct { + Module string + Name string + Descriptor ImportDescriptor + } + + // ImportDescriptor represents a WASM import descriptor. + ImportDescriptor interface { + fmt.Stringer + Kind() ImportDescriptorType + } + + // ImportDescriptorType defines allowed kinds of import descriptors. + ImportDescriptorType int + + // FunctionImport represents a WASM function import statement. + FunctionImport struct { + Func uint32 + } + + // MemoryImport represents a WASM memory import statement. + MemoryImport struct { + Mem MemType + } + + // MemType defines the attributes of a memory import. + MemType struct { + Lim Limit + } + + // TableImport represents a WASM table import statement. + TableImport struct { + Type types.ElementType + Lim Limit + } + + // ElementSegment represents a WASM element segment. + ElementSegment struct { + Index uint32 + Offset Expr + Indices []uint32 + } + + // GlobalImport represents a WASM global variable import statement. + GlobalImport struct { + Type types.ValueType + Mutable bool + } + + // Limit represents a WASM limit. + Limit struct { + Min uint32 + Max *uint32 + } + + // Table represents a WASM table statement. + Table struct { + Type types.ElementType + Lim Limit + } + + // Global represents a WASM global statement. + Global struct { + Type types.ValueType + Mutable bool + Init Expr + } + + // Export represents a WASM export statement. + Export struct { + Name string + Descriptor ExportDescriptor + } + + // ExportDescriptor represents a WASM export descriptor. + ExportDescriptor struct { + Type ExportDescriptorType + Index uint32 + } + + // ExportDescriptorType defines the allowed kinds of export descriptors. + ExportDescriptorType int + + // RawCodeSegment represents a binary-encoded WASM code segment. + RawCodeSegment struct { + Code []byte + } + + // DataSegment represents a WASM data segment. + DataSegment struct { + Index uint32 + Offset Expr + Init []byte + } + + // Expr represents a WASM expression. + Expr struct { + Instrs []instruction.Instruction + } + + // CodeEntry represents a code segment entry. + CodeEntry struct { + Func Function + } + + // Function represents a function in a code segment. + Function struct { + Locals []LocalDeclaration + Expr Expr + } + + // LocalDeclaration represents a local variable declaration. + LocalDeclaration struct { + Count uint32 + Type types.ValueType + } +) + +// Defines the allowed kinds of imports. +const ( + FunctionImportType ImportDescriptorType = iota + TableImportType + MemoryImportType + GlobalImportType +) + +func (x ImportDescriptorType) String() string { + switch x { + case FunctionImportType: + return "func" + case TableImportType: + return "table" + case MemoryImportType: + return "memory" + case GlobalImportType: + return "global" + } + panic("illegal value") +} + +// Defines the allowed kinds of exports. +const ( + FunctionExportType ExportDescriptorType = iota + TableExportType + MemoryExportType + GlobalExportType +) + +func (x ExportDescriptorType) String() string { + switch x { + case FunctionExportType: + return "func" + case TableExportType: + return "table" + case MemoryExportType: + return "memory" + case GlobalExportType: + return "global" + } + panic("illegal value") +} + +// Kind returns the function import type kind. +func (i FunctionImport) Kind() ImportDescriptorType { + return FunctionImportType +} + +func (i FunctionImport) String() string { + return fmt.Sprintf("%v[type=%v]", i.Kind(), i.Func) +} + +// Kind returns the memory import type kind. +func (i MemoryImport) Kind() ImportDescriptorType { + return MemoryImportType +} + +func (i MemoryImport) String() string { + return fmt.Sprintf("%v[%v]", i.Kind(), i.Mem.Lim) +} + +// Kind returns the table import type kind. +func (i TableImport) Kind() ImportDescriptorType { + return TableImportType +} + +func (i TableImport) String() string { + return fmt.Sprintf("%v[%v, %v]", i.Kind(), i.Type, i.Lim) +} + +// Kind returns the global import type kind. +func (i GlobalImport) Kind() ImportDescriptorType { + return GlobalImportType +} + +func (i GlobalImport) String() string { + return fmt.Sprintf("%v[%v, mut=%v]", i.Kind(), i.Type, i.Mutable) +} + +func (tpe FunctionType) String() string { + params := make([]string, len(tpe.Params)) + results := make([]string, len(tpe.Results)) + for i := range tpe.Params { + params[i] = tpe.Params[i].String() + } + for i := range tpe.Results { + results[i] = tpe.Results[i].String() + } + return "(" + strings.Join(params, ", ") + ") -> (" + strings.Join(results, ", ") + ")" +} + +// Equal returns true if tpe equals other. +func (tpe FunctionType) Equal(other FunctionType) bool { + + if len(tpe.Params) != len(other.Params) || len(tpe.Results) != len(other.Results) { + return false + } + + for i := range tpe.Params { + if tpe.Params[i] != other.Params[i] { + return false + } + } + + for i := range tpe.Results { + if tpe.Results[i] != other.Results[i] { + return false + } + } + + return true +} + +func (imp Import) String() string { + return fmt.Sprintf("%v %v.%v", imp.Descriptor.String(), imp.Module, imp.Name) +} + +func (exp Export) String() string { + return fmt.Sprintf("%v[%v] %v", exp.Descriptor.Type, exp.Descriptor.Index, exp.Name) +} + +func (seg RawCodeSegment) String() string { + return fmt.Sprintf("", len(seg.Code)) +} + +func (seg DataSegment) String() string { + return fmt.Sprintf("", seg.Index, seg.Offset, len(seg.Init)) +} + +func (e Expr) String() string { + return fmt.Sprintf("%d instr(s)", len(e.Instrs)) +} + +func (lim Limit) String() string { + if lim.Max == nil { + return fmt.Sprintf("min=%v", lim.Min) + } + return fmt.Sprintf("min=%v max=%v", lim.Min, lim.Max) +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/module/pretty.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/module/pretty.go new file mode 100644 index 000000000..2b28ad85b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/module/pretty.go @@ -0,0 +1,84 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package module + +import ( + "encoding/hex" + "fmt" + "io" +) + +// PrettyOption defines options for controlling pretty printing. +type PrettyOption struct { + Contents bool // show raw byte content of data+code sections. +} + +// Pretty writes a human-readable representation of m to w. +func Pretty(w io.Writer, m *Module, opts ...PrettyOption) { + fmt.Fprintln(w, "version:", m.Version) + fmt.Fprintln(w, "types:") + for _, fn := range m.Type.Functions { + fmt.Fprintln(w, " -", fn) + } + fmt.Fprintln(w, "imports:") + for i, imp := range m.Import.Imports { + if imp.Descriptor.Kind() == FunctionImportType { + fmt.Printf(" - [%d] %v\n", i, imp) + } else { + fmt.Fprintln(w, " -", imp) + } + } + fmt.Fprintln(w, "functions:") + for _, fn := range m.Function.TypeIndices { + if fn >= uint32(len(m.Type.Functions)) { + fmt.Fprintln(w, " -", "???") + } else { + fmt.Fprintln(w, " -", m.Type.Functions[fn]) + } + } + fmt.Fprintln(w, "exports:") + for _, exp := range m.Export.Exports { + fmt.Fprintln(w, " -", exp) + } + fmt.Fprintln(w, "code:") + for _, seg := range m.Code.Segments { + fmt.Fprintln(w, " -", seg) + } + fmt.Fprintln(w, "data:") + for _, seg := range m.Data.Segments { + fmt.Fprintln(w, " -", seg) + } + if len(opts) == 0 { + return + } + fmt.Fprintln(w) + for _, opt := range opts { + if opt.Contents { + newline := false + if len(m.Data.Segments) > 0 { + fmt.Fprintln(w, "data section:") + for _, seg := range m.Data.Segments { + if newline { + fmt.Fprintln(w) + } + fmt.Fprintln(w, hex.Dump(seg.Init)) + newline = true + } + newline = false + } + if len(m.Code.Segments) > 0 { + fmt.Fprintln(w, "code section:") + for _, seg := range m.Code.Segments { + if newline { + fmt.Fprintln(w) + } + fmt.Fprintln(w, hex.Dump(seg.Code)) + newline = true + } + newline = false + } + } + } +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/opcode/opcode.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/opcode/opcode.go new file mode 100644 index 000000000..7d35a3012 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/opcode/opcode.go @@ -0,0 +1,218 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package opcode contains constants and utilities for working with WASM opcodes. +package opcode + +// Opcode represents a WASM instruction opcode. +type Opcode byte + +// Control instructions. +const ( + Unreachable Opcode = iota + Nop + Block + Loop + If + Else +) + +const ( + // End defines the special end WASM opcode. + End Opcode = 0x0B +) + +// Extended control instructions. +const ( + Br Opcode = iota + 0x0C + BrIf + BrTable + Return + Call + CallIndirect +) + +// Parameter instructions. +const ( + Drop Opcode = iota + 0x1A + Select +) + +// Variable instructions. +const ( + GetLocal Opcode = iota + 0x20 + SetLocal + TeeLocal + GetGlobal + SetGlobal +) + +// Memory instructions. +const ( + I32Load Opcode = iota + 0x28 + I64Load + F32Load + F64Load + I32Load8S + I32Load8U + I32Load16S + I32Load16U + I64Load8S + I64Load8U + I64Load16S + I64Load16U + I64Load32S + I64Load32U + I32Store + I64Store + F32Store + F64Store + I32Store8 + I32Store16 + I64Store8 + I64Store16 + I64Store32 + MemorySize + MemoryGrow +) + +// Numeric instructions. +const ( + I32Const Opcode = iota + 0x41 + I64Const + F32Const + F64Const + + I32Eqz + I32Eq + I32Ne + I32LtS + I32LtU + I32GtS + I32GtU + I32LeS + I32LeU + I32GeS + I32GeU + + I64Eqz + I64Eq + I64Ne + I64LtS + I64LtU + I64GtS + I64GtU + I64LeS + I64LeU + I64GeS + I64GeU + + F32Eq + F32Ne + F32Lt + F32Gt + F32Le + F32Ge + + F64Eq + F64Ne + F64Lt + F64Gt + F64Le + F64Ge + + I32Clz + I32Ctz + I32Popcnt + I32Add + I32Sub + I32Mul + I32DivS + I32DivU + I32RemS + I32RemU + I32And + I32Or + I32Xor + I32Shl + I32ShrS + I32ShrU + I32Rotl + I32Rotr + + I64Clz + I64Ctz + I64Popcnt + I64Add + I64Sub + I64Mul + I64DivS + I64DivU + I64RemS + I64RemU + I64And + I64Or + I64Xor + I64Shl + I64ShrS + I64ShrU + I64Rotl + I64Rotr + + F32Abs + F32Neg + F32Ceil + F32Floor + F32Trunc + F32Nearest + F32Sqrt + F32Add + F32Sub + F32Mul + F32Div + F32Min + F32Max + F32Copysign + + F64Abs + F64Neg + F64Ceil + F64Floor + F64Trunc + F64Nearest + F64Sqrt + F64Add + F64Sub + F64Mul + F64Div + F64Min + F64Max + F64Copysign + + I32WrapI64 + I32TruncSF32 + I32TruncUF32 + I32TruncSF64 + I32TruncUF64 + I64ExtendSI32 + I64ExtendUI32 + I64TruncSF32 + I64TruncUF32 + I64TruncSF64 + I64TruncUF64 + F32ConvertSI32 + F32ConvertUI32 + F32ConvertSI64 + F32ConvertUI64 + F32DemoteF64 + F64ConvertSI32 + F64ConvertUI32 + F64ConvertSI64 + F64ConvertUI64 + F64PromoteF32 + I32ReinterpretF32 + I64ReinterpretF64 + F32ReinterpretI32 + F64ReinterpretI64 +) diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/types/types.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/types/types.go new file mode 100644 index 000000000..4e2b77622 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/types/types.go @@ -0,0 +1,36 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types defines the WASM value type constants. +package types + +// ValueType represents an intrinsic value in WASM. +type ValueType int + +// Defines the intrinsic value types. +const ( + I32 ValueType = iota + I64 + F32 + F64 +) + +func (tpe ValueType) String() string { + if tpe == I32 { + return "i32" + } else if tpe == I64 { + return "i64" + } else if tpe == F32 { + return "f32" + } + return "f64" +} + +// ElementType defines the type of table elements. +type ElementType int + +const ( + // Anyfunc is the union of all table types. + Anyfunc ElementType = iota +) diff --git a/vendor/github.com/open-policy-agent/opa/loader/errors.go b/vendor/github.com/open-policy-agent/opa/loader/errors.go new file mode 100644 index 000000000..b2f29c648 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/loader/errors.go @@ -0,0 +1,68 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package loader + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/ast" +) + +// Errors is a wrapper for multiple loader errors. +type Errors []error + +func (e Errors) Error() string { + if len(e) == 0 { + return "no error(s)" + } + if len(e) == 1 { + return "1 error occurred during loading: " + e[0].Error() + } + buf := make([]string, len(e)) + for i := range buf { + buf[i] = e[i].Error() + } + return fmt.Sprintf("%v errors occurred during loading:\n", len(e)) + strings.Join(buf, "\n") +} + +func (e *Errors) add(err error) { + if errs, ok := err.(ast.Errors); ok { + for i := range errs { + *e = append(*e, errs[i]) + } + } else { + *e = append(*e, err) + } +} + +type unsupportedDocumentType string + +func (path unsupportedDocumentType) Error() string { + return string(path) + ": bad document type" +} + +type unrecognizedFile string + +func (path unrecognizedFile) Error() string { + return string(path) + ": can't recognize file type" +} + +func isUnrecognizedFile(err error) bool { + _, ok := err.(unrecognizedFile) + return ok +} + +type mergeError string + +func (e mergeError) Error() string { + return string(e) + ": merge error" +} + +type emptyModuleError string + +func (e emptyModuleError) Error() string { + return string(e) + ": empty policy" +} diff --git a/vendor/github.com/open-policy-agent/opa/loader/loader.go b/vendor/github.com/open-policy-agent/opa/loader/loader.go new file mode 100644 index 000000000..83f6a6592 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/loader/loader.go @@ -0,0 +1,476 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package loader contains utilities for loading files into OPA. +package loader + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/ghodss/yaml" + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/metrics" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/bundle" + fileurl "github.com/open-policy-agent/opa/internal/file/url" + "github.com/open-policy-agent/opa/internal/merge" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/storage/inmem" + "github.com/open-policy-agent/opa/util" +) + +// Result represents the result of successfully loading zero or more files. +type Result struct { + Documents map[string]interface{} + Modules map[string]*RegoFile + path []string +} + +// ParsedModules returns the parsed modules stored on the result. +func (l *Result) ParsedModules() map[string]*ast.Module { + modules := make(map[string]*ast.Module) + for _, module := range l.Modules { + modules[module.Name] = module.Parsed + } + return modules +} + +// Compiler returns a Compiler object with the compiled modules from this loader +// result. +func (l *Result) Compiler() (*ast.Compiler, error) { + compiler := ast.NewCompiler() + compiler.Compile(l.ParsedModules()) + if compiler.Failed() { + return nil, compiler.Errors + } + return compiler, nil +} + +// Store returns a Store object with the documents from this loader result. +func (l *Result) Store() (storage.Store, error) { + return inmem.NewFromObject(l.Documents), nil +} + +// RegoFile represents the result of loading a single Rego source file. +type RegoFile struct { + Name string + Parsed *ast.Module + Raw []byte +} + +// Filter defines the interface for filtering files during loading. If the +// filter returns true, the file should be excluded from the result. +type Filter func(abspath string, info os.FileInfo, depth int) bool + +// GlobExcludeName excludes files and directories whose names do not match the +// shell style pattern at minDepth or greater. +func GlobExcludeName(pattern string, minDepth int) Filter { + return func(abspath string, info os.FileInfo, depth int) bool { + match, _ := filepath.Match(pattern, info.Name()) + return match && depth >= minDepth + } +} + +// FileLoader defines an interface for loading OPA data files +// and Rego policies. +type FileLoader interface { + All(paths []string) (*Result, error) + Filtered(paths []string, filter Filter) (*Result, error) + AsBundle(path string) (*bundle.Bundle, error) + + WithMetrics(m metrics.Metrics) FileLoader +} + +// NewFileLoader returns a new FileLoader instance. +func NewFileLoader() FileLoader { + return &fileLoader{ + metrics: metrics.New(), + } +} + +type fileLoader struct { + metrics metrics.Metrics +} + +// WithMetrics provides the metrics instance to use while loading +func (fl *fileLoader) WithMetrics(m metrics.Metrics) FileLoader { + fl.metrics = m + return fl +} + +// All returns a Result object loaded (recursively) from the specified paths. +func (fl fileLoader) All(paths []string) (*Result, error) { + return fl.Filtered(paths, nil) +} + +// Filtered returns a Result object loaded (recursively) from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +func (fl fileLoader) Filtered(paths []string, filter Filter) (*Result, error) { + return all(paths, filter, func(curr *Result, path string, depth int) error { + + bs, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + result, err := loadKnownTypes(path, bs, fl.metrics) + if err != nil { + if !isUnrecognizedFile(err) { + return err + } + if depth > 0 { + return nil + } + result, err = loadFileForAnyType(path, bs, fl.metrics) + if err != nil { + return err + } + } + + return curr.merge(path, result) + }) +} + +// AsBundle loads a path as a bundle. If it is a single file +// it will be treated as a normal tarball bundle. If a directory +// is supplied it will be loaded as an unzipped bundle tree. +func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, err + } + + fi, err := os.Stat(path) + if err != nil { + return nil, fmt.Errorf("error reading %q: %s", path, err) + } + + var bundleLoader bundle.DirectoryLoader + + if fi.IsDir() { + bundleLoader = bundle.NewDirectoryLoader(path) + } else { + fh, err := os.Open(path) + if err != nil { + return nil, err + } + bundleLoader = bundle.NewTarballLoader(fh) + } + + br := bundle.NewCustomReader(bundleLoader).WithMetrics(fl.metrics) + + // For bundle directories add the full path in front of module file names + // to simplify debugging. + if fi.IsDir() { + br.WithBaseDir(path) + } + + b, err := br.Read() + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("bundle %s", path)) + } + + return &b, err +} + +// All returns a Result object loaded (recursively) from the specified paths. +// Deprecated: Use FileLoader.Filtered() instead. +func All(paths []string) (*Result, error) { + return NewFileLoader().Filtered(paths, nil) +} + +// Filtered returns a Result object loaded (recursively) from the specified +// paths while applying the given filters. If any filter returns true, the +// file/directory is excluded. +// Deprecated: Use FileLoader.Filtered() instead. +func Filtered(paths []string, filter Filter) (*Result, error) { + return NewFileLoader().Filtered(paths, filter) +} + +// AsBundle loads a path as a bundle. If it is a single file +// it will be treated as a normal tarball bundle. If a directory +// is supplied it will be loaded as an unzipped bundle tree. +// Deprecated: Use FileLoader.AsBundle() instead. +func AsBundle(path string) (*bundle.Bundle, error) { + return NewFileLoader().AsBundle(path) +} + +// AllRegos returns a Result object loaded (recursively) with all Rego source +// files from the specified paths. +func AllRegos(paths []string) (*Result, error) { + return NewFileLoader().Filtered(paths, func(_ string, info os.FileInfo, depth int) bool { + return !info.IsDir() && !strings.HasSuffix(info.Name(), bundle.RegoExt) + }) +} + +// Rego returns a RegoFile object loaded from the given path. +func Rego(path string) (*RegoFile, error) { + path, err := fileurl.Clean(path) + if err != nil { + return nil, err + } + bs, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return loadRego(path, bs, metrics.New()) +} + +// CleanPath returns the normalized version of a path that can be used as an identifier. +func CleanPath(path string) string { + return strings.Trim(path, "/") +} + +// Paths returns a sorted list of files contained at path. If recurse is true +// and path is a directory, then Paths will walk the directory structure +// recursively and list files at each level. +func Paths(path string, recurse bool) (paths []string, err error) { + path, err = fileurl.Clean(path) + if err != nil { + return nil, err + } + err = filepath.Walk(path, func(f string, info os.FileInfo, err error) error { + if !recurse { + if path != f && path != filepath.Dir(f) { + return filepath.SkipDir + } + } + paths = append(paths, f) + return nil + }) + return paths, err +} + +// SplitPrefix returns a tuple specifying the document prefix and the file +// path. +func SplitPrefix(path string) ([]string, string) { + // Non-prefixed URLs can be returned without modification and their contents + // can be rooted directly under data. + if strings.Index(path, "://") == strings.Index(path, ":") { + return nil, path + } + parts := strings.SplitN(path, ":", 2) + if len(parts) == 2 && len(parts[0]) > 0 { + return strings.Split(parts[0], "."), parts[1] + } + return nil, path +} + +func (l *Result) merge(path string, result interface{}) error { + switch result := result.(type) { + case bundle.Bundle: + for _, module := range result.Modules { + l.Modules[module.Path] = &RegoFile{ + Name: module.Path, + Parsed: module.Parsed, + Raw: module.Raw, + } + } + return l.mergeDocument(path, result.Data) + case *RegoFile: + l.Modules[CleanPath(path)] = result + return nil + default: + return l.mergeDocument(path, result) + } +} + +func (l *Result) mergeDocument(path string, doc interface{}) error { + obj, ok := makeDir(l.path, doc) + if !ok { + return unsupportedDocumentType(path) + } + merged, ok := merge.InterfaceMaps(l.Documents, obj) + if !ok { + return mergeError(path) + } + for k := range merged { + l.Documents[k] = merged[k] + } + return nil +} + +func (l *Result) withParent(p string) *Result { + path := append(l.path, p) + return &Result{ + Documents: l.Documents, + Modules: l.Modules, + path: path, + } +} + +func newResult() *Result { + return &Result{ + Documents: map[string]interface{}{}, + Modules: map[string]*RegoFile{}, + } +} + +func all(paths []string, filter Filter, f func(*Result, string, int) error) (*Result, error) { + errors := Errors{} + root := newResult() + + for _, path := range paths { + + // Paths can be prefixed with a string that specifies where content should be + // loaded under data. E.g., foo.bar:/path/to/some.json will load the content + // of some.json under {"foo": {"bar": ...}}. + loaded := root + prefix, path := SplitPrefix(path) + if len(prefix) > 0 { + for _, part := range prefix { + loaded = loaded.withParent(part) + } + } + + allRec(path, filter, &errors, loaded, 0, f) + } + + if len(errors) > 0 { + return nil, errors + } + + return root, nil +} + +func allRec(path string, filter Filter, errors *Errors, loaded *Result, depth int, f func(*Result, string, int) error) { + + path, err := fileurl.Clean(path) + if err != nil { + errors.add(err) + return + } + + info, err := os.Stat(path) + if err != nil { + errors.add(err) + return + } + + if filter != nil && filter(path, info, depth) { + return + } + + if !info.IsDir() { + if err := f(loaded, path, depth); err != nil { + errors.add(err) + } + return + } + + // If we are recursing on directories then content must be loaded under path + // specified by directory hierarchy. + if depth > 0 { + loaded = loaded.withParent(info.Name()) + } + + files, err := ioutil.ReadDir(path) + if err != nil { + errors.add(err) + return + } + + for _, file := range files { + allRec(filepath.Join(path, file.Name()), filter, errors, loaded, depth+1, f) + } +} + +func loadKnownTypes(path string, bs []byte, m metrics.Metrics) (interface{}, error) { + switch filepath.Ext(path) { + case ".json": + return loadJSON(path, bs, m) + case ".rego": + return loadRego(path, bs, m) + case ".yaml", ".yml": + return loadYAML(path, bs, m) + default: + if strings.HasSuffix(path, ".tar.gz") { + r, err := loadBundleFile(bs, m) + if err != nil { + err = errors.Wrap(err, fmt.Sprintf("bundle %s", path)) + } + return r, err + } + } + return nil, unrecognizedFile(path) +} + +func loadFileForAnyType(path string, bs []byte, m metrics.Metrics) (interface{}, error) { + module, err := loadRego(path, bs, m) + if err == nil { + return module, nil + } + doc, err := loadJSON(path, bs, m) + if err == nil { + return doc, nil + } + doc, err = loadYAML(path, bs, m) + if err == nil { + return doc, nil + } + return nil, unrecognizedFile(path) +} + +func loadBundleFile(bs []byte, m metrics.Metrics) (bundle.Bundle, error) { + tl := bundle.NewTarballLoader(bytes.NewBuffer(bs)) + br := bundle.NewCustomReader(tl).WithMetrics(m).IncludeManifestInData(true) + return br.Read() +} + +func loadRego(path string, bs []byte, m metrics.Metrics) (*RegoFile, error) { + m.Timer(metrics.RegoModuleParse).Start() + module, err := ast.ParseModule(path, string(bs)) + m.Timer(metrics.RegoModuleParse).Stop() + if err != nil { + return nil, err + } + result := &RegoFile{ + Name: path, + Parsed: module, + Raw: bs, + } + return result, nil +} + +func loadJSON(path string, bs []byte, m metrics.Metrics) (interface{}, error) { + m.Timer(metrics.RegoDataParse).Start() + buf := bytes.NewBuffer(bs) + decoder := util.NewJSONDecoder(buf) + var x interface{} + err := decoder.Decode(&x) + m.Timer(metrics.RegoDataParse).Stop() + if err != nil { + return nil, errors.Wrap(err, path) + } + return x, nil +} + +func loadYAML(path string, bs []byte, m metrics.Metrics) (interface{}, error) { + m.Timer(metrics.RegoDataParse).Start() + bs, err := yaml.YAMLToJSON(bs) + m.Timer(metrics.RegoDataParse).Stop() + if err != nil { + return nil, fmt.Errorf("%v: error converting YAML to JSON: %v", path, err) + } + return loadJSON(path, bs, m) +} + +func makeDir(path []string, x interface{}) (map[string]interface{}, bool) { + if len(path) == 0 { + obj, ok := x.(map[string]interface{}) + if !ok { + return nil, false + } + return obj, true + } + return makeDir(path[:len(path)-1], map[string]interface{}{path[len(path)-1]: x}) +} diff --git a/vendor/github.com/open-policy-agent/opa/metrics/metrics.go b/vendor/github.com/open-policy-agent/opa/metrics/metrics.go new file mode 100644 index 000000000..70677e123 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/metrics/metrics.go @@ -0,0 +1,282 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package metrics contains helpers for performance metric management inside the policy engine. +package metrics + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + go_metrics "github.com/rcrowley/go-metrics" +) + +// Well-known metric names. +const ( + ServerHandler = "server_handler" + ServerQueryCacheHit = "server_query_cache_hit" + RegoQueryCompile = "rego_query_compile" + RegoQueryEval = "rego_query_eval" + RegoQueryParse = "rego_query_parse" + RegoModuleParse = "rego_module_parse" + RegoDataParse = "rego_data_parse" + RegoModuleCompile = "rego_module_compile" + RegoPartialEval = "rego_partial_eval" + RegoInputParse = "rego_input_parse" + RegoLoadFiles = "rego_load_files" + RegoLoadBundles = "rego_load_bundles" +) + +// Info contains attributes describing the underlying metrics provider. +type Info struct { + Name string `json:"name"` // name is a unique human-readable identifier for the provider. +} + +// Metrics defines the interface for a collection of performance metrics in the +// policy engine. +type Metrics interface { + Info() Info + Timer(name string) Timer + Histogram(name string) Histogram + Counter(name string) Counter + All() map[string]interface{} + Clear() + json.Marshaler +} + +type metrics struct { + mtx sync.Mutex + timers map[string]Timer + histograms map[string]Histogram + counters map[string]Counter +} + +// New returns a new Metrics object. +func New() Metrics { + m := &metrics{} + m.Clear() + return m +} + +type metric struct { + Key string + Value interface{} +} + +func (m *metrics) Info() Info { + return Info{ + Name: "", + } +} + +func (m *metrics) String() string { + + all := m.All() + sorted := make([]metric, 0, len(all)) + + for key, value := range all { + sorted = append(sorted, metric{ + Key: key, + Value: value, + }) + } + + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Key < sorted[j].Key + }) + + buf := make([]string, len(sorted)) + for i := range sorted { + buf[i] = fmt.Sprintf("%v:%v", sorted[i].Key, sorted[i].Value) + } + + return strings.Join(buf, " ") +} + +func (m *metrics) MarshalJSON() ([]byte, error) { + return json.Marshal(m.All()) +} + +func (m *metrics) Timer(name string) Timer { + m.mtx.Lock() + defer m.mtx.Unlock() + t, ok := m.timers[name] + if !ok { + t = &timer{} + m.timers[name] = t + } + return t +} + +func (m *metrics) Histogram(name string) Histogram { + m.mtx.Lock() + defer m.mtx.Unlock() + h, ok := m.histograms[name] + if !ok { + h = newHistogram() + m.histograms[name] = h + } + return h +} + +func (m *metrics) Counter(name string) Counter { + m.mtx.Lock() + defer m.mtx.Unlock() + c, ok := m.counters[name] + if !ok { + zero := counter{} + c = &zero + m.counters[name] = c + } + return c +} + +func (m *metrics) All() map[string]interface{} { + m.mtx.Lock() + defer m.mtx.Unlock() + result := map[string]interface{}{} + for name, timer := range m.timers { + result[m.formatKey(name, timer)] = timer.Value() + } + for name, hist := range m.histograms { + result[m.formatKey(name, hist)] = hist.Value() + } + for name, cntr := range m.counters { + result[m.formatKey(name, cntr)] = cntr.Value() + } + return result +} + +func (m *metrics) Clear() { + m.mtx.Lock() + defer m.mtx.Unlock() + m.timers = map[string]Timer{} + m.histograms = map[string]Histogram{} + m.counters = map[string]Counter{} +} + +func (m *metrics) formatKey(name string, metrics interface{}) string { + switch metrics.(type) { + case Timer: + return "timer_" + name + "_ns" + case Histogram: + return "histogram_" + name + case Counter: + return "counter_" + name + default: + return name + } +} + +// Timer defines the interface for a restartable timer that accumulates elapsed +// time. +type Timer interface { + Value() interface{} + Int64() int64 + Start() + Stop() int64 +} + +type timer struct { + mtx sync.Mutex + start time.Time + value int64 +} + +func (t *timer) Start() { + t.mtx.Lock() + defer t.mtx.Unlock() + t.start = time.Now() +} + +func (t *timer) Stop() int64 { + t.mtx.Lock() + defer t.mtx.Unlock() + delta := time.Now().Sub(t.start).Nanoseconds() + t.value += delta + return delta +} + +func (t *timer) Value() interface{} { + return t.Int64() +} + +func (t *timer) Int64() int64 { + t.mtx.Lock() + defer t.mtx.Unlock() + return t.value +} + +// Histogram defines the interface for a histogram with hardcoded percentiles. +type Histogram interface { + Value() interface{} + Update(int64) +} + +type histogram struct { + hist go_metrics.Histogram // is thread-safe because of the underlying ExpDecaySample +} + +func newHistogram() Histogram { + // NOTE(tsandall): the reservoir size and alpha factor are taken from + // https://github.com/rcrowley/go-metrics. They may need to be tweaked in + // the future. + sample := go_metrics.NewExpDecaySample(1028, 0.015) + hist := go_metrics.NewHistogram(sample) + return &histogram{hist} +} + +func (h *histogram) Update(v int64) { + h.hist.Update(v) +} + +func (h *histogram) Value() interface{} { + values := map[string]interface{}{} + snap := h.hist.Snapshot() + percentiles := snap.Percentiles([]float64{ + 0.5, + 0.75, + 0.9, + 0.95, + 0.99, + 0.999, + 0.9999, + }) + values["count"] = snap.Count() + values["min"] = snap.Min() + values["max"] = snap.Max() + values["mean"] = snap.Mean() + values["stddev"] = snap.StdDev() + values["median"] = percentiles[0] + values["75%"] = percentiles[1] + values["90%"] = percentiles[2] + values["95%"] = percentiles[3] + values["99%"] = percentiles[4] + values["99.9%"] = percentiles[5] + values["99.99%"] = percentiles[6] + return values +} + +// Counter defines the interface for a monotonic increasing counter. +type Counter interface { + Value() interface{} + Incr() +} + +type counter struct { + c uint64 +} + +func (c *counter) Incr() { + atomic.AddUint64(&c.c, 1) +} + +func (c *counter) Value() interface{} { + return atomic.LoadUint64(&c.c) +} diff --git a/vendor/github.com/open-policy-agent/opa/rego/rego.go b/vendor/github.com/open-policy-agent/opa/rego/rego.go new file mode 100644 index 000000000..703a1524e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/rego/rego.go @@ -0,0 +1,1997 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package rego exposes high level APIs for evaluating Rego policies. +package rego + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "strings" + + "github.com/open-policy-agent/opa/loader" + "github.com/open-policy-agent/opa/types" + + "github.com/open-policy-agent/opa/bundle" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/internal/compiler/wasm" + "github.com/open-policy-agent/opa/internal/ir" + "github.com/open-policy-agent/opa/internal/planner" + "github.com/open-policy-agent/opa/internal/wasm/encoding" + "github.com/open-policy-agent/opa/metrics" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/storage/inmem" + "github.com/open-policy-agent/opa/topdown" + "github.com/open-policy-agent/opa/util" +) + +const defaultPartialNamespace = "partial" + +// CompileResult represents the result of compiling a Rego query, zero or more +// Rego modules, and arbitrary contextual data into an executable. +type CompileResult struct { + Bytes []byte `json:"bytes"` +} + +// PartialQueries contains the queries and support modules produced by partial +// evaluation. +type PartialQueries struct { + Queries []ast.Body `json:"queries,omitempty"` + Support []*ast.Module `json:"modules,omitempty"` +} + +// PartialResult represents the result of partial evaluation. The result can be +// used to generate a new query that can be run when inputs are known. +type PartialResult struct { + compiler *ast.Compiler + store storage.Store + body ast.Body + builtinDecls map[string]*ast.Builtin + builtinFuncs map[string]*topdown.Builtin +} + +// Rego returns an object that can be evaluated to produce a query result. +func (pr PartialResult) Rego(options ...func(*Rego)) *Rego { + options = append(options, Compiler(pr.compiler), Store(pr.store), ParsedQuery(pr.body)) + r := New(options...) + + // Propagate any custom builtins. + for k, v := range pr.builtinDecls { + r.builtinDecls[k] = v + } + for k, v := range pr.builtinFuncs { + r.builtinFuncs[k] = v + } + return r +} + +// preparedQuery is a wrapper around a Rego object which has pre-processed +// state stored on it. Once prepared there are a more limited number of actions +// that can be taken with it. It will, however, be able to evaluate faster since +// it will not have to re-parse or compile as much. +type preparedQuery struct { + r *Rego + cfg *PrepareConfig +} + +// EvalContext defines the set of options allowed to be set at evaluation +// time. Any other options will need to be set on a new Rego object. +type EvalContext struct { + hasInput bool + rawInput *interface{} + parsedInput ast.Value + metrics metrics.Metrics + txn storage.Transaction + instrument bool + instrumentation *topdown.Instrumentation + partialNamespace string + tracers []topdown.Tracer + compiledQuery compiledQuery + unknowns []string + disableInlining []ast.Ref + parsedUnknowns []*ast.Term + indexing bool +} + +// EvalOption defines a function to set an option on an EvalConfig +type EvalOption func(*EvalContext) + +// EvalInput configures the input for a Prepared Query's evaluation +func EvalInput(input interface{}) EvalOption { + return func(e *EvalContext) { + e.rawInput = &input + e.hasInput = true + } +} + +// EvalParsedInput configures the input for a Prepared Query's evaluation +func EvalParsedInput(input ast.Value) EvalOption { + return func(e *EvalContext) { + e.parsedInput = input + e.hasInput = true + } +} + +// EvalMetrics configures the metrics for a Prepared Query's evaluation +func EvalMetrics(metric metrics.Metrics) EvalOption { + return func(e *EvalContext) { + e.metrics = metric + } +} + +// EvalTransaction configures the Transaction for a Prepared Query's evaluation +func EvalTransaction(txn storage.Transaction) EvalOption { + return func(e *EvalContext) { + e.txn = txn + } +} + +// EvalInstrument enables or disables instrumenting for a Prepared Query's evaluation +func EvalInstrument(instrument bool) EvalOption { + return func(e *EvalContext) { + e.instrument = instrument + } +} + +// EvalTracer configures a tracer for a Prepared Query's evaluation +func EvalTracer(tracer topdown.Tracer) EvalOption { + return func(e *EvalContext) { + if tracer != nil { + e.tracers = append(e.tracers, tracer) + } + } +} + +// EvalPartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func EvalPartialNamespace(ns string) EvalOption { + return func(e *EvalContext) { + e.partialNamespace = ns + } +} + +// EvalUnknowns returns an argument that sets the values to treat as +// unknown during partial evaluation. +func EvalUnknowns(unknowns []string) EvalOption { + return func(e *EvalContext) { + e.unknowns = unknowns + } +} + +// EvalDisableInlining returns an argument that adds a set of paths to exclude from +// partial evaluation inlining. +func EvalDisableInlining(paths []ast.Ref) EvalOption { + return func(e *EvalContext) { + e.disableInlining = paths + } +} + +// EvalParsedUnknowns returns an argument that sets the values to treat +// as unknown during partial evaluation. +func EvalParsedUnknowns(unknowns []*ast.Term) EvalOption { + return func(e *EvalContext) { + e.parsedUnknowns = unknowns + } +} + +// EvalRuleIndexing will disable indexing optimizations for the +// evaluation. This should only be used when tracing in debug mode. +func EvalRuleIndexing(enabled bool) EvalOption { + return func(e *EvalContext) { + e.indexing = enabled + } +} + +func (pq preparedQuery) Modules() map[string]*ast.Module { + mods := make(map[string]*ast.Module) + + for name, mod := range pq.r.parsedModules { + mods[name] = mod + } + + for path, b := range pq.r.bundles { + for name, mod := range b.ParsedModules(path) { + mods[name] = mod + } + } + + return mods +} + +// newEvalContext creates a new EvalContext overlaying any EvalOptions over top +// the Rego object on the preparedQuery. The returned function should be called +// once the evaluation is complete to close any transactions that might have +// been opened. +func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption) (*EvalContext, func(context.Context), error) { + ectx := &EvalContext{ + hasInput: false, + rawInput: nil, + parsedInput: nil, + metrics: nil, + txn: nil, + instrument: false, + instrumentation: nil, + partialNamespace: pq.r.partialNamespace, + tracers: nil, + unknowns: pq.r.unknowns, + parsedUnknowns: pq.r.parsedUnknowns, + compiledQuery: compiledQuery{}, + indexing: true, + } + + for _, o := range options { + o(ectx) + } + + if ectx.metrics == nil { + ectx.metrics = metrics.New() + } + + if ectx.instrument { + ectx.instrumentation = topdown.NewInstrumentation(ectx.metrics) + } + + // Default to an empty "finish" function + finishFunc := func(context.Context) {} + + var err error + ectx.disableInlining, err = parseStringsToRefs(pq.r.disableInlining) + if err != nil { + return nil, finishFunc, err + } + + if ectx.txn == nil { + ectx.txn, err = pq.r.store.NewTransaction(ctx) + if err != nil { + return nil, finishFunc, err + } + finishFunc = func(ctx context.Context) { + pq.r.store.Abort(ctx, ectx.txn) + } + } + + // If we didn't get an input specified in the Eval options + // then fall back to the Rego object's input fields. + if !ectx.hasInput { + ectx.rawInput = pq.r.rawInput + ectx.parsedInput = pq.r.parsedInput + } + + if ectx.parsedInput == nil { + if ectx.rawInput == nil { + // Fall back to the original Rego objects input if none was specified + // Note that it could still be nil + ectx.rawInput = pq.r.rawInput + } + ectx.parsedInput, err = pq.r.parseRawInput(ectx.rawInput, ectx.metrics) + if err != nil { + return nil, finishFunc, err + } + } + + return ectx, finishFunc, nil +} + +// PreparedEvalQuery holds the prepared Rego state that has been pre-processed +// for subsequent evaluations. +type PreparedEvalQuery struct { + preparedQuery +} + +// Eval evaluates this PartialResult's Rego object with additional eval options +// and returns a ResultSet. +// If options are provided they will override the original Rego options respective value. +// The original Rego object transaction will *not* be re-used. A new transaction will be opened +// if one is not provided with an EvalOption. +func (pq PreparedEvalQuery) Eval(ctx context.Context, options ...EvalOption) (ResultSet, error) { + ectx, finish, err := pq.newEvalContext(ctx, options) + if err != nil { + return nil, err + } + defer finish(ctx) + + ectx.compiledQuery = pq.r.compiledQueries[evalQueryType] + + return pq.r.eval(ctx, ectx) +} + +// PreparedPartialQuery holds the prepared Rego state that has been pre-processed +// for partial evaluations. +type PreparedPartialQuery struct { + preparedQuery +} + +// Partial runs partial evaluation on the prepared query and returns the result. +// The original Rego object transaction will *not* be re-used. A new transaction will be opened +// if one is not provided with an EvalOption. +func (pq PreparedPartialQuery) Partial(ctx context.Context, options ...EvalOption) (*PartialQueries, error) { + ectx, finish, err := pq.newEvalContext(ctx, options) + if err != nil { + return nil, err + } + defer finish(ctx) + + ectx.compiledQuery = pq.r.compiledQueries[partialQueryType] + + return pq.r.partial(ctx, ectx) +} + +// Result defines the output of Rego evaluation. +type Result struct { + Expressions []*ExpressionValue `json:"expressions"` + Bindings Vars `json:"bindings,omitempty"` +} + +func newResult() Result { + return Result{ + Bindings: Vars{}, + } +} + +// Location defines a position in a Rego query or module. +type Location struct { + Row int `json:"row"` + Col int `json:"col"` +} + +// ExpressionValue defines the value of an expression in a Rego query. +type ExpressionValue struct { + Value interface{} `json:"value"` + Text string `json:"text"` + Location *Location `json:"location"` +} + +func newExpressionValue(expr *ast.Expr, value interface{}) *ExpressionValue { + result := &ExpressionValue{ + Value: value, + } + if expr.Location != nil { + result.Text = string(expr.Location.Text) + result.Location = &Location{ + Row: expr.Location.Row, + Col: expr.Location.Col, + } + } + return result +} + +func (ev *ExpressionValue) String() string { + return fmt.Sprint(ev.Value) +} + +// ResultSet represents a collection of output from Rego evaluation. An empty +// result set represents an undefined query. +type ResultSet []Result + +// Vars represents a collection of variable bindings. The keys are the variable +// names and the values are the binding values. +type Vars map[string]interface{} + +// WithoutWildcards returns a copy of v with wildcard variables removed. +func (v Vars) WithoutWildcards() Vars { + n := Vars{} + for k, v := range v { + if ast.Var(k).IsWildcard() || ast.Var(k).IsGenerated() { + continue + } + n[k] = v + } + return n +} + +// Errors represents a collection of errors returned when evaluating Rego. +type Errors []error + +func (errs Errors) Error() string { + if len(errs) == 0 { + return "no error" + } + if len(errs) == 1 { + return fmt.Sprintf("1 error occurred: %v", errs[0].Error()) + } + buf := []string{fmt.Sprintf("%v errors occurred", len(errs))} + for _, err := range errs { + buf = append(buf, err.Error()) + } + return strings.Join(buf, "\n") +} + +type compiledQuery struct { + query ast.Body + compiler ast.QueryCompiler +} + +type queryType int + +// Define a query type for each of the top level Rego +// API's that compile queries differently. +const ( + evalQueryType queryType = iota + partialResultQueryType queryType = iota + partialQueryType queryType = iota + compileQueryType queryType = iota +) + +type loadPaths struct { + paths []string + filter loader.Filter +} + +// Rego constructs a query and can be evaluated to obtain results. +type Rego struct { + query string + parsedQuery ast.Body + compiledQueries map[queryType]compiledQuery + pkg string + parsedPackage *ast.Package + imports []string + parsedImports []*ast.Import + rawInput *interface{} + parsedInput ast.Value + unknowns []string + parsedUnknowns []*ast.Term + disableInlining []string + partialNamespace string + modules []rawModule + parsedModules map[string]*ast.Module + compiler *ast.Compiler + store storage.Store + ownStore bool + txn storage.Transaction + metrics metrics.Metrics + tracers []topdown.Tracer + tracebuf *topdown.BufferTracer + trace bool + instrumentation *topdown.Instrumentation + instrument bool + capture map[*ast.Expr]ast.Var // map exprs to generated capture vars + termVarID int + dump io.Writer + runtime *ast.Term + builtinDecls map[string]*ast.Builtin + builtinFuncs map[string]*topdown.Builtin + unsafeBuiltins map[string]struct{} + loadPaths loadPaths + bundlePaths []string + bundles map[string]*bundle.Bundle +} + +// Function represents a built-in function that is callable in Rego. +type Function struct { + Name string + Decl *types.Function + Memoize bool +} + +// BuiltinContext contains additional attributes from the evaluator that +// built-in functions can use, e.g., the request context.Context, caches, etc. +type BuiltinContext = topdown.BuiltinContext + +type ( + // Builtin1 defines a built-in function that accepts 1 argument. + Builtin1 func(bctx BuiltinContext, op1 *ast.Term) (*ast.Term, error) + + // Builtin2 defines a built-in function that accepts 2 arguments. + Builtin2 func(bctx BuiltinContext, op1, op2 *ast.Term) (*ast.Term, error) + + // Builtin3 defines a built-in function that accepts 3 argument. + Builtin3 func(bctx BuiltinContext, op1, op2, op3 *ast.Term) (*ast.Term, error) + + // Builtin4 defines a built-in function that accepts 4 argument. + Builtin4 func(bctx BuiltinContext, op1, op2, op3, op4 *ast.Term) (*ast.Term, error) + + // BuiltinDyn defines a built-in function that accepts a list of arguments. + BuiltinDyn func(bctx BuiltinContext, terms []*ast.Term) (*ast.Term, error) +) + +// Function1 returns an option that adds a built-in function to the Rego object. +func Function1(decl *Function, f Builtin1) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function2 returns an option that adds a built-in function to the Rego object. +func Function2(decl *Function, f Builtin2) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function3 returns an option that adds a built-in function to the Rego object. +func Function3(decl *Function, f Builtin3) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// Function4 returns an option that adds a built-in function to the Rego object. +func Function4(decl *Function, f Builtin4) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms[0], terms[1], terms[2], terms[3]) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// FunctionDyn returns an option that adds a built-in function to the Rego object. +func FunctionDyn(decl *Function, f BuiltinDyn) func(*Rego) { + return newFunction(decl, func(bctx BuiltinContext, terms []*ast.Term, iter func(*ast.Term) error) error { + result, err := memoize(decl, bctx, terms, func() (*ast.Term, error) { return f(bctx, terms) }) + return finishFunction(decl.Name, bctx, result, err, iter) + }) +} + +// FunctionDecl returns an option that adds a custom-built-in function +// __declaration__. NO implementation is provided. This is used for +// non-interpreter execution envs (e.g., Wasm). +func FunctionDecl(decl *Function) func(*Rego) { + return newDecl(decl) +} + +func newDecl(decl *Function) func(*Rego) { + return func(r *Rego) { + r.builtinDecls[decl.Name] = &ast.Builtin{ + Name: decl.Name, + Decl: decl.Decl, + } + } +} + +type memo struct { + term *ast.Term + err error +} + +type memokey string + +func memoize(decl *Function, bctx BuiltinContext, terms []*ast.Term, ifEmpty func() (*ast.Term, error)) (*ast.Term, error) { + + if !decl.Memoize { + return ifEmpty() + } + + // NOTE(tsandall): we assume memoization is applied to infrequent built-in + // calls that do things like fetch data from remote locations. As such, + // converting the terms to strings is acceptable for now. + var b strings.Builder + if _, err := b.WriteString(decl.Name); err != nil { + return nil, err + } + + // The term slice _may_ include an output term depending on how the caller + // referred to the built-in function. Only use the arguments as the cache + // key. Unification ensures we don't get false positive matches. + for i := 0; i < len(decl.Decl.Args()); i++ { + if _, err := b.WriteString(terms[i].String()); err != nil { + return nil, err + } + } + + key := memokey(b.String()) + hit, ok := bctx.Cache.Get(key) + var m memo + if ok { + m = hit.(memo) + } else { + m.term, m.err = ifEmpty() + bctx.Cache.Put(key, m) + } + + return m.term, m.err +} + +// Dump returns an argument that sets the writer to dump debugging information to. +func Dump(w io.Writer) func(r *Rego) { + return func(r *Rego) { + r.dump = w + } +} + +// Query returns an argument that sets the Rego query. +func Query(q string) func(r *Rego) { + return func(r *Rego) { + r.query = q + } +} + +// ParsedQuery returns an argument that sets the Rego query. +func ParsedQuery(q ast.Body) func(r *Rego) { + return func(r *Rego) { + r.parsedQuery = q + } +} + +// Package returns an argument that sets the Rego package on the query's +// context. +func Package(p string) func(r *Rego) { + return func(r *Rego) { + r.pkg = p + } +} + +// ParsedPackage returns an argument that sets the Rego package on the query's +// context. +func ParsedPackage(pkg *ast.Package) func(r *Rego) { + return func(r *Rego) { + r.parsedPackage = pkg + } +} + +// Imports returns an argument that adds a Rego import to the query's context. +func Imports(p []string) func(r *Rego) { + return func(r *Rego) { + r.imports = append(r.imports, p...) + } +} + +// ParsedImports returns an argument that adds Rego imports to the query's +// context. +func ParsedImports(imp []*ast.Import) func(r *Rego) { + return func(r *Rego) { + r.parsedImports = append(r.parsedImports, imp...) + } +} + +// Input returns an argument that sets the Rego input document. Input should be +// a native Go value representing the input document. +func Input(x interface{}) func(r *Rego) { + return func(r *Rego) { + r.rawInput = &x + } +} + +// ParsedInput returns an argument that sets the Rego input document. +func ParsedInput(x ast.Value) func(r *Rego) { + return func(r *Rego) { + r.parsedInput = x + } +} + +// Unknowns returns an argument that sets the values to treat as unknown during +// partial evaluation. +func Unknowns(unknowns []string) func(r *Rego) { + return func(r *Rego) { + r.unknowns = unknowns + } +} + +// ParsedUnknowns returns an argument that sets the values to treat as unknown +// during partial evaluation. +func ParsedUnknowns(unknowns []*ast.Term) func(r *Rego) { + return func(r *Rego) { + r.parsedUnknowns = unknowns + } +} + +// DisableInlining adds a set of paths to exclude from partial evaluation inlining. +func DisableInlining(paths []string) func(r *Rego) { + return func(r *Rego) { + r.disableInlining = paths + } +} + +// PartialNamespace returns an argument that sets the namespace to use for +// partial evaluation results. The namespace must be a valid package path +// component. +func PartialNamespace(ns string) func(r *Rego) { + return func(r *Rego) { + r.partialNamespace = ns + } +} + +// Module returns an argument that adds a Rego module. +func Module(filename, input string) func(r *Rego) { + return func(r *Rego) { + r.modules = append(r.modules, rawModule{ + filename: filename, + module: input, + }) + } +} + +// ParsedModule returns an argument that adds a parsed Rego module. If a string +// module with the same filename name is added, it will override the parsed +// module. +func ParsedModule(module *ast.Module) func(*Rego) { + return func(r *Rego) { + var filename string + if module.Package.Location != nil { + filename = module.Package.Location.File + } else { + filename = fmt.Sprintf("module_%p.rego", module) + } + r.parsedModules[filename] = module + } +} + +// Load returns an argument that adds a filesystem path to load data +// and Rego modules from. Any file with a *.rego, *.yaml, or *.json +// extension will be loaded. The path can be either a directory or file, +// directories are loaded recursively. The optional ignore string patterns +// can be used to filter which files are used. +// The Load option can only be used once. +// Note: Loading files will require a write transaction on the store. +func Load(paths []string, filter loader.Filter) func(r *Rego) { + return func(r *Rego) { + r.loadPaths = loadPaths{paths, filter} + } +} + +// LoadBundle returns an argument that adds a filesystem path to load +// a bundle from. The path can be a compressed bundle file or a directory +// to be loaded as a bundle. +// Note: Loading bundles will require a write transaction on the store. +func LoadBundle(path string) func(r *Rego) { + return func(r *Rego) { + r.bundlePaths = append(r.bundlePaths, path) + } +} + +// ParsedBundle returns an argument that adds a bundle to be loaded. +func ParsedBundle(name string, b *bundle.Bundle) func(r *Rego) { + return func(r *Rego) { + r.bundles[name] = b + } +} + +// Compiler returns an argument that sets the Rego compiler. +func Compiler(c *ast.Compiler) func(r *Rego) { + return func(r *Rego) { + r.compiler = c + } +} + +// Store returns an argument that sets the policy engine's data storage layer. +// +// If using the Load, LoadBundle, or ParsedBundle options then a transaction +// must also be provided via the Transaction() option. After loading files +// or bundles the transaction should be aborted or committed. +func Store(s storage.Store) func(r *Rego) { + return func(r *Rego) { + r.store = s + } +} + +// Transaction returns an argument that sets the transaction to use for storage +// layer operations. +// +// Requires the store associated with the transaction to be provided via the +// Store() option. If using Load(), LoadBundle(), or ParsedBundle() options +// the transaction will likely require write params. +func Transaction(txn storage.Transaction) func(r *Rego) { + return func(r *Rego) { + r.txn = txn + } +} + +// Metrics returns an argument that sets the metrics collection. +func Metrics(m metrics.Metrics) func(r *Rego) { + return func(r *Rego) { + r.metrics = m + } +} + +// Instrument returns an argument that enables instrumentation for diagnosing +// performance issues. +func Instrument(yes bool) func(r *Rego) { + return func(r *Rego) { + r.instrument = yes + } +} + +// Trace returns an argument that enables tracing on r. +func Trace(yes bool) func(r *Rego) { + return func(r *Rego) { + r.trace = yes + } +} + +// Tracer returns an argument that adds a query tracer to r. +func Tracer(t topdown.Tracer) func(r *Rego) { + return func(r *Rego) { + if t != nil { + r.tracers = append(r.tracers, t) + } + } +} + +// Runtime returns an argument that sets the runtime data to provide to the +// evaluation engine. +func Runtime(term *ast.Term) func(r *Rego) { + return func(r *Rego) { + r.runtime = term + } +} + +// PrintTrace is a helper function to write a human-readable version of the +// trace to the writer w. +func PrintTrace(w io.Writer, r *Rego) { + if r == nil || r.tracebuf == nil { + return + } + topdown.PrettyTrace(w, *r.tracebuf) +} + +// UnsafeBuiltins sets the built-in functions to treat as unsafe and not allow. +// This option is ignored for module compilation if the caller supplies the +// compiler. This option is always honored for query compilation. Provide an +// empty (non-nil) map to disable checks on queries. +func UnsafeBuiltins(unsafeBuiltins map[string]struct{}) func(r *Rego) { + return func(r *Rego) { + r.unsafeBuiltins = unsafeBuiltins + } +} + +// New returns a new Rego object. +func New(options ...func(r *Rego)) *Rego { + + r := &Rego{ + parsedModules: map[string]*ast.Module{}, + capture: map[*ast.Expr]ast.Var{}, + compiledQueries: map[queryType]compiledQuery{}, + builtinDecls: map[string]*ast.Builtin{}, + builtinFuncs: map[string]*topdown.Builtin{}, + bundles: map[string]*bundle.Bundle{}, + } + + for _, option := range options { + option(r) + } + + if r.compiler == nil { + r.compiler = ast.NewCompiler(). + WithUnsafeBuiltins(r.unsafeBuiltins). + WithBuiltins(r.builtinDecls) + } + + if r.store == nil { + r.store = inmem.New() + r.ownStore = true + } else { + r.ownStore = false + } + + if r.metrics == nil { + r.metrics = metrics.New() + } + + if r.instrument { + r.instrumentation = topdown.NewInstrumentation(r.metrics) + r.compiler.WithMetrics(r.metrics) + } + + if r.trace { + r.tracebuf = topdown.NewBufferTracer() + r.tracers = append(r.tracers, r.tracebuf) + } + + if r.partialNamespace == "" { + r.partialNamespace = defaultPartialNamespace + } + + return r +} + +// Eval evaluates this Rego object and returns a ResultSet. +func (r *Rego) Eval(ctx context.Context) (ResultSet, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + pq, err := r.PrepareForEval(ctx) + if err != nil { + txnClose(ctx, err) // Ignore error + return nil, err + } + + evalArgs := []EvalOption{ + EvalTransaction(r.txn), + EvalMetrics(r.metrics), + EvalInstrument(r.instrument), + } + + for _, t := range r.tracers { + evalArgs = append(evalArgs, EvalTracer(t)) + } + + rs, err := pq.Eval(ctx, evalArgs...) + txnErr := txnClose(ctx, err) // Always call closer + if err == nil { + err = txnErr + } + return rs, err +} + +// PartialEval has been deprecated and renamed to PartialResult. +func (r *Rego) PartialEval(ctx context.Context) (PartialResult, error) { + return r.PartialResult(ctx) +} + +// PartialResult partially evaluates this Rego object and returns a PartialResult. +func (r *Rego) PartialResult(ctx context.Context) (PartialResult, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PartialResult{}, err + } + + pq, err := r.PrepareForEval(ctx, WithPartialEval()) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return PartialResult{}, err + } + if txnErr != nil { + return PartialResult{}, txnErr + } + + pr := PartialResult{ + compiler: pq.r.compiler, + store: pq.r.store, + body: pq.r.parsedQuery, + builtinDecls: pq.r.builtinDecls, + builtinFuncs: pq.r.builtinFuncs, + } + + return pr, nil +} + +// Partial runs partial evaluation on r and returns the result. +func (r *Rego) Partial(ctx context.Context) (*PartialQueries, error) { + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + pq, err := r.PrepareForPartial(ctx) + if err != nil { + txnClose(ctx, err) // Ignore error + return nil, err + } + + evalArgs := []EvalOption{ + EvalTransaction(r.txn), + EvalMetrics(r.metrics), + EvalInstrument(r.instrument), + } + + for _, t := range r.tracers { + evalArgs = append(evalArgs, EvalTracer(t)) + } + + pqs, err := pq.Partial(ctx, evalArgs...) + txnErr := txnClose(ctx, err) // Always call closer + if err == nil { + err = txnErr + } + return pqs, err +} + +// CompileOption defines a function to set options on Compile calls. +type CompileOption func(*CompileContext) + +// CompileContext contains options for Compile calls. +type CompileContext struct { + partial bool +} + +// CompilePartial defines an option to control whether partial evaluation is run +// before the query is planned and compiled. +func CompilePartial(yes bool) CompileOption { + return func(cfg *CompileContext) { + cfg.partial = yes + } +} + +// Compile returns a compiled policy query. +func (r *Rego) Compile(ctx context.Context, opts ...CompileOption) (*CompileResult, error) { + + var cfg CompileContext + + for _, opt := range opts { + opt(&cfg) + } + + var queries []ast.Body + var modules []*ast.Module + + if cfg.partial { + + pq, err := r.Partial(ctx) + if err != nil { + return nil, err + } + if r.dump != nil { + if len(pq.Queries) != 0 { + msg := fmt.Sprintf("QUERIES (%d total):", len(pq.Queries)) + fmt.Fprintln(r.dump, msg) + fmt.Fprintln(r.dump, strings.Repeat("-", len(msg))) + for i := range pq.Queries { + fmt.Println(pq.Queries[i]) + } + fmt.Fprintln(r.dump) + } + if len(pq.Support) != 0 { + msg := fmt.Sprintf("SUPPORT (%d total):", len(pq.Support)) + fmt.Fprintln(r.dump, msg) + fmt.Fprintln(r.dump, strings.Repeat("-", len(msg))) + for i := range pq.Support { + fmt.Println(pq.Support[i]) + } + fmt.Fprintln(r.dump) + } + } + + queries = pq.Queries + modules = pq.Support + + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + } else { + var err error + // If creating a new transacation it should be closed before calling the + // planner to avoid holding open the transaction longer than needed. + // + // TODO(tsandall): in future, planner could make use of store, in which + // case this will need to change. + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return nil, err + } + + err = r.prepare(ctx, compileQueryType, nil) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return nil, err + } + if txnErr != nil { + return nil, err + } + + for _, module := range r.compiler.Modules { + modules = append(modules, module) + } + + queries = []ast.Body{r.compiledQueries[compileQueryType].query} + } + + decls := make(map[string]*ast.Builtin, len(r.builtinDecls)+len(ast.BuiltinMap)) + + for k, v := range ast.BuiltinMap { + decls[k] = v + } + + for k, v := range r.builtinDecls { + decls[k] = v + } + + policy, err := planner.New(). + WithQueries(queries). + WithModules(modules). + WithRewrittenVars(r.compiledQueries[compileQueryType].compiler.RewrittenVars()). + WithBuiltinDecls(decls). + Plan() + if err != nil { + return nil, err + } + + if r.dump != nil { + fmt.Fprintln(r.dump, "PLAN:") + fmt.Fprintln(r.dump, "-----") + ir.Pretty(r.dump, policy) + fmt.Fprintln(r.dump) + } + + m, err := wasm.New().WithPolicy(policy).Compile() + if err != nil { + return nil, err + } + + var out bytes.Buffer + + if err := encoding.WriteModule(&out, m); err != nil { + return nil, err + } + + result := &CompileResult{ + Bytes: out.Bytes(), + } + + return result, nil +} + +// PrepareOption defines a function to set an option to control +// the behavior of the Prepare call. +type PrepareOption func(*PrepareConfig) + +// PrepareConfig holds settings to control the behavior of the +// Prepare call. +type PrepareConfig struct { + doPartialEval bool + disableInlining *[]string +} + +// WithPartialEval configures an option for PrepareForEval +// which will have it perform partial evaluation while preparing +// the query (similar to rego.Rego#PartialResult) +func WithPartialEval() PrepareOption { + return func(p *PrepareConfig) { + p.doPartialEval = true + } +} + +// WithNoInline adds a set of paths to exclude from partial evaluation inlining. +func WithNoInline(paths []string) PrepareOption { + return func(p *PrepareConfig) { + p.disableInlining = &paths + } +} + +// PrepareForEval will parse inputs, modules, and query arguments in preparation +// of evaluating them. +func (r *Rego) PrepareForEval(ctx context.Context, opts ...PrepareOption) (PreparedEvalQuery, error) { + if !r.hasQuery() { + return PreparedEvalQuery{}, fmt.Errorf("cannot evaluate empty query") + } + + pCfg := &PrepareConfig{} + for _, o := range opts { + o(pCfg) + } + + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PreparedEvalQuery{}, err + } + + // If the caller wanted to do partial evaluation as part of preparation + // do it now and use the new Rego object. + if pCfg.doPartialEval { + + pr, err := r.partialResult(ctx, pCfg) + if err != nil { + txnClose(ctx, err) // Ignore error + return PreparedEvalQuery{}, err + } + + // Prepare the new query using the result of partial evaluation + pq, err := pr.Rego(Transaction(r.txn)).PrepareForEval(ctx) + txnErr := txnClose(ctx, err) + if err != nil { + return pq, err + } + return pq, txnErr + } + + err = r.prepare(ctx, evalQueryType, []extraStage{ + { + after: "ResolveRefs", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteToCaptureValue", + MetricName: "query_compile_stage_rewrite_to_capture_value", + Stage: r.rewriteQueryToCaptureValue, + }, + }, + }) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return PreparedEvalQuery{}, err + } + if txnErr != nil { + return PreparedEvalQuery{}, txnErr + } + + return PreparedEvalQuery{preparedQuery{r, pCfg}}, err +} + +// PrepareForPartial will parse inputs, modules, and query arguments in preparation +// of partially evaluating them. +func (r *Rego) PrepareForPartial(ctx context.Context, opts ...PrepareOption) (PreparedPartialQuery, error) { + if !r.hasQuery() { + return PreparedPartialQuery{}, fmt.Errorf("cannot evaluate empty query") + } + + pCfg := &PrepareConfig{} + for _, o := range opts { + o(pCfg) + } + + var err error + var txnClose transactionCloser + r.txn, txnClose, err = r.getTxn(ctx) + if err != nil { + return PreparedPartialQuery{}, err + } + + err = r.prepare(ctx, partialQueryType, []extraStage{ + { + after: "CheckSafety", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteEquals", + MetricName: "query_compile_stage_rewrite_equals", + Stage: r.rewriteEqualsForPartialQueryCompile, + }, + }, + }) + txnErr := txnClose(ctx, err) // Always call closer + if err != nil { + return PreparedPartialQuery{}, err + } + if txnErr != nil { + return PreparedPartialQuery{}, txnErr + } + return PreparedPartialQuery{preparedQuery{r, pCfg}}, err +} + +func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage) error { + var err error + + r.parsedInput, err = r.parseInput() + if err != nil { + return err + } + + err = r.loadFiles(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + err = r.loadBundles(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + err = r.parseModules(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + // Compile the modules *before* the query, else functions + // defined in the module won't be found... + err = r.compileModules(ctx, r.txn, r.metrics) + if err != nil { + return err + } + + r.parsedQuery, err = r.parseQuery(r.metrics) + if err != nil { + return err + } + + err = r.compileAndCacheQuery(qType, r.parsedQuery, r.metrics, extras) + if err != nil { + return err + } + + return nil +} + +func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + if len(r.modules) == 0 { + return nil + } + + m.Timer(metrics.RegoModuleParse).Start() + defer m.Timer(metrics.RegoModuleParse).Stop() + var errs Errors + + // Parse any modules in the are saved to the store, but only if + // another compile step is going to occur (ie. we have parsed modules + // that need to be compiled). + ids, err := r.store.ListPolicies(ctx, txn) + if err != nil { + return err + } + + for _, id := range ids { + // if it is already on the compiler we're using + // then don't bother to re-parse it from source + if _, haveMod := r.compiler.Modules[id]; haveMod { + continue + } + + bs, err := r.store.GetPolicy(ctx, txn, id) + if err != nil { + return err + } + + parsed, err := ast.ParseModule(id, string(bs)) + if err != nil { + errs = append(errs, err) + } + + r.parsedModules[id] = parsed + } + + // Parse any passed in as arguments to the Rego object + for _, module := range r.modules { + p, err := module.Parse() + if err != nil { + errs = append(errs, err) + } + r.parsedModules[module.filename] = p + } + + if len(errs) > 0 { + return errs + } + + return nil +} + +func (r *Rego) loadFiles(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + if len(r.loadPaths.paths) == 0 { + return nil + } + + m.Timer(metrics.RegoLoadFiles).Start() + defer m.Timer(metrics.RegoLoadFiles).Stop() + + result, err := loader.NewFileLoader().WithMetrics(m).Filtered(r.loadPaths.paths, r.loadPaths.filter) + if err != nil { + return err + } + for name, mod := range result.Modules { + r.parsedModules[name] = mod.Parsed + } + + if len(result.Documents) > 0 { + err = r.store.Write(ctx, txn, storage.AddOp, storage.Path{}, result.Documents) + if err != nil { + return err + } + } + return nil +} + +func (r *Rego) loadBundles(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + if len(r.bundlePaths) == 0 { + return nil + } + + m.Timer(metrics.RegoLoadBundles).Start() + defer m.Timer(metrics.RegoLoadBundles).Stop() + + for _, path := range r.bundlePaths { + bndl, err := loader.NewFileLoader().WithMetrics(m).AsBundle(path) + if err != nil { + return fmt.Errorf("loading error: %s", err) + } + r.bundles[path] = bndl + } + return nil +} + +func (r *Rego) parseInput() (ast.Value, error) { + if r.parsedInput != nil { + return r.parsedInput, nil + } + return r.parseRawInput(r.rawInput, r.metrics) +} + +func (r *Rego) parseRawInput(rawInput *interface{}, m metrics.Metrics) (ast.Value, error) { + var input ast.Value + + if rawInput == nil { + return input, nil + } + + m.Timer(metrics.RegoInputParse).Start() + defer m.Timer(metrics.RegoInputParse).Stop() + + rawPtr := util.Reference(rawInput) + + // roundtrip through json: this turns slices (e.g. []string, []bool) into + // []interface{}, the only array type ast.InterfaceToValue can work with + if err := util.RoundTrip(rawPtr); err != nil { + return nil, err + } + + return ast.InterfaceToValue(*rawPtr) +} + +func (r *Rego) parseQuery(m metrics.Metrics) (ast.Body, error) { + if r.parsedQuery != nil { + return r.parsedQuery, nil + } + + m.Timer(metrics.RegoQueryParse).Start() + defer m.Timer(metrics.RegoQueryParse).Stop() + + return ast.ParseBody(r.query) +} + +func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { + + // Only compile again if there are new modules. + if len(r.bundles) > 0 || len(r.parsedModules) > 0 { + + // The bundle.Activate call will activate any bundles passed in + // (ie compile + handle data store changes), and include any of + // the additional modules passed in. If no bundles are provided + // it will only compile the passed in modules. + // Use this as the single-point of compiling everything only a + // single time. + opts := &bundle.ActivateOpts{ + Ctx: ctx, + Store: r.store, + Txn: txn, + Compiler: r.compiler.WithPathConflictsCheck(storage.NonEmpty(ctx, r.store, txn)), + Metrics: m, + Bundles: r.bundles, + ExtraModules: r.parsedModules, + } + err := bundle.Activate(opts) + if err != nil { + return err + } + } + return nil +} + +func (r *Rego) compileAndCacheQuery(qType queryType, query ast.Body, m metrics.Metrics, extras []extraStage) error { + m.Timer(metrics.RegoQueryCompile).Start() + defer m.Timer(metrics.RegoQueryCompile).Stop() + + cachedQuery, ok := r.compiledQueries[qType] + if ok && cachedQuery.query != nil && cachedQuery.compiler != nil { + return nil + } + + qc, compiled, err := r.compileQuery(query, m, extras) + if err != nil { + return err + } + + // cache the query for future use + r.compiledQueries[qType] = compiledQuery{ + query: compiled, + compiler: qc, + } + return nil +} + +func (r *Rego) compileQuery(query ast.Body, m metrics.Metrics, extras []extraStage) (ast.QueryCompiler, ast.Body, error) { + var pkg *ast.Package + + if r.pkg != "" { + var err error + pkg, err = ast.ParsePackage(fmt.Sprintf("package %v", r.pkg)) + if err != nil { + return nil, nil, err + } + } else { + pkg = r.parsedPackage + } + + imports := r.parsedImports + + if len(r.imports) > 0 { + s := make([]string, len(r.imports)) + for i := range r.imports { + s[i] = fmt.Sprintf("import %v", r.imports[i]) + } + parsed, err := ast.ParseImports(strings.Join(s, "\n")) + if err != nil { + return nil, nil, err + } + imports = append(imports, parsed...) + } + + qctx := ast.NewQueryContext(). + WithPackage(pkg). + WithImports(imports) + + qc := r.compiler.QueryCompiler(). + WithContext(qctx). + WithUnsafeBuiltins(r.unsafeBuiltins) + + for _, extra := range extras { + qc = qc.WithStageAfter(extra.after, extra.stage) + } + + compiled, err := qc.Compile(query) + + return qc, compiled, err + +} + +func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) { + + q := topdown.NewQuery(ectx.compiledQuery.query). + WithQueryCompiler(ectx.compiledQuery.compiler). + WithCompiler(r.compiler). + WithStore(r.store). + WithTransaction(ectx.txn). + WithBuiltins(r.builtinFuncs). + WithMetrics(ectx.metrics). + WithInstrumentation(ectx.instrumentation). + WithRuntime(r.runtime). + WithIndexing(ectx.indexing) + + for i := range ectx.tracers { + q = q.WithTracer(ectx.tracers[i]) + } + + if ectx.parsedInput != nil { + q = q.WithInput(ast.NewTerm(ectx.parsedInput)) + } + + // Cancel query if context is cancelled or deadline is reached. + c := topdown.NewCancel() + q = q.WithCancel(c) + exit := make(chan struct{}) + defer close(exit) + go waitForDone(ctx, exit, func() { + c.Cancel() + }) + + rewritten := ectx.compiledQuery.compiler.RewrittenVars() + var rs ResultSet + err := q.Iter(ctx, func(qr topdown.QueryResult) error { + result := newResult() + for k := range qr { + v, err := ast.JSON(qr[k].Value) + if err != nil { + return err + } + if rw, ok := rewritten[k]; ok { + k = rw + } + if isTermVar(k) || k.IsGenerated() || k.IsWildcard() { + continue + } + result.Bindings[string(k)] = v + } + for _, expr := range ectx.compiledQuery.query { + if expr.Generated { + continue + } + if k, ok := r.capture[expr]; ok { + v, err := ast.JSON(qr[k].Value) + if err != nil { + return err + } + result.Expressions = append(result.Expressions, newExpressionValue(expr, v)) + } else { + result.Expressions = append(result.Expressions, newExpressionValue(expr, true)) + } + } + rs = append(rs, result) + return nil + }) + + if err != nil { + return nil, err + } + + if len(rs) == 0 { + return nil, nil + } + + return rs, nil +} + +func (r *Rego) partialResult(ctx context.Context, pCfg *PrepareConfig) (PartialResult, error) { + + err := r.prepare(ctx, partialResultQueryType, []extraStage{ + { + after: "ResolveRefs", + stage: ast.QueryCompilerStageDefinition{ + Name: "RewriteForPartialEval", + MetricName: "query_compile_stage_rewrite_for_partial_eval", + Stage: r.rewriteQueryForPartialEval, + }, + }, + }) + if err != nil { + return PartialResult{}, err + } + + ectx := &EvalContext{ + parsedInput: r.parsedInput, + metrics: r.metrics, + txn: r.txn, + partialNamespace: r.partialNamespace, + tracers: r.tracers, + compiledQuery: r.compiledQueries[partialResultQueryType], + instrumentation: r.instrumentation, + indexing: true, + } + + disableInlining := r.disableInlining + + if pCfg.disableInlining != nil { + disableInlining = *pCfg.disableInlining + } + + ectx.disableInlining, err = parseStringsToRefs(disableInlining) + if err != nil { + return PartialResult{}, err + } + + pq, err := r.partial(ctx, ectx) + if err != nil { + return PartialResult{}, err + } + + // Construct module for queries. + module := ast.MustParseModule("package " + ectx.partialNamespace) + module.Rules = make([]*ast.Rule, len(pq.Queries)) + for i, body := range pq.Queries { + module.Rules[i] = &ast.Rule{ + Head: ast.NewHead(ast.Var("__result__"), nil, ast.Wildcard), + Body: body, + Module: module, + } + } + + // Update compiler with partial evaluation output. + r.compiler.Modules["__partialresult__"] = module + for i, module := range pq.Support { + r.compiler.Modules[fmt.Sprintf("__partialsupport%d__", i)] = module + } + + r.metrics.Timer(metrics.RegoModuleCompile).Start() + r.compiler.Compile(r.compiler.Modules) + r.metrics.Timer(metrics.RegoModuleCompile).Stop() + + if r.compiler.Failed() { + return PartialResult{}, r.compiler.Errors + } + + result := PartialResult{ + compiler: r.compiler, + store: r.store, + body: ast.MustParseBody(fmt.Sprintf("data.%v.__result__", ectx.partialNamespace)), + builtinDecls: r.builtinDecls, + builtinFuncs: r.builtinFuncs, + } + + return result, nil +} + +func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries, error) { + + var unknowns []*ast.Term + + if ectx.parsedUnknowns != nil { + unknowns = ectx.parsedUnknowns + } else if ectx.unknowns != nil { + unknowns = make([]*ast.Term, len(ectx.unknowns)) + for i := range ectx.unknowns { + var err error + unknowns[i], err = ast.ParseTerm(ectx.unknowns[i]) + if err != nil { + return nil, err + } + } + } else { + // Use input document as unknown if caller has not specified any. + unknowns = []*ast.Term{ast.NewTerm(ast.InputRootRef)} + } + + // Check partial namespace to ensure it's valid. + if term, err := ast.ParseTerm(ectx.partialNamespace); err != nil { + return nil, err + } else if _, ok := term.Value.(ast.Var); !ok { + return nil, fmt.Errorf("bad partial namespace") + } + + q := topdown.NewQuery(ectx.compiledQuery.query). + WithQueryCompiler(ectx.compiledQuery.compiler). + WithCompiler(r.compiler). + WithStore(r.store). + WithTransaction(ectx.txn). + WithBuiltins(r.builtinFuncs). + WithMetrics(ectx.metrics). + WithInstrumentation(ectx.instrumentation). + WithUnknowns(unknowns). + WithDisableInlining(ectx.disableInlining). + WithRuntime(r.runtime). + WithIndexing(ectx.indexing) + + for i := range ectx.tracers { + q = q.WithTracer(ectx.tracers[i]) + } + + if ectx.parsedInput != nil { + q = q.WithInput(ast.NewTerm(ectx.parsedInput)) + } + + // Cancel query if context is cancelled or deadline is reached. + c := topdown.NewCancel() + q = q.WithCancel(c) + exit := make(chan struct{}) + defer close(exit) + go waitForDone(ctx, exit, func() { + c.Cancel() + }) + + queries, support, err := q.PartialRun(ctx) + if err != nil { + return nil, err + } + + pq := &PartialQueries{ + Queries: queries, + Support: support, + } + + return pq, nil +} + +func (r *Rego) rewriteQueryToCaptureValue(qc ast.QueryCompiler, query ast.Body) (ast.Body, error) { + + checkCapture := iteration(query) || len(query) > 1 + + for _, expr := range query { + + if expr.Negated { + continue + } + + if expr.IsAssignment() || expr.IsEquality() { + continue + } + + var capture *ast.Term + + // If the expression can be evaluated as a function, rewrite it to + // capture the return value. E.g., neq(1,2) becomes neq(1,2,x) but + // plus(1,2,x) does not get rewritten. + switch terms := expr.Terms.(type) { + case *ast.Term: + capture = r.generateTermVar() + expr.Terms = ast.Equality.Expr(terms, capture).Terms + r.capture[expr] = capture.Value.(ast.Var) + case []*ast.Term: + if r.compiler.GetArity(expr.Operator()) == len(terms)-1 { + capture = r.generateTermVar() + expr.Terms = append(terms, capture) + r.capture[expr] = capture.Value.(ast.Var) + } + } + + if capture != nil && checkCapture { + cpy := expr.Copy() + cpy.Terms = capture + cpy.Generated = true + cpy.With = nil + query.Append(cpy) + } + } + + return query, nil +} + +func (r *Rego) rewriteQueryForPartialEval(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { + if len(query) != 1 { + return nil, fmt.Errorf("partial evaluation requires single ref (not multiple expressions)") + } + + term, ok := query[0].Terms.(*ast.Term) + if !ok { + return nil, fmt.Errorf("partial evaluation requires ref (not expression)") + } + + ref, ok := term.Value.(ast.Ref) + if !ok { + return nil, fmt.Errorf("partial evaluation requires ref (not %v)", ast.TypeName(term.Value)) + } + + if !ref.IsGround() { + return nil, fmt.Errorf("partial evaluation requires ground ref") + } + + return ast.NewBody(ast.Equality.Expr(ast.Wildcard, term)), nil +} + +// rewriteEqualsForPartialQueryCompile will rewrite == to = in queries. Normally +// this wouldn't be done, except for handling queries with the `Partial` API +// where rewriting them can substantially simplify the result, and it is unlikely +// that the caller would need expression values. +func (r *Rego) rewriteEqualsForPartialQueryCompile(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { + doubleEq := ast.Equal.Ref() + unifyOp := ast.Equality.Ref() + ast.WalkExprs(query, func(x *ast.Expr) bool { + if x.IsCall() { + operator := x.Operator() + if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + x.SetOperator(ast.NewTerm(unifyOp)) + } + } + return false + }) + return query, nil +} + +func (r *Rego) generateTermVar() *ast.Term { + r.termVarID++ + return ast.VarTerm(ast.WildcardPrefix + fmt.Sprintf("term%v", r.termVarID)) +} + +func (r Rego) hasQuery() bool { + return len(r.query) != 0 || len(r.parsedQuery) != 0 +} + +type transactionCloser func(ctx context.Context, err error) error + +// getTxn will conditionally create a read or write transaction suitable for +// the configured Rego object. The returned function should be used to close the txn +// regardless of status. +func (r *Rego) getTxn(ctx context.Context) (storage.Transaction, transactionCloser, error) { + + noopCloser := func(ctx context.Context, err error) error { + return nil // no-op default + } + + if r.txn != nil { + // Externally provided txn + return r.txn, noopCloser, nil + } + + // Create a new transaction.. + params := storage.TransactionParams{} + + // Bundles and data paths may require writing data files or manifests to storage + if len(r.bundles) > 0 || len(r.bundlePaths) > 0 || len(r.loadPaths.paths) > 0 { + + // If we were given a store we will *not* write to it, only do that on one + // which was created automatically on behalf of the user. + if !r.ownStore { + return nil, noopCloser, errors.New("unable to start write transaction when store was provided") + } + + params.Write = true + } + + txn, err := r.store.NewTransaction(ctx, params) + if err != nil { + return nil, noopCloser, err + } + + // Setup a closer function that will abort or commit as needed. + closer := func(ctx context.Context, txnErr error) error { + var err error + + if txnErr == nil && params.Write { + err = r.store.Commit(ctx, txn) + } else { + r.store.Abort(ctx, txn) + } + + // Clear the auto created transaction now that it is closed. + r.txn = nil + + return err + } + + return txn, closer, nil +} + +func isTermVar(v ast.Var) bool { + return strings.HasPrefix(string(v), ast.WildcardPrefix+"term") +} + +func waitForDone(ctx context.Context, exit chan struct{}, f func()) { + select { + case <-exit: + return + case <-ctx.Done(): + f() + return + } +} + +type rawModule struct { + filename string + module string +} + +func (m rawModule) Parse() (*ast.Module, error) { + return ast.ParseModule(m.filename, m.module) +} + +type extraStage struct { + after string + stage ast.QueryCompilerStageDefinition +} + +func iteration(x interface{}) bool { + + var stopped bool + + vis := ast.NewGenericVisitor(func(x interface{}) bool { + switch x := x.(type) { + case *ast.Term: + if ast.IsComprehension(x.Value) { + return true + } + case ast.Ref: + if !stopped { + if bi := ast.BuiltinMap[x.String()]; bi != nil { + if bi.Relation { + stopped = true + return stopped + } + } + for i := 1; i < len(x); i++ { + if _, ok := x[i].Value.(ast.Var); ok { + stopped = true + return stopped + } + } + } + return stopped + } + return stopped + }) + + vis.Walk(x) + + return stopped +} + +func parseStringsToRefs(s []string) ([]ast.Ref, error) { + + refs := make([]ast.Ref, len(s)) + for i := range refs { + var err error + refs[i], err = ast.ParseRef(s[i]) + if err != nil { + return nil, err + } + } + + return refs, nil +} + +// helper function to finish a built-in function call. If an error occured, +// wrap the error and return it. Otherwise, invoke the iterator if the result +// was defined. +func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, err error, iter func(*ast.Term) error) error { + if err != nil { + return &topdown.Error{ + Code: topdown.BuiltinErr, + Message: fmt.Sprintf("%v: %v", name, err.Error()), + Location: bctx.Location, + } + } + if result == nil { + return nil + } + return iter(result) +} + +// helper function to return an option that sets a custom built-in function. +func newFunction(decl *Function, f topdown.BuiltinFunc) func(*Rego) { + return func(r *Rego) { + r.builtinDecls[decl.Name] = &ast.Builtin{ + Name: decl.Name, + Decl: decl.Decl, + } + r.builtinFuncs[decl.Name] = &topdown.Builtin{ + Decl: r.builtinDecls[decl.Name], + Func: f, + } + } +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/doc.go b/vendor/github.com/open-policy-agent/opa/storage/doc.go new file mode 100644 index 000000000..6fa2f86d9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package storage exposes the policy engine's storage layer. +package storage diff --git a/vendor/github.com/open-policy-agent/opa/storage/errors.go b/vendor/github.com/open-policy-agent/opa/storage/errors.go new file mode 100644 index 000000000..6c8779586 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/errors.go @@ -0,0 +1,135 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "fmt" +) + +const ( + // InternalErr indicates an unknown, internal error has occurred. + InternalErr = "storage_internal_error" + + // NotFoundErr indicates the path used in the storage operation does not + // locate a document. + NotFoundErr = "storage_not_found_error" + + // WriteConflictErr indicates a write on the path enocuntered a conflicting + // value inside the transaction. + WriteConflictErr = "storage_write_conflict_error" + + // InvalidPatchErr indicates an invalid patch/write was issued. The patch + // was rejected. + InvalidPatchErr = "storage_invalid_patch_error" + + // InvalidTransactionErr indicates an invalid operation was performed + // inside of the transaction. + InvalidTransactionErr = "storage_invalid_txn_error" + + // TriggersNotSupportedErr indicates the caller attempted to register a + // trigger against a store that does not support them. + TriggersNotSupportedErr = "storage_triggers_not_supported_error" + + // WritesNotSupportedErr indicate the caller attempted to perform a write + // against a store that does not support them. + WritesNotSupportedErr = "storage_writes_not_supported_error" + + // PolicyNotSupportedErr indicate the caller attempted to perform a policy + // management operation against a store that does not support them. + PolicyNotSupportedErr = "storage_policy_not_supported_error" + + // IndexingNotSupportedErr indicate the caller attempted to perform an + // indexing operation against a store that does not support them. + IndexingNotSupportedErr = "storage_indexing_not_supported_error" +) + +// Error is the error type returned by the storage layer. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func (err *Error) Error() string { + if err.Message != "" { + return fmt.Sprintf("%v: %v", err.Code, err.Message) + } + return string(err.Code) +} + +// IsNotFound returns true if this error is a NotFoundErr. +func IsNotFound(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == NotFoundErr + } + return false +} + +// IsWriteConflictError returns true if this error a WriteConflictErr. +func IsWriteConflictError(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == WriteConflictErr + } + return false +} + +// IsInvalidPatch returns true if this error is a InvalidPatchErr. +func IsInvalidPatch(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == InvalidPatchErr + } + return false +} + +// IsInvalidTransaction returns true if this error is a InvalidTransactionErr. +func IsInvalidTransaction(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == InvalidTransactionErr + } + return false +} + +// IsIndexingNotSupported returns true if this error is a IndexingNotSupportedErr. +func IsIndexingNotSupported(err error) bool { + switch err := err.(type) { + case *Error: + return err.Code == IndexingNotSupportedErr + } + return false +} + +func writeConflictError(path Path) *Error { + return &Error{ + Code: WriteConflictErr, + Message: fmt.Sprint(path), + } +} + +func triggersNotSupportedError() *Error { + return &Error{ + Code: TriggersNotSupportedErr, + } +} + +func writesNotSupportedError() *Error { + return &Error{ + Code: WritesNotSupportedErr, + } +} + +func policyNotSupportedError() *Error { + return &Error{ + Code: PolicyNotSupportedErr, + } +} + +func indexingNotSupportedError() *Error { + return &Error{ + Code: IndexingNotSupportedErr, + } +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/inmem/index.go b/vendor/github.com/open-policy-agent/opa/storage/inmem/index.go new file mode 100644 index 000000000..1a6f4aaf6 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/inmem/index.go @@ -0,0 +1,350 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "context" + "encoding/json" + "fmt" + "hash/fnv" + "strings" + "sync" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/util" +) + +// indices contains a mapping of non-ground references to values to sets of bindings. +// +// +------+------------------------------------+ +// | ref1 | val1 | bindings-1, bindings-2, ... | +// | +------+-----------------------------+ +// | | val2 | bindings-m, bindings-m, ... | +// | +------+-----------------------------+ +// | | .... | ... | +// +------+------+-----------------------------+ +// | ref2 | .... | ... | +// +------+------+-----------------------------+ +// | ... | +// +-------------------------------------------+ +// +// The "value" is the data value stored at the location referred to by the ground +// reference obtained by plugging bindings into the non-ground reference that is the +// index key. +// +type indices struct { + mu sync.Mutex + table map[int]*indicesNode +} + +type indicesNode struct { + key ast.Ref + val *bindingIndex + next *indicesNode +} + +func newIndices() *indices { + return &indices{ + table: map[int]*indicesNode{}, + } +} + +func (ind *indices) Build(ctx context.Context, store storage.Store, txn storage.Transaction, ref ast.Ref) (*bindingIndex, error) { + + ind.mu.Lock() + defer ind.mu.Unlock() + + if exist := ind.get(ref); exist != nil { + return exist, nil + } + + index := newBindingIndex() + + if err := iterStorage(ctx, store, txn, ref, ast.EmptyRef(), ast.NewValueMap(), index.Add); err != nil { + return nil, err + } + + hashCode := ref.Hash() + head := ind.table[hashCode] + entry := &indicesNode{ + key: ref, + val: index, + next: head, + } + + ind.table[hashCode] = entry + + return index, nil +} + +func (ind *indices) get(ref ast.Ref) *bindingIndex { + node := ind.getNode(ref) + if node != nil { + return node.val + } + return nil +} + +func (ind *indices) iter(iter func(ast.Ref, *bindingIndex) error) error { + for _, head := range ind.table { + for entry := head; entry != nil; entry = entry.next { + if err := iter(entry.key, entry.val); err != nil { + return err + } + } + } + return nil +} + +func (ind *indices) getNode(ref ast.Ref) *indicesNode { + hashCode := ref.Hash() + for entry := ind.table[hashCode]; entry != nil; entry = entry.next { + if entry.key.Equal(ref) { + return entry + } + } + return nil +} + +func (ind *indices) String() string { + buf := []string{} + for _, head := range ind.table { + for entry := head; entry != nil; entry = entry.next { + str := fmt.Sprintf("%v: %v", entry.key, entry.val) + buf = append(buf, str) + } + } + return "{" + strings.Join(buf, ", ") + "}" +} + +// bindingIndex contains a mapping of values to bindings. +type bindingIndex struct { + table map[int]*indexNode +} + +type indexNode struct { + key interface{} + val *bindingSet + next *indexNode +} + +func newBindingIndex() *bindingIndex { + return &bindingIndex{ + table: map[int]*indexNode{}, + } +} + +func (ind *bindingIndex) Add(val interface{}, bindings *ast.ValueMap) { + + node := ind.getNode(val) + if node != nil { + node.val.Add(bindings) + return + } + + hashCode := hash(val) + bindingsSet := newBindingSet() + bindingsSet.Add(bindings) + + entry := &indexNode{ + key: val, + val: bindingsSet, + next: ind.table[hashCode], + } + + ind.table[hashCode] = entry +} + +func (ind *bindingIndex) Lookup(_ context.Context, _ storage.Transaction, val interface{}, iter storage.IndexIterator) error { + node := ind.getNode(val) + if node == nil { + return nil + } + return node.val.Iter(iter) +} + +func (ind *bindingIndex) getNode(val interface{}) *indexNode { + hashCode := hash(val) + head := ind.table[hashCode] + for entry := head; entry != nil; entry = entry.next { + if util.Compare(entry.key, val) == 0 { + return entry + } + } + return nil +} + +func (ind *bindingIndex) String() string { + + buf := []string{} + + for _, head := range ind.table { + for entry := head; entry != nil; entry = entry.next { + str := fmt.Sprintf("%v: %v", entry.key, entry.val) + buf = append(buf, str) + } + } + + return "{" + strings.Join(buf, ", ") + "}" +} + +type bindingSetNode struct { + val *ast.ValueMap + next *bindingSetNode +} + +type bindingSet struct { + table map[int]*bindingSetNode +} + +func newBindingSet() *bindingSet { + return &bindingSet{ + table: map[int]*bindingSetNode{}, + } +} + +func (set *bindingSet) Add(val *ast.ValueMap) { + node := set.getNode(val) + if node != nil { + return + } + hashCode := val.Hash() + head := set.table[hashCode] + set.table[hashCode] = &bindingSetNode{val, head} +} + +func (set *bindingSet) Iter(iter func(*ast.ValueMap) error) error { + for _, head := range set.table { + for entry := head; entry != nil; entry = entry.next { + if err := iter(entry.val); err != nil { + return err + } + } + } + return nil +} + +func (set *bindingSet) String() string { + buf := []string{} + set.Iter(func(bindings *ast.ValueMap) error { + buf = append(buf, bindings.String()) + return nil + }) + return "{" + strings.Join(buf, ", ") + "}" +} + +func (set *bindingSet) getNode(val *ast.ValueMap) *bindingSetNode { + hashCode := val.Hash() + for entry := set.table[hashCode]; entry != nil; entry = entry.next { + if entry.val.Equal(val) { + return entry + } + } + return nil +} + +func hash(v interface{}) int { + switch v := v.(type) { + case []interface{}: + var h int + for _, e := range v { + h += hash(e) + } + return h + case map[string]interface{}: + var h int + for k, v := range v { + h += hash(k) + hash(v) + } + return h + case string: + h := fnv.New64a() + h.Write([]byte(v)) + return int(h.Sum64()) + case bool: + if v { + return 1 + } + return 0 + case nil: + return 0 + case json.Number: + h := fnv.New64a() + h.Write([]byte(v)) + return int(h.Sum64()) + } + panic(fmt.Sprintf("illegal argument: %v (%T)", v, v)) +} + +func iterStorage(ctx context.Context, store storage.Store, txn storage.Transaction, nonGround, ground ast.Ref, bindings *ast.ValueMap, iter func(interface{}, *ast.ValueMap)) error { + + if len(nonGround) == 0 { + path, err := storage.NewPathForRef(ground) + if err != nil { + return err + } + node, err := store.Read(ctx, txn, path) + if err != nil { + if storage.IsNotFound(err) { + return nil + } + return err + } + iter(node, bindings) + return nil + } + + head := nonGround[0] + tail := nonGround[1:] + + headVar, isVar := head.Value.(ast.Var) + + if !isVar || len(ground) == 0 { + ground = append(ground, head) + return iterStorage(ctx, store, txn, tail, ground, bindings, iter) + } + + path, err := storage.NewPathForRef(ground) + if err != nil { + return err + } + + node, err := store.Read(ctx, txn, path) + if err != nil { + if storage.IsNotFound(err) { + return nil + } + return err + } + + switch node := node.(type) { + case map[string]interface{}: + for key := range node { + ground = append(ground, ast.StringTerm(key)) + cpy := bindings.Copy() + cpy.Put(headVar, ast.String(key)) + err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) + if err != nil { + return err + } + ground = ground[:len(ground)-1] + } + case []interface{}: + for i := range node { + idx := ast.IntNumberTerm(i) + ground = append(ground, idx) + cpy := bindings.Copy() + cpy.Put(headVar, idx.Value) + err := iterStorage(ctx, store, txn, tail, ground, cpy, iter) + if err != nil { + return err + } + ground = ground[:len(ground)-1] + } + } + + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/inmem/inmem.go b/vendor/github.com/open-policy-agent/opa/storage/inmem/inmem.go new file mode 100644 index 000000000..0b2ebafe0 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/inmem/inmem.go @@ -0,0 +1,287 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package inmem implements an in-memory version of the policy engine's storage +// layer. +// +// The in-memory store is used as the default storage layer implementation. The +// in-memory store supports multi-reader/single-writer concurrency with +// rollback. +// +// Callers should assume the in-memory store does not make copies of written +// data. Once data is written to the in-memory store, it should not be modified +// (outside of calling Store.Write). Furthermore, data read from the in-memory +// store should be treated as read-only. +package inmem + +import ( + "context" + "fmt" + "io" + "sync" + "sync/atomic" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/util" +) + +// New returns an empty in-memory store. +func New() storage.Store { + return &store{ + data: map[string]interface{}{}, + triggers: map[*handle]storage.TriggerConfig{}, + policies: map[string][]byte{}, + indices: newIndices(), + } +} + +// NewFromObject returns a new in-memory store from the supplied data object. +func NewFromObject(data map[string]interface{}) storage.Store { + db := New() + ctx := context.Background() + txn, err := db.NewTransaction(ctx, storage.WriteParams) + if err != nil { + panic(err) + } + if err := db.Write(ctx, txn, storage.AddOp, storage.Path{}, data); err != nil { + panic(err) + } + if err := db.Commit(ctx, txn); err != nil { + panic(err) + } + return db +} + +// NewFromReader returns a new in-memory store from a reader that produces a +// JSON serialized object. This function is for test purposes. +func NewFromReader(r io.Reader) storage.Store { + d := util.NewJSONDecoder(r) + var data map[string]interface{} + if err := d.Decode(&data); err != nil { + panic(err) + } + return NewFromObject(data) +} + +type store struct { + rmu sync.RWMutex // reader-writer lock + wmu sync.Mutex // writer lock + xid uint64 // last generated transaction id + data map[string]interface{} // raw data + policies map[string][]byte // raw policies + triggers map[*handle]storage.TriggerConfig // registered triggers + indices *indices // data ref indices +} + +type handle struct { + db *store +} + +func (db *store) NewTransaction(ctx context.Context, params ...storage.TransactionParams) (storage.Transaction, error) { + var write bool + var context *storage.Context + if len(params) > 0 { + write = params[0].Write + context = params[0].Context + } + xid := atomic.AddUint64(&db.xid, uint64(1)) + if write { + db.wmu.Lock() + } else { + db.rmu.RLock() + } + return newTransaction(xid, write, context, db), nil +} + +func (db *store) Commit(ctx context.Context, txn storage.Transaction) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if underlying.write { + db.rmu.Lock() + event := underlying.Commit() + db.indices = newIndices() + db.runOnCommitTriggers(ctx, txn, event) + // Mark the transaction stale after executing triggers so they can + // perform store operations if needed. + underlying.stale = true + db.rmu.Unlock() + db.wmu.Unlock() + } else { + db.rmu.RUnlock() + } + return nil +} + +func (db *store) Abort(ctx context.Context, txn storage.Transaction) { + underlying, err := db.underlying(txn) + if err != nil { + panic(err) + } + underlying.stale = true + if underlying.write { + db.wmu.Unlock() + } else { + db.rmu.RUnlock() + } +} + +func (db *store) ListPolicies(_ context.Context, txn storage.Transaction) ([]string, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.ListPolicies(), nil +} + +func (db *store) GetPolicy(_ context.Context, txn storage.Transaction, id string) ([]byte, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.GetPolicy(id) +} + +func (db *store) UpsertPolicy(_ context.Context, txn storage.Transaction, id string, bs []byte) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + return underlying.UpsertPolicy(id, bs) +} + +func (db *store) DeletePolicy(_ context.Context, txn storage.Transaction, id string) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + if _, err := underlying.GetPolicy(id); err != nil { + return err + } + return underlying.DeletePolicy(id) +} + +func (db *store) Register(ctx context.Context, txn storage.Transaction, config storage.TriggerConfig) (storage.TriggerHandle, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + if !underlying.write { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be registered with a write transaction", + } + } + h := &handle{db} + db.triggers[h] = config + return h, nil +} + +func (db *store) Read(ctx context.Context, txn storage.Transaction, path storage.Path) (interface{}, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + return underlying.Read(path) +} + +func (db *store) Write(ctx context.Context, txn storage.Transaction, op storage.PatchOp, path storage.Path, value interface{}) error { + underlying, err := db.underlying(txn) + if err != nil { + return err + } + val := util.Reference(value) + if err := util.RoundTrip(val); err != nil { + return err + } + return underlying.Write(op, path, *val) +} + +func (db *store) Build(ctx context.Context, txn storage.Transaction, ref ast.Ref) (storage.Index, error) { + underlying, err := db.underlying(txn) + if err != nil { + return nil, err + } + if underlying.write { + return nil, &storage.Error{ + Code: storage.IndexingNotSupportedErr, + Message: "in-memory store does not support indexing on write transactions", + } + } + return db.indices.Build(ctx, db, txn, ref) +} + +func (h *handle) Unregister(ctx context.Context, txn storage.Transaction) { + underlying, err := h.db.underlying(txn) + if err != nil { + panic(err) + } + if !underlying.write { + panic(&storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "triggers must be unregistered with a write transaction", + }) + } + delete(h.db.triggers, h) +} + +func (db *store) runOnCommitTriggers(ctx context.Context, txn storage.Transaction, event storage.TriggerEvent) { + for _, t := range db.triggers { + t.OnCommit(ctx, txn, event) + } +} + +func (db *store) underlying(txn storage.Transaction) (*transaction, error) { + underlying, ok := txn.(*transaction) + if !ok { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: fmt.Sprintf("unexpected transaction type %T", txn), + } + } + if underlying.db != db { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "unknown transaction", + } + } + if underlying.stale { + return nil, &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "stale transaction", + } + } + return underlying, nil +} + +var doesNotExistMsg = "document does not exist" +var rootMustBeObjectMsg = "root must be object" +var rootCannotBeRemovedMsg = "root cannot be removed" +var outOfRangeMsg = "array index out of range" +var arrayIndexTypeMsg = "array index must be integer" + +func invalidPatchError(f string, a ...interface{}) *storage.Error { + return &storage.Error{ + Code: storage.InvalidPatchErr, + Message: fmt.Sprintf(f, a...), + } +} + +func notFoundError(path storage.Path) *storage.Error { + return notFoundErrorHint(path, doesNotExistMsg) +} + +func notFoundErrorHint(path storage.Path, hint string) *storage.Error { + return notFoundErrorf("%v: %v", path.String(), hint) +} + +func notFoundErrorf(f string, a ...interface{}) *storage.Error { + msg := fmt.Sprintf(f, a...) + return &storage.Error{ + Code: storage.NotFoundErr, + Message: msg, + } +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/inmem/txn.go b/vendor/github.com/open-policy-agent/opa/storage/inmem/txn.go new file mode 100644 index 000000000..5ef02bccb --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/inmem/txn.go @@ -0,0 +1,444 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package inmem + +import ( + "container/list" + "encoding/json" + "strconv" + + "github.com/open-policy-agent/opa/storage" +) + +// transaction implements the low-level read/write operations on the in-memory +// store and contains the state required for pending transactions. +// +// For write transactions, the struct contains a logical set of updates +// performed by write operations in the transaction. Each write operation +// compacts the set such that two updates never overlap: +// +// - If new update path is a prefix of existing update path, existing update is +// removed, new update is added. +// +// - If existing update path is a prefix of new update path, existing update is +// modified. +// +// - Otherwise, new update is added. +// +// Read transactions do not require any special handling and simply passthrough +// to the underlying store. Read transactions do not support upgrade. +type transaction struct { + xid uint64 + write bool + stale bool + db *store + updates *list.List + policies map[string]policyUpdate + context *storage.Context +} + +type policyUpdate struct { + value []byte + remove bool +} + +func newTransaction(xid uint64, write bool, context *storage.Context, db *store) *transaction { + return &transaction{ + xid: xid, + write: write, + db: db, + policies: map[string]policyUpdate{}, + updates: list.New(), + context: context, + } +} + +func (txn *transaction) ID() uint64 { + return txn.xid +} + +func (txn *transaction) Write(op storage.PatchOp, path storage.Path, value interface{}) error { + + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "data write during read transaction", + } + } + + if len(path) == 0 { + return txn.updateRoot(op, value) + } + + for curr := txn.updates.Front(); curr != nil; { + update := curr.Value.(*update) + + // Check if new update masks existing update exactly. In this case, the + // existing update can be removed and no other updates have to be + // visited (because no two updates overlap.) + if update.path.Equal(path) { + if update.remove { + if op != storage.AddOp { + return notFoundError(path) + } + } + txn.updates.Remove(curr) + break + } + + // Check if new update masks existing update. In this case, the + // existing update has to be removed but other updates may overlap, so + // we must continue. + if update.path.HasPrefix(path) { + remove := curr + curr = curr.Next() + txn.updates.Remove(remove) + continue + } + + // Check if new update modifies existing update. In this case, the + // existing update is mutated. + if path.HasPrefix(update.path) { + if update.remove { + return notFoundError(path) + } + suffix := path[len(update.path):] + newUpdate, err := newUpdate(update.value, op, suffix, 0, value) + if err != nil { + return err + } + update.value = newUpdate.Apply(update.value) + return nil + } + + curr = curr.Next() + } + + update, err := newUpdate(txn.db.data, op, path, 0, value) + if err != nil { + return err + } + + txn.updates.PushFront(update) + return nil +} + +func (txn *transaction) updateRoot(op storage.PatchOp, value interface{}) error { + if op == storage.RemoveOp { + return invalidPatchError(rootCannotBeRemovedMsg) + } + if _, ok := value.(map[string]interface{}); !ok { + return invalidPatchError(rootMustBeObjectMsg) + } + txn.updates.Init() + txn.updates.PushFront(&update{ + path: storage.Path{}, + remove: false, + value: value, + }) + return nil +} + +func (txn *transaction) Commit() (result storage.TriggerEvent) { + result.Context = txn.context + for curr := txn.updates.Front(); curr != nil; curr = curr.Next() { + action := curr.Value.(*update) + updated := action.Apply(txn.db.data) + txn.db.data = updated.(map[string]interface{}) + + result.Data = append(result.Data, storage.DataEvent{ + Path: action.path, + Data: action.value, + Removed: action.remove, + }) + } + for id, update := range txn.policies { + if update.remove { + delete(txn.db.policies, id) + } else { + txn.db.policies[id] = update.value + } + + result.Policy = append(result.Policy, storage.PolicyEvent{ + ID: id, + Data: update.value, + Removed: update.remove, + }) + } + return result +} + +func (txn *transaction) Read(path storage.Path) (interface{}, error) { + + if !txn.write { + return ptr(txn.db.data, path) + } + + merge := []*update{} + + for curr := txn.updates.Front(); curr != nil; curr = curr.Next() { + + update := curr.Value.(*update) + + if path.HasPrefix(update.path) { + if update.remove { + return nil, notFoundError(path) + } + return ptr(update.value, path[len(update.path):]) + } + + if update.path.HasPrefix(path) { + merge = append(merge, update) + } + } + + data, err := ptr(txn.db.data, path) + + if err != nil { + return nil, err + } + + if len(merge) == 0 { + return data, nil + } + + cpy := deepCopy(data) + + for _, update := range merge { + cpy = update.Relative(path).Apply(cpy) + } + + return cpy, nil +} + +func (txn *transaction) ListPolicies() []string { + var ids []string + for id := range txn.db.policies { + if _, ok := txn.policies[id]; !ok { + ids = append(ids, id) + } + } + for id, update := range txn.policies { + if !update.remove { + ids = append(ids, id) + } + } + return ids +} + +func (txn *transaction) GetPolicy(id string) ([]byte, error) { + if update, ok := txn.policies[id]; ok { + if !update.remove { + return update.value, nil + } + return nil, notFoundErrorf("policy id %q", id) + } + if exist, ok := txn.db.policies[id]; ok { + return exist, nil + } + return nil, notFoundErrorf("policy id %q", id) +} + +func (txn *transaction) UpsertPolicy(id string, bs []byte) error { + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "policy write during read transaction", + } + } + txn.policies[id] = policyUpdate{bs, false} + return nil +} + +func (txn *transaction) DeletePolicy(id string) error { + if !txn.write { + return &storage.Error{ + Code: storage.InvalidTransactionErr, + Message: "policy write during read transaction", + } + } + txn.policies[id] = policyUpdate{nil, true} + return nil +} + +// update contains state associated with an update to be applied to the +// in-memory data store. +type update struct { + path storage.Path // data path modified by update + remove bool // indicates whether update removes the value at path + value interface{} // value to add/replace at path (ignored if remove is true) +} + +func newUpdate(data interface{}, op storage.PatchOp, path storage.Path, idx int, value interface{}) (*update, error) { + + switch data := data.(type) { + case map[string]interface{}: + return newUpdateObject(data, op, path, idx, value) + + case []interface{}: + return newUpdateArray(data, op, path, idx, value) + + case nil, bool, json.Number, string: + return nil, notFoundError(path) + } + + return nil, &storage.Error{ + Code: storage.InternalErr, + Message: "invalid data value encountered", + } +} + +func newUpdateArray(data []interface{}, op storage.PatchOp, path storage.Path, idx int, value interface{}) (*update, error) { + + if idx == len(path)-1 { + if path[idx] == "-" { + if op != storage.AddOp { + return nil, invalidPatchError("%v: invalid patch path", path) + } + cpy := make([]interface{}, len(data)+1) + copy(cpy, data) + cpy[len(data)] = value + return &update{path[:len(path)-1], false, cpy}, nil + } + + pos, err := validateArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + if op == storage.AddOp { + cpy := make([]interface{}, len(data)+1) + copy(cpy[:pos], data[:pos]) + copy(cpy[pos+1:], data[pos:]) + cpy[pos] = value + return &update{path[:len(path)-1], false, cpy}, nil + + } else if op == storage.RemoveOp { + cpy := make([]interface{}, len(data)-1) + copy(cpy[:pos], data[:pos]) + copy(cpy[pos:], data[pos+1:]) + return &update{path[:len(path)-1], false, cpy}, nil + + } else { + cpy := make([]interface{}, len(data)) + copy(cpy, data) + cpy[pos] = value + return &update{path[:len(path)-1], false, cpy}, nil + } + } + + pos, err := validateArrayIndex(data, path[idx], path) + if err != nil { + return nil, err + } + + return newUpdate(data[pos], op, path, idx+1, value) +} + +func newUpdateObject(data map[string]interface{}, op storage.PatchOp, path storage.Path, idx int, value interface{}) (*update, error) { + + if idx == len(path)-1 { + switch op { + case storage.ReplaceOp, storage.RemoveOp: + if _, ok := data[path[idx]]; !ok { + return nil, notFoundError(path) + } + } + return &update{path, op == storage.RemoveOp, value}, nil + } + + if data, ok := data[path[idx]]; ok { + return newUpdate(data, op, path, idx+1, value) + } + + return nil, notFoundError(path) +} +func (u *update) Apply(data interface{}) interface{} { + if len(u.path) == 0 { + return u.value + } + parent, err := ptr(data, u.path[:len(u.path)-1]) + if err != nil { + panic(err) + } + key := u.path[len(u.path)-1] + if u.remove { + obj := parent.(map[string]interface{}) + delete(obj, key) + return data + } + switch parent := parent.(type) { + case map[string]interface{}: + parent[key] = u.value + case []interface{}: + idx, err := strconv.Atoi(key) + if err != nil { + panic(err) + } + parent[idx] = u.value + } + return data +} + +func (u *update) Relative(path storage.Path) *update { + cpy := *u + cpy.path = cpy.path[len(path):] + return &cpy +} + +func deepCopy(val interface{}) interface{} { + switch val := val.(type) { + case []interface{}: + cpy := make([]interface{}, len(val)) + for i := range cpy { + cpy[i] = deepCopy(val[i]) + } + return cpy + case map[string]interface{}: + cpy := make(map[string]interface{}, len(val)) + for k := range val { + cpy[k] = deepCopy(val[k]) + } + return cpy + default: + return val + } +} + +func ptr(data interface{}, path storage.Path) (interface{}, error) { + + node := data + for i := range path { + key := path[i] + switch curr := node.(type) { + case map[string]interface{}: + var ok bool + if node, ok = curr[key]; !ok { + return nil, notFoundError(path) + } + case []interface{}: + pos, err := validateArrayIndex(curr, key, path) + if err != nil { + return nil, err + } + node = curr[pos] + default: + return nil, notFoundError(path) + } + } + + return node, nil +} + +func validateArrayIndex(arr []interface{}, s string, path storage.Path) (int, error) { + idx, err := strconv.Atoi(s) + if err != nil { + return 0, notFoundErrorHint(path, arrayIndexTypeMsg) + } + if idx < 0 || idx >= len(arr) { + return 0, notFoundErrorHint(path, outOfRangeMsg) + } + return idx, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/interface.go b/vendor/github.com/open-policy-agent/opa/storage/interface.go new file mode 100644 index 000000000..8b1e1626e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/interface.go @@ -0,0 +1,219 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "context" + + "github.com/open-policy-agent/opa/ast" +) + +// Transaction defines the interface that identifies a consistent snapshot over +// the policy engine's storage layer. +type Transaction interface { + ID() uint64 +} + +// Store defines the interface for the storage layer's backend. +type Store interface { + Trigger + Policy + Indexing + + // NewTransaction is called create a new transaction in the store. + NewTransaction(ctx context.Context, params ...TransactionParams) (Transaction, error) + + // Read is called to fetch a document referred to by path. + Read(ctx context.Context, txn Transaction, path Path) (interface{}, error) + + // Write is called to modify a document referred to by path. + Write(ctx context.Context, txn Transaction, op PatchOp, path Path, value interface{}) error + + // Commit is called to finish the transaction. If Commit returns an error, the + // transaction must be automatically aborted by the Store implementation. + Commit(ctx context.Context, txn Transaction) error + + // Abort is called to cancel the transaction. + Abort(ctx context.Context, txn Transaction) +} + +// TransactionParams describes a new transaction. +type TransactionParams struct { + + // Write indicates if this transaction will perform any write operations. + Write bool + + // Context contains key/value pairs passed to triggers. + Context *Context +} + +// Context is a simple container for key/value pairs. +type Context struct { + values map[interface{}]interface{} +} + +// NewContext returns a new context object. +func NewContext() *Context { + return &Context{ + values: map[interface{}]interface{}{}, + } +} + +// Get returns the key value in the context. +func (ctx *Context) Get(key interface{}) interface{} { + if ctx == nil { + return nil + } + return ctx.values[key] +} + +// Put adds a key/value pair to the context. +func (ctx *Context) Put(key, value interface{}) { + ctx.values[key] = value +} + +// WriteParams specifies the TransactionParams for a write transaction. +var WriteParams = TransactionParams{ + Write: true, +} + +// PatchOp is the enumeration of supposed modifications. +type PatchOp int + +// Patch supports add, remove, and replace operations. +const ( + AddOp PatchOp = iota + RemoveOp = iota + ReplaceOp = iota +) + +// WritesNotSupported provides a default implementation of the write +// interface which may be used if the backend does not support writes. +type WritesNotSupported struct{} + +func (WritesNotSupported) Write(ctx context.Context, txn Transaction, op PatchOp, path Path, value interface{}) error { + return writesNotSupportedError() +} + +// Policy defines the interface for policy module storage. +type Policy interface { + ListPolicies(context.Context, Transaction) ([]string, error) + GetPolicy(context.Context, Transaction, string) ([]byte, error) + UpsertPolicy(context.Context, Transaction, string, []byte) error + DeletePolicy(context.Context, Transaction, string) error +} + +// PolicyNotSupported provides a default implementation of the policy interface +// which may be used if the backend does not support policy storage. +type PolicyNotSupported struct{} + +// ListPolicies always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) ListPolicies(context.Context, Transaction) ([]string, error) { + return nil, policyNotSupportedError() +} + +// GetPolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) GetPolicy(context.Context, Transaction, string) ([]byte, error) { + return nil, policyNotSupportedError() +} + +// UpsertPolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) UpsertPolicy(context.Context, Transaction, string, []byte) error { + return policyNotSupportedError() +} + +// DeletePolicy always returns a PolicyNotSupportedErr. +func (PolicyNotSupported) DeletePolicy(context.Context, Transaction, string) error { + return policyNotSupportedError() +} + +// PolicyEvent describes a change to a policy. +type PolicyEvent struct { + ID string + Data []byte + Removed bool +} + +// DataEvent describes a change to a base data document. +type DataEvent struct { + Path Path + Data interface{} + Removed bool +} + +// TriggerEvent describes the changes that caused the trigger to be invoked. +type TriggerEvent struct { + Policy []PolicyEvent + Data []DataEvent + Context *Context +} + +// IsZero returns true if the TriggerEvent indicates no changes occurred. This +// function is primarily for test purposes. +func (e TriggerEvent) IsZero() bool { + return !e.PolicyChanged() && !e.DataChanged() +} + +// PolicyChanged returns true if the trigger was caused by a policy change. +func (e TriggerEvent) PolicyChanged() bool { + return len(e.Policy) > 0 +} + +// DataChanged returns true if the trigger was caused by a data change. +func (e TriggerEvent) DataChanged() bool { + return len(e.Data) > 0 +} + +// TriggerConfig contains the trigger registration configuration. +type TriggerConfig struct { + + // OnCommit is invoked when a transaction is successfully committed. The + // callback is invoked with a handle to the write transaction that + // successfully committed before other clients see the changes. + OnCommit func(ctx context.Context, txn Transaction, event TriggerEvent) +} + +// Trigger defines the interface that stores implement to register for change +// notifications when the store is changed. +type Trigger interface { + Register(ctx context.Context, txn Transaction, config TriggerConfig) (TriggerHandle, error) +} + +// TriggersNotSupported provides default implementations of the Trigger +// interface which may be used if the backend does not support triggers. +type TriggersNotSupported struct{} + +// Register always returns an error indicating triggers are not supported. +func (TriggersNotSupported) Register(context.Context, Transaction, TriggerConfig) (TriggerHandle, error) { + return nil, triggersNotSupportedError() +} + +// TriggerHandle defines the interface that can be used to unregister triggers that have +// been registered on a Store. +type TriggerHandle interface { + Unregister(ctx context.Context, txn Transaction) +} + +// IndexIterator defines the interface for iterating over index results. +type IndexIterator func(*ast.ValueMap) error + +// Indexing defines the interface for building an index. +type Indexing interface { + Build(ctx context.Context, txn Transaction, ref ast.Ref) (Index, error) +} + +// Index defines the interface for searching a pre-built index. +type Index interface { + Lookup(ctx context.Context, txn Transaction, value interface{}, iter IndexIterator) error +} + +// IndexingNotSupported provides default implementations of the Indexing +// interface which may be used if the backend does not support indexing. +type IndexingNotSupported struct{} + +// Build always returns an error indicating indexing is not supported. +func (IndexingNotSupported) Build(context.Context, Transaction, ast.Ref) (Index, error) { + return nil, indexingNotSupportedError() +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/path.go b/vendor/github.com/open-policy-agent/opa/storage/path.go new file mode 100644 index 000000000..fcb0ebee1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/path.go @@ -0,0 +1,154 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "fmt" + "net/url" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/ast" +) + +// Path refers to a document in storage. +type Path []string + +// ParsePath returns a new path for the given str. +func ParsePath(str string) (path Path, ok bool) { + if len(str) == 0 { + return nil, false + } + if str[0] != '/' { + return nil, false + } + if len(str) == 1 { + return Path{}, true + } + parts := strings.Split(str[1:], "/") + return parts, true +} + +// ParsePathEscaped returns a new path for the given escaped str. +func ParsePathEscaped(str string) (path Path, ok bool) { + path, ok = ParsePath(str) + if !ok { + return + } + for i := range path { + segment, err := url.PathUnescape(path[i]) + if err == nil { + path[i] = segment + } + } + return +} + +// NewPathForRef returns a new path for the given ref. +func NewPathForRef(ref ast.Ref) (path Path, err error) { + + if len(ref) == 0 { + return nil, fmt.Errorf("empty reference (indicates error in caller)") + } + + if len(ref) == 1 { + return Path{}, nil + } + + path = make(Path, 0, len(ref)-1) + + for _, term := range ref[1:] { + switch v := term.Value.(type) { + case ast.String: + path = append(path, string(v)) + case ast.Number: + path = append(path, v.String()) + case ast.Boolean, ast.Null: + return nil, &Error{ + Code: NotFoundErr, + Message: fmt.Sprintf("%v: does not exist", ref), + } + case ast.Array, ast.Object, ast.Set: + return nil, fmt.Errorf("composites cannot be base document keys: %v", ref) + default: + return nil, fmt.Errorf("unresolved reference (indicates error in caller): %v", ref) + } + } + + return path, nil +} + +// Compare performs lexigraphical comparison on p and other and returns -1 if p +// is less than other, 0 if p is equal to other, or 1 if p is greater than +// other. +func (p Path) Compare(other Path) (cmp int) { + min := len(p) + if len(other) < min { + min = len(other) + } + for i := 0; i < min; i++ { + if cmp := strings.Compare(p[i], other[i]); cmp != 0 { + return cmp + } + } + if len(p) < len(other) { + return -1 + } + if len(p) == len(other) { + return 0 + } + return 1 +} + +// Equal returns true if p is the same as other. +func (p Path) Equal(other Path) bool { + return p.Compare(other) == 0 +} + +// HasPrefix returns true if p starts with other. +func (p Path) HasPrefix(other Path) bool { + if len(other) > len(p) { + return false + } + for i := range other { + if p[i] != other[i] { + return false + } + } + return true +} + +// Ref returns a ref that represents p rooted at head. +func (p Path) Ref(head *ast.Term) (ref ast.Ref) { + ref = make(ast.Ref, len(p)+1) + ref[0] = head + for i := range p { + idx, err := strconv.ParseInt(p[i], 10, 64) + if err == nil { + ref[i+1] = ast.IntNumberTerm(int(idx)) + } else { + ref[i+1] = ast.StringTerm(p[i]) + } + } + return ref +} + +func (p Path) String() string { + buf := make([]string, len(p)) + for i := range buf { + buf[i] = url.PathEscape(p[i]) + } + return "/" + strings.Join(buf, "/") +} + +// MustParsePath returns a new Path for s. If s cannot be parsed, this function +// will panic. This is mostly for test purposes. +func MustParsePath(s string) Path { + path, ok := ParsePath(s) + if !ok { + panic(s) + } + return path +} diff --git a/vendor/github.com/open-policy-agent/opa/storage/storage.go b/vendor/github.com/open-policy-agent/opa/storage/storage.go new file mode 100644 index 000000000..323a0dba7 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/storage/storage.go @@ -0,0 +1,126 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package storage + +import ( + "context" +) + +// NewTransactionOrDie is a helper function to create a new transaction. If the +// storage layer cannot create a new transaction, this function will panic. This +// function should only be used for tests. +func NewTransactionOrDie(ctx context.Context, store Store, params ...TransactionParams) Transaction { + txn, err := store.NewTransaction(ctx, params...) + if err != nil { + panic(err) + } + return txn +} + +// ReadOne is a convenience function to read a single value from the provided Store. It +// will create a new Transaction to perform the read with, and clean up after itself +// should an error occur. +func ReadOne(ctx context.Context, store Store, path Path) (interface{}, error) { + txn, err := store.NewTransaction(ctx) + if err != nil { + return nil, err + } + defer store.Abort(ctx, txn) + + return store.Read(ctx, txn, path) +} + +// WriteOne is a convenience function to write a single value to the provided Store. It +// will create a new Transaction to perform the write with, and clean up after itself +// should an error occur. +func WriteOne(ctx context.Context, store Store, op PatchOp, path Path, value interface{}) error { + txn, err := store.NewTransaction(ctx, WriteParams) + if err != nil { + return err + } + + if err := store.Write(ctx, txn, op, path, value); err != nil { + store.Abort(ctx, txn) + return err + } + + return store.Commit(ctx, txn) +} + +// MakeDir inserts an empty object at path. If the parent path does not exist, +// MakeDir will create it recursively. +func MakeDir(ctx context.Context, store Store, txn Transaction, path Path) (err error) { + + if len(path) == 0 { + return nil + } + + node, err := store.Read(ctx, txn, path) + + if err != nil { + if !IsNotFound(err) { + return err + } + + if err := MakeDir(ctx, store, txn, path[:len(path)-1]); err != nil { + return err + } else if err := store.Write(ctx, txn, AddOp, path, map[string]interface{}{}); err != nil { + return err + } + + return nil + } + + if _, ok := node.(map[string]interface{}); ok { + return nil + } + + return writeConflictError(path) + +} + +// Txn is a convenience function that executes f inside a new transaction +// opened on the store. If the function returns an error, the transaction is +// aborted and the error is returned. Otherwise, the transaction is committed +// and the result of the commit is returned. +func Txn(ctx context.Context, store Store, params TransactionParams, f func(Transaction) error) error { + + txn, err := store.NewTransaction(ctx, params) + if err != nil { + return err + } + + if err := f(txn); err != nil { + store.Abort(ctx, txn) + return err + } + + return store.Commit(ctx, txn) +} + +// NonEmpty returns a function that tests if a path is non-empty. A +// path is non-empty if a Read on the path returns a value or a Read +// on any of the path prefixes returns a non-object value. +func NonEmpty(ctx context.Context, store Store, txn Transaction) func([]string) (bool, error) { + return func(path []string) (bool, error) { + if _, err := store.Read(ctx, txn, Path(path)); err == nil { + return true, nil + } else if !IsNotFound(err) { + return false, err + } + for i := len(path) - 1; i > 0; i-- { + val, err := store.Read(ctx, txn, Path(path[:i])) + if err != nil && !IsNotFound(err) { + return false, err + } else if err == nil { + if _, ok := val.(map[string]interface{}); ok { + return false, nil + } + return true, nil + } + } + return false, nil + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/aggregates.go b/vendor/github.com/open-policy-agent/opa/topdown/aggregates.go new file mode 100644 index 000000000..0b59487b6 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/aggregates.go @@ -0,0 +1,216 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "math/big" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinCount(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + return ast.IntNumberTerm(len(a)).Value, nil + case ast.Object: + return ast.IntNumberTerm(a.Len()).Value, nil + case ast.Set: + return ast.IntNumberTerm(a.Len()).Value, nil + case ast.String: + return ast.IntNumberTerm(len(a)).Value, nil + } + return nil, builtins.NewOperandTypeErr(1, a, "array", "object", "set") +} + +func builtinSum(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + sum := big.NewFloat(0) + for _, x := range a { + n, ok := x.Value.(ast.Number) + if !ok { + return nil, builtins.NewOperandElementErr(1, a, x.Value, "number") + } + sum = new(big.Float).Add(sum, builtins.NumberToFloat(n)) + } + return builtins.FloatToNumber(sum), nil + case ast.Set: + sum := big.NewFloat(0) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + sum = new(big.Float).Add(sum, builtins.NumberToFloat(n)) + return nil + }) + return builtins.FloatToNumber(sum), err + } + return nil, builtins.NewOperandTypeErr(1, a, "set", "array") +} + +func builtinProduct(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + product := big.NewFloat(1) + for _, x := range a { + n, ok := x.Value.(ast.Number) + if !ok { + return nil, builtins.NewOperandElementErr(1, a, x.Value, "number") + } + product = new(big.Float).Mul(product, builtins.NumberToFloat(n)) + } + return builtins.FloatToNumber(product), nil + case ast.Set: + product := big.NewFloat(1) + err := a.Iter(func(x *ast.Term) error { + n, ok := x.Value.(ast.Number) + if !ok { + return builtins.NewOperandElementErr(1, a, x.Value, "number") + } + product = new(big.Float).Mul(product, builtins.NumberToFloat(n)) + return nil + }) + return builtins.FloatToNumber(product), err + } + return nil, builtins.NewOperandTypeErr(1, a, "set", "array") +} + +func builtinMax(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + if len(a) == 0 { + return nil, BuiltinEmpty{} + } + var max = ast.Value(ast.Null{}) + for i := range a { + if ast.Compare(max, a[i].Value) <= 0 { + max = a[i].Value + } + } + return max, nil + case ast.Set: + if a.Len() == 0 { + return nil, BuiltinEmpty{} + } + max, err := a.Reduce(ast.NullTerm(), func(max *ast.Term, elem *ast.Term) (*ast.Term, error) { + if ast.Compare(max, elem) <= 0 { + return elem, nil + } + return max, nil + }) + return max.Value, err + } + + return nil, builtins.NewOperandTypeErr(1, a, "set", "array") +} + +func builtinMin(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + if len(a) == 0 { + return nil, BuiltinEmpty{} + } + min := a[0].Value + for i := range a { + if ast.Compare(min, a[i].Value) >= 0 { + min = a[i].Value + } + } + return min, nil + case ast.Set: + if a.Len() == 0 { + return nil, BuiltinEmpty{} + } + min, err := a.Reduce(ast.NullTerm(), func(min *ast.Term, elem *ast.Term) (*ast.Term, error) { + // The null term is considered to be less than any other term, + // so in order for min of a set to make sense, we need to check + // for it. + if min.Value.Compare(ast.Null{}) == 0 { + return elem, nil + } + + if ast.Compare(min, elem) >= 0 { + return elem, nil + } + return min, nil + }) + return min.Value, err + } + + return nil, builtins.NewOperandTypeErr(1, a, "set", "array") +} + +func builtinSort(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Array: + return a.Sorted(), nil + case ast.Set: + return a.Sorted(), nil + } + return nil, builtins.NewOperandTypeErr(1, a, "set", "array") +} + +func builtinAll(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Set: + res := true + match := ast.BooleanTerm(true) + val.Foreach(func(term *ast.Term) { + if !term.Equal(match) { + res = false + } + }) + return ast.Boolean(res), nil + case ast.Array: + res := true + match := ast.BooleanTerm(true) + for _, term := range val { + if !term.Equal(match) { + res = false + } + } + return ast.Boolean(res), nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "array", "set") + } +} + +func builtinAny(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Set: + res := false + match := ast.BooleanTerm(true) + val.Foreach(func(term *ast.Term) { + if term.Equal(match) { + res = true + } + }) + return ast.Boolean(res), nil + case ast.Array: + res := false + match := ast.BooleanTerm(true) + for _, term := range val { + if term.Equal(match) { + res = true + } + } + return ast.Boolean(res), nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "array", "set") + } +} + +func init() { + RegisterFunctionalBuiltin1(ast.Count.Name, builtinCount) + RegisterFunctionalBuiltin1(ast.Sum.Name, builtinSum) + RegisterFunctionalBuiltin1(ast.Product.Name, builtinProduct) + RegisterFunctionalBuiltin1(ast.Max.Name, builtinMax) + RegisterFunctionalBuiltin1(ast.Min.Name, builtinMin) + RegisterFunctionalBuiltin1(ast.Sort.Name, builtinSort) + RegisterFunctionalBuiltin1(ast.Any.Name, builtinAny) + RegisterFunctionalBuiltin1(ast.All.Name, builtinAll) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/arithmetic.go b/vendor/github.com/open-policy-agent/opa/topdown/arithmetic.go new file mode 100644 index 000000000..e481cf17b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/arithmetic.go @@ -0,0 +1,156 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "math/big" + + "fmt" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +type arithArity1 func(a *big.Float) (*big.Float, error) +type arithArity2 func(a, b *big.Float) (*big.Float, error) + +func arithAbs(a *big.Float) (*big.Float, error) { + return a.Abs(a), nil +} + +var halfAwayFromZero = big.NewFloat(0.5) + +func arithRound(a *big.Float) (*big.Float, error) { + var i *big.Int + if a.Signbit() { + i, _ = new(big.Float).Sub(a, halfAwayFromZero).Int(nil) + } else { + i, _ = new(big.Float).Add(a, halfAwayFromZero).Int(nil) + } + return new(big.Float).SetInt(i), nil +} + +func arithPlus(a, b *big.Float) (*big.Float, error) { + return new(big.Float).Add(a, b), nil +} + +func arithMinus(a, b *big.Float) (*big.Float, error) { + return new(big.Float).Sub(a, b), nil +} + +func arithMultiply(a, b *big.Float) (*big.Float, error) { + return new(big.Float).Mul(a, b), nil +} + +func arithDivide(a, b *big.Float) (*big.Float, error) { + i, acc := b.Int64() + if acc == big.Exact && i == 0 { + return nil, fmt.Errorf("divide by zero") + } + return new(big.Float).Quo(a, b), nil +} + +func arithRem(a, b *big.Int) (*big.Int, error) { + if b.Int64() == 0 { + return nil, fmt.Errorf("modulo by zero") + } + return new(big.Int).Rem(a, b), nil +} + +func builtinArithArity1(fn arithArity1) FunctionalBuiltin1 { + return func(a ast.Value) (ast.Value, error) { + n, err := builtins.NumberOperand(a, 1) + if err != nil { + return nil, err + } + f, err := fn(builtins.NumberToFloat(n)) + if err != nil { + return nil, err + } + return builtins.FloatToNumber(f), nil + } +} + +func builtinArithArity2(fn arithArity2) FunctionalBuiltin2 { + return func(a, b ast.Value) (ast.Value, error) { + n1, err := builtins.NumberOperand(a, 1) + if err != nil { + return nil, err + } + n2, err := builtins.NumberOperand(b, 2) + if err != nil { + return nil, err + } + f, err := fn(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + if err != nil { + return nil, err + } + return builtins.FloatToNumber(f), nil + } +} + +func builtinMinus(a, b ast.Value) (ast.Value, error) { + + n1, ok1 := a.(ast.Number) + n2, ok2 := b.(ast.Number) + + if ok1 && ok2 { + f, err := arithMinus(builtins.NumberToFloat(n1), builtins.NumberToFloat(n2)) + if err != nil { + return nil, err + } + return builtins.FloatToNumber(f), nil + } + + s1, ok3 := a.(ast.Set) + s2, ok4 := b.(ast.Set) + + if ok3 && ok4 { + return s1.Diff(s2), nil + } + + if !ok1 && !ok3 { + return nil, builtins.NewOperandTypeErr(1, a, "number", "set") + } + + return nil, builtins.NewOperandTypeErr(2, b, "number", "set") +} + +func builtinRem(a, b ast.Value) (ast.Value, error) { + n1, ok1 := a.(ast.Number) + n2, ok2 := b.(ast.Number) + + if ok1 && ok2 { + + op1, err1 := builtins.NumberToInt(n1) + op2, err2 := builtins.NumberToInt(n2) + + if err1 != nil || err2 != nil { + return nil, fmt.Errorf("modulo on floating-point number") + } + + i, err := arithRem(op1, op2) + if err != nil { + return nil, err + } + return builtins.IntToNumber(i), nil + } + + if !ok1 { + return nil, builtins.NewOperandTypeErr(1, a, "number") + } + + return nil, builtins.NewOperandTypeErr(2, b, "number") +} + +func init() { + RegisterFunctionalBuiltin1(ast.Abs.Name, builtinArithArity1(arithAbs)) + RegisterFunctionalBuiltin1(ast.Round.Name, builtinArithArity1(arithRound)) + RegisterFunctionalBuiltin2(ast.Plus.Name, builtinArithArity2(arithPlus)) + RegisterFunctionalBuiltin2(ast.Minus.Name, builtinMinus) + RegisterFunctionalBuiltin2(ast.Multiply.Name, builtinArithArity2(arithMultiply)) + RegisterFunctionalBuiltin2(ast.Divide.Name, builtinArithArity2(arithDivide)) + RegisterFunctionalBuiltin2(ast.Rem.Name, builtinRem) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/array.go b/vendor/github.com/open-policy-agent/opa/topdown/array.go new file mode 100644 index 000000000..ee0398704 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/array.go @@ -0,0 +1,75 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinArrayConcat(a, b ast.Value) (ast.Value, error) { + arrA, err := builtins.ArrayOperand(a, 1) + if err != nil { + return nil, err + } + + arrB, err := builtins.ArrayOperand(b, 2) + if err != nil { + return nil, err + } + + arrC := make(ast.Array, 0, len(arrA)+len(arrB)) + + for _, elemA := range arrA { + arrC = append(arrC, elemA) + } + + for _, elemB := range arrB { + arrC = append(arrC, elemB) + } + + return arrC, nil +} + +func builtinArraySlice(a, i, j ast.Value) (ast.Value, error) { + arr, err := builtins.ArrayOperand(a, 1) + if err != nil { + return nil, err + } + + startIndex, err := builtins.IntOperand(i, 2) + if err != nil { + return nil, err + } + + stopIndex, err := builtins.IntOperand(j, 3) + if err != nil { + return nil, err + } + + // Return empty array if bounds cannot be clamped sensibly. + if (startIndex >= stopIndex) || (startIndex <= 0 && stopIndex <= 0) { + return arr[0:0], nil + } + + // Clamp bounds to avoid out-of-range errors. + if startIndex < 0 { + startIndex = 0 + } + + if stopIndex > len(arr) { + stopIndex = len(arr) + } + + arrb := arr[startIndex:stopIndex] + + return arrb, nil + +} + +func init() { + RegisterFunctionalBuiltin2(ast.ArrayConcat.Name, builtinArrayConcat) + RegisterFunctionalBuiltin3(ast.ArraySlice.Name, builtinArraySlice) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/binary.go b/vendor/github.com/open-policy-agent/opa/topdown/binary.go new file mode 100644 index 000000000..3cab5def1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/binary.go @@ -0,0 +1,45 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinBinaryAnd(a ast.Value, b ast.Value) (ast.Value, error) { + + s1, err := builtins.SetOperand(a, 1) + if err != nil { + return nil, err + } + + s2, err := builtins.SetOperand(b, 2) + if err != nil { + return nil, err + } + + return s1.Intersect(s2), nil +} + +func builtinBinaryOr(a ast.Value, b ast.Value) (ast.Value, error) { + + s1, err := builtins.SetOperand(a, 1) + if err != nil { + return nil, err + } + + s2, err := builtins.SetOperand(b, 2) + if err != nil { + return nil, err + } + + return s1.Union(s2), nil +} + +func init() { + RegisterFunctionalBuiltin2(ast.And.Name, builtinBinaryAnd) + RegisterFunctionalBuiltin2(ast.Or.Name, builtinBinaryOr) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/bindings.go b/vendor/github.com/open-policy-agent/opa/topdown/bindings.go new file mode 100644 index 000000000..35e399415 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/bindings.go @@ -0,0 +1,387 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "strings" + + "github.com/open-policy-agent/opa/ast" +) + +type undo struct { + k *ast.Term + u *bindings + next *undo +} + +func (u *undo) Undo() { + if u == nil { + // Allow call on zero value of Undo for ease-of-use. + return + } + if u.u == nil { + // Call on empty unifier undos a no-op unify operation. + return + } + u.u.delete(u.k) + u.next.Undo() +} + +type bindings struct { + id uint64 + values bindingsArrayHashmap + instr *Instrumentation +} + +func newBindings(id uint64, instr *Instrumentation) *bindings { + values := newBindingsArrayHashmap() + return &bindings{id, values, instr} +} + +func (u *bindings) Iter(caller *bindings, iter func(*ast.Term, *ast.Term) error) error { + + var err error + + u.values.Iter(func(k *ast.Term, v value) bool { + if err != nil { + return true + } + err = iter(k, u.PlugNamespaced(k, caller)) + + return false + }) + + return err +} + +func (u *bindings) Namespace(x ast.Node, caller *bindings) { + vis := namespacingVisitor{ + b: u, + caller: caller, + } + ast.NewGenericVisitor(vis.Visit).Walk(x) +} + +func (u *bindings) Plug(a *ast.Term) *ast.Term { + return u.PlugNamespaced(a, nil) +} + +func (u *bindings) PlugNamespaced(a *ast.Term, caller *bindings) *ast.Term { + if u != nil { + u.instr.startTimer(evalOpPlug) + t := u.plugNamespaced(a, caller) + u.instr.stopTimer(evalOpPlug) + return t + } + + return u.plugNamespaced(a, caller) +} + +func (u *bindings) plugNamespaced(a *ast.Term, caller *bindings) *ast.Term { + switch v := a.Value.(type) { + case ast.Var: + b, next := u.apply(a) + if a != b || u != next { + return next.plugNamespaced(b, caller) + } + return u.namespaceVar(b, caller) + case ast.Array: + cpy := *a + arr := make(ast.Array, len(v)) + for i := 0; i < len(arr); i++ { + arr[i] = u.plugNamespaced(v[i], caller) + } + cpy.Value = arr + return &cpy + case ast.Object: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return u.plugNamespaced(k, caller), u.plugNamespaced(v, caller), nil + }) + return &cpy + case ast.Set: + cpy := *a + cpy.Value, _ = v.Map(func(x *ast.Term) (*ast.Term, error) { + return u.plugNamespaced(x, caller), nil + }) + return &cpy + case ast.Ref: + cpy := *a + ref := make(ast.Ref, len(v)) + for i := 0; i < len(ref); i++ { + ref[i] = u.plugNamespaced(v[i], caller) + } + cpy.Value = ref + return &cpy + } + return a +} + +func (u *bindings) bind(a *ast.Term, b *ast.Term, other *bindings) *undo { + u.values.Put(a, value{ + u: other, + v: b, + }) + return &undo{a, u, nil} +} + +func (u *bindings) apply(a *ast.Term) (*ast.Term, *bindings) { + // Early exit for non-var terms. Only vars are bound in the binding list, + // so the lookup below will always fail for non-var terms. In some cases, + // the lookup may be expensive as it has to hash the term (which for large + // inputs can be costly.) + _, ok := a.Value.(ast.Var) + if !ok { + return a, u + } + val, ok := u.get(a) + if !ok { + return a, u + } + return val.u.apply(val.v) +} + +func (u *bindings) delete(v *ast.Term) { + u.values.Delete(v) +} + +func (u *bindings) get(v *ast.Term) (value, bool) { + if u == nil { + return value{}, false + } + return u.values.Get(v) +} + +func (u *bindings) String() string { + if u == nil { + return "()" + } + var buf []string + u.values.Iter(func(a *ast.Term, b value) bool { + buf = append(buf, fmt.Sprintf("%v: %v", a, b)) + return false + }) + return fmt.Sprintf("({%v}, %v)", strings.Join(buf, ", "), u.id) +} + +func (u *bindings) namespaceVar(v *ast.Term, caller *bindings) *ast.Term { + name, ok := v.Value.(ast.Var) + if !ok { + panic("illegal value") + } + if caller != nil && caller != u { + // Root documents (i.e., data, input) should never be namespaced because they + // are globally unique. + if !ast.RootDocumentNames.Contains(v) { + return ast.NewTerm(ast.Var(string(name) + fmt.Sprint(u.id))) + } + } + return v +} + +type value struct { + u *bindings + v *ast.Term +} + +func (v value) String() string { + return fmt.Sprintf("(%v, %d)", v.v, v.u.id) +} + +func (v value) equal(other *value) bool { + if v.u == other.u { + return v.v.Equal(other.v) + } + return false +} + +type namespacingVisitor struct { + b *bindings + caller *bindings +} + +func (vis namespacingVisitor) Visit(x interface{}) bool { + switch x := x.(type) { + case *ast.ArrayComprehension: + x.Term = vis.namespaceTerm(x.Term) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.SetComprehension: + x.Term = vis.namespaceTerm(x.Term) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.ObjectComprehension: + x.Key = vis.namespaceTerm(x.Key) + x.Value = vis.namespaceTerm(x.Value) + ast.NewGenericVisitor(vis.Visit).Walk(x.Body) + return true + case *ast.Expr: + switch terms := x.Terms.(type) { + case []*ast.Term: + for i := 1; i < len(terms); i++ { + terms[i] = vis.namespaceTerm(terms[i]) + } + case *ast.Term: + x.Terms = vis.namespaceTerm(terms) + } + for _, w := range x.With { + w.Target = vis.namespaceTerm(w.Target) + w.Value = vis.namespaceTerm(w.Value) + } + } + return false +} + +func (vis namespacingVisitor) namespaceTerm(a *ast.Term) *ast.Term { + switch v := a.Value.(type) { + case ast.Var: + return vis.b.namespaceVar(a, vis.caller) + case ast.Array: + cpy := *a + arr := make(ast.Array, len(v)) + for i := 0; i < len(arr); i++ { + arr[i] = vis.namespaceTerm(v[i]) + } + cpy.Value = arr + return &cpy + case ast.Object: + if a.IsGround() { + return a + } + cpy := *a + cpy.Value, _ = v.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return vis.namespaceTerm(k), vis.namespaceTerm(v), nil + }) + return &cpy + case ast.Set: + cpy := *a + cpy.Value, _ = v.Map(func(x *ast.Term) (*ast.Term, error) { + return vis.namespaceTerm(x), nil + }) + return &cpy + case ast.Ref: + cpy := *a + ref := make(ast.Ref, len(v)) + for i := 0; i < len(ref); i++ { + ref[i] = vis.namespaceTerm(v[i]) + } + cpy.Value = ref + return &cpy + } + return a +} + +const maxLinearScan = 16 + +// bindingsArrayHashMap uses an array with linear scan instead of a hash map for smaller # of entries. Hash maps start to show off their performance advantage only after 16 keys. +type bindingsArrayHashmap struct { + n int // Entries in the array. + a *[maxLinearScan]bindingArrayKeyValue + m map[ast.Var]bindingArrayKeyValue +} + +type bindingArrayKeyValue struct { + key *ast.Term + value value +} + +func newBindingsArrayHashmap() bindingsArrayHashmap { + return bindingsArrayHashmap{} +} + +func (b *bindingsArrayHashmap) Put(key *ast.Term, value value) { + if b.m == nil { + if b.a == nil { + b.a = new([maxLinearScan]bindingArrayKeyValue) + } else if i := b.find(key); i >= 0 { + (*b.a)[i].value = value + return + } + + if b.n < maxLinearScan { + (*b.a)[b.n] = bindingArrayKeyValue{key, value} + b.n++ + return + } + + // Array is full, revert to using the hash map instead. + + b.m = make(map[ast.Var]bindingArrayKeyValue, maxLinearScan+1) + for _, kv := range *b.a { + b.m[kv.key.Value.(ast.Var)] = bindingArrayKeyValue{kv.key, kv.value} + } + b.m[key.Value.(ast.Var)] = bindingArrayKeyValue{key, value} + + b.n = 0 + return + } + + b.m[key.Value.(ast.Var)] = bindingArrayKeyValue{key, value} +} + +func (b *bindingsArrayHashmap) Get(key *ast.Term) (value, bool) { + if b.m == nil { + if i := b.find(key); i >= 0 { + return (*b.a)[i].value, true + } + + return value{}, false + } + + v, ok := b.m[key.Value.(ast.Var)] + if ok { + return v.value, true + } + + return value{}, false +} + +func (b *bindingsArrayHashmap) Delete(key *ast.Term) { + if b.m == nil { + if i := b.find(key); i >= 0 { + n := b.n - 1 + if i < n { + (*b.a)[i] = (*b.a)[n] + } + + b.n = n + } + return + } + + delete(b.m, key.Value.(ast.Var)) +} + +func (b *bindingsArrayHashmap) Iter(f func(k *ast.Term, v value) bool) { + if b.m == nil { + for i := 0; i < b.n; i++ { + if f((*b.a)[i].key, (*b.a)[i].value) { + return + } + } + return + } + + for _, v := range b.m { + if f(v.key, v.value) { + return + } + } +} + +func (b *bindingsArrayHashmap) find(key *ast.Term) int { + v := key.Value.(ast.Var) + for i := 0; i < b.n; i++ { + if (*b.a)[i].key.Value.(ast.Var) == v { + return i + } + } + + return -1 +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/bits.go b/vendor/github.com/open-policy-agent/opa/topdown/bits.go new file mode 100644 index 000000000..7a63c0df1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/bits.go @@ -0,0 +1,88 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "math/big" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +type bitsArity1 func(a *big.Int) (*big.Int, error) +type bitsArity2 func(a, b *big.Int) (*big.Int, error) + +func bitsOr(a, b *big.Int) (*big.Int, error) { + return new(big.Int).Or(a, b), nil +} + +func bitsAnd(a, b *big.Int) (*big.Int, error) { + return new(big.Int).And(a, b), nil +} + +func bitsNegate(a *big.Int) (*big.Int, error) { + return new(big.Int).Not(a), nil +} + +func bitsXOr(a, b *big.Int) (*big.Int, error) { + return new(big.Int).Xor(a, b), nil +} + +func bitsShiftLeft(a, b *big.Int) (*big.Int, error) { + if b.Sign() == -1 { + return nil, builtins.NewOperandErr(2, "must be an unsigned integer number but got a negative integer") + } + shift := uint(b.Uint64()) + return new(big.Int).Lsh(a, shift), nil +} + +func bitsShiftRight(a, b *big.Int) (*big.Int, error) { + if b.Sign() == -1 { + return nil, builtins.NewOperandErr(2, "must be an unsigned integer number but got a negative integer") + } + shift := uint(b.Uint64()) + return new(big.Int).Rsh(a, shift), nil +} + +func builtinBitsArity1(fn bitsArity1) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + i, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + iOut, err := fn(i) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.IntToNumber(iOut))) + } +} + +func builtinBitsArity2(fn bitsArity2) BuiltinFunc { + return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + i1, err := builtins.BigIntOperand(operands[0].Value, 1) + if err != nil { + return err + } + i2, err := builtins.BigIntOperand(operands[1].Value, 2) + if err != nil { + return err + } + iOut, err := fn(i1, i2) + if err != nil { + return err + } + return iter(ast.NewTerm(builtins.IntToNumber(iOut))) + } +} + +func init() { + RegisterBuiltinFunc(ast.BitsOr.Name, builtinBitsArity2(bitsOr)) + RegisterBuiltinFunc(ast.BitsAnd.Name, builtinBitsArity2(bitsAnd)) + RegisterBuiltinFunc(ast.BitsNegate.Name, builtinBitsArity1(bitsNegate)) + RegisterBuiltinFunc(ast.BitsXOr.Name, builtinBitsArity2(bitsXOr)) + RegisterBuiltinFunc(ast.BitsShiftLeft.Name, builtinBitsArity2(bitsShiftLeft)) + RegisterBuiltinFunc(ast.BitsShiftRight.Name, builtinBitsArity2(bitsShiftRight)) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/builtins.go b/vendor/github.com/open-policy-agent/opa/topdown/builtins.go new file mode 100644 index 000000000..36e333f88 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/builtins.go @@ -0,0 +1,160 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "context" + "fmt" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +type ( + // FunctionalBuiltin1 is deprecated. Use BuiltinFunc instead. + FunctionalBuiltin1 func(op1 ast.Value) (output ast.Value, err error) + + // FunctionalBuiltin2 is deprecated. Use BuiltinFunc instead. + FunctionalBuiltin2 func(op1, op2 ast.Value) (output ast.Value, err error) + + // FunctionalBuiltin3 is deprecated. Use BuiltinFunc instead. + FunctionalBuiltin3 func(op1, op2, op3 ast.Value) (output ast.Value, err error) + + // FunctionalBuiltin4 is deprecated. Use BuiltinFunc instead. + FunctionalBuiltin4 func(op1, op2, op3, op4 ast.Value) (output ast.Value, err error) + + // BuiltinContext contains context from the evaluator that may be used by + // built-in functions. + BuiltinContext struct { + Context context.Context // request context that was passed when query started + Cancel Cancel // atomic value that signals evaluation to halt + Runtime *ast.Term // runtime information on the OPA instance + Cache builtins.Cache // built-in function state cache + Location *ast.Location // location of built-in call + Tracers []Tracer // tracer objects for trace() built-in function + QueryID uint64 // identifies query being evaluated + ParentID uint64 // identifies parent of query being evaluated + } + + // BuiltinFunc defines an interface for implementing built-in functions. + // The built-in function is called with the plugged operands from the call + // (including the output operands.) The implementation should evaluate the + // operands and invoke the iterator for each successful/defined output + // value. + BuiltinFunc func(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error +) + +// RegisterBuiltinFunc adds a new built-in function to the evaluation engine. +func RegisterBuiltinFunc(name string, f BuiltinFunc) { + builtinFunctions[name] = builtinErrorWrapper(name, f) +} + +// RegisterFunctionalBuiltin1 is deprecated use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin1(name string, fun FunctionalBuiltin1) { + builtinFunctions[name] = functionalWrapper1(name, fun) +} + +// RegisterFunctionalBuiltin2 is deprecated use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin2(name string, fun FunctionalBuiltin2) { + builtinFunctions[name] = functionalWrapper2(name, fun) +} + +// RegisterFunctionalBuiltin3 is deprecated use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin3(name string, fun FunctionalBuiltin3) { + builtinFunctions[name] = functionalWrapper3(name, fun) +} + +// RegisterFunctionalBuiltin4 is deprecated use RegisterBuiltinFunc instead. +func RegisterFunctionalBuiltin4(name string, fun FunctionalBuiltin4) { + builtinFunctions[name] = functionalWrapper4(name, fun) +} + +// GetBuiltin returns a built-in function implementation, nil if no built-in found. +func GetBuiltin(name string) BuiltinFunc { + return builtinFunctions[name] +} + +// BuiltinEmpty is deprecated. +type BuiltinEmpty struct{} + +func (BuiltinEmpty) Error() string { + return "" +} + +var builtinFunctions = map[string]BuiltinFunc{} + +func builtinErrorWrapper(name string, fn BuiltinFunc) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + err := fn(bctx, args, iter) + if err == nil { + return nil + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper1(name string, fn FunctionalBuiltin1) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper2(name string, fn FunctionalBuiltin2) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper3(name string, fn FunctionalBuiltin3) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value, args[2].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func functionalWrapper4(name string, fn FunctionalBuiltin4) BuiltinFunc { + return func(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + result, err := fn(args[0].Value, args[1].Value, args[2].Value, args[3].Value) + if err == nil { + return iter(ast.NewTerm(result)) + } + if _, empty := err.(BuiltinEmpty); empty { + return nil + } + return handleBuiltinErr(name, bctx.Location, err) + } +} + +func handleBuiltinErr(name string, loc *ast.Location, err error) error { + switch err := err.(type) { + case BuiltinEmpty: + return nil + case *Error: + return err + case builtins.ErrOperand: + return &Error{ + Code: TypeErr, + Message: fmt.Sprintf("%v: %v", string(name), err.Error()), + Location: loc, + } + default: + return &Error{ + Code: BuiltinErr, + Message: fmt.Sprintf("%v: %v", string(name), err.Error()), + Location: loc, + } + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/builtins/builtins.go b/vendor/github.com/open-policy-agent/opa/topdown/builtins/builtins.go new file mode 100644 index 000000000..861167f39 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/builtins/builtins.go @@ -0,0 +1,235 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package builtins contains utilities for implementing built-in functions. +package builtins + +import ( + "fmt" + "math/big" + "strings" + + "github.com/open-policy-agent/opa/ast" +) + +// Cache defines the built-in cache used by the top-down evaluation. The keys +// must be comparable and should not be of type string. +type Cache map[interface{}]interface{} + +// Put updates the cache for the named built-in. +func (c Cache) Put(k, v interface{}) { + c[k] = v +} + +// Get returns the cached value for k. +func (c Cache) Get(k interface{}) (interface{}, bool) { + v, ok := c[k] + return v, ok +} + +// ErrOperand represents an invalid operand has been passed to a built-in +// function. Built-ins should return ErrOperand to indicate a type error has +// occurred. +type ErrOperand string + +func (err ErrOperand) Error() string { + return string(err) +} + +// NewOperandErr returns a generic operand error. +func NewOperandErr(pos int, f string, a ...interface{}) error { + f = fmt.Sprintf("operand %v ", pos) + f + return ErrOperand(fmt.Sprintf(f, a...)) +} + +// NewOperandTypeErr returns an operand error indicating the operand's type was wrong. +func NewOperandTypeErr(pos int, got ast.Value, expected ...string) error { + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v but got %v", expected[0], ast.TypeName(got)) + } + + return NewOperandErr(pos, "must be one of {%v} but got %v", strings.Join(expected, ", "), ast.TypeName(got)) +} + +// NewOperandElementErr returns an operand error indicating an element in the +// composite operand was wrong. +func NewOperandElementErr(pos int, composite ast.Value, got ast.Value, expected ...string) error { + + tpe := ast.TypeName(composite) + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v of %vs but got %v containing %v", tpe, expected[0], tpe, ast.TypeName(got)) + } + + return NewOperandErr(pos, "must be %v of (any of) {%v} but got %v containing %v", tpe, strings.Join(expected, ", "), tpe, ast.TypeName(got)) +} + +// NewOperandEnumErr returns an operand error indicating a value was wrong. +func NewOperandEnumErr(pos int, expected ...string) error { + + if len(expected) == 1 { + return NewOperandErr(pos, "must be %v", expected[0]) + } + + return NewOperandErr(pos, "must be one of {%v}", strings.Join(expected, ", ")) +} + +// IntOperand converts x to an int. If the cast fails, a descriptive error is +// returned. +func IntOperand(x ast.Value, pos int) (int, error) { + n, ok := x.(ast.Number) + if !ok { + return 0, NewOperandTypeErr(pos, x, "number") + } + + i, ok := n.Int() + if !ok { + return 0, NewOperandErr(pos, "must be integer number but got floating-point number") + } + + return i, nil +} + +// BigIntOperand converts x to a big int. If the cast fails, a descriptive error +// is returned. +func BigIntOperand(x ast.Value, pos int) (*big.Int, error) { + n, err := NumberOperand(x, 1) + if err != nil { + return nil, NewOperandTypeErr(pos, x, "integer") + } + bi, err := NumberToInt(n) + if err != nil { + return nil, NewOperandErr(pos, "must be integer number but got floating-point number") + } + + return bi, nil +} + +// NumberOperand converts x to a number. If the cast fails, a descriptive error is +// returned. +func NumberOperand(x ast.Value, pos int) (ast.Number, error) { + n, ok := x.(ast.Number) + if !ok { + return ast.Number(""), NewOperandTypeErr(pos, x, "number") + } + return n, nil +} + +// SetOperand converts x to a set. If the cast fails, a descriptive error is +// returned. +func SetOperand(x ast.Value, pos int) (ast.Set, error) { + s, ok := x.(ast.Set) + if !ok { + return nil, NewOperandTypeErr(pos, x, "set") + } + return s, nil +} + +// StringOperand converts x to a string. If the cast fails, a descriptive error is +// returned. +func StringOperand(x ast.Value, pos int) (ast.String, error) { + s, ok := x.(ast.String) + if !ok { + return ast.String(""), NewOperandTypeErr(pos, x, "string") + } + return s, nil +} + +// ObjectOperand converts x to an object. If the cast fails, a descriptive +// error is returned. +func ObjectOperand(x ast.Value, pos int) (ast.Object, error) { + o, ok := x.(ast.Object) + if !ok { + return nil, NewOperandTypeErr(pos, x, "object") + } + return o, nil +} + +// ArrayOperand converts x to an array. If the cast fails, a descriptive +// error is returned. +func ArrayOperand(x ast.Value, pos int) (ast.Array, error) { + a, ok := x.(ast.Array) + if !ok { + return nil, NewOperandTypeErr(pos, x, "array") + } + return a, nil +} + +// NumberToFloat converts n to a big float. +func NumberToFloat(n ast.Number) *big.Float { + r, ok := new(big.Float).SetString(string(n)) + if !ok { + panic("illegal value") + } + return r +} + +// FloatToNumber converts f to a number. +func FloatToNumber(f *big.Float) ast.Number { + return ast.Number(f.String()) +} + +// NumberToInt converts n to a big int. +// If n cannot be converted to an big int, an error is returned. +func NumberToInt(n ast.Number) (*big.Int, error) { + f := NumberToFloat(n) + r, accuracy := f.Int(nil) + if accuracy != big.Exact { + return nil, fmt.Errorf("illegal value") + } + return r, nil +} + +// IntToNumber converts i to a number. +func IntToNumber(i *big.Int) ast.Number { + return ast.Number(i.String()) +} + +// StringSliceOperand converts x to a []string. If the cast fails, a descriptive error is +// returned. +func StringSliceOperand(x ast.Value, pos int) ([]string, error) { + a, err := ArrayOperand(x, pos) + if err != nil { + return nil, err + } + + var f = make([]string, len(a)) + for k, b := range a { + c, ok := b.Value.(ast.String) + if !ok { + return nil, NewOperandElementErr(pos, x, b.Value, "[]string") + } + + f[k] = string(c) + } + + return f, nil +} + +// RuneSliceOperand converts x to a []rune. If the cast fails, a descriptive error is +// returned. +func RuneSliceOperand(x ast.Value, pos int) ([]rune, error) { + a, err := ArrayOperand(x, pos) + if err != nil { + return nil, err + } + + var f = make([]rune, len(a)) + for k, b := range a { + c, ok := b.Value.(ast.String) + if !ok { + return nil, NewOperandElementErr(pos, x, b.Value, "string") + } + + d := []rune(string(c)) + if len(d) != 1 { + return nil, NewOperandElementErr(pos, x, b.Value, "rune") + } + + f[k] = d[0] + } + + return f, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/cache.go b/vendor/github.com/open-policy-agent/opa/topdown/cache.go new file mode 100644 index 000000000..33a8b16f2 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/cache.go @@ -0,0 +1,166 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/util" +) + +type virtualCache struct { + stack []*virtualCacheElem +} + +type virtualCacheElem struct { + value *ast.Term + children *util.HashMap +} + +func newVirtualCache() *virtualCache { + cache := &virtualCache{} + cache.Push() + return cache +} + +func (c *virtualCache) Push() { + c.stack = append(c.stack, newVirtualCacheElem()) +} + +func (c *virtualCache) Pop() { + c.stack = c.stack[:len(c.stack)-1] +} + +func (c *virtualCache) Get(ref ast.Ref) *ast.Term { + node := c.stack[len(c.stack)-1] + for i := 0; i < len(ref); i++ { + x, ok := node.children.Get(ref[i]) + if !ok { + return nil + } + node = x.(*virtualCacheElem) + } + return node.value +} + +func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) { + node := c.stack[len(c.stack)-1] + for i := 0; i < len(ref); i++ { + x, ok := node.children.Get(ref[i]) + if ok { + node = x.(*virtualCacheElem) + } else { + next := newVirtualCacheElem() + node.children.Put(ref[i], next) + node = next + } + } + node.value = value +} + +func newVirtualCacheElem() *virtualCacheElem { + return &virtualCacheElem{children: newVirtualCacheHashMap()} +} + +func newVirtualCacheHashMap() *util.HashMap { + return util.NewHashMap(func(a, b util.T) bool { + return a.(*ast.Term).Equal(b.(*ast.Term)) + }, func(x util.T) int { + return x.(*ast.Term).Hash() + }) +} + +// baseCache implements a trie structure to cache base documents read out of +// storage. Values inserted into the cache may contain other values that were +// previously inserted. In this case, the previous values are erased from the +// structure. +type baseCache struct { + root *baseCacheElem +} + +func newBaseCache() *baseCache { + return &baseCache{ + root: newBaseCacheElem(), + } +} + +func (c *baseCache) Get(ref ast.Ref) ast.Value { + node := c.root + for i := 0; i < len(ref); i++ { + node = node.children[ref[i].Value] + if node == nil { + return nil + } else if node.value != nil { + result, err := node.value.Find(ref[i+1:]) + if err != nil { + return nil + } + return result + } + } + return nil +} + +func (c *baseCache) Put(ref ast.Ref, value ast.Value) { + node := c.root + for i := 0; i < len(ref); i++ { + if child, ok := node.children[ref[i].Value]; ok { + node = child + } else { + child := newBaseCacheElem() + node.children[ref[i].Value] = child + node = child + } + } + node.set(value) +} + +type baseCacheElem struct { + value ast.Value + children map[ast.Value]*baseCacheElem +} + +func newBaseCacheElem() *baseCacheElem { + return &baseCacheElem{ + children: map[ast.Value]*baseCacheElem{}, + } +} + +func (e *baseCacheElem) set(value ast.Value) { + e.value = value + e.children = map[ast.Value]*baseCacheElem{} +} + +type refStack struct { + sl []refStackElem +} + +type refStackElem struct { + refs []ast.Ref +} + +func newRefStack() *refStack { + return &refStack{} +} + +func (s *refStack) Push(refs []ast.Ref) { + s.sl = append(s.sl, refStackElem{refs: refs}) +} + +func (s *refStack) Pop() { + s.sl = s.sl[:len(s.sl)-1] +} + +func (s *refStack) Prefixed(ref ast.Ref) bool { + if s != nil { + for i := len(s.sl) - 1; i >= 0; i-- { + for j := range s.sl[i].refs { + if ref.HasPrefix(s.sl[i].refs[j]) { + return true + } + } + } + } + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/cancel.go b/vendor/github.com/open-policy-agent/opa/topdown/cancel.go new file mode 100644 index 000000000..534e0799a --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/cancel.go @@ -0,0 +1,33 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "sync/atomic" +) + +// Cancel defines the interface for cancelling topdown queries. Cancel +// operations are thread-safe and idempotent. +type Cancel interface { + Cancel() + Cancelled() bool +} + +type cancel struct { + flag int32 +} + +// NewCancel returns a new Cancel object. +func NewCancel() Cancel { + return &cancel{} +} + +func (c *cancel) Cancel() { + atomic.StoreInt32(&c.flag, 1) +} + +func (c *cancel) Cancelled() bool { + return atomic.LoadInt32(&c.flag) != 0 +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/casts.go b/vendor/github.com/open-policy-agent/opa/topdown/casts.go new file mode 100644 index 000000000..d79aac321 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/casts.go @@ -0,0 +1,113 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "strconv" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinToNumber(a ast.Value) (ast.Value, error) { + switch a := a.(type) { + case ast.Null: + return ast.Number("0"), nil + case ast.Boolean: + if a { + return ast.Number("1"), nil + } + return ast.Number("0"), nil + case ast.Number: + return a, nil + case ast.String: + _, err := strconv.ParseFloat(string(a), 64) + if err != nil { + return nil, err + } + return ast.Number(a), nil + } + return nil, builtins.NewOperandTypeErr(1, a, "null", "boolean", "number", "string") +} + +// Deprecated in v0.13.0. +func builtinToArray(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Array: + return val, nil + case ast.Set: + arr := make(ast.Array, val.Len()) + i := 0 + val.Foreach(func(term *ast.Term) { + arr[i] = term + i++ + }) + return arr, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "array", "set") + } +} + +// Deprecated in v0.13.0. +func builtinToSet(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Array: + return ast.NewSet(val...), nil + case ast.Set: + return val, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "array", "set") + } +} + +// Deprecated in v0.13.0. +func builtinToString(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.String: + return val, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "string") + } +} + +// Deprecated in v0.13.0. +func builtinToBoolean(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Boolean: + return val, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "boolean") + } +} + +// Deprecated in v0.13.0. +func builtinToNull(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Null: + return val, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "null") + } +} + +// Deprecated in v0.13.0. +func builtinToObject(a ast.Value) (ast.Value, error) { + switch val := a.(type) { + case ast.Object: + return val, nil + default: + return nil, builtins.NewOperandTypeErr(1, a, "object") + } +} + +func init() { + RegisterFunctionalBuiltin1(ast.ToNumber.Name, builtinToNumber) + RegisterFunctionalBuiltin1(ast.CastArray.Name, builtinToArray) + RegisterFunctionalBuiltin1(ast.CastSet.Name, builtinToSet) + RegisterFunctionalBuiltin1(ast.CastString.Name, builtinToString) + RegisterFunctionalBuiltin1(ast.CastBoolean.Name, builtinToBoolean) + RegisterFunctionalBuiltin1(ast.CastNull.Name, builtinToNull) + RegisterFunctionalBuiltin1(ast.CastObject.Name, builtinToObject) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/cidr.go b/vendor/github.com/open-policy-agent/opa/topdown/cidr.go new file mode 100644 index 000000000..b65628dae --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/cidr.go @@ -0,0 +1,157 @@ +package topdown + +import ( + "fmt" + "math/big" + "net" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func getNetFromOperand(v ast.Value) (*net.IPNet, error) { + subnetStringA, err := builtins.StringOperand(v, 1) + if err != nil { + return nil, err + } + + _, cidrnet, err := net.ParseCIDR(string(subnetStringA)) + if err != nil { + return nil, err + } + + return cidrnet, nil +} + +func getLastIP(cidr *net.IPNet) (net.IP, error) { + prefixLen, bits := cidr.Mask.Size() + if prefixLen == 0 && bits == 0 { + // non-standard mask, see https://golang.org/pkg/net/#IPMask.Size + return nil, fmt.Errorf("CIDR mask is in non-standard format") + } + var lastIP []byte + if prefixLen == bits { + // Special case for single ip address ranges ex: 192.168.1.1/32 + // We can just use the starting IP as the last IP + lastIP = cidr.IP + } else { + // Use big.Int's so we can handle ipv6 addresses + firstIPInt := new(big.Int) + firstIPInt.SetBytes(cidr.IP) + hostLen := uint(bits) - uint(prefixLen) + lastIPInt := big.NewInt(1) + lastIPInt.Lsh(lastIPInt, hostLen) + lastIPInt.Sub(lastIPInt, big.NewInt(1)) + lastIPInt.Or(lastIPInt, firstIPInt) + + ipBytes := lastIPInt.Bytes() + lastIP = make([]byte, bits/8) + + // Pack our IP bytes into the end of the return array, + // since big.Int.Bytes() removes front zero padding. + for i := 1; i <= len(lastIPInt.Bytes()); i++ { + lastIP[len(lastIP)-i] = ipBytes[len(ipBytes)-i] + } + } + + return lastIP, nil +} + +func builtinNetCIDRIntersects(a, b ast.Value) (ast.Value, error) { + cidrnetA, err := getNetFromOperand(a) + if err != nil { + return nil, err + } + + cidrnetB, err := getNetFromOperand(b) + if err != nil { + return nil, err + } + + // If either net contains the others starting IP they are overlapping + cidrsOverlap := (cidrnetA.Contains(cidrnetB.IP) || cidrnetB.Contains(cidrnetA.IP)) + + return ast.Boolean(cidrsOverlap), nil +} + +func builtinNetCIDRContains(a, b ast.Value) (ast.Value, error) { + cidrnetA, err := getNetFromOperand(a) + if err != nil { + return nil, err + } + + // b could be either an IP addressor CIDR string, try to parse it as an IP first, fall back to CIDR + bStr, err := builtins.StringOperand(b, 1) + if err != nil { + return nil, err + } + + ip := net.ParseIP(string(bStr)) + if ip != nil { + return ast.Boolean(cidrnetA.Contains(ip)), nil + } + + // It wasn't an IP, try and parse it as a CIDR + cidrnetB, err := getNetFromOperand(b) + if err != nil { + return nil, fmt.Errorf("not a valid textual representation of an IP address or CIDR: %s", string(bStr)) + } + + // We can determine if cidr A contains cidr B iff A contains the starting address of B and the last address in B. + cidrContained := false + if cidrnetA.Contains(cidrnetB.IP) { + // Only spend time calculating the last IP if the starting IP is already verified to be in cidr A + lastIP, err := getLastIP(cidrnetB) + if err != nil { + return nil, err + } + cidrContained = cidrnetA.Contains(lastIP) + } + + return ast.Boolean(cidrContained), nil +} + +func builtinNetCIDRExpand(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + s, err := builtins.StringOperand(operands[0].Value, 1) + if err != nil { + return err + } + + ip, ipNet, err := net.ParseCIDR(string(s)) + if err != nil { + return err + } + + result := ast.NewSet() + + for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incIP(ip) { + + if bctx.Cancel != nil && bctx.Cancel.Cancelled() { + return &Error{ + Code: CancelErr, + Message: "net.cidr_expand: timed out before generating all IP addresses", + } + } + + result.Add(ast.StringTerm(ip.String())) + } + + return iter(ast.NewTerm(result)) +} + +func incIP(ip net.IP) { + for j := len(ip) - 1; j >= 0; j-- { + ip[j]++ + if ip[j] > 0 { + break + } + } +} + +func init() { + RegisterFunctionalBuiltin2(ast.NetCIDROverlap.Name, builtinNetCIDRContains) + RegisterFunctionalBuiltin2(ast.NetCIDRIntersects.Name, builtinNetCIDRIntersects) + RegisterFunctionalBuiltin2(ast.NetCIDRContains.Name, builtinNetCIDRContains) + RegisterBuiltinFunc(ast.NetCIDRExpand.Name, builtinNetCIDRExpand) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/comparison.go b/vendor/github.com/open-policy-agent/opa/topdown/comparison.go new file mode 100644 index 000000000..96be984ac --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/comparison.go @@ -0,0 +1,48 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/ast" + +type compareFunc func(a, b ast.Value) bool + +func compareGreaterThan(a, b ast.Value) bool { + return ast.Compare(a, b) > 0 +} + +func compareGreaterThanEq(a, b ast.Value) bool { + return ast.Compare(a, b) >= 0 +} + +func compareLessThan(a, b ast.Value) bool { + return ast.Compare(a, b) < 0 +} + +func compareLessThanEq(a, b ast.Value) bool { + return ast.Compare(a, b) <= 0 +} + +func compareNotEq(a, b ast.Value) bool { + return ast.Compare(a, b) != 0 +} + +func compareEq(a, b ast.Value) bool { + return ast.Compare(a, b) == 0 +} + +func builtinCompare(cmp compareFunc) FunctionalBuiltin2 { + return func(a, b ast.Value) (ast.Value, error) { + return ast.Boolean(cmp(a, b)), nil + } +} + +func init() { + RegisterFunctionalBuiltin2(ast.GreaterThan.Name, builtinCompare(compareGreaterThan)) + RegisterFunctionalBuiltin2(ast.GreaterThanEq.Name, builtinCompare(compareGreaterThanEq)) + RegisterFunctionalBuiltin2(ast.LessThan.Name, builtinCompare(compareLessThan)) + RegisterFunctionalBuiltin2(ast.LessThanEq.Name, builtinCompare(compareLessThanEq)) + RegisterFunctionalBuiltin2(ast.NotEqual.Name, builtinCompare(compareNotEq)) + RegisterFunctionalBuiltin2(ast.Equal.Name, builtinCompare(compareEq)) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go b/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go new file mode 100644 index 000000000..820184b66 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go @@ -0,0 +1,484 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package copypropagation + +import ( + "sort" + + "github.com/open-policy-agent/opa/ast" +) + +// CopyPropagator implements a simple copy propagation optimization to remove +// intermediate variables in partial evaluation results. +// +// For example, given the query: input.x > 1 where 'input' is unknown, the +// compiled query would become input.x = a; a > 1 which would remain in the +// partial evaluation result. The CopyPropagator will remove the variable +// assignment so that partial evaluation simply outputs input.x > 1. +// +// In many cases, copy propagation can remove all variables from the result of +// partial evaluation which simplifies evaluation for non-OPA consumers. +// +// In some cases, copy propagation cannot remove all variables. If the output of +// a built-in call is subsequently used as a ref head, the output variable must +// be kept. For example. sort(input, x); x[0] == 1. In this case, copy +// propagation cannot replace x[0] == 1 with sort(input, x)[0] == 1 as this is +// not legal. +type CopyPropagator struct { + livevars ast.VarSet // vars that must be preserved in the resulting query + sorted []ast.Var // sorted copy of vars to ensure deterministic result + ensureNonEmptyBody bool +} + +// New returns a new CopyPropagator that optimizes queries while preserving vars +// in the livevars set. +func New(livevars ast.VarSet) *CopyPropagator { + + sorted := make([]ast.Var, 0, len(livevars)) + for v := range livevars { + sorted = append(sorted, v) + } + + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].Compare(sorted[j]) < 0 + }) + + return &CopyPropagator{livevars: livevars, sorted: sorted} +} + +// WithEnsureNonEmptyBody configures p to ensure that results are always non-empty. +func (p *CopyPropagator) WithEnsureNonEmptyBody(yes bool) *CopyPropagator { + p.ensureNonEmptyBody = yes + return p +} + +// Apply executes the copy propagation optimization and returns a new query. +func (p *CopyPropagator) Apply(query ast.Body) (result ast.Body) { + + uf, ok := makeDisjointSets(p.livevars, query) + if !ok { + return query + } + + // Compute set of vars that appear in the head of refs in the query. If a var + // is dereferenced, we cannot plug it with a constant value so the constant on + // the union-find root must be unset (e.g., [1][0] is not legal.) + headvars := ast.NewVarSet() + ast.WalkRefs(query, func(x ast.Ref) bool { + if v, ok := x[0].Value.(ast.Var); ok { + if root, ok := uf.Find(v); ok { + root.constant = nil + headvars.Add(root.key) + } else { + headvars.Add(v) + } + } + return false + }) + + bindings := map[ast.Var]*binding{} + + for _, expr := range query { + + pctx := &plugContext{ + bindings: bindings, + uf: uf, + negated: expr.Negated, + headvars: headvars, + } + + if expr, keep := p.plugBindings(pctx, expr); keep { + if p.updateBindings(pctx, expr) { + result.Append(expr) + } + } + } + + // Run post-processing step on the query to ensure that all live vars are bound + // in the result. The plugging that happens above substitutes all vars in the + // same set with the root. + // + // This step should run before the next step to prevent unnecessary bindings + // from being added to the result. For example: + // + // - Given the following result: + // - Given the following bindings: x/input.x and y/input + // - Given the following liveset: {x} + // + // If this step were to run AFTER the following step, the output would be: + // + // x = input.x; y = input + // + // Even though y = input is not required. + for _, v := range p.sorted { + if root, ok := uf.Find(v); ok { + if root.constant != nil { + result.Append(ast.Equality.Expr(ast.NewTerm(v), root.constant)) + } else if b, ok := bindings[root.key]; ok { + result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(b.v))) + } else if root.key != v { + result.Append(ast.Equality.Expr(ast.NewTerm(v), ast.NewTerm(root.key))) + } + } + } + + // Run post-processing step on query to ensure that all killed exprs are + // accounted for. If an expr is killed but the binding is never used, the query + // must still include the expr. For example, given the query 'input.x = a' and + // an empty livevar set, the result must include the ref input.x otherwise the + // query could be satisfied without input.x being defined. When exprs are + // killed we initialize the binding counter to zero and then increment it each + // time the binding is substituted. if the binding was never substituted it + // means the binding value must be added back into the query. + for _, b := range sortbindings(bindings) { + if !b.containedIn(result) { + result.Append(ast.Equality.Expr(ast.NewTerm(b.k), ast.NewTerm(b.v))) + } + } + + if p.ensureNonEmptyBody && len(result) == 0 { + result = append(result, ast.NewExpr(ast.BooleanTerm(true))) + } + + return result +} + +// plugBindings applies the binding list and union-find to x. This process +// removes as many variables as possible. +func (p *CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) (*ast.Expr, bool) { + + // Kill single term expressions that are in the binding list. They will be + // re-added during post-processing if needed. + if term, ok := expr.Terms.(*ast.Term); ok { + if v, ok := term.Value.(ast.Var); ok { + if root, ok := pctx.uf.Find(v); ok { + if _, ok := pctx.bindings[root.key]; ok { + return nil, false + } + } + } + } + + xform := bindingPlugTransform{ + pctx: pctx, + } + + // Deep copy the expression as it may be mutated during the transform and + // the caller running copy propagation may have references to the + // expression. Note, the transform does not contain any error paths and + // should never return a non-expression value for the root so consider + // errors unreachable. + x, err := ast.Transform(xform, expr.Copy()) + + if expr, ok := x.(*ast.Expr); !ok || err != nil { + panic("unreachable") + } else { + return expr, true + } +} + +type bindingPlugTransform struct { + pctx *plugContext +} + +func (t bindingPlugTransform) Transform(x interface{}) (interface{}, error) { + switch x := x.(type) { + case ast.Var: + return t.plugBindingsVar(t.pctx, x), nil + case ast.Ref: + return t.plugBindingsRef(t.pctx, x), nil + default: + return x, nil + } +} + +func (t bindingPlugTransform) plugBindingsVar(pctx *plugContext, v ast.Var) (result ast.Value) { + + result = v + + // Apply union-find to remove redundant variables from input. + if root, ok := pctx.uf.Find(v); ok { + result = root.Value() + } + + // Apply binding list to substitute remaining vars. + if v, ok := result.(ast.Var); ok { + if b, ok := pctx.bindings[v]; ok { + if !pctx.negated || b.v.IsGround() { + result = b.v + } + } + } + + return result +} + +func (t bindingPlugTransform) plugBindingsRef(pctx *plugContext, v ast.Ref) ast.Ref { + + // Apply union-find to remove redundant variables from input. + if root, ok := pctx.uf.Find(v[0].Value.(ast.Var)); ok { + v[0].Value = root.Value() + } + + result := v + + // Refs require special handling. If the head of the ref was killed, then + // the rest of the ref must be concatenated with the new base. + // + // Invariant: ref heads can only be replaced by refs (not calls). + if b, ok := pctx.bindings[v[0].Value.(ast.Var)]; ok { + if !pctx.negated || b.v.IsGround() { + result = b.v.(ast.Ref).Concat(v[1:]) + } + } + + return result +} + +// updateBindings returns false if the expression can be killed. If the +// expression is killed, the binding list is updated to map a var to value. +func (p *CopyPropagator) updateBindings(pctx *plugContext, expr *ast.Expr) bool { + if pctx.negated || len(expr.With) > 0 { + return true + } + if expr.IsEquality() { + a, b := expr.Operand(0), expr.Operand(1) + if a.Equal(b) { + return false + } + k, v, keep := p.updateBindingsEq(a, b) + if !keep { + if v != nil { + pctx.bindings[k] = newbinding(k, v) + } + return false + } + } else if expr.IsCall() { + terms := expr.Terms.([]*ast.Term) + output := terms[len(terms)-1] + if k, ok := output.Value.(ast.Var); ok && !p.livevars.Contains(k) && !pctx.headvars.Contains(k) { + pctx.bindings[k] = newbinding(k, ast.CallTerm(terms[:len(terms)-1]...).Value) + return false + } + } + return !isNoop(expr) +} + +func (p *CopyPropagator) updateBindingsEq(a, b *ast.Term) (ast.Var, ast.Value, bool) { + k, v, keep := p.updateBindingsEqAsymmetric(a, b) + if !keep { + return k, v, keep + } + return p.updateBindingsEqAsymmetric(b, a) +} + +func (p *CopyPropagator) updateBindingsEqAsymmetric(a, b *ast.Term) (ast.Var, ast.Value, bool) { + k, ok := a.Value.(ast.Var) + if !ok || p.livevars.Contains(k) { + return "", nil, true + } + + switch b.Value.(type) { + case ast.Ref, ast.Call: + return k, b.Value, false + } + + return "", nil, true +} + +type plugContext struct { + bindings map[ast.Var]*binding + uf *unionFind + headvars ast.VarSet + negated bool +} + +type binding struct { + k ast.Var + v ast.Value +} + +func newbinding(k ast.Var, v ast.Value) *binding { + return &binding{k: k, v: v} +} + +func (b *binding) containedIn(query ast.Body) bool { + var stop bool + switch v := b.v.(type) { + case ast.Ref: + ast.WalkRefs(query, func(other ast.Ref) bool { + if stop || other.HasPrefix(v) { + stop = true + return stop + } + return false + }) + default: + ast.WalkTerms(query, func(other *ast.Term) bool { + if stop || other.Value.Compare(v) == 0 { + stop = true + return stop + } + return false + }) + } + return stop +} + +func sortbindings(bindings map[ast.Var]*binding) []*binding { + sorted := make([]*binding, 0, len(bindings)) + for _, b := range bindings { + sorted = append(sorted, b) + } + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].k.Compare(sorted[j].k) < 0 + }) + return sorted +} + +type unionFind struct { + roots map[ast.Var]*unionFindRoot + parents map[ast.Var]ast.Var + rank rankFunc +} + +// makeDisjointSets builds the union-find structure for the query. The structure +// is built by processing all of the equality exprs in the query. Sets represent +// vars that must be equal to each other. In addition to vars, each set can have +// at most one constant. If the query contains expressions that cannot be +// satisfied (e.g., because a set has multiple constants) this function returns +// false. +func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) { + uf := newUnionFind(func(r1, r2 *unionFindRoot) (*unionFindRoot, *unionFindRoot) { + if livevars.Contains(r1.key) { + return r1, r2 + } + return r2, r1 + }) + for _, expr := range query { + if expr.IsEquality() && !expr.Negated && len(expr.With) == 0 { + a, b := expr.Operand(0), expr.Operand(1) + varA, ok1 := a.Value.(ast.Var) + varB, ok2 := b.Value.(ast.Var) + if ok1 && ok2 { + if _, ok := uf.Merge(varA, varB); !ok { + return nil, false + } + } else if ok1 && ast.IsConstant(b.Value) { + root := uf.MakeSet(varA) + if root.constant != nil && !root.constant.Equal(b) { + return nil, false + } + root.constant = b + } else if ok2 && ast.IsConstant(a.Value) { + root := uf.MakeSet(varB) + if root.constant != nil && !root.constant.Equal(a) { + return nil, false + } + root.constant = a + } + } + } + + return uf, true +} + +type rankFunc func(*unionFindRoot, *unionFindRoot) (*unionFindRoot, *unionFindRoot) + +func newUnionFind(rank rankFunc) *unionFind { + return &unionFind{ + roots: map[ast.Var]*unionFindRoot{}, + parents: map[ast.Var]ast.Var{}, + rank: rank, + } +} + +func (uf *unionFind) MakeSet(v ast.Var) *unionFindRoot { + + root, ok := uf.Find(v) + if ok { + return root + } + + root = newUnionFindRoot(v) + uf.parents[v] = v + uf.roots[v] = root + return uf.roots[v] +} + +func (uf *unionFind) Find(v ast.Var) (*unionFindRoot, bool) { + + parent, ok := uf.parents[v] + if !ok { + return nil, false + } + + if parent == v { + return uf.roots[v], true + } + + return uf.Find(parent) +} + +func (uf *unionFind) Merge(a, b ast.Var) (*unionFindRoot, bool) { + + r1 := uf.MakeSet(a) + r2 := uf.MakeSet(b) + + if r1 != r2 { + + r1, r2 = uf.rank(r1, r2) + + uf.parents[r2.key] = r1.key + delete(uf.roots, r2.key) + + // Sets can have at most one constant value associated with them. When + // unioning, we must preserve this invariant. If a set has two constants, + // there will be no way to prove the query. + if r1.constant != nil && r2.constant != nil && !r1.constant.Equal(r2.constant) { + return nil, false + } else if r1.constant == nil { + r1.constant = r2.constant + } + } + + return r1, true +} + +type unionFindRoot struct { + key ast.Var + constant *ast.Term +} + +func newUnionFindRoot(key ast.Var) *unionFindRoot { + return &unionFindRoot{ + key: key, + } +} + +func (r *unionFindRoot) Value() ast.Value { + if r.constant != nil { + return r.constant.Value + } + return r.key +} + +func isNoop(expr *ast.Expr) bool { + + if !expr.IsCall() { + term := expr.Terms.(*ast.Term) + if !ast.IsConstant(term.Value) { + return false + } + return !ast.Boolean(false).Equal(term.Value) + } + + // A==A can be ignored + if expr.Operator().Equal(ast.Equal.Ref()) { + return expr.Operand(0).Equal(expr.Operand(1)) + } + + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/crypto.go b/vendor/github.com/open-policy-agent/opa/topdown/crypto.go new file mode 100644 index 000000000..c0b10c603 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/crypto.go @@ -0,0 +1,128 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/x509" + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/util" +) + +func builtinCryptoX509ParseCertificates(a ast.Value) (ast.Value, error) { + + str, err := builtinBase64Decode(a) + if err != nil { + return nil, err + } + + certs, err := x509.ParseCertificates([]byte(str.(ast.String))) + if err != nil { + return nil, err + } + + bs, err := json.Marshal(certs) + if err != nil { + return nil, err + } + + var x interface{} + + if err := util.UnmarshalJSON(bs, &x); err != nil { + return nil, err + } + + return ast.InterfaceToValue(x) +} + +func hashHelper(a ast.Value, h func(ast.String) string) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + return ast.String(h(s)), nil +} + +func builtinCryptoMd5(a ast.Value) (ast.Value, error) { + return hashHelper(a, func(s ast.String) string { return fmt.Sprintf("%x", md5.Sum([]byte(s))) }) +} + +func builtinCryptoSha1(a ast.Value) (ast.Value, error) { + return hashHelper(a, func(s ast.String) string { return fmt.Sprintf("%x", sha1.Sum([]byte(s))) }) +} + +func builtinCryptoSha256(a ast.Value) (ast.Value, error) { + return hashHelper(a, func(s ast.String) string { return fmt.Sprintf("%x", sha256.Sum256([]byte(s))) }) +} + +func init() { + RegisterFunctionalBuiltin1(ast.CryptoX509ParseCertificates.Name, builtinCryptoX509ParseCertificates) + RegisterFunctionalBuiltin1(ast.CryptoMd5.Name, builtinCryptoMd5) + RegisterFunctionalBuiltin1(ast.CryptoSha1.Name, builtinCryptoSha1) + RegisterFunctionalBuiltin1(ast.CryptoSha256.Name, builtinCryptoSha256) +} + +// createRootCAs creates a new Cert Pool from scratch or adds to a copy of System Certs +func createRootCAs(tlsCACertFile string, tlsCACertEnvVar []byte, tlsUseSystemCerts bool) (*x509.CertPool, error) { + + var newRootCAs *x509.CertPool + + if tlsUseSystemCerts { + systemCertPool, err := x509.SystemCertPool() + if err != nil { + return nil, err + } + newRootCAs = systemCertPool + } else { + newRootCAs = x509.NewCertPool() + } + + if len(tlsCACertFile) > 0 { + // Append our cert to the system pool + caCert, err := readCertFromFile(tlsCACertFile) + if err != nil { + return nil, err + } + if ok := newRootCAs.AppendCertsFromPEM(caCert); !ok { + return nil, fmt.Errorf("could not append CA cert from %q", tlsCACertFile) + } + } + + if len(tlsCACertEnvVar) > 0 { + // Append our cert to the system pool + if ok := newRootCAs.AppendCertsFromPEM(tlsCACertEnvVar); !ok { + return nil, fmt.Errorf("error appending cert from env var %q into system certs", tlsCACertEnvVar) + } + } + + return newRootCAs, nil +} + +// ReadCertFromFile reads a cert from file +func readCertFromFile(localCertFile string) ([]byte, error) { + // Read in the cert file + certPEM, err := ioutil.ReadFile(localCertFile) + if err != nil { + return nil, err + } + return certPEM, nil +} + +// ReadKeyFromFile reads a key from file +func readKeyFromFile(localKeyFile string) ([]byte, error) { + // Read in the cert file + key, err := ioutil.ReadFile(localKeyFile) + if err != nil { + return nil, err + } + return key, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/doc.go b/vendor/github.com/open-policy-agent/opa/topdown/doc.go new file mode 100644 index 000000000..9aa7aa45c --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/doc.go @@ -0,0 +1,10 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package topdown provides low-level query evaluation support. +// +// The topdown implementation is a modified version of the standard top-down +// evaluation algorithm used in Datalog. References and comprehensions are +// evaluated eagerly while all other terms are evaluated lazily. +package topdown diff --git a/vendor/github.com/open-policy-agent/opa/topdown/encoding.go b/vendor/github.com/open-policy-agent/opa/topdown/encoding.go new file mode 100644 index 000000000..f2074bb46 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/encoding.go @@ -0,0 +1,217 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "net/url" + "strings" + + ghodss "github.com/ghodss/yaml" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/util" +) + +func builtinJSONMarshal(a ast.Value) (ast.Value, error) { + + asJSON, err := ast.JSON(a) + if err != nil { + return nil, err + } + + bs, err := json.Marshal(asJSON) + if err != nil { + return nil, err + } + + return ast.String(string(bs)), nil +} + +func builtinJSONUnmarshal(a ast.Value) (ast.Value, error) { + + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + var x interface{} + + if err := util.UnmarshalJSON([]byte(str), &x); err != nil { + return nil, err + } + + return ast.InterfaceToValue(x) +} + +func builtinBase64Encode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(base64.StdEncoding.EncodeToString([]byte(str))), nil +} + +func builtinBase64Decode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + result, err := base64.StdEncoding.DecodeString(string(str)) + return ast.String(result), err +} + +func builtinBase64UrlEncode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(base64.URLEncoding.EncodeToString([]byte(str))), nil +} + +func builtinBase64UrlDecode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s := string(str) + + // Some base64url encoders omit the padding at the end, so this case + // corrects such representations using the method given in RFC 7515 + // Appendix C: https://tools.ietf.org/html/rfc7515#appendix-C + if !strings.HasSuffix(s, "=") { + switch len(s) % 4 { + case 0: + case 2: + s += "==" + case 3: + s += "=" + default: + return nil, fmt.Errorf("illegal base64url string: %s", s) + } + } + result, err := base64.URLEncoding.DecodeString(s) + return ast.String(result), err +} + +func builtinURLQueryEncode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + return ast.String(url.QueryEscape(string(str))), nil +} + +func builtinURLQueryDecode(a ast.Value) (ast.Value, error) { + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s, err := url.QueryUnescape(string(str)) + if err != nil { + return nil, err + } + return ast.String(s), nil +} + +var encodeObjectErr = builtins.NewOperandErr(1, "values must be string, array[string], or set[string]") + +func builtinURLQueryEncodeObject(a ast.Value) (ast.Value, error) { + asJSON, err := ast.JSON(a) + if err != nil { + return nil, err + } + + inputs, ok := asJSON.(map[string]interface{}) + if !ok { + return nil, builtins.NewOperandTypeErr(1, a, "object") + } + + query := url.Values{} + + for k, v := range inputs { + switch vv := v.(type) { + case string: + query.Set(k, vv) + case []interface{}: + for _, val := range vv { + strVal, ok := val.(string) + if !ok { + return nil, encodeObjectErr + } + query.Add(k, strVal) + } + default: + return nil, encodeObjectErr + } + } + + return ast.String(query.Encode()), nil +} + +func builtinYAMLMarshal(a ast.Value) (ast.Value, error) { + + asJSON, err := ast.JSON(a) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + if err := encoder.Encode(asJSON); err != nil { + return nil, err + } + + bs, err := ghodss.JSONToYAML(buf.Bytes()) + if err != nil { + return nil, err + } + + return ast.String(string(bs)), nil +} + +func builtinYAMLUnmarshal(a ast.Value) (ast.Value, error) { + + str, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + bs, err := ghodss.YAMLToJSON([]byte(str)) + if err != nil { + return nil, err + } + + buf := bytes.NewBuffer(bs) + decoder := util.NewJSONDecoder(buf) + var val interface{} + err = decoder.Decode(&val) + if err != nil { + return nil, err + } + + return ast.InterfaceToValue(val) +} + +func init() { + RegisterFunctionalBuiltin1(ast.JSONMarshal.Name, builtinJSONMarshal) + RegisterFunctionalBuiltin1(ast.JSONUnmarshal.Name, builtinJSONUnmarshal) + RegisterFunctionalBuiltin1(ast.Base64Encode.Name, builtinBase64Encode) + RegisterFunctionalBuiltin1(ast.Base64Decode.Name, builtinBase64Decode) + RegisterFunctionalBuiltin1(ast.Base64UrlEncode.Name, builtinBase64UrlEncode) + RegisterFunctionalBuiltin1(ast.Base64UrlDecode.Name, builtinBase64UrlDecode) + RegisterFunctionalBuiltin1(ast.URLQueryDecode.Name, builtinURLQueryDecode) + RegisterFunctionalBuiltin1(ast.URLQueryEncode.Name, builtinURLQueryEncode) + RegisterFunctionalBuiltin1(ast.URLQueryEncodeObject.Name, builtinURLQueryEncodeObject) + RegisterFunctionalBuiltin1(ast.YAMLMarshal.Name, builtinYAMLMarshal) + RegisterFunctionalBuiltin1(ast.YAMLUnmarshal.Name, builtinYAMLUnmarshal) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/errors.go b/vendor/github.com/open-policy-agent/opa/topdown/errors.go new file mode 100644 index 000000000..d0457f1a8 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/errors.go @@ -0,0 +1,119 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + + "github.com/open-policy-agent/opa/ast" +) + +// Error is the error type returned by the Eval and Query functions when +// an evaluation error occurs. +type Error struct { + Code string `json:"code"` + Message string `json:"message"` + Location *ast.Location `json:"location,omitempty"` +} + +const ( + + // InternalErr represents an unknown evaluation error. + InternalErr string = "eval_internal_error" + + // CancelErr indicates the evaluation process was cancelled. + CancelErr string = "eval_cancel_error" + + // ConflictErr indicates a conflict was encountered during evaluation. For + // instance, a conflict occurs if a rule produces multiple, differing values + // for the same key in an object. Conflict errors indicate the policy does + // not account for the data loaded into the policy engine. + ConflictErr string = "eval_conflict_error" + + // TypeErr indicates evaluation stopped because an expression was applied to + // a value of an inappropriate type. + TypeErr string = "eval_type_error" + + // BuiltinErr indicates a built-in function received a semantically invalid + // input or encountered some kind of runtime error, e.g., connection + // timeout, connection refused, etc. + BuiltinErr string = "eval_builtin_error" + + // WithMergeErr indicates that the real and replacement data could not be merged. + WithMergeErr string = "eval_with_merge_error" +) + +// IsError returns true if the err is an Error. +func IsError(err error) bool { + _, ok := err.(*Error) + return ok +} + +// IsCancel returns true if err was caused by cancellation. +func IsCancel(err error) bool { + if e, ok := err.(*Error); ok { + return e.Code == CancelErr + } + return false +} + +func (e *Error) Error() string { + + msg := fmt.Sprintf("%v: %v", e.Code, e.Message) + + if e.Location != nil { + msg = e.Location.String() + ": " + msg + } + + return msg +} + +func functionConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "functions must not produce multiple outputs for same inputs", + } +} + +func completeDocConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "complete rules must not produce multiple outputs", + } +} + +func objectDocKeyConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "object keys must be unique", + } +} + +func documentConflictErr(loc *ast.Location) error { + return &Error{ + Code: ConflictErr, + Location: loc, + Message: "base and virtual document keys must be disjoint", + } +} + +func unsupportedBuiltinErr(loc *ast.Location) error { + return &Error{ + Code: InternalErr, + Location: loc, + Message: "unsupported built-in", + } +} + +func mergeConflictErr(loc *ast.Location) error { + return &Error{ + Code: WithMergeErr, + Location: loc, + Message: "real and replacement data could not be merged", + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/eval.go b/vendor/github.com/open-policy-agent/opa/topdown/eval.go new file mode 100644 index 000000000..1e32f8d45 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/eval.go @@ -0,0 +1,2382 @@ +package topdown + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/topdown/copypropagation" +) + +type evalIterator func(*eval) error + +type unifyIterator func() error + +type queryIDFactory struct { + curr uint64 +} + +// Note: The first call to Next() returns 0. +func (f *queryIDFactory) Next() uint64 { + curr := f.curr + f.curr++ + return curr +} + +type eval struct { + ctx context.Context + queryID uint64 + queryIDFact *queryIDFactory + parent *eval + caller *eval + cancel Cancel + query ast.Body + queryCompiler ast.QueryCompiler + index int + indexing bool + bindings *bindings + store storage.Store + baseCache *baseCache + txn storage.Transaction + compiler *ast.Compiler + input *ast.Term + data *ast.Term + targetStack *refStack + tracers []Tracer + instr *Instrumentation + builtins map[string]*Builtin + builtinCache builtins.Cache + virtualCache *virtualCache + saveSet *saveSet + saveStack *saveStack + saveSupport *saveSupport + saveNamespace *ast.Term + disableInlining [][]ast.Ref + genvarprefix string + runtime *ast.Term +} + +func (e *eval) Run(iter evalIterator) error { + e.traceEnter(e.query) + return e.eval(func(e *eval) error { + e.traceExit(e.query) + err := iter(e) + e.traceRedo(e.query) + return err + }) +} + +func (e *eval) builtinFunc(name string) (*ast.Builtin, BuiltinFunc, bool) { + decl, ok := ast.BuiltinMap[name] + if !ok { + bi, ok := e.builtins[name] + if ok { + return bi.Decl, bi.Func, true + } + } else { + f, ok := builtinFunctions[name] + if ok { + return decl, f, true + } + } + return nil, nil, false +} + +func (e *eval) closure(query ast.Body) *eval { + cpy := *e + cpy.index = 0 + cpy.query = query + cpy.queryID = cpy.queryIDFact.Next() + cpy.parent = e + return &cpy +} + +func (e *eval) child(query ast.Body) *eval { + cpy := *e + cpy.index = 0 + cpy.query = query + cpy.queryID = cpy.queryIDFact.Next() + cpy.bindings = newBindings(cpy.queryID, e.instr) + cpy.parent = e + return &cpy +} + +func (e *eval) next(iter evalIterator) error { + e.index++ + err := e.evalExpr(iter) + e.index-- + return err +} + +func (e *eval) partial() bool { + return e.saveSet != nil +} + +func (e *eval) unknown(x interface{}, b *bindings) bool { + if !e.partial() { + return false + } + + // If the caller provided an ast.Value directly (e.g., an ast.Ref) wrap + // it as an ast.Term because the saveSet Contains() function expects + // ast.Term. + if v, ok := x.(ast.Value); ok { + x = ast.NewTerm(v) + } + + return saveRequired(e.compiler, e.saveSet, b, x, false) +} + +func (e *eval) traceEnter(x ast.Node) { + e.traceEvent(EnterOp, x, "") +} + +func (e *eval) traceExit(x ast.Node) { + e.traceEvent(ExitOp, x, "") +} + +func (e *eval) traceEval(x ast.Node) { + e.traceEvent(EvalOp, x, "") +} + +func (e *eval) traceFail(x ast.Node) { + e.traceEvent(FailOp, x, "") +} + +func (e *eval) traceRedo(x ast.Node) { + e.traceEvent(RedoOp, x, "") +} + +func (e *eval) traceSave(x ast.Node) { + e.traceEvent(SaveOp, x, "") +} + +func (e *eval) traceIndex(x ast.Node, msg string) { + e.traceEvent(IndexOp, x, msg) +} + +func (e *eval) traceEvent(op Op, x ast.Node, msg string) { + + if !traceIsEnabled(e.tracers) { + return + } + + locals := ast.NewValueMap() + localMeta := map[ast.Var]VarMetadata{} + + e.bindings.Iter(nil, func(k, v *ast.Term) error { + original := k.Value.(ast.Var) + rewritten, _ := e.rewrittenVar(original) + localMeta[original] = VarMetadata{ + Name: rewritten, + Location: k.Loc(), + } + + // For backwards compatibility save a copy of the values too.. + locals.Put(k.Value, v.Value) + return nil + }) + + ast.WalkTerms(x, func(term *ast.Term) bool { + if v, ok := term.Value.(ast.Var); ok { + if _, ok := localMeta[v]; !ok { + if rewritten, ok := e.rewrittenVar(v); ok { + localMeta[v] = VarMetadata{ + Name: rewritten, + Location: term.Loc(), + } + } + } + } + return false + }) + + var parentID uint64 + if e.parent != nil { + parentID = e.parent.queryID + } + + evt := &Event{ + QueryID: e.queryID, + ParentID: parentID, + Op: op, + Node: x, + Location: x.Loc(), + Locals: locals, + LocalMetadata: localMeta, + Message: msg, + } + + for i := range e.tracers { + if e.tracers[i].Enabled() { + e.tracers[i].Trace(evt) + } + } +} + +func (e *eval) eval(iter evalIterator) error { + return e.evalExpr(iter) +} + +func (e *eval) evalExpr(iter evalIterator) error { + + if e.cancel != nil && e.cancel.Cancelled() { + return &Error{ + Code: CancelErr, + Message: "caller cancelled query execution", + } + } + + if e.index >= len(e.query) { + return iter(e) + } + + expr := e.query[e.index] + + e.traceEval(expr) + + if len(expr.With) > 0 { + return e.evalWith(iter) + } + + return e.evalStep(func(e *eval) error { + return e.next(iter) + }) +} + +func (e *eval) evalStep(iter evalIterator) error { + + expr := e.query[e.index] + + if expr.Negated { + return e.evalNot(iter) + } + + var defined bool + var err error + + switch terms := expr.Terms.(type) { + case []*ast.Term: + if expr.IsEquality() { + err = e.unify(terms[1], terms[2], func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + } else { + err = e.evalCall(terms, func() error { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + }) + } + case *ast.Term: + rterm := e.generateVar(fmt.Sprintf("term_%d_%d", e.queryID, e.index)) + err = e.unify(terms, rterm, func() error { + if e.saveSet.Contains(rterm, e.bindings) { + return e.saveExpr(ast.NewExpr(rterm), e.bindings, func() error { + return iter(e) + }) + } + if !e.bindings.Plug(rterm).Equal(ast.BooleanTerm(false)) { + defined = true + err := iter(e) + e.traceRedo(expr) + return err + } + return nil + }) + } + + if err != nil { + return err + } + + if !defined { + e.traceFail(expr) + } + + return nil +} + +func (e *eval) evalNot(iter evalIterator) error { + + expr := e.query[e.index] + + if e.unknown(expr, e.bindings) { + return e.evalNotPartial(iter) + } + + negation := ast.NewBody(expr.Complement().NoWith()) + child := e.closure(negation) + + var defined bool + child.traceEnter(negation) + + err := child.eval(func(*eval) error { + child.traceExit(negation) + defined = true + child.traceRedo(negation) + return nil + }) + + if err != nil { + return err + } + + if !defined { + return iter(e) + } + + e.traceFail(expr) + return nil +} + +func (e *eval) evalWith(iter evalIterator) error { + + expr := e.query[e.index] + var disable []ast.Ref + + if e.partial() { + + // If the value is unknown the with statement cannot be evaluated and so + // the entire expression should be saved to be safe. In the future this + // could be relaxed in certain cases (e.g., if the with statement would + // have no affect.) + for _, with := range expr.With { + if e.saveSet.ContainsRecursive(with.Value, e.bindings) { + return e.saveExpr(expr, e.bindings, func() error { + return e.next(iter) + }) + } + } + + // Disable inlining on all references in the expression so the result of + // partial evaluation has the same semamntics w/ the with statements + // preserved. + ast.WalkRefs(expr, func(x ast.Ref) bool { + disable = append(disable, x.GroundPrefix()) + return false + }) + } + + pairsInput := [][2]*ast.Term{} + pairsData := [][2]*ast.Term{} + targets := []ast.Ref{} + + for i := range expr.With { + plugged := e.bindings.Plug(expr.With[i].Value) + if isInputRef(expr.With[i].Target) { + pairsInput = append(pairsInput, [...]*ast.Term{expr.With[i].Target, plugged}) + } else if isDataRef(expr.With[i].Target) { + pairsData = append(pairsData, [...]*ast.Term{expr.With[i].Target, plugged}) + } + targets = append(targets, expr.With[i].Target.Value.(ast.Ref)) + } + + input, err := mergeTermWithValues(e.input, pairsInput) + + if err != nil { + return &Error{ + Code: ConflictErr, + Location: expr.Location, + Message: err.Error(), + } + } + + data, err := mergeTermWithValues(e.data, pairsData) + if err != nil { + return &Error{ + Code: ConflictErr, + Location: expr.Location, + Message: err.Error(), + } + } + + oldInput, oldData := e.evalWithPush(input, data, targets, disable) + + err = e.evalStep(func(e *eval) error { + e.evalWithPop(oldInput, oldData) + err := e.next(iter) + oldInput, oldData = e.evalWithPush(input, data, targets, disable) + return err + }) + + e.evalWithPop(oldInput, oldData) + + return err +} + +func (e *eval) evalWithPush(input *ast.Term, data *ast.Term, targets []ast.Ref, disable []ast.Ref) (*ast.Term, *ast.Term) { + + var oldInput *ast.Term + + if input != nil { + oldInput = e.input + e.input = input + } + + var oldData *ast.Term + + if data != nil { + oldData = e.data + e.data = data + } + + e.virtualCache.Push() + e.targetStack.Push(targets) + e.disableInlining = append(e.disableInlining, disable) + + return oldInput, oldData +} + +func (e *eval) evalWithPop(input *ast.Term, data *ast.Term) { + e.disableInlining = e.disableInlining[:len(e.disableInlining)-1] + e.targetStack.Pop() + e.virtualCache.Pop() + e.data = data + e.input = input +} + +func (e *eval) evalNotPartial(iter evalIterator) error { + + // Prepare query normally. + expr := e.query[e.index] + negation := expr.Complement().NoWith() + child := e.closure(ast.NewBody(negation)) + + // Unknowns is the set of variables that are marked as unknown. The variables + // are namespaced with the query ID that they originate in. This ensures that + // variables across two or more queries are identified uniquely. + // + // NOTE(tsandall): this is greedy in the sense that we only need variable + // dependencies of the negation. + unknowns := e.saveSet.Vars(e.caller.bindings) + + // Run partial evaluation, plugging the result and applying copy propagation to + // each result. Since the result may require support, push a new query onto the + // save stack to avoid mutating the current save query. + p := copypropagation.New(unknowns).WithEnsureNonEmptyBody(true) + var savedQueries []ast.Body + e.saveStack.PushQuery(nil) + + child.eval(func(*eval) error { + query := e.saveStack.Peek() + plugged := query.Plug(e.caller.bindings) + result := applyCopyPropagation(p, e.instr, plugged) + savedQueries = append(savedQueries, result) + return nil + }) + + e.saveStack.PopQuery() + + // If partial evaluation produced no results, the expression is always undefined + // so it does not have to be saved. + if len(savedQueries) == 0 { + return iter(e) + } + + // Check if the partial evaluation result can be inlined in this query. If not, + // generate support rules for the result. Depending on the size of the partial + // evaluation result and the contents, it may or may not be inlinable. We treat + // the unknowns as safe because vars in the save set will either be known to + // the caller or made safe by an expression on the save stack. + if !canInlineNegation(unknowns, savedQueries) { + return e.evalNotPartialSupport(expr, unknowns, savedQueries, iter) + } + + // If we can inline the result, we have to generate the cross product of the + // queries. For example: + // + // (A && B) || (C && D) + // + // Becomes: + // + // (!A && !C) || (!A && !D) || (!B && !C) || (!B && !D) + return complementedCartesianProduct(savedQueries, 0, nil, func(q ast.Body) error { + return e.saveInlinedNegatedExprs(q, func() error { + return iter(e) + }) + }) +} + +func (e *eval) evalNotPartialSupport(expr *ast.Expr, unknowns ast.VarSet, queries []ast.Body, iter evalIterator) error { + + // Prepare support rule head. + supportName := fmt.Sprintf("__not%d_%d__", e.queryID, e.index) + term := ast.RefTerm(ast.DefaultRootDocument, e.saveNamespace, ast.StringTerm(supportName)) + path := term.Value.(ast.Ref) + head := ast.NewHead(ast.Var(supportName), nil, ast.BooleanTerm(true)) + + bodyVars := ast.NewVarSet() + + for _, q := range queries { + bodyVars.Update(q.Vars(ast.VarVisitorParams{})) + } + + unknowns = unknowns.Intersect(bodyVars) + + // Make rule args. Sort them to ensure order is deterministic. + args := make([]*ast.Term, 0, len(unknowns)) + + for v := range unknowns { + args = append(args, ast.NewTerm(v)) + } + + sort.Slice(args, func(i, j int) bool { + return args[i].Value.Compare(args[j].Value) < 0 + }) + + if len(args) > 0 { + head.Args = ast.Args(args) + } + + // Save support rules. + for _, query := range queries { + e.saveSupport.Insert(path, &ast.Rule{ + Head: head, + Body: query, + }) + } + + // Save expression that refers to support rule set. + expr = expr.Copy() + if len(args) > 0 { + terms := make([]*ast.Term, len(args)+1) + terms[0] = term + for i := 0; i < len(args); i++ { + terms[i+1] = args[i] + } + expr.Terms = terms + } else { + expr.Terms = term + } + + return e.saveInlinedNegatedExprs([]*ast.Expr{expr}, func() error { + return e.next(iter) + }) +} + +func (e *eval) evalCall(terms []*ast.Term, iter unifyIterator) error { + + ref := terms[0].Value.(ast.Ref) + + if ref[0].Equal(ast.DefaultRootDocument) { + eval := evalFunc{ + e: e, + ref: ref, + terms: terms, + } + return eval.eval(iter) + } + + bi, f, ok := e.builtinFunc(ref.String()) + if !ok { + return unsupportedBuiltinErr(e.query[e.index].Location) + } + + if e.unknown(e.query[e.index], e.bindings) { + return e.saveCall(len(bi.Decl.Args()), terms, iter) + } + + var parentID uint64 + if e.parent != nil { + parentID = e.parent.queryID + } + + bctx := BuiltinContext{ + Context: e.ctx, + Cancel: e.cancel, + Runtime: e.runtime, + Cache: e.builtinCache, + Location: e.query[e.index].Location, + Tracers: e.tracers, + QueryID: e.queryID, + ParentID: parentID, + } + + eval := evalBuiltin{ + e: e, + bi: bi, + bctx: bctx, + f: f, + terms: terms[1:], + } + return eval.eval(iter) +} + +func (e *eval) unify(a, b *ast.Term, iter unifyIterator) error { + return e.biunify(a, b, e.bindings, e.bindings, iter) +} + +func (e *eval) biunify(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + a, b1 = b1.apply(a) + b, b2 = b2.apply(b) + switch vA := a.Value.(type) { + case ast.Var, ast.Ref, *ast.ArrayComprehension, *ast.SetComprehension, *ast.ObjectComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case ast.Null: + switch b.Value.(type) { + case ast.Var, ast.Null, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.Boolean: + switch b.Value.(type) { + case ast.Var, ast.Boolean, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.Number: + switch b.Value.(type) { + case ast.Var, ast.Number, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.String: + switch b.Value.(type) { + case ast.Var, ast.String, ast.Ref: + return e.biunifyValues(a, b, b1, b2, iter) + } + case ast.Array: + switch vB := b.Value.(type) { + case ast.Var, ast.Ref, *ast.ArrayComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case ast.Array: + return e.biunifyArrays(vA, vB, b1, b2, iter) + } + case ast.Object: + switch vB := b.Value.(type) { + case ast.Var, ast.Ref, *ast.ObjectComprehension: + return e.biunifyValues(a, b, b1, b2, iter) + case ast.Object: + return e.biunifyObjects(vA, vB, b1, b2, iter) + } + case ast.Set: + return e.biunifyValues(a, b, b1, b2, iter) + } + return nil +} + +func (e *eval) biunifyArrays(a, b ast.Array, b1, b2 *bindings, iter unifyIterator) error { + if len(a) != len(b) { + return nil + } + return e.biunifyArraysRec(a, b, b1, b2, iter, 0) +} + +func (e *eval) biunifyArraysRec(a, b ast.Array, b1, b2 *bindings, iter unifyIterator, idx int) error { + if idx == len(a) { + return iter() + } + return e.biunify(a[idx], b[idx], b1, b2, func() error { + return e.biunifyArraysRec(a, b, b1, b2, iter, idx+1) + }) +} + +func (e *eval) biunifyObjects(a, b ast.Object, b1, b2 *bindings, iter unifyIterator) error { + if a.Len() != b.Len() { + return nil + } + + // Objects must not contain unbound variables as keys at this point as we + // cannot unify them. Similar to sets, plug both sides before comparing the + // keys and unifying the values. + if nonGroundKeys(a) { + a = plugKeys(a, b1) + } + + if nonGroundKeys(b) { + b = plugKeys(b, b2) + } + + return e.biunifyObjectsRec(a, b, b1, b2, iter, a.Keys(), 0) +} + +func (e *eval) biunifyObjectsRec(a, b ast.Object, b1, b2 *bindings, iter unifyIterator, keys []*ast.Term, idx int) error { + if idx == len(keys) { + return iter() + } + v2 := b.Get(keys[idx]) + if v2 == nil { + return nil + } + return e.biunify(a.Get(keys[idx]), v2, b1, b2, func() error { + return e.biunifyObjectsRec(a, b, b1, b2, iter, keys, idx+1) + }) +} + +func (e *eval) biunifyValues(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + // Try to evaluate refs and comprehensions. If partial evaluation is + // enabled, then skip evaluation (and save the expression) if the term is + // in the save set. Currently, comprehensions are not evaluated during + // partial eval. This could be improved in the future. + + var saveA, saveB bool + + if _, ok := a.Value.(ast.Set); ok { + saveA = e.saveSet.ContainsRecursive(a, b1) + } else { + saveA = e.saveSet.Contains(a, b1) + if !saveA { + if _, refA := a.Value.(ast.Ref); refA { + return e.biunifyRef(a, b, b1, b2, iter) + } + } + } + + if _, ok := b.Value.(ast.Set); ok { + saveB = e.saveSet.ContainsRecursive(b, b2) + } else { + saveB = e.saveSet.Contains(b, b2) + if !saveB { + if _, refB := b.Value.(ast.Ref); refB { + return e.biunifyRef(b, a, b2, b1, iter) + } + } + } + + if saveA || saveB { + return e.saveUnify(a, b, b1, b2, iter) + } + + if ast.IsComprehension(a.Value) { + return e.biunifyComprehension(a, b, b1, b2, false, iter) + } else if ast.IsComprehension(b.Value) { + return e.biunifyComprehension(b, a, b2, b1, true, iter) + } + + // Perform standard unification. + _, varA := a.Value.(ast.Var) + _, varB := b.Value.(ast.Var) + + if varA && varB { + if b1 == b2 && a.Equal(b) { + return iter() + } + undo := b1.bind(a, b, b2) + err := iter() + undo.Undo() + return err + } else if varA && !varB { + undo := b1.bind(a, b, b2) + err := iter() + undo.Undo() + return err + } else if varB && !varA { + undo := b2.bind(b, a, b1) + err := iter() + undo.Undo() + return err + } + + // Sets must not contain unbound variables at this point as we cannot unify + // them. So simply plug both sides (to substitute any bound variables with + // values) and then check for equality. + switch a.Value.(type) { + case ast.Set: + a = b1.Plug(a) + b = b2.Plug(b) + } + + if a.Equal(b) { + return iter() + } + + return nil +} + +func (e *eval) biunifyRef(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + + ref := a.Value.(ast.Ref) + + if ref[0].Equal(ast.DefaultRootDocument) { + node := e.compiler.RuleTree.Child(ref[0].Value) + + eval := evalTree{ + e: e, + ref: ref, + pos: 1, + plugged: ref.Copy(), + bindings: b1, + rterm: b, + rbindings: b2, + node: node, + } + return eval.eval(iter) + } + + var term *ast.Term + var termbindings *bindings + + if ref[0].Equal(ast.InputRootDocument) { + term = e.input + termbindings = b1 + } else { + term, termbindings = b1.apply(ref[0]) + if term == ref[0] { + term = nil + } + } + + if term == nil { + return nil + } + + eval := evalTerm{ + e: e, + ref: ref, + pos: 1, + bindings: b1, + term: term, + termbindings: termbindings, + rterm: b, + rbindings: b2, + } + + return eval.eval(iter) +} + +func (e *eval) biunifyComprehension(a, b *ast.Term, b1, b2 *bindings, swap bool, iter unifyIterator) error { + + if e.unknown(a, b1) { + return e.biunifyComprehensionPartial(a, b, b1, b2, swap, iter) + } + + switch a := a.Value.(type) { + case *ast.ArrayComprehension: + return e.biunifyComprehensionArray(a, b, b1, b2, iter) + case *ast.SetComprehension: + return e.biunifyComprehensionSet(a, b, b1, b2, iter) + case *ast.ObjectComprehension: + return e.biunifyComprehensionObject(a, b, b1, b2, iter) + } + + return fmt.Errorf("illegal comprehension %T", a) +} + +func (e *eval) biunifyComprehensionPartial(a, b *ast.Term, b1, b2 *bindings, swap bool, iter unifyIterator) error { + + // Capture bindings available to the comprehension. We will add expressions + // to the comprehension body that ensure the comprehension body is safe. + // Currently this process adds _all_ bindings (even if they are not + // needed.) Eventually we may want to make the logic a bit smarter. + var extras []*ast.Expr + + err := b1.Iter(e.caller.bindings, func(k, v *ast.Term) error { + extras = append(extras, ast.Equality.Expr(k, v)) + return nil + }) + + if err != nil { + return err + } + + // Namespace the variables in the body to avoid collision when the final + // queries returned by partial evaluation. + var body *ast.Body + + switch a := a.Value.(type) { + case *ast.ArrayComprehension: + body = &a.Body + case *ast.SetComprehension: + body = &a.Body + case *ast.ObjectComprehension: + body = &a.Body + default: + return fmt.Errorf("illegal comprehension %T", a) + } + + for _, e := range extras { + body.Append(e) + } + + b1.Namespace(a, e.caller.bindings) + + // The other term might need to be plugged so include the bindings. The + // bindings for the comprehension term are saved (for compatibility) but + // the eventual plug operation on the comprehension will be a no-op. + if !swap { + return e.saveUnify(a, b, b1, b2, iter) + } + + return e.saveUnify(b, a, b2, b1, iter) +} + +func (e *eval) biunifyComprehensionArray(x *ast.ArrayComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + result := ast.Array{} + child := e.closure(x.Body) + err := child.Run(func(child *eval) error { + result = append(result, child.bindings.Plug(x.Term)) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +func (e *eval) biunifyComprehensionSet(x *ast.SetComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + result := ast.NewSet() + child := e.closure(x.Body) + err := child.Run(func(child *eval) error { + result.Add(child.bindings.Plug(x.Term)) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +func (e *eval) biunifyComprehensionObject(x *ast.ObjectComprehension, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + result := ast.NewObject() + child := e.closure(x.Body) + err := child.Run(func(child *eval) error { + key := child.bindings.Plug(x.Key) + value := child.bindings.Plug(x.Value) + exist := result.Get(key) + if exist != nil && !exist.Equal(value) { + return objectDocKeyConflictErr(x.Key.Location) + } + result.Insert(key, value) + return nil + }) + if err != nil { + return err + } + return e.biunify(ast.NewTerm(result), b, b1, b2, iter) +} + +type savePair struct { + term *ast.Term + b *bindings +} + +func getSavePairs(x *ast.Term, b *bindings, result []savePair) []savePair { + if _, ok := x.Value.(ast.Var); ok { + result = append(result, savePair{x, b}) + return result + } + vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ + SkipClosures: true, + SkipRefHead: true, + }) + vis.Walk(x) + for v := range vis.Vars() { + y, next := b.apply(ast.NewTerm(v)) + result = getSavePairs(y, next, result) + } + return result +} + +func (e *eval) saveExpr(expr *ast.Expr, b *bindings, iter unifyIterator) error { + expr.With = e.query[e.index].With + e.saveStack.Push(expr, b, b) + e.traceSave(expr) + err := iter() + e.saveStack.Pop() + return err +} + +func (e *eval) saveUnify(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) error { + e.instr.startTimer(partialOpSaveUnify) + expr := ast.Equality.Expr(a, b) + expr.With = e.query[e.index].With + pops := 0 + if pairs := getSavePairs(a, b1, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + + } + if pairs := getSavePairs(b, b2, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + } + e.saveStack.Push(expr, b1, b2) + e.traceSave(expr) + e.instr.stopTimer(partialOpSaveUnify) + err := iter() + + e.saveStack.Pop() + for i := 0; i < pops; i++ { + e.saveSet.Pop() + } + + return err +} + +func (e *eval) saveCall(declArgsLen int, terms []*ast.Term, iter unifyIterator) error { + expr := ast.NewExpr(terms) + expr.With = e.query[e.index].With + + // If call-site includes output value then partial eval must add vars in output + // position to the save set. + pops := 0 + if declArgsLen == len(terms)-2 { + if pairs := getSavePairs(terms[len(terms)-1], e.bindings, nil); len(pairs) > 0 { + pops += len(pairs) + for _, p := range pairs { + e.saveSet.Push([]*ast.Term{p.term}, p.b) + } + } + } + e.saveStack.Push(expr, e.bindings, nil) + e.traceSave(expr) + err := iter() + + e.saveStack.Pop() + for i := 0; i < pops; i++ { + e.saveSet.Pop() + } + return err +} + +func (e *eval) saveInlinedNegatedExprs(exprs []*ast.Expr, iter unifyIterator) error { + + // This function does not include with statements on the exprs because + // they will have already been saved and therefore had their any relevant + // with statements set. + for _, expr := range exprs { + e.saveStack.Push(expr, nil, nil) + e.traceSave(expr) + } + err := iter() + for i := 0; i < len(exprs); i++ { + e.saveStack.Pop() + } + return err +} + +func (e *eval) getRules(ref ast.Ref) (*ast.IndexResult, error) { + e.instr.startTimer(evalOpRuleIndex) + defer e.instr.stopTimer(evalOpRuleIndex) + + index := e.compiler.RuleIndex(ref) + if index == nil { + return nil, nil + } + + var result *ast.IndexResult + var err error + if e.indexing { + result, err = index.Lookup(e) + } else { + result, err = index.AllRules(e) + } + + if err != nil { + return nil, err + } + + var msg string + if len(result.Rules) == 1 { + msg = "(matched 1 rule)" + } else { + var b strings.Builder + b.Grow(len("(matched NNNN rules)")) + b.WriteString("matched ") + b.WriteString(strconv.FormatInt(int64(len(result.Rules)), 10)) + b.WriteString(" rules)") + msg = b.String() + } + e.traceIndex(e.query[e.index], msg) + return result, err +} + +func (e *eval) Resolve(ref ast.Ref) (ast.Value, error) { + e.instr.startTimer(evalOpResolve) + + if e.saveSet.Contains(ast.NewTerm(ref), nil) { + e.instr.stopTimer(evalOpResolve) + return nil, ast.UnknownValueErr{} + } + + if ref[0].Equal(ast.InputRootDocument) { + if e.input != nil { + v, err := e.input.Value.Find(ref[1:]) + if err != nil { + v = nil + } + e.instr.stopTimer(evalOpResolve) + return v, nil + } + e.instr.stopTimer(evalOpResolve) + return nil, nil + } + + if ref[0].Equal(ast.DefaultRootDocument) { + + var repValue ast.Value + + if e.data != nil { + if v, err := e.data.Value.Find(ref[1:]); err == nil { + repValue = v + } else { + repValue = nil + } + } + + if e.targetStack.Prefixed(ref) { + e.instr.stopTimer(evalOpResolve) + return repValue, nil + } + + var merged ast.Value + var err error + + // Converting large JSON values into AST values can be fairly expensive. For + // example, a 2MB JSON value can take upwards of 30 millisceonds to convert. + // We cache the result of conversion here in case the same base document is + // being read multiple times during evaluation. + realValue := e.baseCache.Get(ref) + if realValue != nil { + e.instr.counterIncr(evalOpBaseCacheHit) + if repValue == nil { + e.instr.stopTimer(evalOpResolve) + return realValue, nil + } + var ok bool + merged, ok = merge(repValue, realValue) + if !ok { + err = mergeConflictErr(ref[0].Location) + } + } else { + e.instr.counterIncr(evalOpBaseCacheMiss) + merged, err = e.resolveReadFromStorage(ref, repValue) + } + e.instr.stopTimer(evalOpResolve) + return merged, err + } + e.instr.stopTimer(evalOpResolve) + return nil, fmt.Errorf("illegal ref") +} + +func (e *eval) resolveReadFromStorage(ref ast.Ref, a ast.Value) (ast.Value, error) { + if refContainsNonScalar(ref) { + return a, nil + } + + path, err := storage.NewPathForRef(ref) + if err != nil { + if !storage.IsNotFound(err) { + return nil, err + } + return a, nil + } + + blob, err := e.store.Read(e.ctx, e.txn, path) + if err != nil { + if !storage.IsNotFound(err) { + return nil, err + } + return a, nil + } + + if len(path) == 0 { + obj := blob.(map[string]interface{}) + if len(obj) > 0 { + cpy := make(map[string]interface{}, len(obj)-1) + for k, v := range obj { + if string(ast.SystemDocumentKey) == k { + continue + } + cpy[k] = v + } + blob = cpy + } + } + + v, err := ast.InterfaceToValue(blob) + if err != nil { + return nil, err + } + + e.baseCache.Put(ref, v) + + if a == nil { + return v, nil + } + + merged, ok := merge(a, v) + if !ok { + return nil, mergeConflictErr(ref[0].Location) + } + return merged, nil +} + +func (e *eval) generateVar(suffix string) *ast.Term { + return ast.VarTerm(fmt.Sprintf("%v_%v", e.genvarprefix, suffix)) +} + +func (e *eval) rewrittenVar(v ast.Var) (ast.Var, bool) { + if e.compiler != nil { + if rw, ok := e.compiler.RewrittenVars[v]; ok { + return rw, true + } + } + if e.queryCompiler != nil { + if rw, ok := e.queryCompiler.RewrittenVars()[v]; ok { + return rw, true + } + } + return v, false +} + +type evalBuiltin struct { + e *eval + bi *ast.Builtin + bctx BuiltinContext + f BuiltinFunc + terms []*ast.Term +} + +func (e evalBuiltin) eval(iter unifyIterator) error { + + operands := make([]*ast.Term, len(e.terms)) + + for i := 0; i < len(e.terms); i++ { + operands[i] = e.e.bindings.Plug(e.terms[i]) + } + + numDeclArgs := len(e.bi.Decl.Args()) + + e.e.instr.startTimer(evalOpBuiltinCall) + + err := e.f(e.bctx, operands, func(output *ast.Term) error { + + e.e.instr.stopTimer(evalOpBuiltinCall) + + var err error + if len(operands) == numDeclArgs { + if output.Value.Compare(ast.Boolean(false)) != 0 { + err = iter() + } + } else { + err = e.e.unify(e.terms[len(e.terms)-1], output, iter) + } + e.e.instr.startTimer(evalOpBuiltinCall) + return err + }) + + e.e.instr.stopTimer(evalOpBuiltinCall) + return err +} + +type evalFunc struct { + e *eval + ref ast.Ref + terms []*ast.Term +} + +func (e evalFunc) eval(iter unifyIterator) error { + + ir, err := e.e.getRules(e.ref) + if err != nil { + return err + } + + if ir.Empty() { + return nil + } + + if len(ir.Else) > 0 && e.e.unknown(e.e.query[e.e.index], e.e.bindings) { + // Partial evaluation of ordered rules is not supported currently. Save the + // expression and continue. This could be revisited in the future. + return e.e.saveCall(len(ir.Rules[0].Head.Args), e.terms, iter) + } + + var prev *ast.Term + + for i := range ir.Rules { + next, err := e.evalOneRule(iter, ir.Rules[i], prev) + if err != nil { + return err + } + if next == nil { + for _, rule := range ir.Else[ir.Rules[i]] { + next, err = e.evalOneRule(iter, rule, prev) + if err != nil { + return err + } + if next != nil { + break + } + } + } + if next != nil { + prev = next + } + } + + return nil +} + +func (e evalFunc) evalOneRule(iter unifyIterator, rule *ast.Rule, prev *ast.Term) (*ast.Term, error) { + + child := e.e.child(rule.Body) + + args := make(ast.Array, len(e.terms)-1) + + for i := range rule.Head.Args { + args[i] = rule.Head.Args[i] + } + + if len(args) == len(rule.Head.Args)+1 { + args[len(args)-1] = rule.Head.Value + } + + var result *ast.Term + + child.traceEnter(rule) + + err := child.biunifyArrays(e.terms[1:], args, e.e.bindings, child.bindings, func() error { + return child.eval(func(child *eval) error { + child.traceExit(rule) + result = child.bindings.Plug(rule.Head.Value) + + if len(rule.Head.Args) == len(e.terms)-1 { + if result.Value.Compare(ast.Boolean(false)) == 0 { + return nil + } + } + + // Partial evaluation should explore all rules and may not produce + // a ground result so we do not perform conflict detection or + // deduplication. See "ignore conflicts: functions" test case for + // an example. + if !e.e.partial() { + if prev != nil { + if ast.Compare(prev, result) != 0 { + return functionConflictErr(rule.Location) + } + child.traceRedo(rule) + return nil + } + } + + prev = result + + if err := iter(); err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + }) + + return result, err +} + +type evalTree struct { + e *eval + ref ast.Ref + plugged ast.Ref + pos int + bindings *bindings + rterm *ast.Term + rbindings *bindings + node *ast.TreeNode +} + +func (e evalTree) eval(iter unifyIterator) error { + + if len(e.ref) == e.pos { + return e.finish(iter) + } + + plugged := e.bindings.Plug(e.ref[e.pos]) + + if plugged.IsGround() { + return e.next(iter, plugged) + } + + return e.enumerate(iter) +} + +func (e evalTree) finish(iter unifyIterator) error { + + // During partial evaluation it may not be possible to compute the value + // for this reference if it refers to a virtual document so save the entire + // expression. See "save: full extent" test case for an example. + if e.node != nil && e.e.unknown(e.ref, e.e.bindings) { + return e.e.saveUnify(ast.NewTerm(e.plugged), e.rterm, e.bindings, e.rbindings, iter) + } + + v, err := e.extent() + if err != nil || v == nil { + return err + } + + return e.e.biunify(e.rterm, v, e.rbindings, e.bindings, func() error { + return iter() + }) +} + +func (e evalTree) next(iter unifyIterator, plugged *ast.Term) error { + + var node *ast.TreeNode + + cpy := e + cpy.plugged[e.pos] = plugged + cpy.pos++ + + if !e.e.targetStack.Prefixed(cpy.plugged[:cpy.pos]) { + if e.node != nil { + node = e.node.Child(plugged.Value) + if node != nil && len(node.Values) > 0 { + r := evalVirtual{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + r.plugged[e.pos] = plugged + return r.eval(iter) + } + } + } + + cpy.node = node + return cpy.eval(iter) +} + +func (e evalTree) enumerate(iter unifyIterator) error { + doc, err := e.e.Resolve(e.plugged[:e.pos]) + if err != nil { + return err + } + + if doc != nil { + switch doc := doc.(type) { + case ast.Array: + for i := range doc { + k := ast.IntNumberTerm(i) + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, k) + }) + if err != nil { + return err + } + } + case ast.Object: + err := doc.Iter(func(k, _ *ast.Term) error { + return e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, k) + }) + }) + if err != nil { + return err + } + case ast.Set: + err := doc.Iter(func(elem *ast.Term) error { + return e.e.biunify(elem, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, elem) + }) + }) + if err != nil { + return err + } + } + } + + if e.node == nil { + return nil + } + + for k := range e.node.Children { + key := ast.NewTerm(k) + if err := e.e.biunify(key, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, key) + }); err != nil { + return err + } + } + + return nil +} + +func (e evalTree) extent() (*ast.Term, error) { + base, err := e.e.Resolve(e.plugged) + if err != nil { + return nil, err + } + + virtual, err := e.leaves(e.plugged, e.node) + if err != nil { + return nil, err + } + + if virtual == nil { + if base == nil { + return nil, nil + } + return ast.NewTerm(base), nil + } + + if base != nil { + merged, ok := merge(base, virtual) + if !ok { + return nil, mergeConflictErr(e.plugged[0].Location) + } + return ast.NewTerm(merged), nil + } + + return ast.NewTerm(virtual), nil +} + +func (e evalTree) leaves(plugged ast.Ref, node *ast.TreeNode) (ast.Object, error) { + + if e.node == nil { + return nil, nil + } + + result := ast.NewObject() + + for _, child := range node.Children { + if child.Hide { + continue + } + + plugged = append(plugged, ast.NewTerm(child.Key)) + + var save ast.Value + var err error + + if len(child.Values) > 0 { + rterm := e.e.generateVar("leaf") + err = e.e.unify(ast.NewTerm(plugged), rterm, func() error { + save = e.e.bindings.Plug(rterm).Value + return nil + }) + } else { + save, err = e.leaves(plugged, child) + } + + if err != nil { + return nil, err + } + + if save != nil { + v := ast.NewObject([2]*ast.Term{plugged[len(plugged)-1], ast.NewTerm(save)}) + result, _ = result.Merge(v) + } + + plugged = plugged[:len(plugged)-1] + } + + return result, nil +} + +type evalVirtual struct { + e *eval + ref ast.Ref + plugged ast.Ref + pos int + bindings *bindings + rterm *ast.Term + rbindings *bindings +} + +func (e evalVirtual) eval(iter unifyIterator) error { + + ir, err := e.e.getRules(e.plugged[:e.pos+1]) + if err != nil { + return err + } + + // Partial evaluation of ordered rules is not supported currently. Save the + // expression and continue. This could be revisited in the future. + if len(ir.Else) > 0 && e.e.unknown(e.ref, e.bindings) { + return e.e.saveUnify(ast.NewTerm(e.ref), e.rterm, e.bindings, e.rbindings, iter) + } + + switch ir.Kind { + case ast.PartialSetDoc: + eval := evalVirtualPartial{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + empty: ast.SetTerm(), + } + return eval.eval(iter) + case ast.PartialObjectDoc: + eval := evalVirtualPartial{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + empty: ast.ObjectTerm(), + } + return eval.eval(iter) + default: + eval := evalVirtualComplete{ + e: e.e, + ref: e.ref, + plugged: e.plugged, + pos: e.pos, + ir: ir, + bindings: e.bindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) + } +} + +type evalVirtualPartial struct { + e *eval + ref ast.Ref + plugged ast.Ref + pos int + ir *ast.IndexResult + bindings *bindings + rterm *ast.Term + rbindings *bindings + empty *ast.Term +} + +func (e evalVirtualPartial) eval(iter unifyIterator) error { + + if len(e.ref) == e.pos+1 { + // During partial evaluation, it may not be possible to produce a value + // for this reference so save the entire expression. See "save: full + // extent: partial object" test case for an example. + if e.e.unknown(e.ref, e.bindings) { + return e.e.saveUnify(ast.NewTerm(e.ref), e.rterm, e.bindings, e.rbindings, iter) + } + return e.evalAllRules(iter, e.ir.Rules) + } + + var cacheKey ast.Ref + + if e.ir.Kind == ast.PartialObjectDoc { + plugged := e.bindings.Plug(e.ref[e.pos+1]) + + if plugged.IsGround() { + path := e.plugged[:e.pos+2] + path[len(path)-1] = plugged + cached := e.e.virtualCache.Get(path) + + if cached != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + return e.evalTerm(iter, cached, e.bindings) + } + + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + cacheKey = path + } + } + + generateSupport := anyRefSetContainsPrefix(e.e.disableInlining, e.plugged[:e.pos+1]) + + if generateSupport { + return e.partialEvalSupport(iter) + } + + for _, rule := range e.ir.Rules { + if err := e.evalOneRule(iter, rule, cacheKey); err != nil { + return err + } + } + + return nil +} + +func (e evalVirtualPartial) evalAllRules(iter unifyIterator, rules []*ast.Rule) error { + + result := e.empty + + for _, rule := range rules { + child := e.e.child(rule.Body) + child.traceEnter(rule) + + err := child.eval(func(*eval) error { + child.traceExit(rule) + var err error + result, err = e.reduce(rule.Head, child.bindings, result) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + + if err != nil { + return err + } + } + + return e.e.biunify(result, e.rterm, e.bindings, e.bindings, iter) +} + +func (e evalVirtualPartial) evalOneRule(iter unifyIterator, rule *ast.Rule, cacheKey ast.Ref) error { + + key := e.ref[e.pos+1] + child := e.e.child(rule.Body) + + child.traceEnter(rule) + var defined bool + + err := child.biunify(rule.Head.Key, key, child.bindings, e.bindings, func() error { + defined = true + return child.eval(func(child *eval) error { + child.traceExit(rule) + + term := rule.Head.Value + if term == nil { + term = rule.Head.Key + } + + if cacheKey != nil { + result := child.bindings.Plug(term) + e.e.virtualCache.Put(cacheKey, result) + } + + term, termbindings := child.bindings.apply(term) + err := e.evalTerm(iter, term, termbindings) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + }) + + if err != nil { + return err + } + + if !defined { + child.traceFail(rule) + } + + return nil +} + +func (e evalVirtualPartial) partialEvalSupport(iter unifyIterator) error { + + path := e.plugged[:e.pos+1].Insert(e.e.saveNamespace, 1) + + if !e.e.saveSupport.Exists(path) { + for i := range e.ir.Rules { + err := e.partialEvalSupportRule(iter, e.ir.Rules[i], path) + if err != nil { + return err + } + } + } + + rewritten := ast.NewTerm(e.ref.Insert(e.e.saveNamespace, 1)) + return e.e.saveUnify(rewritten, e.rterm, e.bindings, e.rbindings, iter) +} + +func (e evalVirtualPartial) partialEvalSupportRule(iter unifyIterator, rule *ast.Rule, path ast.Ref) error { + + child := e.e.child(rule.Body) + child.traceEnter(rule) + + e.e.saveStack.PushQuery(nil) + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + + current := e.e.saveStack.PopQuery() + plugged := current.Plug(e.e.caller.bindings) + + var key, value *ast.Term + + if rule.Head.Key != nil { + key = child.bindings.PlugNamespaced(rule.Head.Key, e.e.caller.bindings) + } + + if rule.Head.Value != nil { + value = child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings) + } + + head := ast.NewHead(rule.Head.Name, key, value) + p := copypropagation.New(head.Vars()).WithEnsureNonEmptyBody(true) + + e.e.saveSupport.Insert(path, &ast.Rule{ + Head: head, + Body: p.Apply(plugged), + Default: rule.Default, + }) + + child.traceRedo(rule) + e.e.saveStack.PushQuery(current) + return nil + }) + e.e.saveStack.PopQuery() + return err +} + +func (e evalVirtualPartial) evalTerm(iter unifyIterator, term *ast.Term, termbindings *bindings) error { + eval := evalTerm{ + e: e.e, + ref: e.ref, + pos: e.pos + 2, + bindings: e.bindings, + term: term, + termbindings: termbindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) +} + +func (e evalVirtualPartial) reduce(head *ast.Head, b *bindings, result *ast.Term) (*ast.Term, error) { + + switch v := result.Value.(type) { + case ast.Set: + v.Add(b.Plug(head.Key)) + case ast.Object: + key := b.Plug(head.Key) + value := b.Plug(head.Value) + exist := v.Get(key) + if exist != nil && !exist.Equal(value) { + return nil, objectDocKeyConflictErr(head.Location) + } + v.Insert(key, value) + result.Value = v + } + + return result, nil +} + +type evalVirtualComplete struct { + e *eval + ref ast.Ref + plugged ast.Ref + pos int + ir *ast.IndexResult + bindings *bindings + rterm *ast.Term + rbindings *bindings +} + +func (e evalVirtualComplete) eval(iter unifyIterator) error { + + if e.ir.Empty() { + return nil + } + + if len(e.ir.Rules) > 0 && len(e.ir.Rules[0].Head.Args) > 0 { + return nil + } + + if !e.e.unknown(e.ref, e.bindings) { + return e.evalValue(iter) + } + + var generateSupport bool + + if e.ir.Default != nil { + // If the other term is not constant OR it's equal to the default value, then + // a support rule must be produced as the default value _may_ be required. On + // the other hand, if the other term is constant (i.e., it does not require + // evaluation) and it differs from the default value then the default value is + // _not_ required, so partially evaluate the rule normally. + rterm := e.rbindings.Plug(e.rterm) + generateSupport = !ast.IsConstant(rterm.Value) || e.ir.Default.Head.Value.Equal(rterm) + } + + generateSupport = generateSupport || anyRefSetContainsPrefix(e.e.disableInlining, e.plugged[:e.pos+1]) + + if generateSupport { + return e.partialEvalSupport(iter) + } + + return e.partialEval(iter) +} + +func (e evalVirtualComplete) evalValue(iter unifyIterator) error { + cached := e.e.virtualCache.Get(e.plugged[:e.pos+1]) + if cached != nil { + e.e.instr.counterIncr(evalOpVirtualCacheHit) + return e.evalTerm(iter, cached, e.bindings) + } + + e.e.instr.counterIncr(evalOpVirtualCacheMiss) + + var prev *ast.Term + + for i := range e.ir.Rules { + next, err := e.evalValueRule(iter, e.ir.Rules[i], prev) + if err != nil { + return err + } + if next == nil { + for _, rule := range e.ir.Else[e.ir.Rules[i]] { + next, err = e.evalValueRule(iter, rule, prev) + if err != nil { + return err + } + if next != nil { + break + } + } + } + if next != nil { + prev = next + } + } + + if e.ir.Default != nil && prev == nil { + _, err := e.evalValueRule(iter, e.ir.Default, prev) + return err + } + + return nil +} + +func (e evalVirtualComplete) evalValueRule(iter unifyIterator, rule *ast.Rule, prev *ast.Term) (*ast.Term, error) { + + child := e.e.child(rule.Body) + child.traceEnter(rule) + var result *ast.Term + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + result = child.bindings.Plug(rule.Head.Value) + + if prev != nil { + if ast.Compare(result, prev) != 0 { + return completeDocConflictErr(rule.Location) + } + child.traceRedo(rule) + return nil + } + + prev = result + e.e.virtualCache.Put(e.plugged[:e.pos+1], result) + term, termbindings := child.bindings.apply(rule.Head.Value) + + err := e.evalTerm(iter, term, termbindings) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + + return result, err +} + +func (e evalVirtualComplete) partialEval(iter unifyIterator) error { + + for _, rule := range e.ir.Rules { + child := e.e.child(rule.Body) + child.traceEnter(rule) + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + term, termbindings := child.bindings.apply(rule.Head.Value) + + err := e.evalTerm(iter, term, termbindings) + if err != nil { + return err + } + + child.traceRedo(rule) + return nil + }) + + if err != nil { + return err + } + } + + return nil +} + +func (e evalVirtualComplete) partialEvalSupport(iter unifyIterator) error { + + path := e.plugged[:e.pos+1].Insert(e.e.saveNamespace, 1) + + if !e.e.saveSupport.Exists(path) { + + for i := range e.ir.Rules { + err := e.partialEvalSupportRule(iter, e.ir.Rules[i], path) + if err != nil { + return err + } + } + + if e.ir.Default != nil { + err := e.partialEvalSupportRule(iter, e.ir.Default, path) + if err != nil { + return err + } + } + } + + rewritten := ast.NewTerm(e.ref.Insert(e.e.saveNamespace, 1)) + return e.e.saveUnify(rewritten, e.rterm, e.bindings, e.rbindings, iter) +} + +func (e evalVirtualComplete) partialEvalSupportRule(iter unifyIterator, rule *ast.Rule, path ast.Ref) error { + + child := e.e.child(rule.Body) + child.traceEnter(rule) + + e.e.saveStack.PushQuery(nil) + + err := child.eval(func(child *eval) error { + child.traceExit(rule) + + current := e.e.saveStack.PopQuery() + plugged := current.Plug(e.e.caller.bindings) + + head := ast.NewHead(rule.Head.Name, nil, child.bindings.PlugNamespaced(rule.Head.Value, e.e.caller.bindings)) + p := copypropagation.New(head.Vars()).WithEnsureNonEmptyBody(true) + + e.e.saveSupport.Insert(path, &ast.Rule{ + Head: head, + Body: applyCopyPropagation(p, e.e.instr, plugged), + Default: rule.Default, + }) + + child.traceRedo(rule) + e.e.saveStack.PushQuery(current) + return nil + }) + e.e.saveStack.PopQuery() + return err +} + +func (e evalVirtualComplete) evalTerm(iter unifyIterator, term *ast.Term, termbindings *bindings) error { + eval := evalTerm{ + e: e.e, + ref: e.ref, + pos: e.pos + 1, + bindings: e.bindings, + term: term, + termbindings: termbindings, + rterm: e.rterm, + rbindings: e.rbindings, + } + return eval.eval(iter) +} + +type evalTerm struct { + e *eval + ref ast.Ref + pos int + bindings *bindings + term *ast.Term + termbindings *bindings + rterm *ast.Term + rbindings *bindings +} + +func (e evalTerm) eval(iter unifyIterator) error { + + if len(e.ref) == e.pos { + return e.e.biunify(e.term, e.rterm, e.termbindings, e.rbindings, iter) + } + + if e.e.saveSet.Contains(e.term, e.termbindings) { + return e.save(iter) + } + + plugged := e.bindings.Plug(e.ref[e.pos]) + + if plugged.IsGround() { + return e.next(iter, plugged) + } + + return e.enumerate(iter) +} + +func (e evalTerm) next(iter unifyIterator, plugged *ast.Term) error { + + term, bindings := e.get(plugged) + if term == nil { + return nil + } + + cpy := e + cpy.term = term + cpy.termbindings = bindings + cpy.pos++ + return cpy.eval(iter) +} + +func (e evalTerm) enumerate(iter unifyIterator) error { + + switch v := e.term.Value.(type) { + case ast.Array: + for i := range v { + k := ast.IntNumberTerm(i) + err := e.e.biunify(k, e.ref[e.pos], e.bindings, e.bindings, func() error { + return e.next(iter, k) + }) + if err != nil { + return err + } + } + case ast.Object: + return v.Iter(func(k, _ *ast.Term) error { + return e.e.biunify(k, e.ref[e.pos], e.termbindings, e.bindings, func() error { + return e.next(iter, e.termbindings.Plug(k)) + }) + }) + case ast.Set: + return v.Iter(func(elem *ast.Term) error { + return e.e.biunify(elem, e.ref[e.pos], e.termbindings, e.bindings, func() error { + return e.next(iter, e.termbindings.Plug(elem)) + }) + }) + } + + return nil +} + +func (e evalTerm) get(plugged *ast.Term) (*ast.Term, *bindings) { + switch v := e.term.Value.(type) { + case ast.Set: + if v.IsGround() { + if v.Contains(plugged) { + return e.termbindings.apply(plugged) + } + } else { + var t *ast.Term + var b *bindings + stop := v.Until(func(elem *ast.Term) bool { + if e.termbindings.Plug(elem).Equal(plugged) { + t, b = e.termbindings.apply(plugged) + return true + } + return false + }) + if stop { + return t, b + } + } + case ast.Object: + if v.IsGround() { + term := v.Get(plugged) + if term != nil { + return e.termbindings.apply(term) + } + } else { + var t *ast.Term + var b *bindings + stop := v.Until(func(k, v *ast.Term) bool { + if e.termbindings.Plug(k).Equal(plugged) { + t, b = e.termbindings.apply(v) + return true + } + return false + }) + if stop { + return t, b + } + } + case ast.Array: + term := v.Get(plugged) + if term != nil { + return e.termbindings.apply(term) + } + } + return nil, nil +} + +func (e evalTerm) save(iter unifyIterator) error { + + suffix := e.ref[e.pos:] + ref := make(ast.Ref, len(suffix)+1) + ref[0] = e.term + + for i := 0; i < len(suffix); i++ { + ref[i+1] = suffix[i] + } + + return e.e.biunify(ast.NewTerm(ref), e.rterm, e.termbindings, e.rbindings, iter) +} + +func applyCopyPropagation(p *copypropagation.CopyPropagator, instr *Instrumentation, body ast.Body) ast.Body { + instr.startTimer(partialOpCopyPropagation) + result := p.Apply(body) + instr.stopTimer(partialOpCopyPropagation) + return result +} + +func nonGroundKeys(a ast.Object) bool { + return a.Until(func(k, _ *ast.Term) bool { + return !k.IsGround() + }) +} + +func plugKeys(a ast.Object, b *bindings) ast.Object { + plugged, _ := a.Map(func(k, v *ast.Term) (*ast.Term, *ast.Term, error) { + return b.Plug(k), v, nil + }) + return plugged +} + +func plugSlice(xs []*ast.Term, b *bindings) []*ast.Term { + cpy := make([]*ast.Term, len(xs)) + for i := range cpy { + cpy[i] = b.Plug(xs[i]) + } + return cpy +} + +func canInlineNegation(safe ast.VarSet, queries []ast.Body) bool { + + size := 1 + + for _, query := range queries { + size *= len(query) + for _, expr := range query { + if !expr.Negated { + // Positive expressions containing variables cannot be trivially negated + // because they become unsafe (e.g., "x = 1" negated is "not x = 1" making x + // unsafe.) We check if the vars in the expr are already safe. + vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ + SkipRefCallHead: true, + SkipClosures: true, + }) + vis.Walk(expr) + unsafe := vis.Vars().Diff(safe).Diff(ast.ReservedVars) + if len(unsafe) > 0 { + return false + } + } + } + } + + // NOTE(tsandall): this limit is arbitrary–it's only in place to prevent the + // partial evaluation result from blowing up. In the future, we could make this + // configurable or do something more clever. + if size > 16 { + return false + } + + return true +} + +func complementedCartesianProduct(queries []ast.Body, idx int, curr ast.Body, iter func(ast.Body) error) error { + if idx == len(queries) { + return iter(curr) + } + for _, expr := range queries[idx] { + curr = append(curr, expr.Complement()) + if err := complementedCartesianProduct(queries, idx+1, curr, iter); err != nil { + return err + } + curr = curr[:len(curr)-1] + } + return nil +} + +func isInputRef(term *ast.Term) bool { + if ref, ok := term.Value.(ast.Ref); ok { + if ref.HasPrefix(ast.InputRootRef) { + return true + } + } + return false +} + +func isDataRef(term *ast.Term) bool { + if ref, ok := term.Value.(ast.Ref); ok { + if ref.HasPrefix(ast.DefaultRootRef) { + return true + } + } + return false +} + +func merge(a, b ast.Value) (ast.Value, bool) { + aObj, ok1 := a.(ast.Object) + bObj, ok2 := b.(ast.Object) + + if ok1 && ok2 { + return mergeObjects(aObj, bObj) + } + return nil, false +} + +// mergeObjects returns a new Object containing the non-overlapping keys of +// the objA and objB. If there are overlapping keys between objA and objB, +// the values of associated with the keys are merged. Only +// objects can be merged with other objects. If the values cannot be merged, +// objB value will be overwritten by objA value. +func mergeObjects(objA, objB ast.Object) (result ast.Object, ok bool) { + result = ast.NewObject() + stop := objA.Until(func(k, v *ast.Term) bool { + if v2 := objB.Get(k); v2 == nil { + result.Insert(k, v) + } else { + obj1, ok1 := v.Value.(ast.Object) + obj2, ok2 := v2.Value.(ast.Object) + + if !ok1 || !ok2 { + result.Insert(k, v) + return false + } + obj3, ok := mergeObjects(obj1, obj2) + if !ok { + return true + } + result.Insert(k, ast.NewTerm(obj3)) + } + return false + }) + if stop { + return nil, false + } + objB.Foreach(func(k, v *ast.Term) { + if v2 := objA.Get(k); v2 == nil { + result.Insert(k, v) + } + }) + return result, true +} + +func anyRefSetContainsPrefix(s [][]ast.Ref, prefix ast.Ref) bool { + for _, refs := range s { + for _, ref := range refs { + if ref.HasPrefix(prefix) { + return true + } + } + } + return false +} + +func refContainsNonScalar(ref ast.Ref) bool { + for _, term := range ref[1:] { + if !ast.IsScalar(term.Value) { + return true + } + } + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/glob.go b/vendor/github.com/open-policy-agent/opa/topdown/glob.go new file mode 100644 index 000000000..98052a0c6 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/glob.go @@ -0,0 +1,65 @@ +package topdown + +import ( + "fmt" + "sync" + + "github.com/gobwas/glob" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +var globCacheLock = sync.Mutex{} +var globCache map[string]glob.Glob + +func builtinGlobMatch(a, b, c ast.Value) (ast.Value, error) { + pattern, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + delimiters, err := builtins.RuneSliceOperand(b, 2) + if err != nil { + return nil, err + } + + if len(delimiters) == 0 { + delimiters = []rune{'.'} + } + + match, err := builtins.StringOperand(c, 3) + if err != nil { + return nil, err + } + + id := fmt.Sprintf("%s-%v", pattern, delimiters) + + globCacheLock.Lock() + defer globCacheLock.Unlock() + p, ok := globCache[id] + if !ok { + var err error + if p, err = glob.Compile(string(pattern), delimiters...); err != nil { + return nil, err + } + globCache[id] = p + } + + return ast.Boolean(p.Match(string(match))), nil +} + +func builtinGlobQuoteMeta(a ast.Value) (ast.Value, error) { + pattern, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(glob.QuoteMeta(string(pattern))), nil +} + +func init() { + globCache = map[string]glob.Glob{} + RegisterFunctionalBuiltin3(ast.GlobMatch.Name, builtinGlobMatch) + RegisterFunctionalBuiltin1(ast.GlobQuoteMeta.Name, builtinGlobQuoteMeta) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/http.go b/vendor/github.com/open-policy-agent/opa/topdown/http.go new file mode 100644 index 000000000..7c6cae2ad --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/http.go @@ -0,0 +1,466 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/url" + "strconv" + + "github.com/open-policy-agent/opa/internal/version" + + "net/http" + "os" + "strings" + "time" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +const defaultHTTPRequestTimeoutEnv = "HTTP_SEND_TIMEOUT" + +var defaultHTTPRequestTimeout = time.Second * 5 + +var allowedKeyNames = [...]string{ + "method", + "url", + "body", + "enable_redirect", + "force_json_decode", + "headers", + "raw_body", + "tls_use_system_certs", + "tls_ca_cert_file", + "tls_ca_cert_env_variable", + "tls_client_cert_env_variable", + "tls_client_key_env_variable", + "tls_client_cert_file", + "tls_client_key_file", + "tls_insecure_skip_verify", + "timeout", +} +var allowedKeys = ast.NewSet() + +var requiredKeys = ast.NewSet(ast.StringTerm("method"), ast.StringTerm("url")) + +type httpSendKey string + +// httpSendBuiltinCacheKey is the key in the builtin context cache that +// points to the http.send() specific cache resides at. +const httpSendBuiltinCacheKey httpSendKey = "HTTP_SEND_CACHE_KEY" + +func builtinHTTPSend(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + + req, err := validateHTTPRequestOperand(args[0], 1) + if err != nil { + return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) + } + + // check if cache already has a response for this query + resp := checkHTTPSendCache(bctx, req) + if resp == nil { + var err error + resp, err = executeHTTPRequest(bctx, req) + if err != nil { + return handleHTTPSendErr(bctx, err) + } + + // add result to cache + insertIntoHTTPSendCache(bctx, req, resp) + } + + return iter(ast.NewTerm(resp)) +} + +func init() { + createAllowedKeys() + initDefaults() + RegisterBuiltinFunc(ast.HTTPSend.Name, builtinHTTPSend) +} + +func handleHTTPSendErr(bctx BuiltinContext, err error) error { + // Return HTTP client timeout errors in a generic error message to avoid confusion about what happened. + // Do not do this if the builtin context was cancelled and is what caused the request to stop. + if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() && bctx.Context.Err() == nil { + err = fmt.Errorf("%s %s: request timed out", urlErr.Op, urlErr.URL) + } + return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) +} + +func initDefaults() { + timeoutDuration := os.Getenv(defaultHTTPRequestTimeoutEnv) + if timeoutDuration != "" { + var err error + defaultHTTPRequestTimeout, err = time.ParseDuration(timeoutDuration) + if err != nil { + // If it is set to something not valid don't let the process continue in a state + // that will almost definitely give unexpected results by having it set at 0 + // which means no timeout.. + // This environment variable isn't considered part of the public API. + // TODO(patrick-east): Remove the environment variable + panic(fmt.Sprintf("invalid value for HTTP_SEND_TIMEOUT: %s", err)) + } + } +} + +func validateHTTPRequestOperand(term *ast.Term, pos int) (ast.Object, error) { + + obj, err := builtins.ObjectOperand(term.Value, pos) + if err != nil { + return nil, err + } + + requestKeys := ast.NewSet(obj.Keys()...) + + invalidKeys := requestKeys.Diff(allowedKeys) + if invalidKeys.Len() != 0 { + return nil, builtins.NewOperandErr(pos, "invalid request parameters(s): %v", invalidKeys) + } + + missingKeys := requiredKeys.Diff(requestKeys) + if missingKeys.Len() != 0 { + return nil, builtins.NewOperandErr(pos, "missing required request parameters(s): %v", missingKeys) + } + + return obj, nil + +} + +// Adds custom headers to a new HTTP request. +func addHeaders(req *http.Request, headers map[string]interface{}) (bool, error) { + for k, v := range headers { + // Type assertion + header, ok := v.(string) + if !ok { + return false, fmt.Errorf("invalid type for headers value %q", v) + } + + // If the Host header is given, bump that up to + // the request. Otherwise, just collect it in the + // headers. + k := http.CanonicalHeaderKey(k) + switch k { + case "Host": + req.Host = header + default: + req.Header.Add(k, header) + } + } + + return true, nil +} + +func executeHTTPRequest(bctx BuiltinContext, obj ast.Object) (ast.Value, error) { + var url string + var method string + var tlsCaCertEnvVar []byte + var tlsCaCertFile string + var tlsClientKeyEnvVar []byte + var tlsClientCertEnvVar []byte + var tlsClientCertFile string + var tlsClientKeyFile string + var body *bytes.Buffer + var rawBody *bytes.Buffer + var enableRedirect bool + var forceJSONDecode bool + var tlsUseSystemCerts bool + var tlsConfig tls.Config + var clientCerts []tls.Certificate + var customHeaders map[string]interface{} + var tlsInsecureSkipVerify bool + var timeout = defaultHTTPRequestTimeout + + for _, val := range obj.Keys() { + key, err := ast.JSON(val.Value) + if err != nil { + return nil, err + } + key = key.(string) + + switch key { + case "method": + method = obj.Get(val).String() + method = strings.ToUpper(strings.Trim(method, "\"")) + case "url": + url = obj.Get(val).String() + url = strings.Trim(url, "\"") + case "enable_redirect": + enableRedirect, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, err + } + case "force_json_decode": + forceJSONDecode, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, err + } + case "body": + bodyVal := obj.Get(val).Value + bodyValInterface, err := ast.JSON(bodyVal) + if err != nil { + return nil, err + } + + bodyValBytes, err := json.Marshal(bodyValInterface) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(bodyValBytes) + case "raw_body": + s, ok := obj.Get(val).Value.(ast.String) + if !ok { + return nil, fmt.Errorf("raw_body must be a string") + } + rawBody = bytes.NewBuffer([]byte(s)) + case "tls_use_system_certs": + tlsUseSystemCerts, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, err + } + case "tls_ca_cert_file": + tlsCaCertFile = obj.Get(val).String() + tlsCaCertFile = strings.Trim(tlsCaCertFile, "\"") + case "tls_ca_cert_env_variable": + caCertEnv := obj.Get(val).String() + caCertEnv = strings.Trim(caCertEnv, "\"") + tlsCaCertEnvVar = []byte(os.Getenv(caCertEnv)) + case "tls_client_cert_env_variable": + clientCertEnv := obj.Get(val).String() + clientCertEnv = strings.Trim(clientCertEnv, "\"") + tlsClientCertEnvVar = []byte(os.Getenv(clientCertEnv)) + case "tls_client_key_env_variable": + clientKeyEnv := obj.Get(val).String() + clientKeyEnv = strings.Trim(clientKeyEnv, "\"") + tlsClientKeyEnvVar = []byte(os.Getenv(clientKeyEnv)) + case "tls_client_cert_file": + tlsClientCertFile = obj.Get(val).String() + tlsClientCertFile = strings.Trim(tlsClientCertFile, "\"") + case "tls_client_key_file": + tlsClientKeyFile = obj.Get(val).String() + tlsClientKeyFile = strings.Trim(tlsClientKeyFile, "\"") + case "headers": + headersVal := obj.Get(val).Value + headersValInterface, err := ast.JSON(headersVal) + if err != nil { + return nil, err + } + var ok bool + customHeaders, ok = headersValInterface.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("invalid type for headers key") + } + case "tls_insecure_skip_verify": + tlsInsecureSkipVerify, err = strconv.ParseBool(obj.Get(val).String()) + if err != nil { + return nil, err + } + case "timeout": + timeout, err = parseTimeout(obj.Get(val).Value) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("invalid parameter %q", key) + } + } + + client := &http.Client{ + Timeout: timeout, + } + + if tlsInsecureSkipVerify { + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: tlsInsecureSkipVerify}, + } + } + if tlsClientCertFile != "" && tlsClientKeyFile != "" { + clientCertFromFile, err := tls.LoadX509KeyPair(tlsClientCertFile, tlsClientKeyFile) + if err != nil { + return nil, err + } + clientCerts = append(clientCerts, clientCertFromFile) + } + + if len(tlsClientCertEnvVar) > 0 && len(tlsClientKeyEnvVar) > 0 { + clientCertFromEnv, err := tls.X509KeyPair(tlsClientCertEnvVar, tlsClientKeyEnvVar) + if err != nil { + return nil, err + } + clientCerts = append(clientCerts, clientCertFromEnv) + } + + isTLS := false + if len(clientCerts) > 0 { + isTLS = true + tlsConfig.Certificates = append(tlsConfig.Certificates, clientCerts...) + } + + if tlsUseSystemCerts || len(tlsCaCertFile) > 0 || len(tlsCaCertEnvVar) > 0 { + isTLS = true + connRootCAs, err := createRootCAs(tlsCaCertFile, tlsCaCertEnvVar, tlsUseSystemCerts) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = connRootCAs + } + + if isTLS { + client.Transport = &http.Transport{ + TLSClientConfig: &tlsConfig, + } + } + + // check if redirects are enabled + if !enableRedirect { + client.CheckRedirect = func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + } + } + + if rawBody != nil { + body = rawBody + } else if body == nil { + body = bytes.NewBufferString("") + } + + // create the http request, use the builtin context's context to ensure + // the request is cancelled if evaluation is cancelled. + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + req = req.WithContext(bctx.Context) + + // Add custom headers + if len(customHeaders) != 0 { + if ok, err := addHeaders(req, customHeaders); !ok { + return nil, err + } + // Don't overwrite or append to one that was set in the custom headers + if _, hasUA := customHeaders["User-Agent"]; !hasUA { + req.Header.Add("User-Agent", version.UserAgent) + } + } + + // execute the http request + resp, err := client.Do(req) + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + // format the http result + var resultBody interface{} + var resultRawBody []byte + + var buf bytes.Buffer + tee := io.TeeReader(resp.Body, &buf) + resultRawBody, err = ioutil.ReadAll(tee) + if err != nil { + return nil, err + } + + // If the response body cannot be JSON decoded, + // an error will not be returned. Instead the "body" field + // in the result will be null. + if isContentTypeJSON(resp.Header) || forceJSONDecode { + json.NewDecoder(&buf).Decode(&resultBody) + } + + result := make(map[string]interface{}) + result["status"] = resp.Status + result["status_code"] = resp.StatusCode + result["body"] = resultBody + result["raw_body"] = string(resultRawBody) + + resultObj, err := ast.InterfaceToValue(result) + if err != nil { + return nil, err + } + + return resultObj, nil +} + +func isContentTypeJSON(header http.Header) bool { + return strings.Contains(header.Get("Content-Type"), "application/json") +} + +// In the BuiltinContext cache we only store a single entry that points to +// our ValueMap which is the "real" http.send() cache. +func getHTTPSendCache(bctx BuiltinContext) *ast.ValueMap { + raw, ok := bctx.Cache.Get(httpSendBuiltinCacheKey) + if !ok { + // Initialize if it isn't there + cache := ast.NewValueMap() + bctx.Cache.Put(httpSendBuiltinCacheKey, cache) + return cache + } + + cache, ok := raw.(*ast.ValueMap) + if !ok { + return nil + } + return cache +} + +// checkHTTPSendCache checks for the given key's value in the cache +func checkHTTPSendCache(bctx BuiltinContext, key ast.Object) ast.Value { + requestCache := getHTTPSendCache(bctx) + if requestCache == nil { + return nil + } + + return requestCache.Get(key) +} + +func insertIntoHTTPSendCache(bctx BuiltinContext, key ast.Object, value ast.Value) { + requestCache := getHTTPSendCache(bctx) + if requestCache == nil { + // Should never happen.. if it does just skip caching the value + return + } + requestCache.Put(key, value) +} + +func createAllowedKeys() { + for _, element := range allowedKeyNames { + allowedKeys.Add(ast.StringTerm(element)) + } +} + +func parseTimeout(timeoutVal ast.Value) (time.Duration, error) { + var timeout time.Duration + switch t := timeoutVal.(type) { + case ast.Number: + timeoutInt, ok := t.Int64() + if !ok { + return timeout, fmt.Errorf("invalid timeout number value %v, must be int64", timeoutVal) + } + return time.Duration(timeoutInt), nil + case ast.String: + // Support strings without a unit, treat them the same as just a number value (ns) + var err error + timeoutInt, err := strconv.ParseInt(string(t), 10, 64) + if err == nil { + return time.Duration(timeoutInt), nil + } + + // Try parsing it as a duration (requires a supported units suffix) + timeout, err = time.ParseDuration(string(t)) + if err != nil { + return timeout, fmt.Errorf("invalid timeout value %v: %s", timeoutVal, err) + } + return timeout, nil + default: + return timeout, builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got %s", ast.TypeName(t)) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/input.go b/vendor/github.com/open-policy-agent/opa/topdown/input.go new file mode 100644 index 000000000..a74ef199f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/input.go @@ -0,0 +1,74 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + + "github.com/open-policy-agent/opa/ast" +) + +var errConflictingDoc = fmt.Errorf("conflicting documents") +var errBadPath = fmt.Errorf("bad document path") + +func mergeTermWithValues(exist *ast.Term, pairs [][2]*ast.Term) (*ast.Term, error) { + + var result *ast.Term + + if exist != nil { + result = exist.Copy() + } + + for _, pair := range pairs { + + if err := ast.IsValidImportPath(pair[0].Value); err != nil { + return nil, errBadPath + } + + target := pair[0].Value.(ast.Ref) + + if len(target) == 1 { + result = pair[1] + } else if result == nil { + result = ast.NewTerm(makeTree(target[1:], pair[1])) + } else { + node := result + done := false + for i := 1; i < len(target)-1 && !done; i++ { + if child := node.Get(target[i]); child == nil { + obj, ok := node.Value.(ast.Object) + if !ok { + return nil, errConflictingDoc + } + obj.Insert(target[i], ast.NewTerm(makeTree(target[i+1:], pair[1]))) + done = true + } else { + node = child + } + } + if !done { + obj, ok := node.Value.(ast.Object) + if !ok { + return nil, errConflictingDoc + } + obj.Insert(target[len(target)-1], pair[1]) + } + } + } + + return result, nil +} + +// makeTree returns an object that represents a document where the value v is +// the leaf and elements in k represent intermediate objects. +func makeTree(k ast.Ref, v *ast.Term) ast.Object { + var obj ast.Object + for i := len(k) - 1; i >= 1; i-- { + obj = ast.NewObject(ast.Item(k[i], v)) + v = &ast.Term{Value: obj} + } + obj = ast.NewObject(ast.Item(k[0], v)) + return obj +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/instrumentation.go b/vendor/github.com/open-policy-agent/opa/topdown/instrumentation.go new file mode 100644 index 000000000..fa871cf90 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/instrumentation.go @@ -0,0 +1,59 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/metrics" + +const ( + evalOpPlug = "eval_op_plug" + evalOpResolve = "eval_op_resolve" + evalOpRuleIndex = "eval_op_rule_index" + evalOpBuiltinCall = "eval_op_builtin_call" + evalOpVirtualCacheHit = "eval_op_virtual_cache_hit" + evalOpVirtualCacheMiss = "eval_op_virtual_cache_miss" + evalOpBaseCacheHit = "eval_op_base_cache_hit" + evalOpBaseCacheMiss = "eval_op_base_cache_miss" + partialOpSaveUnify = "partial_op_save_unify" + partialOpSaveSetContains = "partial_op_save_set_contains" + partialOpSaveSetContainsRec = "partial_op_save_set_contains_rec" + partialOpCopyPropagation = "partial_op_copy_propagation" +) + +// Instrumentation implements helper functions to instrument query evaluation +// to diagnose performance issues. Instrumentation may be expensive in some +// cases, so it is disabled by default. +type Instrumentation struct { + m metrics.Metrics +} + +// NewInstrumentation returns a new Instrumentation object. Performance +// diagnostics recorded on this Instrumentation object will stored in m. +func NewInstrumentation(m metrics.Metrics) *Instrumentation { + return &Instrumentation{ + m: m, + } +} + +func (instr *Instrumentation) startTimer(name string) { + if instr == nil { + return + } + instr.m.Timer(name).Start() +} + +func (instr *Instrumentation) stopTimer(name string) { + if instr == nil { + return + } + delta := instr.m.Timer(name).Stop() + instr.m.Histogram(name).Update(delta) +} + +func (instr *Instrumentation) counterIncr(name string) { + if instr == nil { + return + } + instr.m.Counter(name).Incr() +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/LICENSE b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/LICENSE new file mode 100644 index 000000000..6369f4fcc --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 lestrrat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/buffer/buffer.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/buffer/buffer.go new file mode 100644 index 000000000..ca4ac419b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/buffer/buffer.go @@ -0,0 +1,113 @@ +// Package buffer provides a very thin wrapper around []byte buffer called +// `Buffer`, to provide functionalities that are often used within the jwx +// related packages +package buffer + +import ( + "encoding/base64" + "encoding/binary" + "encoding/json" + + "github.com/pkg/errors" +) + +// Buffer wraps `[]byte` and provides functions that are often used in +// the jwx related packages. One notable difference is that while +// encoding/json marshalls `[]byte` using base64.StdEncoding, this +// module uses base64.RawURLEncoding as mandated by the spec +type Buffer []byte + +// FromUint creates a `Buffer` from an unsigned int +func FromUint(v uint64) Buffer { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, v) + + i := 0 + for ; i < len(data); i++ { + if data[i] != 0x0 { + break + } + } + return Buffer(data[i:]) +} + +// FromBase64 constructs a new Buffer from a base64 encoded data +func FromBase64(v []byte) (Buffer, error) { + b := Buffer{} + if err := b.Base64Decode(v); err != nil { + return Buffer(nil), errors.Wrap(err, "failed to decode from base64") + } + + return b, nil +} + +// FromNData constructs a new Buffer from a "n:data" format +// (I made that name up) +func FromNData(v []byte) (Buffer, error) { + size := binary.BigEndian.Uint32(v) + buf := make([]byte, int(size)) + copy(buf, v[4:4+size]) + return Buffer(buf), nil +} + +// Bytes returns the raw bytes that comprises the Buffer +func (b Buffer) Bytes() []byte { + return []byte(b) +} + +// NData returns Datalen || Data, where Datalen is a 32 bit counter for +// the length of the following data, and Data is the octets that comprise +// the buffer data +func (b Buffer) NData() []byte { + buf := make([]byte, 4+b.Len()) + binary.BigEndian.PutUint32(buf, uint32(b.Len())) + + copy(buf[4:], b.Bytes()) + return buf +} + +// Len returns the number of bytes that the Buffer holds +func (b Buffer) Len() int { + return len(b) +} + +// Base64Encode encodes the contents of the Buffer using base64.RawURLEncoding +func (b Buffer) Base64Encode() ([]byte, error) { + enc := base64.RawURLEncoding + out := make([]byte, enc.EncodedLen(len(b))) + enc.Encode(out, b) + return out, nil +} + +// Base64Decode decodes the contents of the Buffer using base64.RawURLEncoding +func (b *Buffer) Base64Decode(v []byte) error { + enc := base64.RawURLEncoding + out := make([]byte, enc.DecodedLen(len(v))) + n, err := enc.Decode(out, v) + if err != nil { + return errors.Wrap(err, "failed to decode from base64") + } + out = out[:n] + *b = Buffer(out) + return nil +} + +// MarshalJSON marshals the buffer into JSON format after encoding the buffer +// with base64.RawURLEncoding +func (b Buffer) MarshalJSON() ([]byte, error) { + v, err := b.Base64Encode() + if err != nil { + return nil, errors.Wrap(err, "failed to encode to base64") + } + return json.Marshal(string(v)) +} + +// UnmarshalJSON unmarshals from a JSON string into a Buffer, after decoding it +// with base64.RawURLEncoding +func (b *Buffer) UnmarshalJSON(data []byte) error { + var x string + if err := json.Unmarshal(data, &x); err != nil { + return errors.Wrap(err, "failed to unmarshal JSON") + } + return b.Base64Decode([]byte(x)) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/elliptic.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/elliptic.go new file mode 100644 index 000000000..b7e35dc70 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/elliptic.go @@ -0,0 +1,11 @@ +package jwa + +// EllipticCurveAlgorithm represents the algorithms used for EC keys +type EllipticCurveAlgorithm string + +// Supported values for EllipticCurveAlgorithm +const ( + P256 EllipticCurveAlgorithm = "P-256" + P384 EllipticCurveAlgorithm = "P-384" + P521 EllipticCurveAlgorithm = "P-521" +) diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/key_type.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/key_type.go new file mode 100644 index 000000000..076bd39ed --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/key_type.go @@ -0,0 +1,67 @@ +package jwa + +import ( + "strconv" + + "github.com/pkg/errors" +) + +// KeyType represents the key type ("kty") that are supported +type KeyType string + +var keyTypeAlg = map[string]struct{}{"EC": {}, "oct": {}, "RSA": {}} + +// Supported values for KeyType +const ( + EC KeyType = "EC" // Elliptic Curve + InvalidKeyType KeyType = "" // Invalid KeyType + OctetSeq KeyType = "oct" // Octet sequence (used to represent symmetric keys) + RSA KeyType = "RSA" // RSA +) + +// Accept is used when conversion from values given by +// outside sources (such as JSON payloads) is required +func (keyType *KeyType) Accept(value interface{}) error { + var tmp KeyType + switch x := value.(type) { + case string: + tmp = KeyType(x) + case KeyType: + tmp = x + default: + return errors.Errorf(`invalid type for jwa.KeyType: %T`, value) + } + _, ok := keyTypeAlg[tmp.String()] + if !ok { + return errors.Errorf("Unknown Key Type algorithm") + } + + *keyType = tmp + return nil +} + +// String returns the string representation of a KeyType +func (keyType KeyType) String() string { + return string(keyType) +} + +// UnmarshalJSON unmarshals and checks data as KeyType Algorithm +func (keyType *KeyType) UnmarshalJSON(data []byte) error { + var quote byte = '"' + var quoted string + if data[0] == quote { + var err error + quoted, err = strconv.Unquote(string(data)) + if err != nil { + return errors.Wrap(err, "Failed to process signature algorithm") + } + } else { + quoted = string(data) + } + _, ok := keyTypeAlg[quoted] + if !ok { + return errors.Errorf("Unknown signature algorithm") + } + *keyType = KeyType(quoted) + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/parameters.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/parameters.go new file mode 100644 index 000000000..63c5a6462 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/parameters.go @@ -0,0 +1,29 @@ +package jwa + +import ( + "crypto/elliptic" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/buffer" +) + +// EllipticCurve provides a indirect type to standard elliptic curve such that we can +// use it for unmarshal +type EllipticCurve struct { + elliptic.Curve +} + +// AlgorithmParameters provides a single structure suitable to unmarshaling any JWK +type AlgorithmParameters struct { + N buffer.Buffer `json:"n,omitempty"` + E buffer.Buffer `json:"e,omitempty"` + D buffer.Buffer `json:"d,omitempty"` + P buffer.Buffer `json:"p,omitempty"` + Q buffer.Buffer `json:"q,omitempty"` + Dp buffer.Buffer `json:"dp,omitempty"` + Dq buffer.Buffer `json:"dq,omitempty"` + Qi buffer.Buffer `json:"qi,omitempty"` + Crv EllipticCurveAlgorithm `json:"crv,omitempty"` + X buffer.Buffer `json:"x,omitempty"` + Y buffer.Buffer `json:"y,omitempty"` + K buffer.Buffer `json:"k,omitempty"` +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/signature.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/signature.go new file mode 100644 index 000000000..a0988ecab --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwa/signature.go @@ -0,0 +1,76 @@ +package jwa + +import ( + "strconv" + + "github.com/pkg/errors" +) + +// SignatureAlgorithm represents the various signature algorithms as described in https://tools.ietf.org/html/rfc7518#section-3.1 +type SignatureAlgorithm string + +var signatureAlg = map[string]struct{}{"ES256": {}, "ES384": {}, "ES512": {}, "HS256": {}, "HS384": {}, "HS512": {}, "PS256": {}, "PS384": {}, "PS512": {}, "RS256": {}, "RS384": {}, "RS512": {}, "none": {}} + +// Supported values for SignatureAlgorithm +const ( + ES256 SignatureAlgorithm = "ES256" // ECDSA using P-256 and SHA-256 + ES384 SignatureAlgorithm = "ES384" // ECDSA using P-384 and SHA-384 + ES512 SignatureAlgorithm = "ES512" // ECDSA using P-521 and SHA-512 + HS256 SignatureAlgorithm = "HS256" // HMAC using SHA-256 + HS384 SignatureAlgorithm = "HS384" // HMAC using SHA-384 + HS512 SignatureAlgorithm = "HS512" // HMAC using SHA-512 + NoSignature SignatureAlgorithm = "none" + PS256 SignatureAlgorithm = "PS256" // RSASSA-PSS using SHA256 and MGF1-SHA256 + PS384 SignatureAlgorithm = "PS384" // RSASSA-PSS using SHA384 and MGF1-SHA384 + PS512 SignatureAlgorithm = "PS512" // RSASSA-PSS using SHA512 and MGF1-SHA512 + RS256 SignatureAlgorithm = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 + RS384 SignatureAlgorithm = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 + RS512 SignatureAlgorithm = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 + NoValue SignatureAlgorithm = "" // No value is different from none +) + +// Accept is used when conversion from values given by +// outside sources (such as JSON payloads) is required +func (signature *SignatureAlgorithm) Accept(value interface{}) error { + var tmp SignatureAlgorithm + switch x := value.(type) { + case string: + tmp = SignatureAlgorithm(x) + case SignatureAlgorithm: + tmp = x + default: + return errors.Errorf(`invalid type for jwa.SignatureAlgorithm: %T`, value) + } + _, ok := signatureAlg[tmp.String()] + if !ok { + return errors.Errorf("Unknown signature algorithm") + } + *signature = tmp + return nil +} + +// String returns the string representation of a SignatureAlgorithm +func (signature SignatureAlgorithm) String() string { + return string(signature) +} + +// UnmarshalJSON unmarshals and checks data as Signature Algorithm +func (signature *SignatureAlgorithm) UnmarshalJSON(data []byte) error { + var quote byte = '"' + var quoted string + if data[0] == quote { + var err error + quoted, err = strconv.Unquote(string(data)) + if err != nil { + return errors.Wrap(err, "Failed to process signature algorithm") + } + } else { + quoted = string(data) + } + _, ok := signatureAlg[quoted] + if !ok { + return errors.Errorf("Unknown signature algorithm") + } + *signature = SignatureAlgorithm(quoted) + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/ecdsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/ecdsa.go new file mode 100644 index 000000000..7bff2bf8e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/ecdsa.go @@ -0,0 +1,120 @@ +package jwk + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "math/big" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +func newECDSAPublicKey(key *ecdsa.PublicKey) (*ECDSAPublicKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.EC) + if err != nil { + return nil, errors.Wrapf(err, "Failed to set Key Type") + } + + return &ECDSAPublicKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +func newECDSAPrivateKey(key *ecdsa.PrivateKey) (*ECDSAPrivateKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.EC) + if err != nil { + return nil, errors.Wrapf(err, "Failed to set Key Type") + } + + return &ECDSAPrivateKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +// Materialize returns the EC-DSA public key represented by this JWK +func (k ECDSAPublicKey) Materialize() (interface{}, error) { + return k.key, nil +} + +// Materialize returns the EC-DSA private key represented by this JWK +func (k ECDSAPrivateKey) Materialize() (interface{}, error) { + return k.key, nil +} + +// GenerateKey creates a ECDSAPublicKey from JWK format +func (k *ECDSAPublicKey) GenerateKey(keyJSON *RawKeyJSON) error { + + var x, y big.Int + + if keyJSON.X == nil || keyJSON.Y == nil || keyJSON.Crv == "" { + return errors.Errorf("Missing mandatory key parameters X, Y or Crv") + } + + x.SetBytes(keyJSON.X.Bytes()) + y.SetBytes(keyJSON.Y.Bytes()) + + var curve elliptic.Curve + switch keyJSON.Crv { + case jwa.P256: + curve = elliptic.P256() + case jwa.P384: + curve = elliptic.P384() + case jwa.P521: + curve = elliptic.P521() + default: + return errors.Errorf(`invalid curve name %s`, keyJSON.Crv) + } + + *k = ECDSAPublicKey{ + StandardHeaders: &keyJSON.StandardHeaders, + key: &ecdsa.PublicKey{ + Curve: curve, + X: &x, + Y: &y, + }, + } + return nil +} + +// GenerateKey creates a ECDSAPrivateKey from JWK format +func (k *ECDSAPrivateKey) GenerateKey(keyJSON *RawKeyJSON) error { + + if keyJSON.D == nil { + return errors.Errorf("Missing mandatory key parameter D") + } + eCDSAPublicKey := &ECDSAPublicKey{} + err := eCDSAPublicKey.GenerateKey(keyJSON) + if err != nil { + return errors.Wrap(err, `failed to generate public key`) + } + dBytes := keyJSON.D.Bytes() + // The length of this octet string MUST be ceiling(log-base-2(n)/8) + // octets (where n is the order of the curve). This is because the private + // key d must be in the interval [1, n-1] so the bitlength of d should be + // no larger than the bitlength of n-1. The easiest way to find the octet + // length is to take bitlength(n-1), add 7 to force a carry, and shift this + // bit sequence right by 3, which is essentially dividing by 8 and adding + // 1 if there is any remainder. Thus, the private key value d should be + // output to (bitlength(n-1)+7)>>3 octets. + n := eCDSAPublicKey.key.Params().N + octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3 + if octetLength-len(dBytes) != 0 { + return errors.Errorf("Failed to generate private key. Incorrect D value") + } + privateKey := &ecdsa.PrivateKey{ + PublicKey: *eCDSAPublicKey.key, + D: (&big.Int{}).SetBytes(keyJSON.D.Bytes()), + } + + k.key = privateKey + k.StandardHeaders = &keyJSON.StandardHeaders + + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/headers.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/headers.go new file mode 100644 index 000000000..8f310a4c1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/headers.go @@ -0,0 +1,178 @@ +package jwk + +import ( + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// Convenience constants for common JWK parameters +const ( + AlgorithmKey = "alg" + KeyIDKey = "kid" + KeyOpsKey = "key_ops" + KeyTypeKey = "kty" + KeyUsageKey = "use" + PrivateParamsKey = "privateParams" +) + +// Headers provides a common interface to all future possible headers +type Headers interface { + Get(string) (interface{}, bool) + Set(string, interface{}) error + Walk(func(string, interface{}) error) error + GetAlgorithm() jwa.SignatureAlgorithm + GetKeyID() string + GetKeyOps() KeyOperationList + GetKeyType() jwa.KeyType + GetKeyUsage() string + GetPrivateParams() map[string]interface{} +} + +// StandardHeaders stores the common JWK parameters +type StandardHeaders struct { + Algorithm *jwa.SignatureAlgorithm `json:"alg,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.4 + KeyID string `json:"kid,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 + KeyOps KeyOperationList `json:"key_ops,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.3 + KeyType jwa.KeyType `json:"kty,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.1 + KeyUsage string `json:"use,omitempty"` // https://tools.ietf.org/html/rfc7517#section-4.2 + PrivateParams map[string]interface{} `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 +} + +// GetAlgorithm is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetAlgorithm() jwa.SignatureAlgorithm { + if v := h.Algorithm; v != nil { + return *v + } + return jwa.NoValue +} + +// GetKeyID is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyID() string { + return h.KeyID +} + +// GetKeyOps is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyOps() KeyOperationList { + return h.KeyOps +} + +// GetKeyType is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyType() jwa.KeyType { + return h.KeyType +} + +// GetKeyUsage is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetKeyUsage() string { + return h.KeyUsage +} + +// GetPrivateParams is a convenience function to retrieve the corresponding value stored in the StandardHeaders +func (h *StandardHeaders) GetPrivateParams() map[string]interface{} { + return h.PrivateParams +} + +// Get is a general getter function for JWK StandardHeaders structure +func (h *StandardHeaders) Get(name string) (interface{}, bool) { + switch name { + case AlgorithmKey: + alg := h.GetAlgorithm() + if alg != jwa.NoValue { + return alg, true + } + return nil, false + case KeyIDKey: + v := h.KeyID + if v == "" { + return nil, false + } + return v, true + case KeyOpsKey: + v := h.KeyOps + if v == nil { + return nil, false + } + return v, true + case KeyTypeKey: + v := h.KeyType + if v == jwa.InvalidKeyType { + return nil, false + } + return v, true + case KeyUsageKey: + v := h.KeyUsage + if v == "" { + return nil, false + } + return v, true + case PrivateParamsKey: + v := h.PrivateParams + if len(v) == 0 { + return nil, false + } + return v, true + default: + return nil, false + } +} + +// Set is a general getter function for JWK StandardHeaders structure +func (h *StandardHeaders) Set(name string, value interface{}) error { + switch name { + case AlgorithmKey: + var acceptor jwa.SignatureAlgorithm + if err := acceptor.Accept(value); err != nil { + return errors.Wrapf(err, `invalid value for %s key`, AlgorithmKey) + } + h.Algorithm = &acceptor + return nil + case KeyIDKey: + if v, ok := value.(string); ok { + h.KeyID = v + return nil + } + return errors.Errorf("invalid value for %s key: %T", KeyIDKey, value) + case KeyOpsKey: + if err := h.KeyOps.Accept(value); err != nil { + return errors.Wrapf(err, "invalid value for %s key", KeyOpsKey) + } + return nil + case KeyTypeKey: + if err := h.KeyType.Accept(value); err != nil { + return errors.Wrapf(err, "invalid value for %s key", KeyTypeKey) + } + return nil + case KeyUsageKey: + if v, ok := value.(string); ok { + h.KeyUsage = v + return nil + } + return errors.Errorf("invalid value for %s key: %T", KeyUsageKey, value) + case PrivateParamsKey: + if v, ok := value.(map[string]interface{}); ok { + h.PrivateParams = v + return nil + } + return errors.Errorf("invalid value for %s key: %T", PrivateParamsKey, value) + default: + return errors.Errorf(`invalid key: %s`, name) + } +} + +// Walk iterates over all JWK standard headers fields while applying a function to its value. +func (h StandardHeaders) Walk(f func(string, interface{}) error) error { + for _, key := range []string{AlgorithmKey, KeyIDKey, KeyOpsKey, KeyTypeKey, KeyUsageKey, PrivateParamsKey} { + if v, ok := h.Get(key); ok { + if err := f(key, v); err != nil { + return errors.Wrapf(err, `walk function returned error for %s`, key) + } + } + } + + for k, v := range h.PrivateParams { + if err := f(k, v); err != nil { + return errors.Wrapf(err, `walk function returned error for %s`, k) + } + } + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/interface.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/interface.go new file mode 100644 index 000000000..f718bec67 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/interface.go @@ -0,0 +1,70 @@ +package jwk + +import ( + "crypto/ecdsa" + "crypto/rsa" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// Set is a convenience struct to allow generating and parsing +// JWK sets as opposed to single JWKs +type Set struct { + Keys []Key `json:"keys"` +} + +// Key defines the minimal interface for each of the +// key types. Their use and implementation differ significantly +// between each key types, so you should use type assertions +// to perform more specific tasks with each key +type Key interface { + Headers + + // Materialize creates the corresponding key. For example, + // RSA types would create *rsa.PublicKey or *rsa.PrivateKey, + // EC types would create *ecdsa.PublicKey or *ecdsa.PrivateKey, + // and OctetSeq types create a []byte key. + Materialize() (interface{}, error) + GenerateKey(*RawKeyJSON) error +} + +// RawKeyJSON is generic type that represents any kind JWK +type RawKeyJSON struct { + StandardHeaders + jwa.AlgorithmParameters +} + +// RawKeySetJSON is generic type that represents a JWK Set +type RawKeySetJSON struct { + Keys []RawKeyJSON `json:"keys"` +} + +// RSAPublicKey is a type of JWK generated from RSA public keys +type RSAPublicKey struct { + *StandardHeaders + key *rsa.PublicKey +} + +// RSAPrivateKey is a type of JWK generated from RSA private keys +type RSAPrivateKey struct { + *StandardHeaders + key *rsa.PrivateKey +} + +// SymmetricKey is a type of JWK generated from symmetric keys +type SymmetricKey struct { + *StandardHeaders + key []byte +} + +// ECDSAPublicKey is a type of JWK generated from ECDSA public keys +type ECDSAPublicKey struct { + *StandardHeaders + key *ecdsa.PublicKey +} + +// ECDSAPrivateKey is a type of JWK generated from ECDH-ES private keys +type ECDSAPrivateKey struct { + *StandardHeaders + key *ecdsa.PrivateKey +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/jwk.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/jwk.go new file mode 100644 index 000000000..18835cbb3 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/jwk.go @@ -0,0 +1,150 @@ +// Package jwk implements JWK as described in https://tools.ietf.org/html/rfc7517 +package jwk + +import ( + "crypto/ecdsa" + "crypto/rsa" + "encoding/json" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// GetPublicKey returns the public key based on the private key type. +// For rsa key types *rsa.PublicKey is returned; for ecdsa key types *ecdsa.PublicKey; +// for byte slice (raw) keys, the key itself is returned. If the corresponding +// public key cannot be deduced, an error is returned +func GetPublicKey(key interface{}) (interface{}, error) { + if key == nil { + return nil, errors.New(`jwk.New requires a non-nil key`) + } + + switch v := key.(type) { + // Mental note: although Public() is defined in both types, + // you can not coalesce the clauses for rsa.PrivateKey and + // ecdsa.PrivateKey, as then `v` becomes interface{} + // b/c the compiler cannot deduce the exact type. + case *rsa.PrivateKey: + return v.Public(), nil + case *ecdsa.PrivateKey: + return v.Public(), nil + case []byte: + return v, nil + default: + return nil, errors.Errorf(`invalid key type %T`, key) + } +} + +// GetKeyTypeFromKey creates a jwk.Key from the given key. +func GetKeyTypeFromKey(key interface{}) jwa.KeyType { + + switch key.(type) { + case *rsa.PrivateKey, *rsa.PublicKey: + return jwa.RSA + case *ecdsa.PrivateKey, *ecdsa.PublicKey: + return jwa.EC + case []byte: + return jwa.OctetSeq + default: + return jwa.InvalidKeyType + } +} + +// New creates a jwk.Key from the given key. +func New(key interface{}) (Key, error) { + if key == nil { + return nil, errors.New(`jwk.New requires a non-nil key`) + } + + switch v := key.(type) { + case *rsa.PrivateKey: + return newRSAPrivateKey(v) + case *rsa.PublicKey: + return newRSAPublicKey(v) + case *ecdsa.PrivateKey: + return newECDSAPrivateKey(v) + case *ecdsa.PublicKey: + return newECDSAPublicKey(v) + case []byte: + return newSymmetricKey(v) + default: + return nil, errors.Errorf(`invalid key type %T`, key) + } +} + +func parse(jwkSrc string) (*Set, error) { + + var jwkKeySet Set + var jwkKey Key + rawKeySetJSON := &RawKeySetJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeySetJSON) + if err != nil { + return nil, errors.Wrap(err, "Failed to unmarshal JWK Set") + } + if len(rawKeySetJSON.Keys) == 0 { + + // It might be a single key + rawKeyJSON := &RawKeyJSON{} + err := json.Unmarshal([]byte(jwkSrc), rawKeyJSON) + if err != nil { + return nil, errors.Wrap(err, "Failed to unmarshal JWK") + } + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + return nil, errors.Wrap(err, "Failed to generate key") + } + // Add to set + jwkKeySet.Keys = append(jwkKeySet.Keys, jwkKey) + } else { + for i := range rawKeySetJSON.Keys { + rawKeyJSON := rawKeySetJSON.Keys[i] + jwkKey, err = rawKeyJSON.GenerateKey() + if err != nil { + return nil, errors.Wrap(err, "Failed to generate key: %s") + } + jwkKeySet.Keys = append(jwkKeySet.Keys, jwkKey) + } + } + return &jwkKeySet, nil +} + +// ParseBytes parses JWK from the incoming byte buffer. +func ParseBytes(buf []byte) (*Set, error) { + return parse(string(buf[:])) +} + +// ParseString parses JWK from the incoming string. +func ParseString(s string) (*Set, error) { + return parse(s) +} + +// GenerateKey creates an internal representation of a key from a raw JWK JSON +func (r *RawKeyJSON) GenerateKey() (Key, error) { + + var key Key + + switch r.KeyType { + case jwa.RSA: + if r.D != nil { + key = &RSAPrivateKey{} + } else { + key = &RSAPublicKey{} + } + case jwa.EC: + if r.D != nil { + key = &ECDSAPrivateKey{} + } else { + key = &ECDSAPublicKey{} + } + case jwa.OctetSeq: + key = &SymmetricKey{} + default: + return nil, errors.Errorf(`Unrecognized key type`) + } + err := key.GenerateKey(r) + if err != nil { + return nil, errors.Wrap(err, "Failed to generate key from JWK") + } + return key, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/key_ops.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/key_ops.go new file mode 100644 index 000000000..f9c7a4639 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/key_ops.go @@ -0,0 +1,68 @@ +package jwk + +import ( + "encoding/json" + "fmt" + + "github.com/pkg/errors" +) + +// KeyUsageType is used to denote what this key should be used for +type KeyUsageType string + +const ( + // ForSignature is the value used in the headers to indicate that + // this key should be used for signatures + ForSignature KeyUsageType = "sig" + // ForEncryption is the value used in the headers to indicate that + // this key should be used for encryptiong + ForEncryption KeyUsageType = "enc" +) + +// KeyOperation is used to denote the allowed operations for a Key +type KeyOperation string + +// KeyOperationList represents an slice of KeyOperation +type KeyOperationList []KeyOperation + +var keyOps = map[string]struct{}{"sign": {}, "verify": {}, "encrypt": {}, "decrypt": {}, "wrapKey": {}, "unwrapKey": {}, "deriveKey": {}, "deriveBits": {}} + +// KeyOperation constants +const ( + KeyOpSign KeyOperation = "sign" // (compute digital signature or MAC) + KeyOpVerify = "verify" // (verify digital signature or MAC) + KeyOpEncrypt = "encrypt" // (encrypt content) + KeyOpDecrypt = "decrypt" // (decrypt content and validate decryption, if applicable) + KeyOpWrapKey = "wrapKey" // (encrypt key) + KeyOpUnwrapKey = "unwrapKey" // (decrypt key and validate decryption, if applicable) + KeyOpDeriveKey = "deriveKey" // (derive key) + KeyOpDeriveBits = "deriveBits" // (derive bits not to be used as a key) +) + +// Accept determines if Key Operation is valid +func (keyOperationList *KeyOperationList) Accept(v interface{}) error { + switch x := v.(type) { + case KeyOperationList: + *keyOperationList = x + return nil + default: + return errors.Errorf(`invalid value %T`, v) + } +} + +// UnmarshalJSON unmarshals and checks data as KeyType Algorithm +func (keyOperationList *KeyOperationList) UnmarshalJSON(data []byte) error { + var tempKeyOperationList []string + err := json.Unmarshal(data, &tempKeyOperationList) + if err != nil { + return fmt.Errorf("invalid key operation") + } + for _, value := range tempKeyOperationList { + _, ok := keyOps[value] + if !ok { + return fmt.Errorf("unknown key operation") + } + *keyOperationList = append(*keyOperationList, KeyOperation(value)) + } + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/rsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/rsa.go new file mode 100644 index 000000000..e15e907d5 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/rsa.go @@ -0,0 +1,103 @@ +package jwk + +import ( + "crypto/rsa" + "math/big" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +func newRSAPublicKey(key *rsa.PublicKey) (*RSAPublicKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.RSA) + if err != nil { + return nil, errors.Wrapf(err, "Failed to set Key Type") + } + return &RSAPublicKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +func newRSAPrivateKey(key *rsa.PrivateKey) (*RSAPrivateKey, error) { + + var hdr StandardHeaders + err := hdr.Set(KeyTypeKey, jwa.RSA) + if err != nil { + return nil, errors.Wrapf(err, "Failed to set Key Type") + } + return &RSAPrivateKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +// Materialize returns the standard RSA Public Key representation stored in the internal representation +func (k *RSAPublicKey) Materialize() (interface{}, error) { + if k.key == nil { + return nil, errors.New(`key has no rsa.PublicKey associated with it`) + } + return k.key, nil +} + +// Materialize returns the standard RSA Private Key representation stored in the internal representation +func (k *RSAPrivateKey) Materialize() (interface{}, error) { + if k.key == nil { + return nil, errors.New(`key has no rsa.PrivateKey associated with it`) + } + return k.key, nil +} + +// GenerateKey creates a RSAPublicKey from a RawKeyJSON +func (k *RSAPublicKey) GenerateKey(keyJSON *RawKeyJSON) error { + + if keyJSON.N == nil || keyJSON.E == nil { + return errors.Errorf("Missing mandatory key parameters N or E") + } + rsaPublicKey := &rsa.PublicKey{ + N: (&big.Int{}).SetBytes(keyJSON.N.Bytes()), + E: int((&big.Int{}).SetBytes(keyJSON.E.Bytes()).Int64()), + } + k.key = rsaPublicKey + k.StandardHeaders = &keyJSON.StandardHeaders + return nil +} + +// GenerateKey creates a RSAPublicKey from a RawKeyJSON +func (k *RSAPrivateKey) GenerateKey(keyJSON *RawKeyJSON) error { + + rsaPublicKey := &RSAPublicKey{} + err := rsaPublicKey.GenerateKey(keyJSON) + if err != nil { + return errors.Wrap(err, "failed to generate public key") + } + + if keyJSON.D == nil || keyJSON.P == nil || keyJSON.Q == nil { + return errors.Errorf("Missing mandatory key parameters D, P or Q") + } + privateKey := &rsa.PrivateKey{ + PublicKey: *rsaPublicKey.key, + D: (&big.Int{}).SetBytes(keyJSON.D.Bytes()), + Primes: []*big.Int{ + (&big.Int{}).SetBytes(keyJSON.P.Bytes()), + (&big.Int{}).SetBytes(keyJSON.Q.Bytes()), + }, + } + + if keyJSON.Dp.Len() > 0 { + privateKey.Precomputed.Dp = (&big.Int{}).SetBytes(keyJSON.Dp.Bytes()) + } + if keyJSON.Dq.Len() > 0 { + privateKey.Precomputed.Dq = (&big.Int{}).SetBytes(keyJSON.Dq.Bytes()) + } + if keyJSON.Qi.Len() > 0 { + privateKey.Precomputed.Qinv = (&big.Int{}).SetBytes(keyJSON.Qi.Bytes()) + } + + k.key = privateKey + k.StandardHeaders = &keyJSON.StandardHeaders + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/symmetric.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/symmetric.go new file mode 100644 index 000000000..6d1da1e40 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jwk/symmetric.go @@ -0,0 +1,41 @@ +package jwk + +import ( + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +func newSymmetricKey(key []byte) (*SymmetricKey, error) { + var hdr StandardHeaders + + err := hdr.Set(KeyTypeKey, jwa.OctetSeq) + if err != nil { + return nil, errors.Wrapf(err, "Failed to set Key Type") + } + return &SymmetricKey{ + StandardHeaders: &hdr, + key: key, + }, nil +} + +// Materialize returns the octets for this symmetric key. +// Since this is a symmetric key, this just calls Octets +func (s SymmetricKey) Materialize() (interface{}, error) { + return s.Octets(), nil +} + +// Octets returns the octets in the key +func (s SymmetricKey) Octets() []byte { + return s.key +} + +// GenerateKey creates a Symmetric key from a RawKeyJSON +func (s *SymmetricKey) GenerateKey(keyJSON *RawKeyJSON) error { + + *s = SymmetricKey{ + StandardHeaders: &keyJSON.StandardHeaders, + key: keyJSON.K, + } + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/headers.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/headers.go new file mode 100644 index 000000000..fd6ffbe0e --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/headers.go @@ -0,0 +1,154 @@ +package jws + +import ( + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// Constants for JWS Common parameters +const ( + AlgorithmKey = "alg" + ContentTypeKey = "cty" + CriticalKey = "crit" + JWKKey = "jwk" + JWKSetURLKey = "jku" + KeyIDKey = "kid" + PrivateParamsKey = "privateParams" + TypeKey = "typ" +) + +// Headers provides a common interface for common header parameters +type Headers interface { + Get(string) (interface{}, bool) + Set(string, interface{}) error + GetAlgorithm() jwa.SignatureAlgorithm +} + +// StandardHeaders contains JWS common parameters. +type StandardHeaders struct { + Algorithm jwa.SignatureAlgorithm `json:"alg,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.1 + ContentType string `json:"cty,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.10 + Critical []string `json:"crit,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.11 + JWK string `json:"jwk,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.3 + JWKSetURL string `json:"jku,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.2 + KeyID string `json:"kid,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.4 + PrivateParams map[string]interface{} `json:"privateParams,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9 + Type string `json:"typ,omitempty"` // https://tools.ietf.org/html/rfc7515#section-4.1.9 +} + +// GetAlgorithm returns algorithm +func (h *StandardHeaders) GetAlgorithm() jwa.SignatureAlgorithm { + return h.Algorithm +} + +// Get is a general getter function for StandardHeaders structure +func (h *StandardHeaders) Get(name string) (interface{}, bool) { + switch name { + case AlgorithmKey: + v := h.Algorithm + if v == "" { + return nil, false + } + return v, true + case ContentTypeKey: + v := h.ContentType + if v == "" { + return nil, false + } + return v, true + case CriticalKey: + v := h.Critical + if len(v) == 0 { + return nil, false + } + return v, true + case JWKKey: + v := h.JWK + if v == "" { + return nil, false + } + return v, true + case JWKSetURLKey: + v := h.JWKSetURL + if v == "" { + return nil, false + } + return v, true + case KeyIDKey: + v := h.KeyID + if v == "" { + return nil, false + } + return v, true + case PrivateParamsKey: + v := h.PrivateParams + if len(v) == 0 { + return nil, false + } + return v, true + case TypeKey: + v := h.Type + if v == "" { + return nil, false + } + return v, true + default: + return nil, false + } +} + +// Set is a general setter function for StandardHeaders structure +func (h *StandardHeaders) Set(name string, value interface{}) error { + switch name { + case AlgorithmKey: + if err := h.Algorithm.Accept(value); err != nil { + return errors.Wrapf(err, `invalid value for %s key`, AlgorithmKey) + } + return nil + case ContentTypeKey: + if v, ok := value.(string); ok { + h.ContentType = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value) + case CriticalKey: + if v, ok := value.([]string); ok { + h.Critical = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, CriticalKey, value) + case JWKKey: + if v, ok := value.(string); ok { + h.JWK = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, JWKKey, value) + case JWKSetURLKey: + if v, ok := value.(string); ok { + h.JWKSetURL = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value) + case KeyIDKey: + if v, ok := value.(string); ok { + h.KeyID = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value) + case PrivateParamsKey: + if v, ok := value.(map[string]interface{}); ok { + h.PrivateParams = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, PrivateParamsKey, value) + case TypeKey: + if v, ok := value.(string); ok { + h.Type = v + return nil + } + return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value) + default: + return errors.Errorf(`invalid key: %s`, name) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/interface.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/interface.go new file mode 100644 index 000000000..e647c8ac9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/interface.go @@ -0,0 +1,22 @@ +package jws + +// Message represents a full JWS encoded message. Flattened serialization +// is not supported as a struct, but rather it's represented as a +// Message struct with only one `Signature` element. +// +// Do not expect to use the Message object to verify or construct a +// signed payloads with. You should only use this when you want to actually +// want to programmatically view the contents for the full JWS Payload. +// +// To sign and verify, use the appropriate `SignWithOption()` nad `Verify()` functions +type Message struct { + Payload []byte `json:"payload"` + Signatures []*Signature `json:"signatures,omitempty"` +} + +// Signature represents the headers and signature of a JWS message +type Signature struct { + Headers Headers `json:"header,omitempty"` // Unprotected Headers + Protected Headers `json:"Protected,omitempty"` // Protected Headers + Signature []byte `json:"signature,omitempty"` // GetSignature +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/jws.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/jws.go new file mode 100644 index 000000000..34e18a499 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/jws.go @@ -0,0 +1,210 @@ +// Package jws implements the digital Signature on JSON based data +// structures as described in https://tools.ietf.org/html/rfc7515 +// +// If you do not care about the details, the only things that you +// would need to use are the following functions: +// +// jws.SignWithOption(Payload, algorithm, key) +// jws.Verify(encodedjws, algorithm, key) +// +// To sign, simply use `jws.SignWithOption`. `Payload` is a []byte buffer that +// contains whatever data you want to sign. `alg` is one of the +// jwa.SignatureAlgorithm constants from package jwa. For RSA and +// ECDSA family of algorithms, you will need to prepare a private key. +// For HMAC family, you just need a []byte value. The `jws.SignWithOption` +// function will return the encoded JWS message on success. +// +// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer +// and verify the result using `algorithm` and `key`. Upon successful +// verification, the original Payload is returned, so you can work on it. +package jws + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "strings" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwk" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify" + + "github.com/pkg/errors" +) + +// SignLiteral generates a Signature for the given Payload and Headers, and serializes +// it in compact serialization format. In this format you may NOT use +// multiple signers. +// +func SignLiteral(payload []byte, alg jwa.SignatureAlgorithm, key interface{}, hdrBuf []byte) ([]byte, error) { + encodedHdr := base64.RawURLEncoding.EncodeToString(hdrBuf) + encodedPayload := base64.RawURLEncoding.EncodeToString(payload) + signingInput := strings.Join( + []string{ + encodedHdr, + encodedPayload, + }, ".", + ) + signer, err := sign.New(alg) + if err != nil { + return nil, errors.Wrap(err, `failed to create signer`) + } + signature, err := signer.Sign([]byte(signingInput), key) + if err != nil { + return nil, errors.Wrap(err, `failed to sign Payload`) + } + encodedSignature := base64.RawURLEncoding.EncodeToString(signature) + compactSerialization := strings.Join( + []string{ + signingInput, + encodedSignature, + }, ".", + ) + return []byte(compactSerialization), nil +} + +// SignWithOption generates a Signature for the given Payload, and serializes +// it in compact serialization format. In this format you may NOT use +// multiple signers. +// +// If you would like to pass custom Headers, use the WithHeaders option. +func SignWithOption(payload []byte, alg jwa.SignatureAlgorithm, key interface{}) ([]byte, error) { + var headers Headers = &StandardHeaders{} + + err := headers.Set(AlgorithmKey, alg) + if err != nil { + return nil, errors.Wrap(err, "Failed to set alg value") + } + + hdrBuf, err := json.Marshal(headers) + if err != nil { + return nil, errors.Wrap(err, `failed to marshal Headers`) + } + return SignLiteral(payload, alg, key, hdrBuf) +} + +// Verify checks if the given JWS message is verifiable using `alg` and `key`. +// If the verification is successful, `err` is nil, and the content of the +// Payload that was signed is returned. If you need more fine-grained +// control of the verification process, manually call `Parse`, generate a +// verifier, and call `Verify` on the parsed JWS message object. +func Verify(buf []byte, alg jwa.SignatureAlgorithm, key interface{}) (ret []byte, err error) { + + verifier, err := verify.New(alg) + if err != nil { + return nil, errors.Wrap(err, "failed to create verifier") + } + + buf = bytes.TrimSpace(buf) + if len(buf) == 0 { + return nil, errors.New(`attempt to verify empty buffer`) + } + + parts, err := SplitCompact(string(buf[:])) + if err != nil { + return nil, errors.Wrap(err, `failed extract from compact serialization format`) + } + + signingInput := strings.Join( + []string{ + parts[0], + parts[1], + }, ".", + ) + + decodedSignature, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + return nil, errors.Wrap(err, "Failed to decode signature") + } + if err := verifier.Verify([]byte(signingInput), decodedSignature, key); err != nil { + return nil, errors.Wrap(err, "Failed to verify message") + } + + if decodedPayload, err := base64.RawURLEncoding.DecodeString(parts[1]); err == nil { + return decodedPayload, nil + } + return nil, errors.Wrap(err, "Failed to decode Payload") +} + +// VerifyWithJWK verifies the JWS message using the specified JWK +func VerifyWithJWK(buf []byte, key jwk.Key) (payload []byte, err error) { + + keyVal, err := key.Materialize() + if err != nil { + return nil, errors.Wrap(err, "Failed to materialize key") + } + return Verify(buf, key.GetAlgorithm(), keyVal) +} + +// VerifyWithJWKSet verifies the JWS message using JWK key set. +// By default it will only pick up keys that have the "use" key +// set to either "sig" or "enc", but you can override it by +// providing a keyaccept function. +func VerifyWithJWKSet(buf []byte, keyset *jwk.Set) (payload []byte, err error) { + + for _, key := range keyset.Keys { + payload, err := VerifyWithJWK(buf, key) + if err == nil { + return payload, nil + } + } + return nil, errors.New("failed to verify with any of the keys") +} + +// ParseByte parses a JWS value serialized via compact serialization and provided as []byte. +func ParseByte(jwsCompact []byte) (m *Message, err error) { + return parseCompact(string(jwsCompact[:])) +} + +// ParseString parses a JWS value serialized via compact serialization and provided as string. +func ParseString(s string) (*Message, error) { + return parseCompact(s) +} + +// SplitCompact splits a JWT and returns its three parts +// separately: Protected Headers, Payload and Signature. +func SplitCompact(jwsCompact string) ([]string, error) { + + parts := strings.Split(jwsCompact, ".") + if len(parts) < 3 { + return nil, errors.New("Failed to split compact serialization") + } + return parts, nil +} + +// parseCompact parses a JWS value serialized via compact serialization. +func parseCompact(str string) (m *Message, err error) { + + var decodedHeader, decodedPayload, decodedSignature []byte + parts, err := SplitCompact(str) + if err != nil { + return nil, errors.Wrap(err, `invalid compact serialization format`) + } + + if decodedHeader, err = base64.RawURLEncoding.DecodeString(parts[0]); err != nil { + return nil, errors.Wrap(err, `failed to decode Headers`) + } + var hdr StandardHeaders + if err := json.Unmarshal(decodedHeader, &hdr); err != nil { + return nil, errors.Wrap(err, `failed to parse JOSE Headers`) + } + + if decodedPayload, err = base64.RawURLEncoding.DecodeString(parts[1]); err != nil { + return nil, errors.Wrap(err, `failed to decode Payload`) + } + + if len(parts) > 2 { + if decodedSignature, err = base64.RawURLEncoding.DecodeString(parts[2]); err != nil { + return nil, errors.Wrap(err, `failed to decode Signature`) + } + } + + var msg Message + msg.Payload = decodedPayload + msg.Signatures = append(msg.Signatures, &Signature{ + Protected: &hdr, + Signature: decodedSignature, + }) + return &msg, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/message.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/message.go new file mode 100644 index 000000000..1366a3d7b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/message.go @@ -0,0 +1,26 @@ +package jws + +// PublicHeaders returns the public headers in a JWS +func (s Signature) PublicHeaders() Headers { + return s.Headers +} + +// ProtectedHeaders returns the protected headers in a JWS +func (s Signature) ProtectedHeaders() Headers { + return s.Protected +} + +// GetSignature returns the signature in a JWS +func (s Signature) GetSignature() []byte { + return s.Signature +} + +// GetPayload returns the payload in a JWS +func (m Message) GetPayload() []byte { + return m.Payload +} + +// GetSignatures returns the all signatures in a JWS +func (m Message) GetSignatures() []*Signature { + return m.Signatures +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/ecdsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/ecdsa.go new file mode 100644 index 000000000..02fc9f022 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/ecdsa.go @@ -0,0 +1,84 @@ +package sign + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rand" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + + "github.com/pkg/errors" +) + +var ecdsaSignFuncs = map[jwa.SignatureAlgorithm]ecdsaSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]crypto.Hash{ + jwa.ES256: crypto.SHA256, + jwa.ES384: crypto.SHA384, + jwa.ES512: crypto.SHA512, + } + + for alg, h := range algs { + ecdsaSignFuncs[alg] = makeECDSASignFunc(h) + } +} + +func makeECDSASignFunc(hash crypto.Hash) ecdsaSignFunc { + return ecdsaSignFunc(func(payload []byte, key *ecdsa.PrivateKey) ([]byte, error) { + curveBits := key.Curve.Params().BitSize + keyBytes := curveBits / 8 + // Curve bits do not need to be a multiple of 8. + if curveBits%8 > 0 { + keyBytes++ + } + h := hash.New() + h.Write(payload) + r, s, err := ecdsa.Sign(rand.Reader, key, h.Sum(nil)) + if err != nil { + return nil, errors.Wrap(err, "failed to sign payload using ecdsa") + } + + rBytes := r.Bytes() + rBytesPadded := make([]byte, keyBytes) + copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) + + sBytes := s.Bytes() + sBytesPadded := make([]byte, keyBytes) + copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) + + out := append(rBytesPadded, sBytesPadded...) + return out, nil + }) +} + +func newECDSA(alg jwa.SignatureAlgorithm) (*ECDSASigner, error) { + signfn, ok := ecdsaSignFuncs[alg] + if !ok { + return nil, errors.Errorf(`unsupported algorithm while trying to create ECDSA signer: %s`, alg) + } + + return &ECDSASigner{ + alg: alg, + sign: signfn, + }, nil +} + +// Algorithm returns the signer algorithm +func (s ECDSASigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// Sign signs payload with a ECDSA private key +func (s ECDSASigner) Sign(payload []byte, key interface{}) ([]byte, error) { + if key == nil { + return nil, errors.New(`missing private key while signing payload`) + } + + privateKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.Errorf(`invalid key type %T. *ecdsa.PrivateKey is required`, key) + } + + return s.sign(payload, privateKey) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/hmac.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/hmac.go new file mode 100644 index 000000000..f86283efb --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/hmac.go @@ -0,0 +1,66 @@ +package sign + +import ( + "crypto/hmac" + "crypto/sha256" + "crypto/sha512" + "hash" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + + "github.com/pkg/errors" +) + +var hmacSignFuncs = map[jwa.SignatureAlgorithm]hmacSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]func() hash.Hash{ + jwa.HS256: sha256.New, + jwa.HS384: sha512.New384, + jwa.HS512: sha512.New, + } + + for alg, h := range algs { + hmacSignFuncs[alg] = makeHMACSignFunc(h) + + } +} + +func newHMAC(alg jwa.SignatureAlgorithm) (*HMACSigner, error) { + signer, ok := hmacSignFuncs[alg] + if !ok { + return nil, errors.Errorf(`unsupported algorithm while trying to create HMAC signer: %s`, alg) + } + + return &HMACSigner{ + alg: alg, + sign: signer, + }, nil +} + +func makeHMACSignFunc(hfunc func() hash.Hash) hmacSignFunc { + return hmacSignFunc(func(payload []byte, key []byte) ([]byte, error) { + h := hmac.New(hfunc, key) + h.Write(payload) + return h.Sum(nil), nil + }) +} + +// Algorithm returns the signer algorithm +func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// Sign signs payload with a Symmetric key +func (s HMACSigner) Sign(payload []byte, key interface{}) ([]byte, error) { + hmackey, ok := key.([]byte) + if !ok { + return nil, errors.Errorf(`invalid key type %T. []byte is required`, key) + } + + if len(hmackey) == 0 { + return nil, errors.New(`missing key while signing payload`) + } + + return s.sign(payload, hmackey) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/interface.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/interface.go new file mode 100644 index 000000000..c79fd3e93 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/interface.go @@ -0,0 +1,45 @@ +package sign + +import ( + "crypto/ecdsa" + "crypto/rsa" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// Signer provides a common interface for supported alg signing methods +type Signer interface { + // Sign creates a signature for the given `payload`. + // `key` is the key used for signing the payload, and is usually + // the private key type associated with the signature method. For example, + // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the + // `*"crypto/rsa".PrivateKey` type. + // Check the documentation for each signer for details + Sign(payload []byte, key interface{}) ([]byte, error) + + Algorithm() jwa.SignatureAlgorithm +} + +type rsaSignFunc func([]byte, *rsa.PrivateKey) ([]byte, error) + +// RSASigner uses crypto/rsa to sign the payloads. +type RSASigner struct { + alg jwa.SignatureAlgorithm + sign rsaSignFunc +} + +type ecdsaSignFunc func([]byte, *ecdsa.PrivateKey) ([]byte, error) + +// ECDSASigner uses crypto/ecdsa to sign the payloads. +type ECDSASigner struct { + alg jwa.SignatureAlgorithm + sign ecdsaSignFunc +} + +type hmacSignFunc func([]byte, []byte) ([]byte, error) + +// HMACSigner uses crypto/hmac to sign the payloads. +type HMACSigner struct { + alg jwa.SignatureAlgorithm + sign hmacSignFunc +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/rsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/rsa.go new file mode 100644 index 000000000..d9cc13af9 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/rsa.go @@ -0,0 +1,97 @@ +package sign + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + + "github.com/pkg/errors" +) + +var rsaSignFuncs = map[jwa.SignatureAlgorithm]rsaSignFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]struct { + Hash crypto.Hash + SignFunc func(crypto.Hash) rsaSignFunc + }{ + jwa.RS256: { + Hash: crypto.SHA256, + SignFunc: makeSignPKCS1v15, + }, + jwa.RS384: { + Hash: crypto.SHA384, + SignFunc: makeSignPKCS1v15, + }, + jwa.RS512: { + Hash: crypto.SHA512, + SignFunc: makeSignPKCS1v15, + }, + jwa.PS256: { + Hash: crypto.SHA256, + SignFunc: makeSignPSS, + }, + jwa.PS384: { + Hash: crypto.SHA384, + SignFunc: makeSignPSS, + }, + jwa.PS512: { + Hash: crypto.SHA512, + SignFunc: makeSignPSS, + }, + } + + for alg, item := range algs { + rsaSignFuncs[alg] = item.SignFunc(item.Hash) + } +} + +func makeSignPKCS1v15(hash crypto.Hash) rsaSignFunc { + return rsaSignFunc(func(payload []byte, key *rsa.PrivateKey) ([]byte, error) { + h := hash.New() + h.Write(payload) + return rsa.SignPKCS1v15(rand.Reader, key, hash, h.Sum(nil)) + }) +} + +func makeSignPSS(hash crypto.Hash) rsaSignFunc { + return rsaSignFunc(func(payload []byte, key *rsa.PrivateKey) ([]byte, error) { + h := hash.New() + h.Write(payload) + return rsa.SignPSS(rand.Reader, key, hash, h.Sum(nil), &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + }) + }) +} + +func newRSA(alg jwa.SignatureAlgorithm) (*RSASigner, error) { + signfn, ok := rsaSignFuncs[alg] + if !ok { + return nil, errors.Errorf(`unsupported algorithm while trying to create RSA signer: %s`, alg) + } + return &RSASigner{ + alg: alg, + sign: signfn, + }, nil +} + +// Algorithm returns the signer algorithm +func (s RSASigner) Algorithm() jwa.SignatureAlgorithm { + return s.alg +} + +// Sign creates a signature using crypto/rsa. key must be a non-nil instance of +// `*"crypto/rsa".PrivateKey`. +func (s RSASigner) Sign(payload []byte, key interface{}) ([]byte, error) { + if key == nil { + return nil, errors.New(`missing private key while signing payload`) + } + rsakey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.Errorf(`invalid key type %T. *rsa.PrivateKey is required`, key) + } + + return s.sign(payload, rsakey) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/sign.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/sign.go new file mode 100644 index 000000000..fd4b0645f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign/sign.go @@ -0,0 +1,21 @@ +package sign + +import ( + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// New creates a signer that signs payloads using the given signature algorithm. +func New(alg jwa.SignatureAlgorithm) (Signer, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512: + return newRSA(alg) + case jwa.ES256, jwa.ES384, jwa.ES512: + return newECDSA(alg) + case jwa.HS256, jwa.HS384, jwa.HS512: + return newHMAC(alg) + default: + return nil, errors.Errorf(`unsupported signature algorithm %s`, alg) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/ecdsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/ecdsa.go new file mode 100644 index 000000000..5adccda30 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/ecdsa.go @@ -0,0 +1,67 @@ +package verify + +import ( + "crypto" + "crypto/ecdsa" + "math/big" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +var ecdsaVerifyFuncs = map[jwa.SignatureAlgorithm]ecdsaVerifyFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]crypto.Hash{ + jwa.ES256: crypto.SHA256, + jwa.ES384: crypto.SHA384, + jwa.ES512: crypto.SHA512, + } + + for alg, h := range algs { + ecdsaVerifyFuncs[alg] = makeECDSAVerifyFunc(h) + } +} + +func makeECDSAVerifyFunc(hash crypto.Hash) ecdsaVerifyFunc { + return ecdsaVerifyFunc(func(payload []byte, signature []byte, key *ecdsa.PublicKey) error { + + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + + h := hash.New() + h.Write(payload) + + if !ecdsa.Verify(key, h.Sum(nil), r, s) { + return errors.New(`failed to verify signature using ecdsa`) + } + return nil + }) +} + +func newECDSA(alg jwa.SignatureAlgorithm) (*ECDSAVerifier, error) { + verifyfn, ok := ecdsaVerifyFuncs[alg] + if !ok { + return nil, errors.Errorf(`unsupported algorithm while trying to create ECDSA verifier: %s`, alg) + } + + return &ECDSAVerifier{ + verify: verifyfn, + }, nil +} + +// Verify checks whether the signature for a given input and key is correct +func (v ECDSAVerifier) Verify(payload []byte, signature []byte, key interface{}) error { + if key == nil { + return errors.New(`missing public key while verifying payload`) + } + ecdsakey, ok := key.(*ecdsa.PublicKey) + if !ok { + return errors.Errorf(`invalid key type %T. *ecdsa.PublicKey is required`, key) + } + + return v.verify(payload, signature, ecdsakey) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/hmac.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/hmac.go new file mode 100644 index 000000000..e0b5e1981 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/hmac.go @@ -0,0 +1,33 @@ +package verify + +import ( + "crypto/hmac" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign" +) + +func newHMAC(alg jwa.SignatureAlgorithm) (*HMACVerifier, error) { + + s, err := sign.New(alg) + if err != nil { + return nil, errors.Wrap(err, `failed to generate HMAC signer`) + } + return &HMACVerifier{signer: s}, nil +} + +// Verify checks whether the signature for a given input and key is correct +func (v HMACVerifier) Verify(signingInput, signature []byte, key interface{}) (err error) { + + expected, err := v.signer.Sign(signingInput, key) + if err != nil { + return errors.Wrap(err, `failed to generated signature`) + } + + if !hmac.Equal(signature, expected) { + return errors.New(`failed to match hmac signature`) + } + return nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/interface.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/interface.go new file mode 100644 index 000000000..b72b7232a --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/interface.go @@ -0,0 +1,39 @@ +package verify + +import ( + "crypto/ecdsa" + "crypto/rsa" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jws/sign" +) + +// Verifier provides a common interface for supported alg verification methods +type Verifier interface { + // Verify checks whether the payload and signature are valid for + // the given key. + // `key` is the key used for verifying the payload, and is usually + // the public key associated with the signature method. For example, + // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the + // `*"crypto/rsa".PublicKey` type. + // Check the documentation for each verifier for details + Verify(payload []byte, signature []byte, key interface{}) error +} + +type rsaVerifyFunc func([]byte, []byte, *rsa.PublicKey) error + +// RSAVerifier implements the Verifier interface +type RSAVerifier struct { + verify rsaVerifyFunc +} + +type ecdsaVerifyFunc func([]byte, []byte, *ecdsa.PublicKey) error + +// ECDSAVerifier implements the Verifier interface +type ECDSAVerifier struct { + verify ecdsaVerifyFunc +} + +// HMACVerifier implements the Verifier interface +type HMACVerifier struct { + signer sign.Signer +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/rsa.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/rsa.go new file mode 100644 index 000000000..26f341d12 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/rsa.go @@ -0,0 +1,88 @@ +package verify + +import ( + "crypto" + "crypto/rsa" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" + + "github.com/pkg/errors" +) + +var rsaVerifyFuncs = map[jwa.SignatureAlgorithm]rsaVerifyFunc{} + +func init() { + algs := map[jwa.SignatureAlgorithm]struct { + Hash crypto.Hash + VerifyFunc func(crypto.Hash) rsaVerifyFunc + }{ + jwa.RS256: { + Hash: crypto.SHA256, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.RS384: { + Hash: crypto.SHA384, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.RS512: { + Hash: crypto.SHA512, + VerifyFunc: makeVerifyPKCS1v15, + }, + jwa.PS256: { + Hash: crypto.SHA256, + VerifyFunc: makeVerifyPSS, + }, + jwa.PS384: { + Hash: crypto.SHA384, + VerifyFunc: makeVerifyPSS, + }, + jwa.PS512: { + Hash: crypto.SHA512, + VerifyFunc: makeVerifyPSS, + }, + } + + for alg, item := range algs { + rsaVerifyFuncs[alg] = item.VerifyFunc(item.Hash) + } +} + +func makeVerifyPKCS1v15(hash crypto.Hash) rsaVerifyFunc { + return rsaVerifyFunc(func(payload, signature []byte, key *rsa.PublicKey) error { + h := hash.New() + h.Write(payload) + return rsa.VerifyPKCS1v15(key, hash, h.Sum(nil), signature) + }) +} + +func makeVerifyPSS(hash crypto.Hash) rsaVerifyFunc { + return rsaVerifyFunc(func(payload, signature []byte, key *rsa.PublicKey) error { + h := hash.New() + h.Write(payload) + return rsa.VerifyPSS(key, hash, h.Sum(nil), signature, nil) + }) +} + +func newRSA(alg jwa.SignatureAlgorithm) (*RSAVerifier, error) { + verifyfn, ok := rsaVerifyFuncs[alg] + if !ok { + return nil, errors.Errorf(`unsupported algorithm while trying to create RSA verifier: %s`, alg) + } + + return &RSAVerifier{ + verify: verifyfn, + }, nil +} + +// Verify checks if a JWS is valid. +func (v RSAVerifier) Verify(payload, signature []byte, key interface{}) error { + if key == nil { + return errors.New(`missing public key while verifying payload`) + } + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return errors.Errorf(`invalid key type %T. *rsa.PublicKey is required`, key) + } + + return v.verify(payload, signature, rsaKey) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/verify.go b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/verify.go new file mode 100644 index 000000000..d484cda0b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/internal/jwx/jws/verify/verify.go @@ -0,0 +1,22 @@ +package verify + +import ( + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwa" +) + +// New creates a new JWS verifier using the specified algorithm +// and the public key +func New(alg jwa.SignatureAlgorithm) (Verifier, error) { + switch alg { + case jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512: + return newRSA(alg) + case jwa.ES256, jwa.ES384, jwa.ES512: + return newECDSA(alg) + case jwa.HS256, jwa.HS384, jwa.HS512: + return newHMAC(alg) + default: + return nil, errors.Errorf(`unsupported signature algorithm: %s`, alg) + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/json.go b/vendor/github.com/open-policy-agent/opa/topdown/json.go new file mode 100644 index 000000000..1253015c5 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/json.go @@ -0,0 +1,235 @@ +// Copyright 2019 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinJSONRemove(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + // Expect an object and a string or array/set of strings + _, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a list of json pointers to remove + paths, err := getJSONPaths(operands[1].Value) + if err != nil { + return err + } + + newObj, err := jsonRemove(operands[0], ast.NewTerm(pathsToObject(paths))) + if err != nil { + return err + } + + if newObj == nil { + return nil + } + + return iter(newObj) +} + +// jsonRemove returns a new term that is the result of walking +// through a and omitting removing any values that are in b but +// have ast.Null values (ie leaf nodes for b). +func jsonRemove(a *ast.Term, b *ast.Term) (*ast.Term, error) { + if b == nil { + // The paths diverged, return a + return a, nil + } + + var bObj ast.Object + switch bValue := b.Value.(type) { + case ast.Object: + bObj = bValue + case ast.Null: + // Means we hit a leaf node on "b", dont add the value for a + return nil, nil + default: + // The paths diverged, return a + return a, nil + } + + switch aValue := a.Value.(type) { + case ast.String, ast.Number, ast.Boolean, ast.Null: + return a, nil + case ast.Object: + newObj := ast.NewObject() + err := aValue.Iter(func(k *ast.Term, v *ast.Term) error { + // recurse and add the diff of sub objects as needed + diffValue, err := jsonRemove(v, bObj.Get(k)) + if err != nil || diffValue == nil { + return err + } + newObj.Insert(k, diffValue) + return nil + }) + if err != nil { + return nil, err + } + return ast.NewTerm(newObj), nil + case ast.Set: + newSet := ast.NewSet() + err := aValue.Iter(func(v *ast.Term) error { + // recurse and add the diff of sub objects as needed + diffValue, err := jsonRemove(v, bObj.Get(v)) + if err != nil || diffValue == nil { + return err + } + newSet.Add(diffValue) + return nil + }) + if err != nil { + return nil, err + } + return ast.NewTerm(newSet), nil + case ast.Array: + // When indexes are removed we shift left to close empty spots in the array + // as per the JSON patch spec. + var newArray ast.Array + for i, v := range aValue { + // recurse and add the diff of sub objects as needed + // Note: Keys in b will be strings for the index, eg path /a/1/b => {"a": {"1": {"b": null}}} + diffValue, err := jsonRemove(v, bObj.Get(ast.StringTerm(strconv.Itoa(i)))) + if err != nil { + return nil, err + } + if diffValue != nil { + newArray = append(newArray, diffValue) + } + } + return ast.NewTerm(newArray), nil + default: + return nil, fmt.Errorf("invalid value type %T", a) + } +} + +func builtinJSONFilter(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + + // Ensure we have the right parameters, expect an object and a string or array/set of strings + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a list of filter strings + filters, err := getJSONPaths(operands[1].Value) + if err != nil { + return err + } + + // Actually do the filtering + filterObj := pathsToObject(filters) + r, err := obj.Filter(filterObj) + if err != nil { + return err + } + + return iter(ast.NewTerm(r)) +} + +func getJSONPaths(operand ast.Value) ([]ast.Ref, error) { + var paths []ast.Ref + + switch v := operand.(type) { + case ast.Array: + for _, f := range v { + filter, err := parsePath(f) + if err != nil { + return nil, err + } + paths = append(paths, filter) + } + case ast.Set: + err := v.Iter(func(f *ast.Term) error { + filter, err := parsePath(f) + if err != nil { + return err + } + paths = append(paths, filter) + return nil + }) + if err != nil { + return nil, err + } + default: + return nil, builtins.NewOperandTypeErr(2, v, "set", "array") + } + + return paths, nil +} + +func parsePath(path *ast.Term) (ast.Ref, error) { + // paths can either be a `/` separated json path or + // an array or set of values + var pathSegments ast.Ref + switch p := path.Value.(type) { + case ast.String: + parts := strings.Split(strings.Trim(string(p), "/"), "/") + for _, part := range parts { + part = strings.ReplaceAll(strings.ReplaceAll(part, "~1", "/"), "~0", "~") + pathSegments = append(pathSegments, ast.StringTerm(part)) + } + case ast.Array: + for _, term := range p { + pathSegments = append(pathSegments, term) + } + default: + return nil, builtins.NewOperandErr(2, "must be one of {set, array} containing string paths or array of path segments but got %v", ast.TypeName(p)) + } + + return pathSegments, nil +} + +func pathsToObject(paths []ast.Ref) ast.Object { + + root := ast.NewObject() + + for _, path := range paths { + node := root + var done bool + + for i := 0; i < len(path)-1 && !done; i++ { + + k := path[i] + child := node.Get(k) + + if child == nil { + obj := ast.NewObject() + node.Insert(k, ast.NewTerm(obj)) + node = obj + continue + } + + switch v := child.Value.(type) { + case ast.Null: + done = true + case ast.Object: + node = v + default: + panic("unreachable") + } + } + + if !done { + node.Insert(path[len(path)-1], ast.NullTerm()) + } + } + + return root +} + +func init() { + RegisterBuiltinFunc(ast.JSONFilter.Name, builtinJSONFilter) + RegisterBuiltinFunc(ast.JSONRemove.Name, builtinJSONRemove) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/object.go b/vendor/github.com/open-policy-agent/opa/topdown/object.go new file mode 100644 index 000000000..5fb3ee847 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/object.go @@ -0,0 +1,139 @@ +// Copyright 2020 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/types" +) + +func builtinObjectUnion(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + objA, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + objB, err := builtins.ObjectOperand(operands[1].Value, 2) + if err != nil { + return err + } + + r := mergeWithOverwrite(objA, objB) + + return iter(ast.NewTerm(r)) +} + +func builtinObjectRemove(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Expect an object and an array/set/object of keys + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a set of keys to remove + keysToRemove, err := getObjectKeysParam(operands[1].Value) + if err != nil { + return err + } + r := ast.NewObject() + obj.Foreach(func(key *ast.Term, value *ast.Term) { + if !keysToRemove.Contains(key) { + r.Insert(key, value) + } + }) + + return iter(ast.NewTerm(r)) +} + +func builtinObjectFilter(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + // Expect an object and an array/set/object of keys + obj, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + // Build a new object from the supplied filter keys + keys, err := getObjectKeysParam(operands[1].Value) + if err != nil { + return err + } + + filterObj := ast.NewObject() + keys.Foreach(func(key *ast.Term) { + filterObj.Insert(key, ast.NullTerm()) + }) + + // Actually do the filtering + r, err := obj.Filter(filterObj) + if err != nil { + return err + } + + return iter(ast.NewTerm(r)) +} + +func builtinObjectGet(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + object, err := builtins.ObjectOperand(operands[0].Value, 1) + if err != nil { + return err + } + + if ret := object.Get(operands[1]); ret != nil { + return iter(ret) + } + + return iter(operands[2]) +} + +// getObjectKeysParam returns a set of key values +// from a supplied ast array, object, set value +func getObjectKeysParam(arrayOrSet ast.Value) (ast.Set, error) { + keys := ast.NewSet() + + switch v := arrayOrSet.(type) { + case ast.Array: + for _, f := range v { + keys.Add(f) + } + case ast.Set: + _ = v.Iter(func(f *ast.Term) error { + keys.Add(f) + return nil + }) + case ast.Object: + _ = v.Iter(func(k *ast.Term, _ *ast.Term) error { + keys.Add(k) + return nil + }) + default: + return nil, builtins.NewOperandTypeErr(2, arrayOrSet, ast.TypeName(types.Object{}), ast.TypeName(types.S), ast.TypeName(types.Array{})) + } + + return keys, nil +} + +func mergeWithOverwrite(objA, objB ast.Object) ast.Object { + merged, _ := objA.MergeWith(objB, func(v1, v2 *ast.Term) (*ast.Term, bool) { + originalValueObj, ok2 := v1.Value.(ast.Object) + updateValueObj, ok1 := v2.Value.(ast.Object) + if !ok1 || !ok2 { + // If we can't merge, stick with the right-hand value + return v2, false + } + + // Recursively update the existing value + merged := mergeWithOverwrite(originalValueObj, updateValueObj) + return ast.NewTerm(merged), false + }) + return merged +} + +func init() { + RegisterBuiltinFunc(ast.ObjectUnion.Name, builtinObjectUnion) + RegisterBuiltinFunc(ast.ObjectRemove.Name, builtinObjectRemove) + RegisterBuiltinFunc(ast.ObjectFilter.Name, builtinObjectFilter) + RegisterBuiltinFunc(ast.ObjectGet.Name, builtinObjectGet) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/parse.go b/vendor/github.com/open-policy-agent/opa/topdown/parse.go new file mode 100644 index 000000000..dc6e6fc51 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/parse.go @@ -0,0 +1,47 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "bytes" + "encoding/json" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinRegoParseModule(a, b ast.Value) (ast.Value, error) { + + filename, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + input, err := builtins.StringOperand(b, 1) + if err != nil { + return nil, err + } + + module, err := ast.ParseModule(string(filename), string(input)) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(module); err != nil { + return nil, err + } + + term, err := ast.ParseTerm(buf.String()) + if err != nil { + return nil, err + } + + return term.Value, nil +} + +func init() { + RegisterFunctionalBuiltin2(ast.RegoParseModule.Name, builtinRegoParseModule) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go b/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go new file mode 100644 index 000000000..817edd5ee --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go @@ -0,0 +1,153 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "math/big" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +const ( + none int64 = 1 + kb = 1000 + ki = 1024 + mb = kb * 1000 + mi = ki * 1024 + gb = mb * 1000 + gi = mi * 1024 + tb = gb * 1000 + ti = gi * 1024 +) + +// The rune values for 0..9 as well as the period symbol (for parsing floats) +var numRunes = []rune("0123456789.") + +func parseNumBytesError(msg string) error { + return fmt.Errorf("%s error: %s", ast.UnitsParseBytes.Name, msg) +} + +func errUnitNotRecognized(unit string) error { + return parseNumBytesError(fmt.Sprintf("byte unit %s not recognized", unit)) +} + +var ( + errNoAmount = parseNumBytesError("no byte amount provided") + errIntConv = parseNumBytesError("could not parse byte amount to integer") + errIncludesSpaces = parseNumBytesError("spaces not allowed in resource strings") +) + +func builtinNumBytes(a ast.Value) (ast.Value, error) { + var m int64 + + raw, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + s := formatString(raw) + + if strings.Contains(s, " ") { + return nil, errIncludesSpaces + } + + numStr, unitStr := extractNumAndUnit(s) + + if numStr == "" { + return nil, errNoAmount + } + + switch unitStr { + case "": + m = none + case "kb": + m = kb + case "kib": + m = ki + case "mb": + m = mb + case "mib": + m = mi + case "gb": + m = gb + case "gib": + m = gi + case "tb": + m = tb + case "tib": + m = ti + default: + return nil, errUnitNotRecognized(unitStr) + } + + num, err := strconv.ParseInt(numStr, 10, 64) + if err != nil { + return nil, errIntConv + } + + total := num * m + + return builtins.IntToNumber(big.NewInt(total)), nil +} + +// Makes the string lower case and removes spaces and quotation marks +func formatString(s ast.String) string { + str := string(s) + lower := strings.ToLower(str) + return strings.Replace(lower, "\"", "", -1) +} + +// Splits the string into a number string à la "10" or "10.2" and a unit string à la "gb" or "MiB" or "foo". Either +// can be an empty string (error handling is provided elsewhere). +func extractNumAndUnit(s string) (string, string) { + isNum := func(r rune) (isNum bool) { + for _, nr := range numRunes { + if nr == r { + return true + } + } + + return false + } + + // Returns the index of the first rune that's not a number (or 0 if there are only numbers) + getFirstNonNumIdx := func(s string) int { + for idx, r := range s { + if !isNum(r) { + return idx + } + } + + return 0 + } + + firstRuneIsNum := func(s string) bool { + return isNum(rune(s[0])) + } + + firstNonNumIdx := getFirstNonNumIdx(s) + + // The string contains only a number + numOnly := firstNonNumIdx == 0 && firstRuneIsNum(s) + + // The string contains only a unit + unitOnly := firstNonNumIdx == 0 && !firstRuneIsNum(s) + + if numOnly { + return s, "" + } else if unitOnly { + return "", s + } else { + return s[0:firstNonNumIdx], s[firstNonNumIdx:] + } +} + +func init() { + RegisterFunctionalBuiltin1(ast.UnitsParseBytes.Name, builtinNumBytes) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/query.go b/vendor/github.com/open-policy-agent/opa/topdown/query.go new file mode 100644 index 000000000..95fab6b52 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/query.go @@ -0,0 +1,317 @@ +package topdown + +import ( + "context" + "sort" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/metrics" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/topdown/copypropagation" +) + +// QueryResultSet represents a collection of results returned by a query. +type QueryResultSet []QueryResult + +// QueryResult represents a single result returned by a query. The result +// contains bindings for all variables that appear in the query. +type QueryResult map[ast.Var]*ast.Term + +// Query provides a configurable interface for performing query evaluation. +type Query struct { + cancel Cancel + query ast.Body + queryCompiler ast.QueryCompiler + compiler *ast.Compiler + store storage.Store + txn storage.Transaction + input *ast.Term + tracers []Tracer + unknowns []*ast.Term + partialNamespace string + metrics metrics.Metrics + instr *Instrumentation + disableInlining []ast.Ref + genvarprefix string + runtime *ast.Term + builtins map[string]*Builtin + indexing bool +} + +// Builtin represents a built-in function that queries can call. +type Builtin struct { + Decl *ast.Builtin + Func BuiltinFunc +} + +// NewQuery returns a new Query object that can be run. +func NewQuery(query ast.Body) *Query { + return &Query{ + query: query, + genvarprefix: ast.WildcardPrefix, + indexing: true, + } +} + +// WithQueryCompiler sets the queryCompiler used for the query. +func (q *Query) WithQueryCompiler(queryCompiler ast.QueryCompiler) *Query { + q.queryCompiler = queryCompiler + return q +} + +// WithCompiler sets the compiler to use for the query. +func (q *Query) WithCompiler(compiler *ast.Compiler) *Query { + q.compiler = compiler + return q +} + +// WithStore sets the store to use for the query. +func (q *Query) WithStore(store storage.Store) *Query { + q.store = store + return q +} + +// WithTransaction sets the transaction to use for the query. All queries +// should be performed over a consistent snapshot of the storage layer. +func (q *Query) WithTransaction(txn storage.Transaction) *Query { + q.txn = txn + return q +} + +// WithCancel sets the cancellation object to use for the query. Set this if +// you need to abort queries based on a deadline. This is optional. +func (q *Query) WithCancel(cancel Cancel) *Query { + q.cancel = cancel + return q +} + +// WithInput sets the input object to use for the query. References rooted at +// input will be evaluated against this value. This is optional. +func (q *Query) WithInput(input *ast.Term) *Query { + q.input = input + return q +} + +// WithTracer adds a query tracer to use during evaluation. This is optional. +func (q *Query) WithTracer(tracer Tracer) *Query { + q.tracers = append(q.tracers, tracer) + return q +} + +// WithMetrics sets the metrics collection to add evaluation metrics to. This +// is optional. +func (q *Query) WithMetrics(m metrics.Metrics) *Query { + q.metrics = m + return q +} + +// WithInstrumentation sets the instrumentation configuration to enable on the +// evaluation process. By default, instrumentation is turned off. +func (q *Query) WithInstrumentation(instr *Instrumentation) *Query { + q.instr = instr + return q +} + +// WithUnknowns sets the initial set of variables or references to treat as +// unknown during query evaluation. This is required for partial evaluation. +func (q *Query) WithUnknowns(terms []*ast.Term) *Query { + q.unknowns = terms + return q +} + +// WithPartialNamespace sets the namespace to use for supporting rules +// generated as part of the partial evaluation process. The ns value must be a +// valid package path component. +func (q *Query) WithPartialNamespace(ns string) *Query { + q.partialNamespace = ns + return q +} + +// WithDisableInlining adds a set of paths to the query that should be excluded from +// inlining. Inlining during partial evaluation can be expensive in some cases +// (e.g., when a cross-product is computed.) Disabling inlining avoids expensive +// computation at the cost of generating support rules. +func (q *Query) WithDisableInlining(paths []ast.Ref) *Query { + q.disableInlining = paths + return q +} + +// WithRuntime sets the runtime data to execute the query with. The runtime data +// can be returned by the `opa.runtime` built-in function. +func (q *Query) WithRuntime(runtime *ast.Term) *Query { + q.runtime = runtime + return q +} + +// WithBuiltins adds a set of built-in functions that can be called by the +// query. +func (q *Query) WithBuiltins(builtins map[string]*Builtin) *Query { + q.builtins = builtins + return q +} + +// WithIndexing will enable or disable using rule indexing for the evaluation +// of the query. The default is enabled. +func (q *Query) WithIndexing(enabled bool) *Query { + q.indexing = enabled + return q +} + +// PartialRun executes partial evaluation on the query with respect to unknown +// values. Partial evaluation attempts to evaluate as much of the query as +// possible without requiring values for the unknowns set on the query. The +// result of partial evaluation is a new set of queries that can be evaluated +// once the unknown value is known. In addition to new queries, partial +// evaluation may produce additional support modules that should be used in +// conjunction with the partially evaluated queries. +func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []*ast.Module, err error) { + if q.partialNamespace == "" { + q.partialNamespace = "partial" // lazily initialize partial namespace + } + f := &queryIDFactory{} + b := newBindings(0, q.instr) + e := &eval{ + ctx: ctx, + cancel: q.cancel, + query: q.query, + queryCompiler: q.queryCompiler, + queryIDFact: f, + queryID: f.Next(), + bindings: b, + compiler: q.compiler, + store: q.store, + baseCache: newBaseCache(), + targetStack: newRefStack(), + txn: q.txn, + input: q.input, + tracers: q.tracers, + instr: q.instr, + builtins: q.builtins, + builtinCache: builtins.Cache{}, + virtualCache: newVirtualCache(), + saveSet: newSaveSet(q.unknowns, b, q.instr), + saveStack: newSaveStack(), + saveSupport: newSaveSupport(), + saveNamespace: ast.StringTerm(q.partialNamespace), + genvarprefix: q.genvarprefix, + runtime: q.runtime, + indexing: q.indexing, + } + + if len(q.disableInlining) > 0 { + e.disableInlining = [][]ast.Ref{q.disableInlining} + } + + e.caller = e + q.startTimer(metrics.RegoPartialEval) + defer q.stopTimer(metrics.RegoPartialEval) + + livevars := ast.NewVarSet() + + ast.WalkVars(q.query, func(x ast.Var) bool { + if !x.IsGenerated() { + livevars.Add(x) + } + return false + }) + + p := copypropagation.New(livevars) + + err = e.Run(func(e *eval) error { + + // Build output from saved expressions. + body := ast.NewBody() + + for _, elem := range e.saveStack.Stack[len(e.saveStack.Stack)-1] { + body.Append(elem.Plug(e.bindings)) + } + + // Include bindings as exprs so that when caller evals the result, they + // can obtain values for the vars in their query. + bindingExprs := []*ast.Expr{} + e.bindings.Iter(e.bindings, func(a, b *ast.Term) error { + bindingExprs = append(bindingExprs, ast.Equality.Expr(a, b)) + return nil + }) + + // Sort binding expressions so that results are deterministic. + sort.Slice(bindingExprs, func(i, j int) bool { + return bindingExprs[i].Compare(bindingExprs[j]) < 0 + }) + + for i := range bindingExprs { + body.Append(bindingExprs[i]) + } + + partials = append(partials, applyCopyPropagation(p, e.instr, body)) + return nil + }) + + support = e.saveSupport.List() + + return partials, support, err +} + +// Run is a wrapper around Iter that accumulates query results and returns them +// in one shot. +func (q *Query) Run(ctx context.Context) (QueryResultSet, error) { + qrs := QueryResultSet{} + return qrs, q.Iter(ctx, func(qr QueryResult) error { + qrs = append(qrs, qr) + return nil + }) +} + +// Iter executes the query and invokes the iter function with query results +// produced by evaluating the query. +func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { + f := &queryIDFactory{} + e := &eval{ + ctx: ctx, + cancel: q.cancel, + query: q.query, + queryCompiler: q.queryCompiler, + queryIDFact: f, + queryID: f.Next(), + bindings: newBindings(0, q.instr), + compiler: q.compiler, + store: q.store, + baseCache: newBaseCache(), + targetStack: newRefStack(), + txn: q.txn, + input: q.input, + tracers: q.tracers, + instr: q.instr, + builtins: q.builtins, + builtinCache: builtins.Cache{}, + virtualCache: newVirtualCache(), + genvarprefix: q.genvarprefix, + runtime: q.runtime, + indexing: q.indexing, + } + e.caller = e + q.startTimer(metrics.RegoQueryEval) + err := e.Run(func(e *eval) error { + qr := QueryResult{} + e.bindings.Iter(nil, func(k, v *ast.Term) error { + qr[k.Value.(ast.Var)] = v + return nil + }) + return iter(qr) + }) + q.stopTimer(metrics.RegoQueryEval) + return err +} + +func (q *Query) startTimer(name string) { + if q.metrics != nil { + q.metrics.Timer(name).Start() + } +} + +func (q *Query) stopTimer(name string) { + if q.metrics != nil { + q.metrics.Timer(name).Stop() + } +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/regex.go b/vendor/github.com/open-policy-agent/opa/topdown/regex.go new file mode 100644 index 000000000..7005cad2a --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/regex.go @@ -0,0 +1,201 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "regexp" + "sync" + + "github.com/yashtewari/glob-intersection" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +var regexpCacheLock = sync.Mutex{} +var regexpCache map[string]*regexp.Regexp + +func builtinRegexMatch(a, b ast.Value) (ast.Value, error) { + s1, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s2, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + re, err := getRegexp(string(s1)) + if err != nil { + return nil, err + } + return ast.Boolean(re.Match([]byte(s2))), nil +} + +func builtinRegexMatchTemplate(a, b, c, d ast.Value) (ast.Value, error) { + pattern, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + match, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + start, err := builtins.StringOperand(c, 3) + if err != nil { + return nil, err + } + end, err := builtins.StringOperand(d, 4) + if err != nil { + return nil, err + } + if len(start) != 1 { + return nil, fmt.Errorf("start delimiter has to be exactly one character long but is %d long", len(start)) + } + if len(end) != 1 { + return nil, fmt.Errorf("end delimiter has to be exactly one character long but is %d long", len(start)) + } + re, err := getRegexpTemplate(string(pattern), string(start)[0], string(end)[0]) + if err != nil { + return nil, err + } + return ast.Boolean(re.MatchString(string(match))), nil +} + +func builtinRegexSplit(a, b ast.Value) (ast.Value, error) { + s1, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s2, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + re, err := getRegexp(string(s1)) + if err != nil { + return nil, err + } + + elems := re.Split(string(s2), -1) + arr := make(ast.Array, len(elems)) + for i := range arr { + arr[i] = ast.StringTerm(elems[i]) + } + return arr, nil +} + +func getRegexp(pat string) (*regexp.Regexp, error) { + regexpCacheLock.Lock() + defer regexpCacheLock.Unlock() + re, ok := regexpCache[pat] + if !ok { + var err error + re, err = regexp.Compile(string(pat)) + if err != nil { + return nil, err + } + regexpCache[pat] = re + } + return re, nil +} + +func getRegexpTemplate(pat string, delimStart, delimEnd byte) (*regexp.Regexp, error) { + regexpCacheLock.Lock() + defer regexpCacheLock.Unlock() + re, ok := regexpCache[pat] + if !ok { + var err error + re, err = compileRegexTemplate(string(pat), delimStart, delimEnd) + if err != nil { + return nil, err + } + regexpCache[pat] = re + } + return re, nil +} + +func builtinGlobsMatch(a, b ast.Value) (ast.Value, error) { + s1, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s2, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + ne, err := gintersect.NonEmpty(string(s1), string(s2)) + if err != nil { + return nil, err + } + return ast.Boolean(ne), nil +} + +func builtinRegexFind(a, b, c ast.Value) (ast.Value, error) { + s1, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s2, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + n, err := builtins.IntOperand(c, 3) + if err != nil { + return nil, err + } + re, err := getRegexp(string(s1)) + if err != nil { + return nil, err + } + + elems := re.FindAllString(string(s2), n) + arr := make(ast.Array, len(elems)) + for i := range arr { + arr[i] = ast.StringTerm(elems[i]) + } + return arr, nil +} + +func builtinRegexFindAllStringSubmatch(a, b, c ast.Value) (ast.Value, error) { + s1, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + s2, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + n, err := builtins.IntOperand(c, 3) + if err != nil { + return nil, err + } + + re, err := getRegexp(string(s1)) + if err != nil { + return nil, err + } + matches := re.FindAllStringSubmatch(string(s2), n) + + outer := make(ast.Array, len(matches)) + for i := range outer { + inner := make(ast.Array, len(matches[i])) + for j := range inner { + inner[j] = ast.StringTerm(matches[i][j]) + } + outer[i] = ast.ArrayTerm(inner...) + } + + return outer, nil +} + +func init() { + regexpCache = map[string]*regexp.Regexp{} + RegisterFunctionalBuiltin2(ast.RegexMatch.Name, builtinRegexMatch) + RegisterFunctionalBuiltin2(ast.RegexSplit.Name, builtinRegexSplit) + RegisterFunctionalBuiltin2(ast.GlobsMatch.Name, builtinGlobsMatch) + RegisterFunctionalBuiltin4(ast.RegexTemplateMatch.Name, builtinRegexMatchTemplate) + RegisterFunctionalBuiltin3(ast.RegexFind.Name, builtinRegexFind) + RegisterFunctionalBuiltin3(ast.RegexFindAllStringSubmatch.Name, builtinRegexFindAllStringSubmatch) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/regex_template.go b/vendor/github.com/open-policy-agent/opa/topdown/regex_template.go new file mode 100644 index 000000000..39f92346f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/regex_template.go @@ -0,0 +1,122 @@ +package topdown + +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license as follows: + +// Copyright (c) 2012 Rodrigo Moraes. 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. + +// This file was forked from https://github.com/gorilla/mux/commit/eac83ba2c004bb75 + +import ( + "bytes" + "fmt" + "regexp" +) + +// delimiterIndices returns the first level delimiter indices from a string. +// It returns an error in case of unbalanced delimiters. +func delimiterIndices(s string, delimiterStart, delimiterEnd byte) ([]int, error) { + var level, idx int + idxs := make([]int, 0) + for i := 0; i < len(s); i++ { + switch s[i] { + case delimiterStart: + if level++; level == 1 { + idx = i + } + case delimiterEnd: + if level--; level == 0 { + idxs = append(idxs, idx, i+1) + } else if level < 0 { + return nil, fmt.Errorf(`unbalanced braces in %q`, s) + } + } + } + + if level != 0 { + return nil, fmt.Errorf(`unbalanced braces in %q`, s) + } + + return idxs, nil +} + +// compileRegexTemplate parses a template and returns a Regexp. +// +// You can define your own delimiters. It is e.g. common to use curly braces {} but I recommend using characters +// which have no special meaning in Regex, e.g.: <, > +// +// reg, err := compiler.CompileRegex("foo:bar.baz:<[0-9]{2,10}>", '<', '>') +// // if err != nil ... +// reg.MatchString("foo:bar.baz:123") +func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regexp.Regexp, error) { + // Check if it is well-formed. + idxs, errBraces := delimiterIndices(tpl, delimiterStart, delimiterEnd) + if errBraces != nil { + return nil, errBraces + } + varsR := make([]*regexp.Regexp, len(idxs)/2) + pattern := bytes.NewBufferString("") + + // WriteByte's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteByte('^') + + var end int + var err error + for i := 0; i < len(idxs); i += 2 { + // Set all values we are interested in. + raw := tpl[end:idxs[i]] + end = idxs[i+1] + patt := tpl[idxs[i]+1 : end-1] + // Build the regexp pattern. + varIdx := i / 2 + fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt) + varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt)) + if err != nil { + return nil, err + } + } + + // Add the remaining. + raw := tpl[end:] + + // WriteString's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteString(regexp.QuoteMeta(raw)) + + // WriteByte's error value is always nil for bytes.Buffer, no need to check it. + pattern.WriteByte('$') + + // Compile full regexp. + reg, errCompile := regexp.Compile(pattern.String()) + if errCompile != nil { + return nil, errCompile + } + + return reg, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/runtime.go b/vendor/github.com/open-policy-agent/opa/topdown/runtime.go new file mode 100644 index 000000000..67e183d01 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/runtime.go @@ -0,0 +1,20 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import "github.com/open-policy-agent/opa/ast" + +func builtinOPARuntime(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { + + if bctx.Runtime == nil { + return iter(ast.ObjectTerm()) + } + + return iter(bctx.Runtime) +} + +func init() { + RegisterBuiltinFunc(ast.OPARuntime.Name, builtinOPARuntime) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/save.go b/vendor/github.com/open-policy-agent/opa/topdown/save.go new file mode 100644 index 000000000..4037530a8 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/save.go @@ -0,0 +1,382 @@ +package topdown + +import ( + "container/list" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/ast" +) + +// saveSet contains a stack of terms that are considered 'unknown' during +// partial evaluation. Only var and ref terms (rooted at one of the root +// documents) can be added to the save set. Vars added to the save set are +// namespaced by the binding list they are added with. This means the save set +// can be shared across queries. +type saveSet struct { + instr *Instrumentation + l *list.List +} + +func newSaveSet(ts []*ast.Term, b *bindings, instr *Instrumentation) *saveSet { + ss := &saveSet{ + l: list.New(), + instr: instr, + } + ss.Push(ts, b) + return ss +} + +func (ss *saveSet) Push(ts []*ast.Term, b *bindings) { + ss.l.PushBack(newSaveSetElem(ts, b)) +} + +func (ss *saveSet) Pop() { + ss.l.Remove(ss.l.Back()) +} + +// Contains returns true if the term t is contained in the save set. Non-var and +// non-ref terms are never contained. Ref terms are contained if they share a +// prefix with a ref that was added (in either direction). +func (ss *saveSet) Contains(t *ast.Term, b *bindings) bool { + if ss != nil { + ss.instr.startTimer(partialOpSaveSetContains) + ret := ss.contains(t, b) + ss.instr.stopTimer(partialOpSaveSetContains) + return ret + } + return false +} + +func (ss *saveSet) contains(t *ast.Term, b *bindings) bool { + for el := ss.l.Back(); el != nil; el = el.Prev() { + if el.Value.(*saveSetElem).Contains(t, b) { + return true + } + } + return false +} + +// ContainsRecursive retruns true if the term t is or contains a term that is +// contained in the save set. This function will close over the binding list +// when it encounters vars. +func (ss *saveSet) ContainsRecursive(t *ast.Term, b *bindings) bool { + if ss != nil { + ss.instr.startTimer(partialOpSaveSetContainsRec) + ret := ss.containsrec(t, b) + ss.instr.stopTimer(partialOpSaveSetContainsRec) + return ret + } + return false +} + +func (ss *saveSet) containsrec(t *ast.Term, b *bindings) bool { + var found bool + ast.WalkTerms(t, func(x *ast.Term) bool { + if _, ok := x.Value.(ast.Var); ok { + x1, b1 := b.apply(x) + if x1 != x || b1 != b { + if ss.containsrec(x1, b1) { + found = true + } + } else if ss.contains(x1, b1) { + found = true + } + } + return found + }) + return found +} + +func (ss *saveSet) Vars(caller *bindings) ast.VarSet { + result := ast.NewVarSet() + for x := ss.l.Front(); x != nil; x = x.Next() { + elem := x.Value.(*saveSetElem) + for _, v := range elem.vars { + if v, ok := elem.b.PlugNamespaced(v, caller).Value.(ast.Var); ok { + result.Add(v) + } + } + } + return result +} + +func (ss *saveSet) String() string { + var buf []string + + for x := ss.l.Front(); x != nil; x = x.Next() { + buf = append(buf, x.Value.(*saveSetElem).String()) + } + + return "(" + strings.Join(buf, " ") + ")" +} + +type saveSetElem struct { + refs []ast.Ref + vars []*ast.Term + b *bindings +} + +func newSaveSetElem(ts []*ast.Term, b *bindings) *saveSetElem { + + var refs []ast.Ref + var vars []*ast.Term + + for _, t := range ts { + switch v := t.Value.(type) { + case ast.Var: + vars = append(vars, t) + case ast.Ref: + refs = append(refs, v) + default: + panic("illegal value") + } + } + + return &saveSetElem{ + b: b, + vars: vars, + refs: refs, + } +} + +func (sse *saveSetElem) Contains(t *ast.Term, b *bindings) bool { + switch other := t.Value.(type) { + case ast.Var: + return sse.containsVar(t, b) + case ast.Ref: + for _, ref := range sse.refs { + if ref.HasPrefix(other) || other.HasPrefix(ref) { + return true + } + } + return sse.containsVar(other[0], b) + } + return false +} + +func (sse *saveSetElem) String() string { + return fmt.Sprintf("(refs: %v, vars: %v, b: %v)", sse.refs, sse.vars, sse.b) +} + +func (sse *saveSetElem) containsVar(t *ast.Term, b *bindings) bool { + if b == sse.b { + for _, v := range sse.vars { + if v.Equal(t) { + return true + } + } + } + return false +} + +// saveStack contains a stack of queries that represent the result of partial +// evaluation. When partial evaluation completes, the top of the stack +// represents a complete, partially evaluated query that can be saved and +// evaluated later. +// +// The result is stored in a stack so that partial evaluation of a query can be +// paused and then resumed in cases where different queries make up the result +// of partial evaluation, such as when a rule with a default clause is +// partially evaluated. In this case, the partially evaluated rule will be +// output in the support module. +type saveStack struct { + Stack []saveStackQuery +} + +func newSaveStack() *saveStack { + return &saveStack{ + Stack: []saveStackQuery{ + {}, + }, + } +} + +func (s *saveStack) PushQuery(query saveStackQuery) { + s.Stack = append(s.Stack, query) +} + +func (s *saveStack) PopQuery() saveStackQuery { + last := s.Stack[len(s.Stack)-1] + s.Stack = s.Stack[:len(s.Stack)-1] + return last +} + +func (s *saveStack) Peek() saveStackQuery { + return s.Stack[len(s.Stack)-1] +} + +func (s *saveStack) Push(expr *ast.Expr, b1 *bindings, b2 *bindings) { + idx := len(s.Stack) - 1 + s.Stack[idx] = append(s.Stack[idx], saveStackElem{expr, b1, b2}) +} + +func (s *saveStack) Pop() { + idx := len(s.Stack) - 1 + query := s.Stack[idx] + s.Stack[idx] = query[:len(query)-1] +} + +type saveStackQuery []saveStackElem + +func (s saveStackQuery) Plug(b *bindings) ast.Body { + if len(s) == 0 { + return ast.NewBody(ast.NewExpr(ast.BooleanTerm(true))) + } + result := make(ast.Body, len(s)) + for i := range s { + expr := s[i].Plug(b) + result.Set(expr, i) + } + return result +} + +type saveStackElem struct { + Expr *ast.Expr + B1 *bindings + B2 *bindings +} + +func (e saveStackElem) Plug(caller *bindings) *ast.Expr { + if e.B1 == nil && e.B2 == nil { + return e.Expr + } + expr := e.Expr.Copy() + switch terms := expr.Terms.(type) { + case []*ast.Term: + if expr.IsEquality() { + terms[1] = e.B1.PlugNamespaced(terms[1], caller) + terms[2] = e.B2.PlugNamespaced(terms[2], caller) + } else { + for i := 1; i < len(terms); i++ { + terms[i] = e.B1.PlugNamespaced(terms[i], caller) + } + } + case *ast.Term: + expr.Terms = e.B1.PlugNamespaced(terms, caller) + } + for i := range expr.With { + expr.With[i].Value = e.B1.PlugNamespaced(expr.With[i].Value, caller) + } + return expr +} + +// saveSupport contains additional partially evaluated policies that are part +// of the output of partial evaluation. +// +// The support structure is accumulated as partial evaluation runs and then +// considered complete once partial evaluation finishes (but not before). This +// differs from partially evaluated queries which are considered complete as +// soon as each one finishes. +type saveSupport struct { + modules map[string]*ast.Module +} + +func newSaveSupport() *saveSupport { + return &saveSupport{ + modules: map[string]*ast.Module{}, + } +} + +func (s *saveSupport) List() []*ast.Module { + result := []*ast.Module{} + for _, module := range s.modules { + result = append(result, module) + } + return result +} + +func (s *saveSupport) Exists(path ast.Ref) bool { + k := path[:len(path)-1].String() + module, ok := s.modules[k] + if !ok { + return false + } + name := ast.Var(path[len(path)-1].Value.(ast.String)) + for _, rule := range module.Rules { + if rule.Head.Name.Equal(name) { + return true + } + } + return false +} + +func (s *saveSupport) Insert(path ast.Ref, rule *ast.Rule) { + pkg := path[:len(path)-1] + k := pkg.String() + module, ok := s.modules[k] + if !ok { + module = &ast.Module{ + Package: &ast.Package{ + Path: pkg, + }, + } + s.modules[k] = module + } + rule.Module = module + module.Rules = append(module.Rules, rule) +} + +// saveRequired returns true if the statement x will result in some expressions +// being saved. This check allows the evaluator to evaluate statements +// completely during partial evaluation as long as they do not depend on any +// kind of unknown value or statements that would generate saves. +func saveRequired(c *ast.Compiler, ss *saveSet, b *bindings, x interface{}, rec bool) bool { + + var found bool + + vis := ast.NewGenericVisitor(func(node interface{}) bool { + if found { + return found + } + switch node := node.(type) { + case *ast.Expr: + found = len(node.With) > 0 || ignoreExprDuringPartial(node) + case *ast.Term: + switch v := node.Value.(type) { + case ast.Var: + // Variables only need to be tested in the node from call site + // because once traversal recurses into a rule existing unknown + // variables are out-of-scope. + if !rec && ss.ContainsRecursive(node, b) { + found = true + } + case ast.Ref: + if ss.Contains(node, b) { + found = true + } else { + for _, rule := range c.GetRulesDynamic(v) { + if saveRequired(c, ss, b, rule, true) { + found = true + break + } + } + } + } + } + return found + }) + + vis.Walk(x) + + return found +} + +func ignoreExprDuringPartial(expr *ast.Expr) bool { + if !expr.IsCall() { + return false + } + + bi, ok := ast.BuiltinMap[expr.Operator().String()] + + return ok && ignoreDuringPartial(bi) +} + +func ignoreDuringPartial(bi *ast.Builtin) bool { + for _, ignore := range ast.IgnoreDuringPartialEval { + if bi == ignore { + return true + } + } + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/sets.go b/vendor/github.com/open-policy-agent/opa/topdown/sets.go new file mode 100644 index 000000000..a9c5ad86c --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/sets.go @@ -0,0 +1,84 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +// Deprecated in v0.4.2 in favour of minus/infix "-" operation. +func builtinSetDiff(a, b ast.Value) (ast.Value, error) { + + s1, err := builtins.SetOperand(a, 1) + if err != nil { + return nil, err + } + + s2, err := builtins.SetOperand(b, 2) + if err != nil { + return nil, err + } + + return s1.Diff(s2), nil +} + +// builtinSetIntersection returns the intersection of the given input sets +func builtinSetIntersection(a ast.Value) (ast.Value, error) { + + inputSet, err := builtins.SetOperand(a, 1) + if err != nil { + return nil, err + } + + // empty input set + if inputSet.Len() == 0 { + return ast.NewSet(), nil + } + + var result ast.Set + + err = inputSet.Iter(func(x *ast.Term) error { + n, err := builtins.SetOperand(x.Value, 1) + if err != nil { + return err + } + + if result == nil { + result = n + } else { + result = result.Intersect(n) + } + return nil + }) + return result, err +} + +// builtinSetUnion returns the union of the given input sets +func builtinSetUnion(a ast.Value) (ast.Value, error) { + + inputSet, err := builtins.SetOperand(a, 1) + if err != nil { + return nil, err + } + + result := ast.NewSet() + + err = inputSet.Iter(func(x *ast.Term) error { + n, err := builtins.SetOperand(x.Value, 1) + if err != nil { + return err + } + result = result.Union(n) + return nil + }) + return result, err +} + +func init() { + RegisterFunctionalBuiltin2(ast.SetDiff.Name, builtinSetDiff) + RegisterFunctionalBuiltin1(ast.Intersection.Name, builtinSetIntersection) + RegisterFunctionalBuiltin1(ast.Union.Name, builtinSetUnion) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/strings.go b/vendor/github.com/open-policy-agent/opa/topdown/strings.go new file mode 100644 index 000000000..15e592763 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/strings.go @@ -0,0 +1,393 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "errors" + "fmt" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +func builtinFormatInt(a, b ast.Value) (ast.Value, error) { + + input, err := builtins.NumberOperand(a, 1) + if err != nil { + return nil, err + } + + base, err := builtins.NumberOperand(b, 2) + if err != nil { + return nil, err + } + + var format string + switch base { + case ast.Number("2"): + format = "%b" + case ast.Number("8"): + format = "%o" + case ast.Number("10"): + format = "%d" + case ast.Number("16"): + format = "%x" + default: + return nil, builtins.NewOperandEnumErr(2, "2", "8", "10", "16") + } + + f := builtins.NumberToFloat(input) + i, _ := f.Int(nil) + + return ast.String(fmt.Sprintf(format, i)), nil +} + +func builtinConcat(a, b ast.Value) (ast.Value, error) { + + join, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + strs := []string{} + + switch b := b.(type) { + case ast.Array: + for i := range b { + s, ok := b[i].Value.(ast.String) + if !ok { + return nil, builtins.NewOperandElementErr(2, b, b[i].Value, "string") + } + strs = append(strs, string(s)) + } + case ast.Set: + err := b.Iter(func(x *ast.Term) error { + s, ok := x.Value.(ast.String) + if !ok { + return builtins.NewOperandElementErr(2, b, x.Value, "string") + } + strs = append(strs, string(s)) + return nil + }) + if err != nil { + return nil, err + } + default: + return nil, builtins.NewOperandTypeErr(2, b, "set", "array") + } + + return ast.String(strings.Join(strs, string(join))), nil +} + +func builtinIndexOf(a, b ast.Value) (ast.Value, error) { + base, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + search, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + index := strings.Index(string(base), string(search)) + return ast.IntNumberTerm(index).Value, nil +} + +func builtinSubstring(a, b, c ast.Value) (ast.Value, error) { + + base, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + startIndex, err := builtins.IntOperand(b, 2) + if err != nil { + return nil, err + } else if startIndex >= len(base) { + return ast.String(""), nil + } else if startIndex < 0 { + return nil, fmt.Errorf("negative offset") + } + + length, err := builtins.IntOperand(c, 3) + if err != nil { + return nil, err + } + + var s ast.String + if length < 0 { + s = ast.String(base[startIndex:]) + } else { + upto := startIndex + length + if len(base) < upto { + upto = len(base) + } + s = ast.String(base[startIndex:upto]) + } + + return s, nil +} + +func builtinContains(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + substr, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.Boolean(strings.Contains(string(s), string(substr))), nil +} + +func builtinStartsWith(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + prefix, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.Boolean(strings.HasPrefix(string(s), string(prefix))), nil +} + +func builtinEndsWith(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + suffix, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.Boolean(strings.HasSuffix(string(s), string(suffix))), nil +} + +func builtinLower(a ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(strings.ToLower(string(s))), nil +} + +func builtinUpper(a ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(strings.ToUpper(string(s))), nil +} + +func builtinSplit(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + d, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + elems := strings.Split(string(s), string(d)) + arr := make(ast.Array, len(elems)) + for i := range arr { + arr[i] = ast.StringTerm(elems[i]) + } + return arr, nil +} + +func builtinReplace(a, b, c ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + old, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + new, err := builtins.StringOperand(c, 3) + if err != nil { + return nil, err + } + + return ast.String(strings.Replace(string(s), string(old), string(new), -1)), nil +} + +func builtinReplaceN(a, b ast.Value) (ast.Value, error) { + asJSON, err := ast.JSON(a) + if err != nil { + return nil, err + } + oldnewObj, ok := asJSON.(map[string]interface{}) + if !ok { + return nil, builtins.NewOperandTypeErr(1, a, "object") + } + + s, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + var oldnewArr []string + for k, v := range oldnewObj { + strVal, ok := v.(string) + if !ok { + return nil, errors.New("non-string value found in pattern object") + } + oldnewArr = append(oldnewArr, k, strVal) + } + + r := strings.NewReplacer(oldnewArr...) + replaced := r.Replace(string(s)) + + return ast.String(replaced), nil +} + +func builtinTrim(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + c, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.String(strings.Trim(string(s), string(c))), nil +} + +func builtinTrimLeft(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + c, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.String(strings.TrimLeft(string(s), string(c))), nil +} + +func builtinTrimPrefix(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + pre, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.String(strings.TrimPrefix(string(s), string(pre))), nil +} + +func builtinTrimRight(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + c, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.String(strings.TrimRight(string(s), string(c))), nil +} + +func builtinTrimSuffix(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + suf, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + return ast.String(strings.TrimSuffix(string(s), string(suf))), nil +} + +func builtinTrimSpace(a ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + return ast.String(strings.TrimSpace(string(s))), nil +} + +func builtinSprintf(a, b ast.Value) (ast.Value, error) { + s, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + astArr, ok := b.(ast.Array) + if !ok { + return nil, builtins.NewOperandTypeErr(2, b, "array") + } + + args := make([]interface{}, len(astArr)) + + for i := range astArr { + switch v := astArr[i].Value.(type) { + case ast.Number: + if n, ok := v.Int(); ok { + args[i] = n + } else if f, ok := v.Float64(); ok { + args[i] = f + } else { + args[i] = v.String() + } + case ast.String: + args[i] = string(v) + default: + args[i] = astArr[i].String() + } + } + + return ast.String(fmt.Sprintf(string(s), args...)), nil +} + +func init() { + RegisterFunctionalBuiltin2(ast.FormatInt.Name, builtinFormatInt) + RegisterFunctionalBuiltin2(ast.Concat.Name, builtinConcat) + RegisterFunctionalBuiltin2(ast.IndexOf.Name, builtinIndexOf) + RegisterFunctionalBuiltin3(ast.Substring.Name, builtinSubstring) + RegisterFunctionalBuiltin2(ast.Contains.Name, builtinContains) + RegisterFunctionalBuiltin2(ast.StartsWith.Name, builtinStartsWith) + RegisterFunctionalBuiltin2(ast.EndsWith.Name, builtinEndsWith) + RegisterFunctionalBuiltin1(ast.Upper.Name, builtinUpper) + RegisterFunctionalBuiltin1(ast.Lower.Name, builtinLower) + RegisterFunctionalBuiltin2(ast.Split.Name, builtinSplit) + RegisterFunctionalBuiltin3(ast.Replace.Name, builtinReplace) + RegisterFunctionalBuiltin2(ast.ReplaceN.Name, builtinReplaceN) + RegisterFunctionalBuiltin2(ast.Trim.Name, builtinTrim) + RegisterFunctionalBuiltin2(ast.TrimLeft.Name, builtinTrimLeft) + RegisterFunctionalBuiltin2(ast.TrimPrefix.Name, builtinTrimPrefix) + RegisterFunctionalBuiltin2(ast.TrimRight.Name, builtinTrimRight) + RegisterFunctionalBuiltin2(ast.TrimSuffix.Name, builtinTrimSuffix) + RegisterFunctionalBuiltin1(ast.TrimSpace.Name, builtinTrimSpace) + RegisterFunctionalBuiltin2(ast.Sprintf.Name, builtinSprintf) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/time.go b/vendor/github.com/open-policy-agent/opa/topdown/time.go new file mode 100644 index 000000000..33de89dd2 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/time.go @@ -0,0 +1,203 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "encoding/json" + "fmt" + "math/big" + "strconv" + "sync" + "time" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +type nowKeyID string + +var nowKey = nowKeyID("time.now_ns") +var tzCache map[string]*time.Location +var tzCacheMutex *sync.Mutex + +func builtinTimeNowNanos(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { + + exist, ok := bctx.Cache.Get(nowKey) + var now *ast.Term + + if !ok { + curr := time.Now() + now = ast.NewTerm(ast.Number(int64ToJSONNumber(curr.UnixNano()))) + bctx.Cache.Put(nowKey, now) + } else { + now = exist.(*ast.Term) + } + + return iter(now) +} + +func builtinTimeParseNanos(a, b ast.Value) (ast.Value, error) { + + format, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + value, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + result, err := time.Parse(string(format), string(value)) + if err != nil { + return nil, err + } + + return ast.Number(int64ToJSONNumber(result.UnixNano())), nil +} + +func builtinTimeParseRFC3339Nanos(a ast.Value) (ast.Value, error) { + + value, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + result, err := time.Parse(time.RFC3339, string(value)) + if err != nil { + return nil, err + } + + return ast.Number(int64ToJSONNumber(result.UnixNano())), nil +} +func builtinParseDurationNanos(a ast.Value) (ast.Value, error) { + + duration, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + value, err := time.ParseDuration(string(duration)) + if err != nil { + return nil, err + } + return ast.Number(int64ToJSONNumber(int64(value))), nil +} + +func builtinDate(a ast.Value) (ast.Value, error) { + t, err := tzTime(a) + if err != nil { + return nil, err + } + year, month, day := t.Date() + result := ast.Array{ast.IntNumberTerm(year), ast.IntNumberTerm(int(month)), ast.IntNumberTerm(day)} + return result, nil +} + +func builtinClock(a ast.Value) (ast.Value, error) { + t, err := tzTime(a) + if err != nil { + return nil, err + } + hour, minute, second := t.Clock() + result := ast.Array{ast.IntNumberTerm(hour), ast.IntNumberTerm(minute), ast.IntNumberTerm(second)} + return result, nil +} + +func builtinWeekday(a ast.Value) (ast.Value, error) { + t, err := tzTime(a) + if err != nil { + return nil, err + } + weekday := t.Weekday().String() + return ast.String(weekday), nil +} + +func tzTime(a ast.Value) (t time.Time, err error) { + var nVal ast.Value + loc := time.UTC + + switch va := a.(type) { + case ast.Array: + + if len(va) == 0 { + return time.Time{}, builtins.NewOperandTypeErr(1, a, "either number (ns) or [number (ns), string (tz)]") + } + + nVal, err = builtins.NumberOperand(va[0].Value, 1) + if err != nil { + return time.Time{}, err + } + + if len(va) > 1 { + tzVal, err := builtins.StringOperand(va[1].Value, 1) + if err != nil { + return time.Time{}, err + } + + tzName := string(tzVal) + + switch tzName { + case "", "UTC": + // loc is already UTC + + case "Local": + loc = time.Local + + default: + var ok bool + + tzCacheMutex.Lock() + loc, ok = tzCache[tzName] + + if !ok { + loc, err = time.LoadLocation(tzName) + if err != nil { + tzCacheMutex.Unlock() + return time.Time{}, err + } + tzCache[tzName] = loc + } + tzCacheMutex.Unlock() + } + } + + case ast.Number: + nVal = a + + default: + return time.Time{}, builtins.NewOperandTypeErr(1, a, "either number (ns) or [number (ns), string (tz)]") + } + + value, err := builtins.NumberOperand(nVal, 1) + if err != nil { + return time.Time{}, err + } + + f := builtins.NumberToFloat(value) + i64, acc := f.Int64() + if acc != big.Exact { + return time.Time{}, fmt.Errorf("timestamp too big") + } + + t = time.Unix(0, i64).In(loc) + + return t, nil +} + +func int64ToJSONNumber(i int64) json.Number { + return json.Number(strconv.FormatInt(i, 10)) +} + +func init() { + RegisterBuiltinFunc(ast.NowNanos.Name, builtinTimeNowNanos) + RegisterFunctionalBuiltin1(ast.ParseRFC3339Nanos.Name, builtinTimeParseRFC3339Nanos) + RegisterFunctionalBuiltin2(ast.ParseNanos.Name, builtinTimeParseNanos) + RegisterFunctionalBuiltin1(ast.ParseDurationNanos.Name, builtinParseDurationNanos) + RegisterFunctionalBuiltin1(ast.Date.Name, builtinDate) + RegisterFunctionalBuiltin1(ast.Clock.Name, builtinClock) + RegisterFunctionalBuiltin1(ast.Weekday.Name, builtinWeekday) + tzCacheMutex = &sync.Mutex{} + tzCache = make(map[string]*time.Location) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/tokens.go b/vendor/github.com/open-policy-agent/opa/topdown/tokens.go new file mode 100644 index 000000000..376c5cbfa --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/tokens.go @@ -0,0 +1,967 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "crypto" + "crypto/ecdsa" + "crypto/hmac" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/json" + "encoding/pem" + "fmt" + "math/big" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jwk" + "github.com/open-policy-agent/opa/topdown/internal/jwx/jws" +) + +var ( + jwtEncKey = ast.StringTerm("enc") + jwtCtyKey = ast.StringTerm("cty") + jwtAlgKey = ast.StringTerm("alg") + jwtIssKey = ast.StringTerm("iss") + jwtExpKey = ast.StringTerm("exp") + jwtNbfKey = ast.StringTerm("nbf") + jwtAudKey = ast.StringTerm("aud") +) + +// JSONWebToken represent the 3 parts (header, payload & signature) of +// a JWT in Base64. +type JSONWebToken struct { + header string + payload string + signature string + decodedHeader ast.Object +} + +// decodeHeader populates the decodedHeader field. +func (token *JSONWebToken) decodeHeader() (err error) { + var h ast.Value + if h, err = builtinBase64UrlDecode(ast.String(token.header)); err != nil { + return fmt.Errorf("JWT header had invalid encoding: %v", err) + } + if token.decodedHeader, err = validateJWTHeader(string(h.(ast.String))); err != nil { + return err + } + return +} + +// Implements JWT decoding/validation based on RFC 7519 Section 7.2: +// https://tools.ietf.org/html/rfc7519#section-7.2 +// It does no data validation, it merely checks that the given string +// represents a structurally valid JWT. It supports JWTs using JWS compact +// serialization. +func builtinJWTDecode(a ast.Value) (ast.Value, error) { + token, err := decodeJWT(a) + if err != nil { + return nil, err + } + + if err = token.decodeHeader(); err != nil { + return nil, err + } + + p, err := builtinBase64UrlDecode(ast.String(token.payload)) + if err != nil { + return nil, fmt.Errorf("JWT payload had invalid encoding: %v", err) + } + + if cty := token.decodedHeader.Get(jwtCtyKey); cty != nil { + ctyVal := string(cty.Value.(ast.String)) + // It is possible for the contents of a token to be another + // token as a result of nested signing or encryption. To handle + // the case where we are given a token such as this, we check + // the content type and recurse on the payload if the content + // is "JWT". + // When the payload is itself another encoded JWT, then its + // contents are quoted (behavior of https://jwt.io/). To fix + // this, remove leading and trailing quotes. + if ctyVal == "JWT" { + p, err = builtinTrim(p, ast.String(`"'`)) + if err != nil { + panic("not reached") + } + return builtinJWTDecode(p) + } + } + + payload, err := extractJSONObject(string(p.(ast.String))) + if err != nil { + return nil, err + } + + s, err := builtinBase64UrlDecode(ast.String(token.signature)) + if err != nil { + return nil, fmt.Errorf("JWT signature had invalid encoding: %v", err) + } + sign := hex.EncodeToString([]byte(s.(ast.String))) + + arr := make(ast.Array, 3) + arr[0] = ast.NewTerm(token.decodedHeader) + arr[1] = ast.NewTerm(payload) + arr[2] = ast.StringTerm(sign) + + return arr, nil +} + +// Implements RS256 JWT signature verification +func builtinJWTVerifyRS256(a ast.Value, b ast.Value) (ast.Value, error) { + return builtinJWTVerifyRSA(a, b, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPKCS1v15( + publicKey, + crypto.SHA256, + digest, + signature) + }) +} + +// Implements PS256 JWT signature verification +func builtinJWTVerifyPS256(a ast.Value, b ast.Value) (ast.Value, error) { + return builtinJWTVerifyRSA(a, b, func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error { + return rsa.VerifyPSS( + publicKey, + crypto.SHA256, + digest, + signature, + nil) + }) +} + +// Implements RSA JWT signature verification. +func builtinJWTVerifyRSA(a ast.Value, b ast.Value, verify func(publicKey *rsa.PublicKey, digest []byte, signature []byte) error) (ast.Value, error) { + return builtinJWTVerify(a, b, func(publicKey interface{}, digest []byte, signature []byte) error { + publicKeyRsa, ok := publicKey.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("incorrect public key type") + } + return verify(publicKeyRsa, digest, signature) + }) +} + +// Implements ES256 JWT signature verification. +func builtinJWTVerifyES256(a ast.Value, b ast.Value) (ast.Value, error) { + return builtinJWTVerify(a, b, func(publicKey interface{}, digest []byte, signature []byte) error { + publicKeyEcdsa, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("incorrect public key type") + } + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + if ecdsa.Verify(publicKeyEcdsa, digest, r, s) { + return nil + } + return fmt.Errorf("ECDSA signature verification error") + }) +} + +// getKeyFromCertOrJWK returns the public key found in a X.509 certificate or JWK key(s). +// A valid PEM block is never valid JSON (and vice versa), hence can try parsing both. +func getKeyFromCertOrJWK(certificate string) ([]interface{}, error) { + if block, rest := pem.Decode([]byte(certificate)); block != nil { + if len(rest) > 0 { + return nil, fmt.Errorf("extra data after a PEM certificate block") + } + + if block.Type == "CERTIFICATE" { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, errors.Wrap(err, "failed to parse a PEM certificate") + } + + return []interface{}{cert.PublicKey}, nil + } + + if block.Type == "PUBLIC KEY" { + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, errors.Wrap(err, "failed to parse a PEM public key") + } + + return []interface{}{key}, nil + } + + return nil, fmt.Errorf("failed to extract a Key from the PEM certificate") + } + + jwks, err := jwk.ParseString(certificate) + if err != nil { + return nil, errors.Wrap(err, "failed to parse a JWK key (set)") + } + + var keys []interface{} + for _, k := range jwks.Keys { + key, err := k.Materialize() + if err != nil { + return nil, err + } + keys = append(keys, key) + } + + return keys, nil +} + +// Implements JWT signature verification. +func builtinJWTVerify(a ast.Value, b ast.Value, verify func(publicKey interface{}, digest []byte, signature []byte) error) (ast.Value, error) { + token, err := decodeJWT(a) + if err != nil { + return nil, err + } + + s, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + + keys, err := getKeyFromCertOrJWK(string(s)) + if err != nil { + return nil, err + } + + signature, err := token.decodeSignature() + if err != nil { + return nil, err + } + + // Validate the JWT signature + for _, key := range keys { + err = verify(key, + getInputSHA([]byte(token.header+"."+token.payload)), + []byte(signature)) + + if err == nil { + return ast.Boolean(true), nil + } + } + + // None of the keys worked, return false + return ast.Boolean(false), nil +} + +// Implements HS256 (secret) JWT signature verification +func builtinJWTVerifyHS256(a ast.Value, b ast.Value) (ast.Value, error) { + // Decode the JSON Web Token + token, err := decodeJWT(a) + if err != nil { + return nil, err + } + + // Process Secret input + astSecret, err := builtins.StringOperand(b, 2) + if err != nil { + return nil, err + } + secret := string(astSecret) + + mac := hmac.New(sha256.New, []byte(secret)) + _, err = mac.Write([]byte(token.header + "." + token.payload)) + if err != nil { + return nil, err + } + + signature, err := token.decodeSignature() + if err != nil { + return nil, err + } + + return ast.Boolean(hmac.Equal([]byte(signature), mac.Sum(nil))), nil +} + +// -- Full JWT verification and decoding -- + +// Verification constraints. See tokens_test.go for unit tests. + +// tokenConstraints holds decoded JWT verification constraints. +type tokenConstraints struct { + // The set of asymmetric keys we can verify with. + keys []interface{} + + // The single symmetric key we will verify with. + secret string + + // The algorithm that must be used to verify. + // If "", any algorithm is acceptable. + alg string + + // The required issuer. + // If "", any issuer is acceptable. + iss string + + // The required audience. + // If "", no audience is acceptable. + aud string + + // The time to validate against, or -1 if no constraint set. + // (If unset, the current time will be used.) + time int64 +} + +// tokenConstraintHandler is the handler type for JWT verification constraints. +type tokenConstraintHandler func(value ast.Value, parameters *tokenConstraints) (err error) + +// tokenConstraintTypes maps known JWT verification constraints to handlers. +var tokenConstraintTypes = map[string]tokenConstraintHandler{ + "cert": tokenConstraintCert, + "secret": func(value ast.Value, constraints *tokenConstraints) (err error) { + return tokenConstraintString("secret", value, &constraints.secret) + }, + "alg": func(value ast.Value, constraints *tokenConstraints) (err error) { + return tokenConstraintString("alg", value, &constraints.alg) + }, + "iss": func(value ast.Value, constraints *tokenConstraints) (err error) { + return tokenConstraintString("iss", value, &constraints.iss) + }, + "aud": func(value ast.Value, constraints *tokenConstraints) (err error) { + return tokenConstraintString("aud", value, &constraints.aud) + }, + "time": tokenConstraintTime, +} + +// tokenConstraintCert handles the `cert` constraint. +func tokenConstraintCert(value ast.Value, constraints *tokenConstraints) (err error) { + var s ast.String + var ok bool + if s, ok = value.(ast.String); !ok { + return fmt.Errorf("cert constraint: must be a string") + } + + constraints.keys, err = getKeyFromCertOrJWK(string(s)) + return +} + +// tokenConstraintTime handles the `time` constraint. +func tokenConstraintTime(value ast.Value, constraints *tokenConstraints) (err error) { + var time ast.Number + var ok bool + if time, ok = value.(ast.Number); !ok { + err = fmt.Errorf("token time constraint: must be a number") + return + } + var timeFloat float64 + if timeFloat, err = strconv.ParseFloat(string(time), 64); err != nil { + err = fmt.Errorf("token time constraint: %v", err) + return + } + if timeFloat < 0 { + err = fmt.Errorf("token time constraint: must not be negative") + return + } + constraints.time = int64(timeFloat) + return +} + +// tokenConstraintString handles string constraints. +func tokenConstraintString(name string, value ast.Value, where *string) (err error) { + var av ast.String + var ok bool + if av, ok = value.(ast.String); !ok { + err = fmt.Errorf("%s constraint: must be a string", name) + return + } + *where = string(av) + return +} + +// parseTokenConstraints parses the constraints argument. +func parseTokenConstraints(a ast.Value) (constraints tokenConstraints, err error) { + constraints.time = -1 + var o ast.Object + var ok bool + if o, ok = a.(ast.Object); !ok { + err = fmt.Errorf("token constraints must be object") + return + } + if err = o.Iter(func(k *ast.Term, v *ast.Term) (err error) { + var handler tokenConstraintHandler + var ok bool + name := string(k.Value.(ast.String)) + if handler, ok = tokenConstraintTypes[name]; ok { + if err = handler(v.Value, &constraints); err != nil { + return + } + } else { + // Anything unknown is rejected. + err = fmt.Errorf("unknown token validation constraint: %s", name) + return + } + return + }); err != nil { + return + } + return +} + +// validate validates the constraints argument. +func (constraints *tokenConstraints) validate() (err error) { + keys := 0 + if constraints.keys != nil { + keys++ + } + if constraints.secret != "" { + keys++ + } + if keys > 1 { + err = fmt.Errorf("duplicate key constraints") + return + } + if keys < 1 { + err = fmt.Errorf("no key constraint") + return + } + return +} + +// verify verifies a JWT using the constraints and the algorithm from the header +func (constraints *tokenConstraints) verify(kid, alg, header, payload, signature string) error { + // Construct the payload + plaintext := []byte(header) + plaintext = append(plaintext, []byte(".")...) + plaintext = append(plaintext, payload...) + // Look up the algorithm + var ok bool + var a tokenAlgorithm + a, ok = tokenAlgorithms[alg] + if !ok { + return fmt.Errorf("unknown JWS algorithm: %s", alg) + } + // If we're configured with asymmetric key(s) then only trust that + if constraints.keys != nil { + verified := false + for _, key := range constraints.keys { + err := a.verify(key, a.hash, plaintext, []byte(signature)) + if err == nil { + verified = true + break + } + } + if !verified { + return errSignatureNotVerified + } + return nil + } + if constraints.secret != "" { + return a.verify([]byte(constraints.secret), a.hash, plaintext, []byte(signature)) + } + // (*tokenConstraints)validate() should prevent this happening + return errors.New("unexpectedly found no keys to trust") +} + +// validAudience checks the audience of the JWT. +// It returns true if it meets the constraints and false otherwise. +func (constraints *tokenConstraints) validAudience(aud ast.Value) (valid bool) { + var ok bool + var s ast.String + if s, ok = aud.(ast.String); ok { + return string(s) == constraints.aud + } + var a ast.Array + if a, ok = aud.(ast.Array); ok { + for _, t := range a { + if s, ok = t.Value.(ast.String); ok { + if string(s) == constraints.aud { + return true + } + } else { + // Ill-formed aud claim + return false + } + } + } + return false +} + +// JWT algorithms + +type tokenVerifyFunction func(key interface{}, hash crypto.Hash, payload []byte, signature []byte) (err error) +type tokenVerifyAsymmetricFunction func(key interface{}, hash crypto.Hash, digest []byte, signature []byte) (err error) + +// jwtAlgorithm describes a JWS 'alg' value +type tokenAlgorithm struct { + hash crypto.Hash + verify tokenVerifyFunction +} + +// tokenAlgorithms is the known JWT algorithms +var tokenAlgorithms = map[string]tokenAlgorithm{ + "RS256": {crypto.SHA256, verifyAsymmetric(verifyRSAPKCS)}, + "RS384": {crypto.SHA384, verifyAsymmetric(verifyRSAPKCS)}, + "RS512": {crypto.SHA512, verifyAsymmetric(verifyRSAPKCS)}, + "PS256": {crypto.SHA256, verifyAsymmetric(verifyRSAPSS)}, + "PS384": {crypto.SHA384, verifyAsymmetric(verifyRSAPSS)}, + "PS512": {crypto.SHA512, verifyAsymmetric(verifyRSAPSS)}, + "ES256": {crypto.SHA256, verifyAsymmetric(verifyECDSA)}, + "ES384": {crypto.SHA384, verifyAsymmetric(verifyECDSA)}, + "ES512": {crypto.SHA512, verifyAsymmetric(verifyECDSA)}, + "HS256": {crypto.SHA256, verifyHMAC}, + "HS384": {crypto.SHA384, verifyHMAC}, + "HS512": {crypto.SHA512, verifyHMAC}, +} + +// errSignatureNotVerified is returned when a signature cannot be verified. +var errSignatureNotVerified = errors.New("signature not verified") + +func verifyHMAC(key interface{}, hash crypto.Hash, payload []byte, signature []byte) (err error) { + macKey, ok := key.([]byte) + if !ok { + return fmt.Errorf("incorrect symmetric key type") + } + mac := hmac.New(hash.New, macKey) + if _, err = mac.Write([]byte(payload)); err != nil { + return + } + if !hmac.Equal(signature, mac.Sum([]byte{})) { + err = errSignatureNotVerified + } + return +} + +func verifyAsymmetric(verify tokenVerifyAsymmetricFunction) tokenVerifyFunction { + return func(key interface{}, hash crypto.Hash, payload []byte, signature []byte) (err error) { + h := hash.New() + h.Write(payload) + return verify(key, hash, h.Sum([]byte{}), signature) + } +} + +func verifyRSAPKCS(key interface{}, hash crypto.Hash, digest []byte, signature []byte) (err error) { + publicKeyRsa, ok := key.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("incorrect public key type") + } + if err = rsa.VerifyPKCS1v15(publicKeyRsa, hash, digest, signature); err != nil { + err = errSignatureNotVerified + } + return +} + +func verifyRSAPSS(key interface{}, hash crypto.Hash, digest []byte, signature []byte) (err error) { + publicKeyRsa, ok := key.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("incorrect public key type") + } + if err = rsa.VerifyPSS(publicKeyRsa, hash, digest, signature, nil); err != nil { + err = errSignatureNotVerified + } + return +} + +func verifyECDSA(key interface{}, hash crypto.Hash, digest []byte, signature []byte) (err error) { + publicKeyEcdsa, ok := key.(*ecdsa.PublicKey) + if !ok { + return fmt.Errorf("incorrect public key type") + } + r, s := &big.Int{}, &big.Int{} + n := len(signature) / 2 + r.SetBytes(signature[:n]) + s.SetBytes(signature[n:]) + if ecdsa.Verify(publicKeyEcdsa, digest, r, s) { + return nil + } + return errSignatureNotVerified +} + +// JWT header parsing and parameters. See tokens_test.go for unit tests. + +// tokenHeaderType represents a recognized JWT header field +// tokenHeader is a parsed JWT header +type tokenHeader struct { + alg string + kid string + typ string + cty string + crit map[string]bool + unknown []string +} + +// tokenHeaderHandler handles a JWT header parameters +type tokenHeaderHandler func(header *tokenHeader, value ast.Value) (err error) + +// tokenHeaderTypes maps known JWT header parameters to handlers +var tokenHeaderTypes = map[string]tokenHeaderHandler{ + "alg": func(header *tokenHeader, value ast.Value) (err error) { + return tokenHeaderString("alg", &header.alg, value) + }, + "kid": func(header *tokenHeader, value ast.Value) (err error) { + return tokenHeaderString("kid", &header.kid, value) + }, + "typ": func(header *tokenHeader, value ast.Value) (err error) { + return tokenHeaderString("typ", &header.typ, value) + }, + "cty": func(header *tokenHeader, value ast.Value) (err error) { + return tokenHeaderString("cty", &header.cty, value) + }, + "crit": tokenHeaderCrit, +} + +// tokenHeaderCrit handles the 'crit' header parameter +func tokenHeaderCrit(header *tokenHeader, value ast.Value) (err error) { + var ok bool + var v ast.Array + if v, ok = value.(ast.Array); !ok { + err = fmt.Errorf("crit: must be a list") + return + } + header.crit = map[string]bool{} + for _, t := range v { + var tv ast.String + if tv, ok = t.Value.(ast.String); !ok { + err = fmt.Errorf("crit: must be a list of strings") + return + } + header.crit[string(tv)] = true + } + if len(header.crit) == 0 { + err = fmt.Errorf("crit: must be a nonempty list") // 'MUST NOT' use the empty list + return + } + return +} + +// tokenHeaderString handles string-format JWT header parameters +func tokenHeaderString(name string, where *string, value ast.Value) (err error) { + var ok bool + var v ast.String + if v, ok = value.(ast.String); !ok { + err = fmt.Errorf("%s: must be a string", name) + return + } + *where = string(v) + return +} + +// parseTokenHeader parses the JWT header. +func parseTokenHeader(token *JSONWebToken) (header tokenHeader, err error) { + header.unknown = []string{} + if err = token.decodedHeader.Iter(func(k *ast.Term, v *ast.Term) (err error) { + ks := string(k.Value.(ast.String)) + var ok bool + var handler tokenHeaderHandler + if handler, ok = tokenHeaderTypes[ks]; ok { + if err = handler(&header, v.Value); err != nil { + return + } + } else { + header.unknown = append(header.unknown, ks) + } + return + }); err != nil { + return + } + return +} + +// validTokenHeader returns true if the JOSE header is valid, otherwise false. +func (header *tokenHeader) valid() bool { + // RFC7515 s4.1.1 alg MUST be present + if header.alg == "" { + return false + } + // RFC7515 4.1.11 JWS is invalid if there is a critical parameter that we did not recognize + for _, u := range header.unknown { + if header.crit[u] { + return false + } + } + return true +} + +func commonBuiltinJWTEncodeSign(inputHeaders, jwsPayload, jwkSrc string) (v ast.Value, err error) { + + keys, err := jwk.ParseString(jwkSrc) + if err != nil { + return nil, err + } + key, err := keys.Keys[0].Materialize() + if err != nil { + return nil, err + } + if jwk.GetKeyTypeFromKey(key) != keys.Keys[0].GetKeyType() { + return nil, fmt.Errorf("JWK derived key type and keyType parameter do not match") + } + + standardHeaders := &jws.StandardHeaders{} + jwsHeaders := []byte(inputHeaders) + err = json.Unmarshal(jwsHeaders, standardHeaders) + if err != nil { + return nil, err + } + alg := standardHeaders.GetAlgorithm() + + if (standardHeaders.Type == "" || standardHeaders.Type == "JWT") && !json.Valid([]byte(jwsPayload)) { + return nil, fmt.Errorf("type is JWT but payload is not JSON") + } + + // process payload and sign + var jwsCompact []byte + jwsCompact, err = jws.SignLiteral([]byte(jwsPayload), alg, key, jwsHeaders) + if err != nil { + return nil, err + } + return ast.String(jwsCompact[:]), nil + +} + +func builtinJWTEncodeSign(a ast.Value, b ast.Value, c ast.Value) (v ast.Value, err error) { + + jwkSrc := c.String() + + inputHeaders := a.String() + + jwsPayload := b.String() + + return commonBuiltinJWTEncodeSign(inputHeaders, jwsPayload, jwkSrc) + +} + +func builtinJWTEncodeSignRaw(a ast.Value, b ast.Value, c ast.Value) (v ast.Value, err error) { + + jwkSrc, err := builtins.StringOperand(c, 1) + if err != nil { + return nil, err + } + inputHeaders, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + jwsPayload, err := builtins.StringOperand(b, 1) + if err != nil { + return nil, err + } + return commonBuiltinJWTEncodeSign(string(inputHeaders), string(jwsPayload), string(jwkSrc)) +} + +// Implements full JWT decoding, validation and verification. +func builtinJWTDecodeVerify(a ast.Value, b ast.Value) (v ast.Value, err error) { + // io.jwt.decode_verify(string, constraints, [valid, header, payload]) + // + // If valid is true then the signature verifies and all constraints are met. + // If valid is false then either the signature did not verify or some constrain + // was not met. + // + // Decoding errors etc are returned as errors. + arr := make(ast.Array, 3) + arr[0] = ast.BooleanTerm(false) // by default, not verified + arr[1] = ast.NewTerm(ast.NewObject()) + arr[2] = ast.NewTerm(ast.NewObject()) + var constraints tokenConstraints + if constraints, err = parseTokenConstraints(b); err != nil { + return + } + if err = constraints.validate(); err != nil { + return + } + var token *JSONWebToken + var p ast.Value + for { + // RFC7519 7.2 #1-2 split into parts + if token, err = decodeJWT(a); err != nil { + return + } + // RFC7519 7.2 #3, #4, #6 + if err = token.decodeHeader(); err != nil { + return + } + // RFC7159 7.2 #5 (and RFC7159 5.2 #5) validate header fields + var header tokenHeader + if header, err = parseTokenHeader(token); err != nil { + return + } + if !header.valid() { + return arr, nil + } + // Check constraints that impact signature verification. + if constraints.alg != "" && constraints.alg != header.alg { + return arr, nil + } + // RFC7159 7.2 #7 verify the signature + var signature string + if signature, err = token.decodeSignature(); err != nil { + return + } + if err = constraints.verify(header.kid, header.alg, token.header, token.payload, signature); err != nil { + if err == errSignatureNotVerified { + return arr, nil + } + return + } + // RFC7159 7.2 #9-10 decode the payload + if p, err = builtinBase64UrlDecode(ast.String(token.payload)); err != nil { + return nil, fmt.Errorf("JWT payload had invalid encoding: %v", err) + } + // RFC7159 7.2 #8 and 5.2 cty + if strings.ToUpper(header.cty) == "JWT" { + // Nested JWT, go round again + a = p + continue + } else { + // Non-nested JWT (or we've reached the bottom of the nesting). + break + } + } + var payload ast.Object + if payload, err = extractJSONObject(string(p.(ast.String))); err != nil { + return + } + // Check registered claim names against constraints or environment + // RFC7159 4.1.1 iss + if constraints.iss != "" { + if iss := payload.Get(jwtIssKey); iss != nil { + issVal := string(iss.Value.(ast.String)) + if constraints.iss != issVal { + return arr, nil + } + } + } + // RFC7159 4.1.3 aud + if aud := payload.Get(jwtAudKey); aud != nil { + if !constraints.validAudience(aud.Value) { + return arr, nil + } + } else { + if constraints.aud != "" { + return arr, nil + } + } + // RFC7159 4.1.4 exp + if exp := payload.Get(jwtExpKey); exp != nil { + var expVal int64 + if expVal, err = strconv.ParseInt(string(exp.Value.(ast.Number)), 10, 64); err != nil { + err = fmt.Errorf("parsing 'exp' JWT claim: %v", err) + return + } + if constraints.time < 0 { + constraints.time = time.Now().UnixNano() + } + // constraints.time is in nanoseconds but expVal is in seconds + if constraints.time/1000000000 >= expVal { + return arr, nil + } + } + // RFC7159 4.1.5 nbf + if nbf := payload.Get(jwtNbfKey); nbf != nil { + var nbfVal int64 + if nbfVal, err = strconv.ParseInt(string(nbf.Value.(ast.Number)), 10, 64); err != nil { + err = fmt.Errorf("parsing 'nbf' JWT claim: %v", err) + return + } + if constraints.time < 0 { + constraints.time = time.Now().UnixNano() + } + // constraints.time is in nanoseconds but nbfVal is in seconds + if constraints.time/1000000000 < nbfVal { + return arr, nil + } + } + // Format the result + arr[0] = ast.BooleanTerm(true) + arr[1] = ast.NewTerm(token.decodedHeader) + arr[2] = ast.NewTerm(payload) + return arr, nil +} + +// -- Utilities -- + +func decodeJWT(a ast.Value) (*JSONWebToken, error) { + // Parse the JSON Web Token + astEncode, err := builtins.StringOperand(a, 1) + if err != nil { + return nil, err + } + + encoding := string(astEncode) + if !strings.Contains(encoding, ".") { + return nil, errors.New("encoded JWT had no period separators") + } + + parts := strings.Split(encoding, ".") + if len(parts) != 3 { + return nil, fmt.Errorf("encoded JWT must have 3 sections, found %d", len(parts)) + } + + return &JSONWebToken{header: parts[0], payload: parts[1], signature: parts[2]}, nil +} + +func (token *JSONWebToken) decodeSignature() (string, error) { + decodedSignature, err := builtinBase64UrlDecode(ast.String(token.signature)) + if err != nil { + return "", err + } + + signatureAst, err := builtins.StringOperand(decodedSignature, 1) + if err != nil { + return "", err + } + return string(signatureAst), err +} + +// Extract, validate and return the JWT header as an ast.Object. +func validateJWTHeader(h string) (ast.Object, error) { + header, err := extractJSONObject(h) + if err != nil { + return nil, fmt.Errorf("bad JWT header: %v", err) + } + + // There are two kinds of JWT tokens, a JSON Web Signature (JWS) and + // a JSON Web Encryption (JWE). The latter is very involved, and we + // won't support it for now. + // This code checks which kind of JWT we are dealing with according to + // RFC 7516 Section 9: https://tools.ietf.org/html/rfc7516#section-9 + if header.Get(jwtEncKey) != nil { + return nil, errors.New("JWT is a JWE object, which is not supported") + } + + return header, nil +} + +func extractJSONObject(s string) (ast.Object, error) { + // XXX: This code relies on undocumented behavior of Go's + // json.Unmarshal using the last occurrence of duplicate keys in a JSON + // Object. If duplicate keys are present in a JWT, the last must be + // used or the token rejected. Since detecting duplicates is tantamount + // to parsing it ourselves, we're relying on the Go implementation + // using the last occurring instance of the key, which is the behavior + // as of Go 1.8.1. + v, err := builtinJSONUnmarshal(ast.String(s)) + if err != nil { + return nil, fmt.Errorf("invalid JSON: %v", err) + } + + o, ok := v.(ast.Object) + if !ok { + return nil, errors.New("decoded JSON type was not an Object") + } + + return o, nil +} + +// getInputSha returns the SHA256 checksum of the input +func getInputSHA(input []byte) (hash []byte) { + hasher := sha256.New() + hasher.Write(input) + return hasher.Sum(nil) +} + +func init() { + RegisterFunctionalBuiltin1(ast.JWTDecode.Name, builtinJWTDecode) + RegisterFunctionalBuiltin2(ast.JWTVerifyRS256.Name, builtinJWTVerifyRS256) + RegisterFunctionalBuiltin2(ast.JWTVerifyPS256.Name, builtinJWTVerifyPS256) + RegisterFunctionalBuiltin2(ast.JWTVerifyES256.Name, builtinJWTVerifyES256) + RegisterFunctionalBuiltin2(ast.JWTVerifyHS256.Name, builtinJWTVerifyHS256) + RegisterFunctionalBuiltin2(ast.JWTDecodeVerify.Name, builtinJWTDecodeVerify) + RegisterFunctionalBuiltin3(ast.JWTEncodeSignRaw.Name, builtinJWTEncodeSignRaw) + RegisterFunctionalBuiltin3(ast.JWTEncodeSign.Name, builtinJWTEncodeSign) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/trace.go b/vendor/github.com/open-policy-agent/opa/topdown/trace.go new file mode 100644 index 000000000..ba8fc925b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/trace.go @@ -0,0 +1,304 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + "io" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/topdown/builtins" +) + +// Op defines the types of tracing events. +type Op string + +const ( + // EnterOp is emitted when a new query is about to be evaluated. + EnterOp Op = "Enter" + + // ExitOp is emitted when a query has evaluated to true. + ExitOp Op = "Exit" + + // EvalOp is emitted when an expression is about to be evaluated. + EvalOp Op = "Eval" + + // RedoOp is emitted when an expression, rule, or query is being re-evaluated. + RedoOp Op = "Redo" + + // SaveOp is emitted when an expression is saved instead of evaluated + // during partial evaluation. + SaveOp Op = "Save" + + // FailOp is emitted when an expression evaluates to false. + FailOp Op = "Fail" + + // NoteOp is emitted when an expression invokes a tracing built-in function. + NoteOp Op = "Note" + + // IndexOp is emitted during an expression evaluation to represent lookup + // matches. + IndexOp Op = "Index" +) + +// VarMetadata provides some user facing information about +// a variable in some policy. +type VarMetadata struct { + Name ast.Var `json:"name"` + Location *ast.Location `json:"location"` +} + +// Event contains state associated with a tracing event. +type Event struct { + Op Op // Identifies type of event. + Node ast.Node // Contains AST node relevant to the event. + Location *ast.Location // The location of the Node this event relates to. + QueryID uint64 // Identifies the query this event belongs to. + ParentID uint64 // Identifies the parent query this event belongs to. + Locals *ast.ValueMap // Contains local variable bindings from the query context. + LocalMetadata map[ast.Var]VarMetadata // Contains metadata for the local variable bindings. + Message string // Contains message for Note events. +} + +// HasRule returns true if the Event contains an ast.Rule. +func (evt *Event) HasRule() bool { + _, ok := evt.Node.(*ast.Rule) + return ok +} + +// HasBody returns true if the Event contains an ast.Body. +func (evt *Event) HasBody() bool { + _, ok := evt.Node.(ast.Body) + return ok +} + +// HasExpr returns true if the Event contains an ast.Expr. +func (evt *Event) HasExpr() bool { + _, ok := evt.Node.(*ast.Expr) + return ok +} + +// Equal returns true if this event is equal to the other event. +func (evt *Event) Equal(other *Event) bool { + if evt.Op != other.Op { + return false + } + if evt.QueryID != other.QueryID { + return false + } + if evt.ParentID != other.ParentID { + return false + } + if !evt.equalNodes(other) { + return false + } + return evt.Locals.Equal(other.Locals) +} + +func (evt *Event) String() string { + return fmt.Sprintf("%v %v %v (qid=%v, pqid=%v)", evt.Op, evt.Node, evt.Locals, evt.QueryID, evt.ParentID) +} + +func (evt *Event) equalNodes(other *Event) bool { + switch a := evt.Node.(type) { + case ast.Body: + if b, ok := other.Node.(ast.Body); ok { + return a.Equal(b) + } + case *ast.Rule: + if b, ok := other.Node.(*ast.Rule); ok { + return a.Equal(b) + } + case *ast.Expr: + if b, ok := other.Node.(*ast.Expr); ok { + return a.Equal(b) + } + case nil: + return other.Node == nil + } + return false +} + +// Tracer defines the interface for tracing in the top-down evaluation engine. +type Tracer interface { + Enabled() bool + Trace(*Event) +} + +// BufferTracer implements the Tracer interface by simply buffering all events +// received. +type BufferTracer []*Event + +// NewBufferTracer returns a new BufferTracer. +func NewBufferTracer() *BufferTracer { + return &BufferTracer{} +} + +// Enabled always returns true if the BufferTracer is instantiated. +func (b *BufferTracer) Enabled() bool { + if b == nil { + return false + } + return true +} + +// Trace adds the event to the buffer. +func (b *BufferTracer) Trace(evt *Event) { + *b = append(*b, evt) +} + +// PrettyTrace pretty prints the trace to the writer. +func PrettyTrace(w io.Writer, trace []*Event) { + depths := depths{} + for _, event := range trace { + depth := depths.GetOrSet(event.QueryID, event.ParentID) + fmt.Fprintln(w, formatEvent(event, depth)) + } +} + +// PrettyTraceWithLocation prints the trace to the writer and includes location information +func PrettyTraceWithLocation(w io.Writer, trace []*Event) { + depths := depths{} + for _, event := range trace { + depth := depths.GetOrSet(event.QueryID, event.ParentID) + location := formatLocation(event) + fmt.Fprintln(w, fmt.Sprintf("%v %v", location, formatEvent(event, depth))) + } +} + +func formatEvent(event *Event, depth int) string { + padding := formatEventPadding(event, depth) + if event.Op == NoteOp { + return fmt.Sprintf("%v%v %q", padding, event.Op, event.Message) + } else if event.Message != "" { + return fmt.Sprintf("%v%v %v %v", padding, event.Op, event.Node, event.Message) + } else { + switch node := event.Node.(type) { + case *ast.Rule: + return fmt.Sprintf("%v%v %v", padding, event.Op, node.Path()) + default: + return fmt.Sprintf("%v%v %v", padding, event.Op, rewrite(event).Node) + } + } +} + +func formatEventPadding(event *Event, depth int) string { + spaces := formatEventSpaces(event, depth) + padding := "" + if spaces > 1 { + padding += strings.Repeat("| ", spaces-1) + } + return padding +} + +func formatEventSpaces(event *Event, depth int) int { + switch event.Op { + case EnterOp: + return depth + case RedoOp: + if _, ok := event.Node.(*ast.Expr); !ok { + return depth + } + } + return depth + 1 +} + +func formatLocation(event *Event) string { + if event.Op == NoteOp { + return fmt.Sprintf("%-19v", "note") + } + + location := event.Location + if location == nil { + return fmt.Sprintf("%-19v", "") + } + + if location.File == "" { + return fmt.Sprintf("%-19v", fmt.Sprintf("%.15v:%v", "query", location.Row)) + } + + return fmt.Sprintf("%-19v", fmt.Sprintf("%.15v:%v", location.File, location.Row)) +} + +// depths is a helper for computing the depth of an event. Events within the +// same query all have the same depth. The depth of query is +// depth(parent(query))+1. +type depths map[uint64]int + +func (ds depths) GetOrSet(qid uint64, pqid uint64) int { + depth := ds[qid] + if depth == 0 { + depth = ds[pqid] + depth++ + ds[qid] = depth + } + return depth +} + +func builtinTrace(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + + str, err := builtins.StringOperand(args[0].Value, 1) + if err != nil { + return handleBuiltinErr(ast.Trace.Name, bctx.Location, err) + } + + if !traceIsEnabled(bctx.Tracers) { + return iter(ast.BooleanTerm(true)) + } + + evt := &Event{ + Op: NoteOp, + QueryID: bctx.QueryID, + ParentID: bctx.ParentID, + Message: string(str), + } + + for i := range bctx.Tracers { + bctx.Tracers[i].Trace(evt) + } + + return iter(ast.BooleanTerm(true)) +} + +func traceIsEnabled(tracers []Tracer) bool { + for i := range tracers { + if tracers[i].Enabled() { + return true + } + } + return false +} + +func rewrite(event *Event) *Event { + + cpy := *event + + var node ast.Node + + switch v := event.Node.(type) { + case *ast.Expr: + node = v.Copy() + case ast.Body: + node = v.Copy() + case *ast.Rule: + node = v.Copy() + } + + ast.TransformVars(node, func(v ast.Var) (ast.Value, error) { + if meta, ok := cpy.LocalMetadata[v]; ok { + return meta.Name, nil + } + return v, nil + }) + + cpy.Node = node + + return &cpy +} + +func init() { + RegisterBuiltinFunc(ast.Trace.Name, builtinTrace) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/type.go b/vendor/github.com/open-policy-agent/opa/topdown/type.go new file mode 100644 index 000000000..a4c31fc83 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/type.go @@ -0,0 +1,82 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" +) + +func builtinIsNumber(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Number: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsString(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.String: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsBoolean(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Boolean: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsArray(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Array: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsSet(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Set: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsObject(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Object: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func builtinIsNull(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Null: + return ast.Boolean(true), nil + default: + return nil, BuiltinEmpty{} + } +} + +func init() { + RegisterFunctionalBuiltin1(ast.IsNumber.Name, builtinIsNumber) + RegisterFunctionalBuiltin1(ast.IsString.Name, builtinIsString) + RegisterFunctionalBuiltin1(ast.IsBoolean.Name, builtinIsBoolean) + RegisterFunctionalBuiltin1(ast.IsArray.Name, builtinIsArray) + RegisterFunctionalBuiltin1(ast.IsSet.Name, builtinIsSet) + RegisterFunctionalBuiltin1(ast.IsObject.Name, builtinIsObject) + RegisterFunctionalBuiltin1(ast.IsNull.Name, builtinIsNull) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/type_name.go b/vendor/github.com/open-policy-agent/opa/topdown/type_name.go new file mode 100644 index 000000000..5a77f5d31 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/type_name.go @@ -0,0 +1,36 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "fmt" + + "github.com/open-policy-agent/opa/ast" +) + +func builtinTypeName(a ast.Value) (ast.Value, error) { + switch a.(type) { + case ast.Null: + return ast.String("null"), nil + case ast.Boolean: + return ast.String("boolean"), nil + case ast.Number: + return ast.String("number"), nil + case ast.String: + return ast.String("string"), nil + case ast.Array: + return ast.String("array"), nil + case ast.Object: + return ast.String("object"), nil + case ast.Set: + return ast.String("set"), nil + } + + return nil, fmt.Errorf("illegal value") +} + +func init() { + RegisterFunctionalBuiltin1(ast.TypeNameBuiltin.Name, builtinTypeName) +} diff --git a/vendor/github.com/open-policy-agent/opa/topdown/walk.go b/vendor/github.com/open-policy-agent/opa/topdown/walk.go new file mode 100644 index 000000000..e40de8a12 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/topdown/walk.go @@ -0,0 +1,84 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package topdown + +import ( + "github.com/open-policy-agent/opa/ast" +) + +func evalWalk(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error { + input := args[0] + filter := getOutputPath(args) + var path ast.Array + return walk(filter, path, input, iter) +} + +func walk(filter, path ast.Array, input *ast.Term, iter func(*ast.Term) error) error { + + if len(filter) == 0 { + if err := iter(ast.ArrayTerm(ast.NewTerm(path), input)); err != nil { + return err + } + } + + if len(filter) > 0 { + key := filter[0] + filter = filter[1:] + if key.IsGround() { + if term := input.Get(key); term != nil { + return walk(filter, append(path, key), term, iter) + } + return nil + } + } + + switch v := input.Value.(type) { + case ast.Array: + for i := range v { + path = append(path, ast.IntNumberTerm(i)) + if err := walk(filter, path, v[i], iter); err != nil { + return err + } + path = path[:len(path)-1] + } + case ast.Object: + return v.Iter(func(k, v *ast.Term) error { + path = append(path, k) + if err := walk(filter, path, v, iter); err != nil { + return err + } + path = path[:len(path)-1] + return nil + }) + case ast.Set: + return v.Iter(func(elem *ast.Term) error { + path = append(path, elem) + if err := walk(filter, path, elem, iter); err != nil { + return err + } + path = path[:len(path)-1] + return nil + }) + } + + return nil +} + +func getOutputPath(args []*ast.Term) ast.Array { + if len(args) == 2 { + if arr, ok := args[1].Value.(ast.Array); ok { + if len(arr) == 2 { + if path, ok := arr[0].Value.(ast.Array); ok { + return path + } + } + } + } + return nil +} + +func init() { + RegisterBuiltinFunc(ast.WalkBuiltin.Name, evalWalk) +} diff --git a/vendor/github.com/open-policy-agent/opa/types/types.go b/vendor/github.com/open-policy-agent/opa/types/types.go new file mode 100644 index 000000000..f942c7b88 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/types/types.go @@ -0,0 +1,869 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package types declares data types for Rego values and helper functions to +// operate on these types. +package types + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/open-policy-agent/opa/util" +) + +// Sprint returns the string representation of the type. +func Sprint(x Type) string { + if x == nil { + return "???" + } + return x.String() +} + +// Type represents a type of a term in the language. +type Type interface { + String() string + typeMarker() string + json.Marshaler +} + +func (Null) typeMarker() string { return "null" } +func (Boolean) typeMarker() string { return "boolean" } +func (Number) typeMarker() string { return "number" } +func (String) typeMarker() string { return "string" } +func (*Array) typeMarker() string { return "array" } +func (*Object) typeMarker() string { return "object" } +func (*Set) typeMarker() string { return "set" } +func (Any) typeMarker() string { return "any" } +func (Function) typeMarker() string { return "function" } + +// Null represents the null type. +type Null struct{} + +// NewNull returns a new Null type. +func NewNull() Null { + return Null{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t Null) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "type": t.typeMarker(), + }) +} + +func (t Null) String() string { + return "null" +} + +// Boolean represents the boolean type. +type Boolean struct{} + +// B represents an instance of the boolean type. +var B = NewBoolean() + +// NewBoolean returns a new Boolean type. +func NewBoolean() Boolean { + return Boolean{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t Boolean) MarshalJSON() ([]byte, error) { + repr := map[string]interface{}{ + "type": t.typeMarker(), + } + return json.Marshal(repr) +} + +func (t Boolean) String() string { + return t.typeMarker() +} + +// String represents the string type. +type String struct{} + +// S represents an instance of the string type. +var S = NewString() + +// NewString returns a new String type. +func NewString() String { + return String{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t String) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "type": t.typeMarker(), + }) +} + +func (t String) String() string { + return "string" +} + +// Number represents the number type. +type Number struct{} + +// N represents an instance of the number type. +var N = NewNumber() + +// NewNumber returns a new Number type. +func NewNumber() Number { + return Number{} +} + +// MarshalJSON returns the JSON encoding of t. +func (t Number) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "type": t.typeMarker(), + }) +} + +func (Number) String() string { + return "number" +} + +// Array represents the array type. +type Array struct { + static []Type // static items + dynamic Type // dynamic items +} + +// NewArray returns a new Array type. +func NewArray(static []Type, dynamic Type) *Array { + return &Array{ + static: static, + dynamic: dynamic, + } +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Array) MarshalJSON() ([]byte, error) { + repr := map[string]interface{}{ + "type": t.typeMarker(), + } + if len(t.static) != 0 { + repr["static"] = t.static + } + if t.dynamic != nil { + repr["dynamic"] = t.dynamic + } + return json.Marshal(repr) +} + +func (t *Array) String() string { + prefix := "array" + buf := []string{} + for _, tpe := range t.static { + buf = append(buf, Sprint(tpe)) + } + var repr = prefix + if len(buf) > 0 { + repr += "<" + strings.Join(buf, ", ") + ">" + } + if t.dynamic != nil { + repr += "[" + t.dynamic.String() + "]" + } + return repr +} + +// Dynamic returns the type of the array's dynamic elements. +func (t *Array) Dynamic() Type { + return t.dynamic +} + +// Len returns the number of static array elements. +func (t *Array) Len() int { + return len(t.static) +} + +// Select returns the type of element at the zero-based pos. +func (t *Array) Select(pos int) Type { + if pos >= 0 { + if len(t.static) > pos { + return t.static[pos] + } + if t.dynamic != nil { + return t.dynamic + } + } + return nil +} + +// Set represents the set type. +type Set struct { + of Type +} + +// NewSet returns a new Set type. +func NewSet(of Type) *Set { + return &Set{ + of: of, + } +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Set) MarshalJSON() ([]byte, error) { + repr := map[string]interface{}{ + "type": t.typeMarker(), + } + if t.of != nil { + repr["of"] = t.of + } + return json.Marshal(repr) +} + +func (t *Set) String() string { + prefix := "set" + return prefix + "[" + Sprint(t.of) + "]" +} + +// StaticProperty represents a static object property. +type StaticProperty struct { + Key interface{} + Value Type +} + +// NewStaticProperty returns a new StaticProperty object. +func NewStaticProperty(key interface{}, value Type) *StaticProperty { + return &StaticProperty{ + Key: key, + Value: value, + } +} + +// MarshalJSON returns the JSON encoding of p. +func (p *StaticProperty) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "key": p.Key, + "value": p.Value, + }) +} + +// DynamicProperty represents a dynamic object property. +type DynamicProperty struct { + Key Type + Value Type +} + +// NewDynamicProperty returns a new DynamicProperty object. +func NewDynamicProperty(key, value Type) *DynamicProperty { + return &DynamicProperty{ + Key: key, + Value: value, + } +} + +// MarshalJSON returns the JSON encoding of p. +func (p *DynamicProperty) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "key": p.Key, + "value": p.Value, + }) +} + +func (p *DynamicProperty) String() string { + return fmt.Sprintf("%s: %s", Sprint(p.Key), Sprint(p.Value)) +} + +// Object represents the object type. +type Object struct { + static []*StaticProperty // constant properties + dynamic *DynamicProperty // dynamic properties +} + +// NewObject returns a new Object type. +func NewObject(static []*StaticProperty, dynamic *DynamicProperty) *Object { + sort.Slice(static, func(i, j int) bool { + cmp := util.Compare(static[i].Key, static[j].Key) + return cmp == -1 + }) + return &Object{ + static: static, + dynamic: dynamic, + } +} + +func (t *Object) String() string { + prefix := "object" + buf := make([]string, 0, len(t.static)) + for _, p := range t.static { + buf = append(buf, fmt.Sprintf("%v: %v", p.Key, Sprint(p.Value))) + } + var repr = prefix + if len(buf) > 0 { + repr += "<" + strings.Join(buf, ", ") + ">" + } + if t.dynamic != nil { + repr += "[" + t.dynamic.String() + "]" + } + return repr +} + +// DynamicValue returns the type of the object's dynamic elements. +func (t *Object) DynamicValue() Type { + if t.dynamic == nil { + return nil + } + return t.dynamic.Value +} + +// Keys returns the keys of the object's static elements. +func (t *Object) Keys() []interface{} { + sl := make([]interface{}, 0, len(t.static)) + for _, p := range t.static { + sl = append(sl, p.Key) + } + return sl +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Object) MarshalJSON() ([]byte, error) { + repr := map[string]interface{}{ + "type": t.typeMarker(), + } + if len(t.static) != 0 { + repr["static"] = t.static + } + if t.dynamic != nil { + repr["dynamic"] = t.dynamic + } + return json.Marshal(repr) +} + +// Select returns the type of the named property. +func (t *Object) Select(name interface{}) Type { + for _, p := range t.static { + if util.Compare(p.Key, name) == 0 { + return p.Value + } + } + if t.dynamic != nil { + if Contains(t.dynamic.Key, TypeOf(name)) { + return t.dynamic.Value + } + } + return nil +} + +// Any represents a dynamic type. +type Any []Type + +// A represents the superset of all types. +var A = NewAny() + +// NewAny returns a new Any type. +func NewAny(of ...Type) Any { + sl := make(Any, len(of)) + for i := range sl { + sl[i] = of[i] + } + return sl +} + +// Contains returns true if t is a superset of other. +func (t Any) Contains(other Type) bool { + if _, ok := other.(*Function); ok { + return false + } + for i := range t { + if Compare(t[i], other) == 0 { + return true + } + } + return len(t) == 0 +} + +// MarshalJSON returns the JSON encoding of t. +func (t Any) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]interface{}{ + "type": t.typeMarker(), + "of": []Type(t), + }) +} + +// Merge return a new Any type that is the superset of t and other. +func (t Any) Merge(other Type) Any { + if otherAny, ok := other.(Any); ok { + return t.Union(otherAny) + } + if t.Contains(other) { + return t + } + return append(t, other) +} + +// Union returns a new Any type that is the union of the two Any types. +func (t Any) Union(other Any) Any { + if len(t) == 0 { + return t + } + if len(other) == 0 { + return other + } + cpy := make(Any, len(t)) + for i := range cpy { + cpy[i] = t[i] + } + for i := range other { + if !cpy.Contains(other[i]) { + cpy = append(cpy, other[i]) + } + } + return cpy +} + +func (t Any) String() string { + prefix := "any" + if len(t) == 0 { + return prefix + } + buf := make([]string, len(t)) + for i := range t { + buf[i] = Sprint(t[i]) + } + return prefix + "<" + strings.Join(buf, ", ") + ">" +} + +// Function represents a function type. +type Function struct { + args []Type + result Type +} + +// Args returns an argument list. +func Args(x ...Type) []Type { + return x +} + +// NewFunction returns a new Function object where xs[:len(xs)-1] are arguments +// and xs[len(xs)-1] is the result type. +func NewFunction(args []Type, result Type) *Function { + return &Function{ + args: args, + result: result, + } +} + +// Args returns the function's argument types. +func (t *Function) Args() []Type { + return t.args +} + +// Result returns the function's result type. +func (t *Function) Result() Type { + return t.result +} + +func (t *Function) String() string { + var args string + if len(t.args) != 1 { + args = "(" + } + buf := []string{} + for _, a := range t.Args() { + buf = append(buf, Sprint(a)) + } + args += strings.Join(buf, ", ") + if len(t.args) != 1 { + args += ")" + } + return fmt.Sprintf("%v => %v", args, Sprint(t.Result())) +} + +// MarshalJSON returns the JSON encoding of t. +func (t *Function) MarshalJSON() ([]byte, error) { + repr := map[string]interface{}{ + "type": t.typeMarker(), + } + if len(t.args) > 0 { + repr["args"] = t.args + } + if t.result != nil { + repr["result"] = t.result + } + return json.Marshal(repr) +} + +// Union returns a new function represnting the union of t and other. Functions +// must have the same arity to be unioned. +func (t *Function) Union(other *Function) *Function { + if other == nil { + return t + } else if t == nil { + return other + } + a := t.Args() + b := other.Args() + if len(a) != len(b) { + return nil + } + args := make([]Type, len(a)) + for i := range a { + args[i] = Or(a[i], b[i]) + } + + return NewFunction(args, Or(t.Result(), other.Result())) +} + +// Compare returns -1, 0, 1 based on comparison between a and b. +func Compare(a, b Type) int { + x := typeOrder(a) + y := typeOrder(b) + if x > y { + return 1 + } else if x < y { + return -1 + } + switch a.(type) { + case nil, Null, Boolean, Number, String: + return 0 + case *Array: + arrA := a.(*Array) + arrB := b.(*Array) + if arrA.dynamic != nil && arrB.dynamic == nil { + return 1 + } else if arrB.dynamic != nil && arrA.dynamic == nil { + return -1 + } + if arrB.dynamic != nil && arrA.dynamic != nil { + if cmp := Compare(arrA.dynamic, arrB.dynamic); cmp != 0 { + return cmp + } + } + return typeSliceCompare(arrA.static, arrB.static) + case *Object: + objA := a.(*Object) + objB := b.(*Object) + if objA.dynamic != nil && objB.dynamic == nil { + return 1 + } else if objB.dynamic != nil && objA.dynamic == nil { + return -1 + } + if objA.dynamic != nil && objB.dynamic != nil { + if cmp := Compare(objA.dynamic.Key, objB.dynamic.Key); cmp != 0 { + return cmp + } + if cmp := Compare(objA.dynamic.Value, objB.dynamic.Value); cmp != 0 { + return cmp + } + } + + lenStaticA := len(objA.static) + lenStaticB := len(objB.static) + + minLen := lenStaticA + if lenStaticB < minLen { + minLen = lenStaticB + } + + for i := 0; i < minLen; i++ { + if cmp := util.Compare(objA.static[i].Key, objB.static[i].Key); cmp != 0 { + return cmp + } + if cmp := Compare(objA.static[i].Value, objB.static[i].Value); cmp != 0 { + return cmp + } + } + + if lenStaticA < lenStaticB { + return -1 + } else if lenStaticB < lenStaticA { + return 1 + } + + return 0 + case *Set: + setA := a.(*Set) + setB := b.(*Set) + if setA.of == nil && setB.of == nil { + return 0 + } else if setA.of == nil { + return -1 + } else if setB.of == nil { + return 1 + } + return Compare(setA.of, setB.of) + case Any: + sl1 := typeSlice(a.(Any)) + sl2 := typeSlice(b.(Any)) + sort.Sort(sl1) + sort.Sort(sl2) + return typeSliceCompare(sl1, sl2) + case *Function: + fA := a.(*Function) + fB := b.(*Function) + if len(fA.args) < len(fB.args) { + return -1 + } else if len(fA.args) > len(fB.args) { + return 1 + } + for i := 0; i < len(fA.args); i++ { + if cmp := Compare(fA.args[i], fB.args[i]); cmp != 0 { + return cmp + } + } + return Compare(fA.result, fB.result) + default: + panic("unreachable") + } +} + +// Contains returns true if a is a superset or equal to b. +func Contains(a, b Type) bool { + if any, ok := a.(Any); ok { + return any.Contains(b) + } + return Compare(a, b) == 0 +} + +// Or returns a type that represents the union of a and b. If one type is a +// superset of the other, the superset is returned unchanged. +func Or(a, b Type) Type { + if a == nil { + return b + } else if b == nil { + return a + } + fA, ok1 := a.(*Function) + fB, ok2 := b.(*Function) + if ok1 && ok2 { + return fA.Union(fB) + } else if ok1 || ok2 { + return nil + } + anyA, ok1 := a.(Any) + anyB, ok2 := b.(Any) + if ok1 { + return anyA.Merge(b) + } + if ok2 { + return anyB.Merge(a) + } + if Compare(a, b) == 0 { + return a + } + return NewAny(a, b) +} + +// Select returns a property or item of a. +func Select(a Type, x interface{}) Type { + switch a := a.(type) { + case *Array: + n, ok := x.(json.Number) + if !ok { + return nil + } + pos, err := n.Int64() + if err != nil { + return nil + } + return a.Select(int(pos)) + case *Object: + return a.Select(x) + case *Set: + tpe := TypeOf(x) + if Compare(a.of, tpe) == 0 { + return a.of + } + if any, ok := a.of.(Any); ok { + if any.Contains(tpe) { + return tpe + } + } + return nil + case Any: + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + // TODO(tsandall): test nil/nil + tpe = Or(Select(a[i], x), tpe) + } + return tpe + default: + return nil + } +} + +// Keys returns the type of keys that can be enumerated for a. For arrays, the +// keys are always number types, for objects the keys are always string types, +// and for sets the keys are always the type of the set element. +func Keys(a Type) Type { + switch a := a.(type) { + case *Array: + return N + case *Object: + var tpe Type + for _, k := range a.Keys() { + tpe = Or(tpe, TypeOf(k)) + } + if a.dynamic != nil { + tpe = Or(tpe, a.dynamic.Key) + } + return tpe + case *Set: + return a.of + case Any: + // TODO(tsandall): ditto test + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + tpe = Or(Keys(a[i]), tpe) + } + return tpe + } + return nil +} + +// Values returns the type of values that can be enumerated for a. +func Values(a Type) Type { + switch a := a.(type) { + case *Array: + var tpe Type + for i := range a.static { + tpe = Or(tpe, a.static[i]) + } + return Or(tpe, a.dynamic) + case *Object: + var tpe Type + for _, v := range a.static { + tpe = Or(tpe, v.Value) + } + if a.dynamic != nil { + tpe = Or(tpe, a.dynamic.Value) + } + return tpe + case *Set: + return a.of + case Any: + if Compare(a, A) == 0 { + return A + } + var tpe Type + for i := range a { + tpe = Or(Values(a[i]), tpe) + } + return tpe + } + return nil +} + +// Nil returns true if a's type is unknown. +func Nil(a Type) bool { + switch a := a.(type) { + case nil: + return true + case *Function: + for i := range a.args { + if Nil(a.args[i]) { + return true + } + } + return Nil(a.result) + case *Array: + for i := range a.static { + if Nil(a.static[i]) { + return true + } + } + if a.dynamic != nil { + return Nil(a.dynamic) + } + case *Object: + for i := range a.static { + if Nil(a.static[i].Value) { + return true + } + } + if a.dynamic != nil { + return Nil(a.dynamic.Key) || Nil(a.dynamic.Value) + } + case *Set: + return Nil(a.of) + } + return false +} + +// TypeOf returns the type of the Golang native value. +func TypeOf(x interface{}) Type { + switch x := x.(type) { + case nil: + return NewNull() + case bool: + return B + case string: + return S + case json.Number: + return N + case map[interface{}]interface{}: + static := make([]*StaticProperty, 0, len(x)) + for k, v := range x { + static = append(static, NewStaticProperty(k, TypeOf(v))) + } + return NewObject(static, nil) + case []interface{}: + static := make([]Type, len(x)) + for i := range x { + static[i] = TypeOf(x[i]) + } + return NewArray(static, nil) + } + panic("unreachable") +} + +type typeSlice []Type + +func (s typeSlice) Less(i, j int) bool { return Compare(s[i], s[j]) < 0 } +func (s typeSlice) Swap(i, j int) { x := s[i]; s[i] = s[j]; s[j] = x } +func (s typeSlice) Len() int { return len(s) } + +func typeSliceCompare(a, b []Type) int { + minLen := len(a) + if len(b) < minLen { + minLen = len(b) + } + for i := 0; i < minLen; i++ { + if cmp := Compare(a[i], b[i]); cmp != 0 { + return cmp + } + } + if len(a) < len(b) { + return -1 + } else if len(b) < len(a) { + return 1 + } + return 0 +} + +func typeOrder(x Type) int { + switch x.(type) { + case Null: + return 0 + case Boolean: + return 1 + case Number: + return 2 + case String: + return 3 + case *Array: + return 4 + case *Object: + return 5 + case *Set: + return 6 + case Any: + return 7 + case *Function: + return 8 + case nil: + return -1 + } + panic("unreachable") +} diff --git a/vendor/github.com/open-policy-agent/opa/util/backoff.go b/vendor/github.com/open-policy-agent/opa/util/backoff.go new file mode 100644 index 000000000..9e3a37257 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/backoff.go @@ -0,0 +1,42 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "math/rand" + "time" +) + +// DefaultBackoff returns a delay with an exponential backoff based on the +// number of retries. +func DefaultBackoff(base, max float64, retries int) time.Duration { + return Backoff(base, max, .2, 1.6, retries) +} + +// Backoff returns a delay with an exponential backoff based on the number of +// retries. Same algorithm used in gRPC. +func Backoff(base, max, jitter, factor float64, retries int) time.Duration { + if retries == 0 { + return 0 + } + + backoff, max := float64(base), float64(max) + for backoff < max && retries > 0 { + backoff *= factor + retries-- + } + if backoff > max { + backoff = max + } + + // Randomize backoff delays so that if a cluster of requests start at + // the same time, they won't operate in lockstep. + backoff *= 1 + jitter*(rand.Float64()*2-1) + if backoff < 0 { + return 0 + } + + return time.Duration(backoff) +} diff --git a/vendor/github.com/open-policy-agent/opa/util/close.go b/vendor/github.com/open-policy-agent/opa/util/close.go new file mode 100644 index 000000000..97c917731 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/close.go @@ -0,0 +1,23 @@ +// Copyright 2018 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "io" + "io/ioutil" + "net/http" +) + +// Close reads the remaining bytes from the response and then closes it to +// ensure that the connection is freed. If the body is not read and closed, a +// leak can occur. +func Close(resp *http.Response) { + if resp != nil && resp.Body != nil { + if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + return + } + resp.Body.Close() + } +} diff --git a/vendor/github.com/open-policy-agent/opa/util/compare.go b/vendor/github.com/open-policy-agent/opa/util/compare.go new file mode 100644 index 000000000..dd187590b --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/compare.go @@ -0,0 +1,161 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "encoding/json" + "fmt" + "math/big" + "sort" +) + +// Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a. +// +// For comparison between values of different types, the following ordering is used: +// nil < bool < float64 < string < []interface{} < map[string]interface{}. Slices and maps +// are compared recursively. If one slice or map is a subset of the other slice or map +// it is considered "less than". Nil is always equal to nil. +// +func Compare(a, b interface{}) int { + aSortOrder := sortOrder(a) + bSortOrder := sortOrder(b) + if aSortOrder < bSortOrder { + return -1 + } else if bSortOrder < aSortOrder { + return 1 + } + switch a := a.(type) { + case nil: + return 0 + case bool: + switch b := b.(type) { + case bool: + if a == b { + return 0 + } + if !a { + return -1 + } + return 1 + } + case json.Number: + switch b := b.(type) { + case json.Number: + return compareJSONNumber(a, b) + } + case string: + switch b := b.(type) { + case string: + if a == b { + return 0 + } else if a < b { + return -1 + } + return 1 + } + case []interface{}: + switch b := b.(type) { + case []interface{}: + bLen := len(b) + aLen := len(a) + minLen := aLen + if bLen < minLen { + minLen = bLen + } + for i := 0; i < minLen; i++ { + cmp := Compare(a[i], b[i]) + if cmp != 0 { + return cmp + } + } + if aLen == bLen { + return 0 + } else if aLen < bLen { + return -1 + } + return 1 + } + case map[string]interface{}: + switch b := b.(type) { + case map[string]interface{}: + var aKeys []string + for k := range a { + aKeys = append(aKeys, k) + } + var bKeys []string + for k := range b { + bKeys = append(bKeys, k) + } + sort.Strings(aKeys) + sort.Strings(bKeys) + aLen := len(aKeys) + bLen := len(bKeys) + minLen := aLen + if bLen < minLen { + minLen = bLen + } + for i := 0; i < minLen; i++ { + if aKeys[i] < bKeys[i] { + return -1 + } else if bKeys[i] < aKeys[i] { + return 1 + } + aVal := a[aKeys[i]] + bVal := b[bKeys[i]] + cmp := Compare(aVal, bVal) + if cmp != 0 { + return cmp + } + } + if aLen == bLen { + return 0 + } else if aLen < bLen { + return -1 + } + return 1 + } + } + + panic(fmt.Sprintf("illegal arguments of type %T and type %T", a, b)) +} + +const ( + nilSort = iota + boolSort = iota + numberSort = iota + stringSort = iota + arraySort = iota + objectSort = iota +) + +func compareJSONNumber(a, b json.Number) int { + bigA, ok := new(big.Float).SetString(string(a)) + if !ok { + panic("illegal value") + } + bigB, ok := new(big.Float).SetString(string(b)) + if !ok { + panic("illegal value") + } + return bigA.Cmp(bigB) +} + +func sortOrder(v interface{}) int { + switch v.(type) { + case nil: + return nilSort + case bool: + return boolSort + case json.Number: + return numberSort + case string: + return stringSort + case []interface{}: + return arraySort + case map[string]interface{}: + return objectSort + } + panic(fmt.Sprintf("illegal argument of type %T", v)) +} diff --git a/vendor/github.com/open-policy-agent/opa/util/doc.go b/vendor/github.com/open-policy-agent/opa/util/doc.go new file mode 100644 index 000000000..900dff8c1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/doc.go @@ -0,0 +1,6 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package util provides generic utilities used throughout the policy engine. +package util diff --git a/vendor/github.com/open-policy-agent/opa/util/enumflag.go b/vendor/github.com/open-policy-agent/opa/util/enumflag.go new file mode 100644 index 000000000..02a06a53f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/enumflag.go @@ -0,0 +1,54 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "strings" +) + +// EnumFlag implements the pflag.Value interface to provide enumerated command +// line parameter values. +type EnumFlag struct { + defaultValue string + vs []string + i int +} + +// NewEnumFlag returns a new EnumFlag that has a defaultValue and vs enumerated +// values. +func NewEnumFlag(defaultValue string, vs []string) *EnumFlag { + f := &EnumFlag{ + i: -1, + vs: vs, + defaultValue: defaultValue, + } + return f +} + +// Type returns the valid enumeration values. +func (f *EnumFlag) Type() string { + return "{" + strings.Join(f.vs, ",") + "}" +} + +// String returns the EnumValue's value as string. +func (f *EnumFlag) String() string { + if f.i == -1 { + return f.defaultValue + } + return f.vs[f.i] +} + +// Set sets the enum value. If s is not a valid enum value, an error is +// returned. +func (f *EnumFlag) Set(s string) error { + for i := range f.vs { + if f.vs[i] == s { + f.i = i + return nil + } + } + return fmt.Errorf("must be one of %v", f.Type()) +} diff --git a/vendor/github.com/open-policy-agent/opa/util/graph.go b/vendor/github.com/open-policy-agent/opa/util/graph.go new file mode 100644 index 000000000..f0e824245 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/graph.go @@ -0,0 +1,90 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// Traversal defines a basic interface to perform traversals. +type Traversal interface { + + // Edges should return the neighbours of node "u". + Edges(u T) []T + + // Visited should return true if node "u" has already been visited in this + // traversal. If the same traversal is used multiple times, the state that + // tracks visited nodes should be reset. + Visited(u T) bool +} + +// Equals should return true if node "u" equals node "v". +type Equals func(u T, v T) bool + +// Iter should return true to indicate stop. +type Iter func(u T) bool + +// DFS performs a depth first traversal calling f for each node starting from u. +// If f returns true, traversal stops and DFS returns true. +func DFS(t Traversal, f Iter, u T) bool { + lifo := NewLIFO(u) + for lifo.Size() > 0 { + next, _ := lifo.Pop() + if t.Visited(next) { + continue + } + if f(next) { + return true + } + for _, v := range t.Edges(next) { + lifo.Push(v) + } + } + return false +} + +// BFS performs a breadth first traversal calling f for each node starting from +// u. If f returns true, traversal stops and BFS returns true. +func BFS(t Traversal, f Iter, u T) bool { + fifo := NewFIFO(u) + for fifo.Size() > 0 { + next, _ := fifo.Pop() + if t.Visited(next) { + continue + } + if f(next) { + return true + } + for _, v := range t.Edges(next) { + fifo.Push(v) + } + } + return false +} + +// DFSPath returns a path from node a to node z found by performing +// a depth first traversal. If no path is found, an empty slice is returned. +func DFSPath(t Traversal, eq Equals, a, z T) []T { + p := dfsRecursive(t, eq, a, z, []T{}) + for i := len(p)/2 - 1; i >= 0; i-- { + o := len(p) - i - 1 + p[i], p[o] = p[o], p[i] + } + return p +} + +func dfsRecursive(t Traversal, eq Equals, u, z T, path []T) []T { + if t.Visited(u) { + return path + } + for _, v := range t.Edges(u) { + if eq(v, z) { + path = append(path, z) + path = append(path, u) + return path + } + if p := dfsRecursive(t, eq, v, z, path); len(p) > 0 { + path = append(p, u) + return path + } + } + return path +} diff --git a/vendor/github.com/open-policy-agent/opa/util/hashmap.go b/vendor/github.com/open-policy-agent/opa/util/hashmap.go new file mode 100644 index 000000000..11e7dca40 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/hashmap.go @@ -0,0 +1,157 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "fmt" + "strings" +) + +// T is a concise way to refer to T. +type T interface{} + +type hashEntry struct { + k T + v T + next *hashEntry +} + +// HashMap represents a key/value map. +type HashMap struct { + eq func(T, T) bool + hash func(T) int + table map[int]*hashEntry + size int +} + +// NewHashMap returns a new empty HashMap. +func NewHashMap(eq func(T, T) bool, hash func(T) int) *HashMap { + return &HashMap{ + eq: eq, + hash: hash, + table: make(map[int]*hashEntry), + size: 0, + } +} + +// Copy returns a shallow copy of this HashMap. +func (h *HashMap) Copy() *HashMap { + cpy := NewHashMap(h.eq, h.hash) + h.Iter(func(k, v T) bool { + cpy.Put(k, v) + return false + }) + return cpy +} + +// Equal returns true if this HashMap equals the other HashMap. +// Two hash maps are equal if they contain the same key/value pairs. +func (h *HashMap) Equal(other *HashMap) bool { + if h.Len() != other.Len() { + return false + } + return !h.Iter(func(k, v T) bool { + ov, ok := other.Get(k) + if !ok { + return true + } + return !h.eq(v, ov) + }) +} + +// Get returns the value for k. +func (h *HashMap) Get(k T) (T, bool) { + hash := h.hash(k) + for entry := h.table[hash]; entry != nil; entry = entry.next { + if h.eq(entry.k, k) { + return entry.v, true + } + } + return nil, false +} + +// Delete removes the the key k. +func (h *HashMap) Delete(k T) { + hash := h.hash(k) + var prev *hashEntry + for entry := h.table[hash]; entry != nil; entry = entry.next { + if h.eq(entry.k, k) { + if prev != nil { + prev.next = entry.next + } else { + h.table[hash] = entry.next + } + h.size-- + return + } + prev = entry + } +} + +// Hash returns the hash code for this hash map. +func (h *HashMap) Hash() int { + var hash int + h.Iter(func(k, v T) bool { + hash += h.hash(k) + h.hash(v) + return false + }) + return hash +} + +// Iter invokes the iter function for each element in the HashMap. +// If the iter function returns true, iteration stops and the return value is true. +// If the iter function never returns true, iteration proceeds through all elements +// and the return value is false. +func (h *HashMap) Iter(iter func(T, T) bool) bool { + for _, entry := range h.table { + for ; entry != nil; entry = entry.next { + if iter(entry.k, entry.v) { + return true + } + } + } + return false +} + +// Len returns the current size of this HashMap. +func (h *HashMap) Len() int { + return h.size +} + +// Put inserts a key/value pair into this HashMap. If the key is already present, the existing +// value is overwritten. +func (h *HashMap) Put(k T, v T) { + hash := h.hash(k) + head := h.table[hash] + for entry := head; entry != nil; entry = entry.next { + if h.eq(entry.k, k) { + entry.v = v + return + } + } + h.table[hash] = &hashEntry{k: k, v: v, next: head} + h.size++ +} + +func (h *HashMap) String() string { + var buf []string + h.Iter(func(k T, v T) bool { + buf = append(buf, fmt.Sprintf("%v: %v", k, v)) + return false + }) + return "{" + strings.Join(buf, ", ") + "}" +} + +// Update returns a new HashMap with elements from the other HashMap put into this HashMap. +// If the other HashMap contains elements with the same key as this HashMap, the value +// from the other HashMap overwrites the value from this HashMap. +func (h *HashMap) Update(other *HashMap) *HashMap { + updated := h.Copy() + other.Iter(func(k, v T) bool { + updated.Put(k, v) + return false + }) + return updated +} diff --git a/vendor/github.com/open-policy-agent/opa/util/json.go b/vendor/github.com/open-policy-agent/opa/util/json.go new file mode 100644 index 000000000..c65dac9a4 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/json.go @@ -0,0 +1,99 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import ( + "bytes" + "encoding/json" + "io" + "reflect" + + "github.com/ghodss/yaml" +) + +// UnmarshalJSON parses the JSON encoded data and stores the result in the value +// pointed to by x. +// +// This function is intended to be used in place of the standard json.Marshal +// function when json.Number is required. +func UnmarshalJSON(bs []byte, x interface{}) (err error) { + buf := bytes.NewBuffer(bs) + decoder := NewJSONDecoder(buf) + return decoder.Decode(x) +} + +// NewJSONDecoder returns a new decoder that reads from r. +// +// This function is intended to be used in place of the standard json.NewDecoder +// when json.Number is required. +func NewJSONDecoder(r io.Reader) *json.Decoder { + decoder := json.NewDecoder(r) + decoder.UseNumber() + return decoder +} + +// MustUnmarshalJSON parse the JSON encoded data and returns the result. +// +// If the data cannot be decoded, this function will panic. This function is for +// test purposes. +func MustUnmarshalJSON(bs []byte) interface{} { + var x interface{} + if err := UnmarshalJSON(bs, &x); err != nil { + panic(err) + } + return x +} + +// MustMarshalJSON returns the JSON encoding of x +// +// If the data cannot be encoded, this function will panic. This function is for +// test purposes. +func MustMarshalJSON(x interface{}) []byte { + bs, err := json.Marshal(x) + if err != nil { + panic(err) + } + return bs +} + +// RoundTrip encodes to JSON, and decodes the result again. +// +// Thereby, it is converting its argument to the representation expected by +// rego.Input and inmem's Write operations. Works with both references and +// values. +func RoundTrip(x *interface{}) error { + bs, err := json.Marshal(x) + if err != nil { + return err + } + return UnmarshalJSON(bs, x) +} + +// Reference returns a pointer to its argument unless the argument already is +// a pointer. If the argument is **t, or ***t, etc, it will return *t. +// +// Used for preparing Go types (including pointers to structs) into values to be +// put through util.RoundTrip(). +func Reference(x interface{}) *interface{} { + var y interface{} + rv := reflect.ValueOf(x) + if rv.Kind() == reflect.Ptr { + return Reference(rv.Elem().Interface()) + } + if rv.Kind() != reflect.Invalid { + y = rv.Interface() + return &y + } + return &x +} + +// Unmarshal decodes a YAML or JSON value into the specified type. +func Unmarshal(bs []byte, v interface{}) error { + bs, err := yaml.YAMLToJSON(bs) + if err != nil { + return err + } + return UnmarshalJSON(bs, v) +} diff --git a/vendor/github.com/open-policy-agent/opa/util/queue.go b/vendor/github.com/open-policy-agent/opa/util/queue.go new file mode 100644 index 000000000..63a2ffc16 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/queue.go @@ -0,0 +1,113 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// LIFO represents a simple LIFO queue. +type LIFO struct { + top *queueNode + size int +} + +type queueNode struct { + v T + next *queueNode +} + +// NewLIFO returns a new LIFO queue containing elements ts starting with the +// left-most argument at the bottom. +func NewLIFO(ts ...T) *LIFO { + s := &LIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *LIFO) Push(t T) { + node := &queueNode{v: t, next: s.top} + s.top = node + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *LIFO) Peek() (T, bool) { + if s.top == nil { + return nil, false + } + return s.top.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *LIFO) Pop() (T, bool) { + if s.top == nil { + return nil, false + } + node := s.top + s.top = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *LIFO) Size() int { + return s.size +} + +// FIFO represents a simple FIFO queue. +type FIFO struct { + front *queueNode + back *queueNode + size int +} + +// NewFIFO returns a new FIFO queue containing elements ts starting with the +// left-most argument at the front. +func NewFIFO(ts ...T) *FIFO { + s := &FIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *FIFO) Push(t T) { + node := &queueNode{v: t, next: nil} + if s.front == nil { + s.front = node + s.back = node + } else { + s.back.next = node + s.back = node + } + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *FIFO) Peek() (T, bool) { + if s.front == nil { + return nil, false + } + return s.front.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *FIFO) Pop() (T, bool) { + if s.front == nil { + return nil, false + } + node := s.front + s.front = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *FIFO) Size() int { + return s.size +} diff --git a/vendor/github.com/open-policy-agent/opa/version/version.go b/vendor/github.com/open-policy-agent/opa/version/version.go new file mode 100644 index 000000000..e1cdb4b15 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/version/version.go @@ -0,0 +1,15 @@ +// Copyright 2016 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +// Package version contains version information that is set at build time. +package version + +// Version information that is displayed by the "version" command and used to +// identify the version of running instances of OPA. +var ( + Version = "" + Vcs = "" + Timestamp = "" + Hostname = "" +) diff --git a/vendor/github.com/opentracing/opentracing-go/.gitignore b/vendor/github.com/opentracing/opentracing-go/.gitignore new file mode 100644 index 000000000..c57100a59 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/.gitignore @@ -0,0 +1 @@ +coverage.txt diff --git a/vendor/github.com/opentracing/opentracing-go/.travis.yml b/vendor/github.com/opentracing/opentracing-go/.travis.yml new file mode 100644 index 000000000..8d5b75e41 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/.travis.yml @@ -0,0 +1,20 @@ +language: go + +matrix: + include: + - go: "1.11.x" + - go: "1.12.x" + - go: "tip" + env: + - LINT=true + - COVERAGE=true + +install: + - if [ "$LINT" == true ]; then go get -u golang.org/x/lint/golint/... ; else echo 'skipping lint'; fi + - go get -u github.com/stretchr/testify/... + +script: + - make test + - go build ./... + - if [ "$LINT" == true ]; then make lint ; else echo 'skipping lint'; fi + - if [ "$COVERAGE" == true ]; then make cover && bash <(curl -s https://codecov.io/bash) ; else echo 'skipping coverage'; fi diff --git a/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md b/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md new file mode 100644 index 000000000..7c14febe1 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/CHANGELOG.md @@ -0,0 +1,46 @@ +Changes by Version +================== + +1.1.0 (2019-03-23) +------------------- + +Notable changes: +- The library is now released under Apache 2.0 license +- Use Set() instead of Add() in HTTPHeadersCarrier is functionally a breaking change (fixes issue [#159](https://github.com/opentracing/opentracing-go/issues/159)) +- 'golang.org/x/net/context' is replaced with 'context' from the standard library + +List of all changes: + +- Export StartSpanFromContextWithTracer (#214) +- Add IsGlobalTracerRegistered() to indicate if a tracer has been registered (#201) +- Use Set() instead of Add() in HTTPHeadersCarrier (#191) +- Update license to Apache 2.0 (#181) +- Replace 'golang.org/x/net/context' with 'context' (#176) +- Port of Python opentracing/harness/api_check.py to Go (#146) +- Fix race condition in MockSpan.Context() (#170) +- Add PeerHostIPv4.SetString() (#155) +- Add a Noop log field type to log to allow for optional fields (#150) + + +1.0.2 (2017-04-26) +------------------- + +- Add more semantic tags (#139) + + +1.0.1 (2017-02-06) +------------------- + +- Correct spelling in comments +- Address race in nextMockID() (#123) +- log: avoid panic marshaling nil error (#131) +- Deprecate InitGlobalTracer in favor of SetGlobalTracer (#128) +- Drop Go 1.5 that fails in Travis (#129) +- Add convenience methods Key() and Value() to log.Field +- Add convenience methods to log.Field (2 years, 6 months ago) + +1.0.0 (2016-09-26) +------------------- + +- This release implements OpenTracing Specification 1.0 (https://opentracing.io/spec) + diff --git a/vendor/github.com/opentracing/opentracing-go/LICENSE b/vendor/github.com/opentracing/opentracing-go/LICENSE new file mode 100644 index 000000000..f0027349e --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/LICENSE @@ -0,0 +1,201 @@ + 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 2016 The OpenTracing 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/github.com/opentracing/opentracing-go/Makefile b/vendor/github.com/opentracing/opentracing-go/Makefile new file mode 100644 index 000000000..62abb63f5 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/Makefile @@ -0,0 +1,20 @@ +.DEFAULT_GOAL := test-and-lint + +.PHONY: test-and-lint +test-and-lint: test lint + +.PHONY: test +test: + go test -v -cover -race ./... + +.PHONY: cover +cover: + go test -v -coverprofile=coverage.txt -covermode=atomic -race ./... + +.PHONY: lint +lint: + go fmt ./... + golint ./... + @# Run again with magic to exit non-zero if golint outputs anything. + @! (golint ./... | read dummy) + go vet ./... diff --git a/vendor/github.com/opentracing/opentracing-go/README.md b/vendor/github.com/opentracing/opentracing-go/README.md new file mode 100644 index 000000000..6ef1d7c9d --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/README.md @@ -0,0 +1,171 @@ +[![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/opentracing/public) [![Build Status](https://travis-ci.org/opentracing/opentracing-go.svg?branch=master)](https://travis-ci.org/opentracing/opentracing-go) [![GoDoc](https://godoc.org/github.com/opentracing/opentracing-go?status.svg)](http://godoc.org/github.com/opentracing/opentracing-go) +[![Sourcegraph Badge](https://sourcegraph.com/github.com/opentracing/opentracing-go/-/badge.svg)](https://sourcegraph.com/github.com/opentracing/opentracing-go?badge) + +# OpenTracing API for Go + +This package is a Go platform API for OpenTracing. + +## Required Reading + +In order to understand the Go platform API, one must first be familiar with the +[OpenTracing project](https://opentracing.io) and +[terminology](https://opentracing.io/specification/) more specifically. + +## API overview for those adding instrumentation + +Everyday consumers of this `opentracing` package really only need to worry +about a couple of key abstractions: the `StartSpan` function, the `Span` +interface, and binding a `Tracer` at `main()`-time. Here are code snippets +demonstrating some important use cases. + +#### Singleton initialization + +The simplest starting point is `./default_tracer.go`. As early as possible, call + +```go + import "github.com/opentracing/opentracing-go" + import ".../some_tracing_impl" + + func main() { + opentracing.SetGlobalTracer( + // tracing impl specific: + some_tracing_impl.New(...), + ) + ... + } +``` + +#### Non-Singleton initialization + +If you prefer direct control to singletons, manage ownership of the +`opentracing.Tracer` implementation explicitly. + +#### Creating a Span given an existing Go `context.Context` + +If you use `context.Context` in your application, OpenTracing's Go library will +happily rely on it for `Span` propagation. To start a new (blocking child) +`Span`, you can use `StartSpanFromContext`. + +```go + func xyz(ctx context.Context, ...) { + ... + span, ctx := opentracing.StartSpanFromContext(ctx, "operation_name") + defer span.Finish() + span.LogFields( + log.String("event", "soft error"), + log.String("type", "cache timeout"), + log.Int("waited.millis", 1500)) + ... + } +``` + +#### Starting an empty trace by creating a "root span" + +It's always possible to create a "root" `Span` with no parent or other causal +reference. + +```go + func xyz() { + ... + sp := opentracing.StartSpan("operation_name") + defer sp.Finish() + ... + } +``` + +#### Creating a (child) Span given an existing (parent) Span + +```go + func xyz(parentSpan opentracing.Span, ...) { + ... + sp := opentracing.StartSpan( + "operation_name", + opentracing.ChildOf(parentSpan.Context())) + defer sp.Finish() + ... + } +``` + +#### Serializing to the wire + +```go + func makeSomeRequest(ctx context.Context) ... { + if span := opentracing.SpanFromContext(ctx); span != nil { + httpClient := &http.Client{} + httpReq, _ := http.NewRequest("GET", "http://myservice/", nil) + + // Transmit the span's TraceContext as HTTP headers on our + // outbound request. + opentracing.GlobalTracer().Inject( + span.Context(), + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(httpReq.Header)) + + resp, err := httpClient.Do(httpReq) + ... + } + ... + } +``` + +#### Deserializing from the wire + +```go + http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + var serverSpan opentracing.Span + appSpecificOperationName := ... + wireContext, err := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(req.Header)) + if err != nil { + // Optionally record something about err here + } + + // Create the span referring to the RPC client if available. + // If wireContext == nil, a root span will be created. + serverSpan = opentracing.StartSpan( + appSpecificOperationName, + ext.RPCServerOption(wireContext)) + + defer serverSpan.Finish() + + ctx := opentracing.ContextWithSpan(context.Background(), serverSpan) + ... + } +``` + +#### Conditionally capture a field using `log.Noop` + +In some situations, you may want to dynamically decide whether or not +to log a field. For example, you may want to capture additional data, +such as a customer ID, in non-production environments: + +```go + func Customer(order *Order) log.Field { + if os.Getenv("ENVIRONMENT") == "dev" { + return log.String("customer", order.Customer.ID) + } + return log.Noop() + } +``` + +#### Goroutine-safety + +The entire public API is goroutine-safe and does not require external +synchronization. + +## API pointers for those implementing a tracing system + +Tracing system implementors may be able to reuse or copy-paste-modify the `basictracer` package, found [here](https://github.com/opentracing/basictracer-go). In particular, see `basictracer.New(...)`. + +## API compatibility + +For the time being, "mild" backwards-incompatible changes may be made without changing the major version number. As OpenTracing and `opentracing-go` mature, backwards compatibility will become more of a priority. + +## Tracer test suite + +A test suite is available in the [harness](https://godoc.org/github.com/opentracing/opentracing-go/harness) package that can assist Tracer implementors to assert that their Tracer is working correctly. + +## Licensing + +[Apache 2.0 License](./LICENSE). diff --git a/vendor/github.com/opentracing/opentracing-go/globaltracer.go b/vendor/github.com/opentracing/opentracing-go/globaltracer.go new file mode 100644 index 000000000..4f7066a92 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/globaltracer.go @@ -0,0 +1,42 @@ +package opentracing + +type registeredTracer struct { + tracer Tracer + isRegistered bool +} + +var ( + globalTracer = registeredTracer{NoopTracer{}, false} +) + +// SetGlobalTracer sets the [singleton] opentracing.Tracer returned by +// GlobalTracer(). Those who use GlobalTracer (rather than directly manage an +// opentracing.Tracer instance) should call SetGlobalTracer as early as +// possible in main(), prior to calling the `StartSpan` global func below. +// Prior to calling `SetGlobalTracer`, any Spans started via the `StartSpan` +// (etc) globals are noops. +func SetGlobalTracer(tracer Tracer) { + globalTracer = registeredTracer{tracer, true} +} + +// GlobalTracer returns the global singleton `Tracer` implementation. +// Before `SetGlobalTracer()` is called, the `GlobalTracer()` is a noop +// implementation that drops all data handed to it. +func GlobalTracer() Tracer { + return globalTracer.tracer +} + +// StartSpan defers to `Tracer.StartSpan`. See `GlobalTracer()`. +func StartSpan(operationName string, opts ...StartSpanOption) Span { + return globalTracer.tracer.StartSpan(operationName, opts...) +} + +// InitGlobalTracer is deprecated. Please use SetGlobalTracer. +func InitGlobalTracer(tracer Tracer) { + SetGlobalTracer(tracer) +} + +// IsGlobalTracerRegistered returns a `bool` to indicate if a tracer has been globally registered +func IsGlobalTracerRegistered() bool { + return globalTracer.isRegistered +} diff --git a/vendor/github.com/opentracing/opentracing-go/gocontext.go b/vendor/github.com/opentracing/opentracing-go/gocontext.go new file mode 100644 index 000000000..08c00c04e --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/gocontext.go @@ -0,0 +1,60 @@ +package opentracing + +import "context" + +type contextKey struct{} + +var activeSpanKey = contextKey{} + +// ContextWithSpan returns a new `context.Context` that holds a reference to +// `span`'s SpanContext. +func ContextWithSpan(ctx context.Context, span Span) context.Context { + return context.WithValue(ctx, activeSpanKey, span) +} + +// SpanFromContext returns the `Span` previously associated with `ctx`, or +// `nil` if no such `Span` could be found. +// +// NOTE: context.Context != SpanContext: the former is Go's intra-process +// context propagation mechanism, and the latter houses OpenTracing's per-Span +// identity and baggage information. +func SpanFromContext(ctx context.Context) Span { + val := ctx.Value(activeSpanKey) + if sp, ok := val.(Span); ok { + return sp + } + return nil +} + +// StartSpanFromContext starts and returns a Span with `operationName`, using +// any Span found within `ctx` as a ChildOfRef. If no such parent could be +// found, StartSpanFromContext creates a root (parentless) Span. +// +// The second return value is a context.Context object built around the +// returned Span. +// +// Example usage: +// +// SomeFunction(ctx context.Context, ...) { +// sp, ctx := opentracing.StartSpanFromContext(ctx, "SomeFunction") +// defer sp.Finish() +// ... +// } +func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context) { + return StartSpanFromContextWithTracer(ctx, GlobalTracer(), operationName, opts...) +} + +// StartSpanFromContextWithTracer starts and returns a span with `operationName` +// using a span found within the context as a ChildOfRef. If that doesn't exist +// it creates a root span. It also returns a context.Context object built +// around the returned span. +// +// It's behavior is identical to StartSpanFromContext except that it takes an explicit +// tracer as opposed to using the global tracer. +func StartSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context) { + if parentSpan := SpanFromContext(ctx); parentSpan != nil { + opts = append(opts, ChildOf(parentSpan.Context())) + } + span := tracer.StartSpan(operationName, opts...) + return span, ContextWithSpan(ctx, span) +} diff --git a/vendor/github.com/opentracing/opentracing-go/log/field.go b/vendor/github.com/opentracing/opentracing-go/log/field.go new file mode 100644 index 000000000..50feea341 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/log/field.go @@ -0,0 +1,269 @@ +package log + +import ( + "fmt" + "math" +) + +type fieldType int + +const ( + stringType fieldType = iota + boolType + intType + int32Type + uint32Type + int64Type + uint64Type + float32Type + float64Type + errorType + objectType + lazyLoggerType + noopType +) + +// Field instances are constructed via LogBool, LogString, and so on. +// Tracing implementations may then handle them via the Field.Marshal +// method. +// +// "heavily influenced by" (i.e., partially stolen from) +// https://github.com/uber-go/zap +type Field struct { + key string + fieldType fieldType + numericVal int64 + stringVal string + interfaceVal interface{} +} + +// String adds a string-valued key:value pair to a Span.LogFields() record +func String(key, val string) Field { + return Field{ + key: key, + fieldType: stringType, + stringVal: val, + } +} + +// Bool adds a bool-valued key:value pair to a Span.LogFields() record +func Bool(key string, val bool) Field { + var numericVal int64 + if val { + numericVal = 1 + } + return Field{ + key: key, + fieldType: boolType, + numericVal: numericVal, + } +} + +// Int adds an int-valued key:value pair to a Span.LogFields() record +func Int(key string, val int) Field { + return Field{ + key: key, + fieldType: intType, + numericVal: int64(val), + } +} + +// Int32 adds an int32-valued key:value pair to a Span.LogFields() record +func Int32(key string, val int32) Field { + return Field{ + key: key, + fieldType: int32Type, + numericVal: int64(val), + } +} + +// Int64 adds an int64-valued key:value pair to a Span.LogFields() record +func Int64(key string, val int64) Field { + return Field{ + key: key, + fieldType: int64Type, + numericVal: val, + } +} + +// Uint32 adds a uint32-valued key:value pair to a Span.LogFields() record +func Uint32(key string, val uint32) Field { + return Field{ + key: key, + fieldType: uint32Type, + numericVal: int64(val), + } +} + +// Uint64 adds a uint64-valued key:value pair to a Span.LogFields() record +func Uint64(key string, val uint64) Field { + return Field{ + key: key, + fieldType: uint64Type, + numericVal: int64(val), + } +} + +// Float32 adds a float32-valued key:value pair to a Span.LogFields() record +func Float32(key string, val float32) Field { + return Field{ + key: key, + fieldType: float32Type, + numericVal: int64(math.Float32bits(val)), + } +} + +// Float64 adds a float64-valued key:value pair to a Span.LogFields() record +func Float64(key string, val float64) Field { + return Field{ + key: key, + fieldType: float64Type, + numericVal: int64(math.Float64bits(val)), + } +} + +// Error adds an error with the key "error" to a Span.LogFields() record +func Error(err error) Field { + return Field{ + key: "error", + fieldType: errorType, + interfaceVal: err, + } +} + +// Object adds an object-valued key:value pair to a Span.LogFields() record +func Object(key string, obj interface{}) Field { + return Field{ + key: key, + fieldType: objectType, + interfaceVal: obj, + } +} + +// LazyLogger allows for user-defined, late-bound logging of arbitrary data +type LazyLogger func(fv Encoder) + +// Lazy adds a LazyLogger to a Span.LogFields() record; the tracing +// implementation will call the LazyLogger function at an indefinite time in +// the future (after Lazy() returns). +func Lazy(ll LazyLogger) Field { + return Field{ + fieldType: lazyLoggerType, + interfaceVal: ll, + } +} + +// Noop creates a no-op log field that should be ignored by the tracer. +// It can be used to capture optional fields, for example those that should +// only be logged in non-production environment: +// +// func customerField(order *Order) log.Field { +// if os.Getenv("ENVIRONMENT") == "dev" { +// return log.String("customer", order.Customer.ID) +// } +// return log.Noop() +// } +// +// span.LogFields(log.String("event", "purchase"), customerField(order)) +// +func Noop() Field { + return Field{ + fieldType: noopType, + } +} + +// Encoder allows access to the contents of a Field (via a call to +// Field.Marshal). +// +// Tracer implementations typically provide an implementation of Encoder; +// OpenTracing callers typically do not need to concern themselves with it. +type Encoder interface { + EmitString(key, value string) + EmitBool(key string, value bool) + EmitInt(key string, value int) + EmitInt32(key string, value int32) + EmitInt64(key string, value int64) + EmitUint32(key string, value uint32) + EmitUint64(key string, value uint64) + EmitFloat32(key string, value float32) + EmitFloat64(key string, value float64) + EmitObject(key string, value interface{}) + EmitLazyLogger(value LazyLogger) +} + +// Marshal passes a Field instance through to the appropriate +// field-type-specific method of an Encoder. +func (lf Field) Marshal(visitor Encoder) { + switch lf.fieldType { + case stringType: + visitor.EmitString(lf.key, lf.stringVal) + case boolType: + visitor.EmitBool(lf.key, lf.numericVal != 0) + case intType: + visitor.EmitInt(lf.key, int(lf.numericVal)) + case int32Type: + visitor.EmitInt32(lf.key, int32(lf.numericVal)) + case int64Type: + visitor.EmitInt64(lf.key, int64(lf.numericVal)) + case uint32Type: + visitor.EmitUint32(lf.key, uint32(lf.numericVal)) + case uint64Type: + visitor.EmitUint64(lf.key, uint64(lf.numericVal)) + case float32Type: + visitor.EmitFloat32(lf.key, math.Float32frombits(uint32(lf.numericVal))) + case float64Type: + visitor.EmitFloat64(lf.key, math.Float64frombits(uint64(lf.numericVal))) + case errorType: + if err, ok := lf.interfaceVal.(error); ok { + visitor.EmitString(lf.key, err.Error()) + } else { + visitor.EmitString(lf.key, "") + } + case objectType: + visitor.EmitObject(lf.key, lf.interfaceVal) + case lazyLoggerType: + visitor.EmitLazyLogger(lf.interfaceVal.(LazyLogger)) + case noopType: + // intentionally left blank + } +} + +// Key returns the field's key. +func (lf Field) Key() string { + return lf.key +} + +// Value returns the field's value as interface{}. +func (lf Field) Value() interface{} { + switch lf.fieldType { + case stringType: + return lf.stringVal + case boolType: + return lf.numericVal != 0 + case intType: + return int(lf.numericVal) + case int32Type: + return int32(lf.numericVal) + case int64Type: + return int64(lf.numericVal) + case uint32Type: + return uint32(lf.numericVal) + case uint64Type: + return uint64(lf.numericVal) + case float32Type: + return math.Float32frombits(uint32(lf.numericVal)) + case float64Type: + return math.Float64frombits(uint64(lf.numericVal)) + case errorType, objectType, lazyLoggerType: + return lf.interfaceVal + case noopType: + return nil + default: + return nil + } +} + +// String returns a string representation of the key and value. +func (lf Field) String() string { + return fmt.Sprint(lf.key, ":", lf.Value()) +} diff --git a/vendor/github.com/opentracing/opentracing-go/log/util.go b/vendor/github.com/opentracing/opentracing-go/log/util.go new file mode 100644 index 000000000..3832feb5c --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/log/util.go @@ -0,0 +1,54 @@ +package log + +import "fmt" + +// InterleavedKVToFields converts keyValues a la Span.LogKV() to a Field slice +// a la Span.LogFields(). +func InterleavedKVToFields(keyValues ...interface{}) ([]Field, error) { + if len(keyValues)%2 != 0 { + return nil, fmt.Errorf("non-even keyValues len: %d", len(keyValues)) + } + fields := make([]Field, len(keyValues)/2) + for i := 0; i*2 < len(keyValues); i++ { + key, ok := keyValues[i*2].(string) + if !ok { + return nil, fmt.Errorf( + "non-string key (pair #%d): %T", + i, keyValues[i*2]) + } + switch typedVal := keyValues[i*2+1].(type) { + case bool: + fields[i] = Bool(key, typedVal) + case string: + fields[i] = String(key, typedVal) + case int: + fields[i] = Int(key, typedVal) + case int8: + fields[i] = Int32(key, int32(typedVal)) + case int16: + fields[i] = Int32(key, int32(typedVal)) + case int32: + fields[i] = Int32(key, typedVal) + case int64: + fields[i] = Int64(key, typedVal) + case uint: + fields[i] = Uint64(key, uint64(typedVal)) + case uint64: + fields[i] = Uint64(key, typedVal) + case uint8: + fields[i] = Uint32(key, uint32(typedVal)) + case uint16: + fields[i] = Uint32(key, uint32(typedVal)) + case uint32: + fields[i] = Uint32(key, typedVal) + case float32: + fields[i] = Float32(key, typedVal) + case float64: + fields[i] = Float64(key, typedVal) + default: + // When in doubt, coerce to a string + fields[i] = String(key, fmt.Sprint(typedVal)) + } + } + return fields, nil +} diff --git a/vendor/github.com/opentracing/opentracing-go/noop.go b/vendor/github.com/opentracing/opentracing-go/noop.go new file mode 100644 index 000000000..0d32f692c --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/noop.go @@ -0,0 +1,64 @@ +package opentracing + +import "github.com/opentracing/opentracing-go/log" + +// A NoopTracer is a trivial, minimum overhead implementation of Tracer +// for which all operations are no-ops. +// +// The primary use of this implementation is in libraries, such as RPC +// frameworks, that make tracing an optional feature controlled by the +// end user. A no-op implementation allows said libraries to use it +// as the default Tracer and to write instrumentation that does +// not need to keep checking if the tracer instance is nil. +// +// For the same reason, the NoopTracer is the default "global" tracer +// (see GlobalTracer and SetGlobalTracer functions). +// +// WARNING: NoopTracer does not support baggage propagation. +type NoopTracer struct{} + +type noopSpan struct{} +type noopSpanContext struct{} + +var ( + defaultNoopSpanContext = noopSpanContext{} + defaultNoopSpan = noopSpan{} + defaultNoopTracer = NoopTracer{} +) + +const ( + emptyString = "" +) + +// noopSpanContext: +func (n noopSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {} + +// noopSpan: +func (n noopSpan) Context() SpanContext { return defaultNoopSpanContext } +func (n noopSpan) SetBaggageItem(key, val string) Span { return defaultNoopSpan } +func (n noopSpan) BaggageItem(key string) string { return emptyString } +func (n noopSpan) SetTag(key string, value interface{}) Span { return n } +func (n noopSpan) LogFields(fields ...log.Field) {} +func (n noopSpan) LogKV(keyVals ...interface{}) {} +func (n noopSpan) Finish() {} +func (n noopSpan) FinishWithOptions(opts FinishOptions) {} +func (n noopSpan) SetOperationName(operationName string) Span { return n } +func (n noopSpan) Tracer() Tracer { return defaultNoopTracer } +func (n noopSpan) LogEvent(event string) {} +func (n noopSpan) LogEventWithPayload(event string, payload interface{}) {} +func (n noopSpan) Log(data LogData) {} + +// StartSpan belongs to the Tracer interface. +func (n NoopTracer) StartSpan(operationName string, opts ...StartSpanOption) Span { + return defaultNoopSpan +} + +// Inject belongs to the Tracer interface. +func (n NoopTracer) Inject(sp SpanContext, format interface{}, carrier interface{}) error { + return nil +} + +// Extract belongs to the Tracer interface. +func (n NoopTracer) Extract(format interface{}, carrier interface{}) (SpanContext, error) { + return nil, ErrSpanContextNotFound +} diff --git a/vendor/github.com/opentracing/opentracing-go/propagation.go b/vendor/github.com/opentracing/opentracing-go/propagation.go new file mode 100644 index 000000000..b0c275eb0 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/propagation.go @@ -0,0 +1,176 @@ +package opentracing + +import ( + "errors" + "net/http" +) + +/////////////////////////////////////////////////////////////////////////////// +// CORE PROPAGATION INTERFACES: +/////////////////////////////////////////////////////////////////////////////// + +var ( + // ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or + // Tracer.Extract() is not recognized by the Tracer implementation. + ErrUnsupportedFormat = errors.New("opentracing: Unknown or unsupported Inject/Extract format") + + // ErrSpanContextNotFound occurs when the `carrier` passed to + // Tracer.Extract() is valid and uncorrupted but has insufficient + // information to extract a SpanContext. + ErrSpanContextNotFound = errors.New("opentracing: SpanContext not found in Extract carrier") + + // ErrInvalidSpanContext errors occur when Tracer.Inject() is asked to + // operate on a SpanContext which it is not prepared to handle (for + // example, since it was created by a different tracer implementation). + ErrInvalidSpanContext = errors.New("opentracing: SpanContext type incompatible with tracer") + + // ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Extract() + // implementations expect a different type of `carrier` than they are + // given. + ErrInvalidCarrier = errors.New("opentracing: Invalid Inject/Extract carrier") + + // ErrSpanContextCorrupted occurs when the `carrier` passed to + // Tracer.Extract() is of the expected type but is corrupted. + ErrSpanContextCorrupted = errors.New("opentracing: SpanContext data corrupted in Extract carrier") +) + +/////////////////////////////////////////////////////////////////////////////// +// BUILTIN PROPAGATION FORMATS: +/////////////////////////////////////////////////////////////////////////////// + +// BuiltinFormat is used to demarcate the values within package `opentracing` +// that are intended for use with the Tracer.Inject() and Tracer.Extract() +// methods. +type BuiltinFormat byte + +const ( + // Binary represents SpanContexts as opaque binary data. + // + // For Tracer.Inject(): the carrier must be an `io.Writer`. + // + // For Tracer.Extract(): the carrier must be an `io.Reader`. + Binary BuiltinFormat = iota + + // TextMap represents SpanContexts as key:value string pairs. + // + // Unlike HTTPHeaders, the TextMap format does not restrict the key or + // value character sets in any way. + // + // For Tracer.Inject(): the carrier must be a `TextMapWriter`. + // + // For Tracer.Extract(): the carrier must be a `TextMapReader`. + TextMap + + // HTTPHeaders represents SpanContexts as HTTP header string pairs. + // + // Unlike TextMap, the HTTPHeaders format requires that the keys and values + // be valid as HTTP headers as-is (i.e., character casing may be unstable + // and special characters are disallowed in keys, values should be + // URL-escaped, etc). + // + // For Tracer.Inject(): the carrier must be a `TextMapWriter`. + // + // For Tracer.Extract(): the carrier must be a `TextMapReader`. + // + // See HTTPHeadersCarrier for an implementation of both TextMapWriter + // and TextMapReader that defers to an http.Header instance for storage. + // For example, Inject(): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // err := span.Tracer().Inject( + // span.Context(), opentracing.HTTPHeaders, carrier) + // + // Or Extract(): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // clientContext, err := tracer.Extract( + // opentracing.HTTPHeaders, carrier) + // + HTTPHeaders +) + +// TextMapWriter is the Inject() carrier for the TextMap builtin format. With +// it, the caller can encode a SpanContext for propagation as entries in a map +// of unicode strings. +type TextMapWriter interface { + // Set a key:value pair to the carrier. Multiple calls to Set() for the + // same key leads to undefined behavior. + // + // NOTE: The backing store for the TextMapWriter may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + Set(key, val string) +} + +// TextMapReader is the Extract() carrier for the TextMap builtin format. With it, +// the caller can decode a propagated SpanContext as entries in a map of +// unicode strings. +type TextMapReader interface { + // ForeachKey returns TextMap contents via repeated calls to the `handler` + // function. If any call to `handler` returns a non-nil error, ForeachKey + // terminates and returns that error. + // + // NOTE: The backing store for the TextMapReader may contain data unrelated + // to SpanContext. As such, Inject() and Extract() implementations that + // call the TextMapWriter and TextMapReader interfaces must agree on a + // prefix or other convention to distinguish their own key:value pairs. + // + // The "foreach" callback pattern reduces unnecessary copying in some cases + // and also allows implementations to hold locks while the map is read. + ForeachKey(handler func(key, val string) error) error +} + +// TextMapCarrier allows the use of regular map[string]string +// as both TextMapWriter and TextMapReader. +type TextMapCarrier map[string]string + +// ForeachKey conforms to the TextMapReader interface. +func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error { + for k, v := range c { + if err := handler(k, v); err != nil { + return err + } + } + return nil +} + +// Set implements Set() of opentracing.TextMapWriter +func (c TextMapCarrier) Set(key, val string) { + c[key] = val +} + +// HTTPHeadersCarrier satisfies both TextMapWriter and TextMapReader. +// +// Example usage for server side: +// +// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) +// clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier) +// +// Example usage for client side: +// +// carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) +// err := tracer.Inject( +// span.Context(), +// opentracing.HTTPHeaders, +// carrier) +// +type HTTPHeadersCarrier http.Header + +// Set conforms to the TextMapWriter interface. +func (c HTTPHeadersCarrier) Set(key, val string) { + h := http.Header(c) + h.Set(key, val) +} + +// ForeachKey conforms to the TextMapReader interface. +func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error { + for k, vals := range c { + for _, v := range vals { + if err := handler(k, v); err != nil { + return err + } + } + } + return nil +} diff --git a/vendor/github.com/opentracing/opentracing-go/span.go b/vendor/github.com/opentracing/opentracing-go/span.go new file mode 100644 index 000000000..0d3fb5341 --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/span.go @@ -0,0 +1,189 @@ +package opentracing + +import ( + "time" + + "github.com/opentracing/opentracing-go/log" +) + +// SpanContext represents Span state that must propagate to descendant Spans and across process +// boundaries (e.g., a tuple). +type SpanContext interface { + // ForeachBaggageItem grants access to all baggage items stored in the + // SpanContext. + // The handler function will be called for each baggage key/value pair. + // The ordering of items is not guaranteed. + // + // The bool return value indicates if the handler wants to continue iterating + // through the rest of the baggage items; for example if the handler is trying to + // find some baggage item by pattern matching the name, it can return false + // as soon as the item is found to stop further iterations. + ForeachBaggageItem(handler func(k, v string) bool) +} + +// Span represents an active, un-finished span in the OpenTracing system. +// +// Spans are created by the Tracer interface. +type Span interface { + // Sets the end timestamp and finalizes Span state. + // + // With the exception of calls to Context() (which are always allowed), + // Finish() must be the last call made to any span instance, and to do + // otherwise leads to undefined behavior. + Finish() + // FinishWithOptions is like Finish() but with explicit control over + // timestamps and log data. + FinishWithOptions(opts FinishOptions) + + // Context() yields the SpanContext for this Span. Note that the return + // value of Context() is still valid after a call to Span.Finish(), as is + // a call to Span.Context() after a call to Span.Finish(). + Context() SpanContext + + // Sets or changes the operation name. + // + // Returns a reference to this Span for chaining. + SetOperationName(operationName string) Span + + // Adds a tag to the span. + // + // If there is a pre-existing tag set for `key`, it is overwritten. + // + // Tag values can be numeric types, strings, or bools. The behavior of + // other tag value types is undefined at the OpenTracing level. If a + // tracing system does not know how to handle a particular value type, it + // may ignore the tag, but shall not panic. + // + // Returns a reference to this Span for chaining. + SetTag(key string, value interface{}) Span + + // LogFields is an efficient and type-checked way to record key:value + // logging data about a Span, though the programming interface is a little + // more verbose than LogKV(). Here's an example: + // + // span.LogFields( + // log.String("event", "soft error"), + // log.String("type", "cache timeout"), + // log.Int("waited.millis", 1500)) + // + // Also see Span.FinishWithOptions() and FinishOptions.BulkLogData. + LogFields(fields ...log.Field) + + // LogKV is a concise, readable way to record key:value logging data about + // a Span, though unfortunately this also makes it less efficient and less + // type-safe than LogFields(). Here's an example: + // + // span.LogKV( + // "event", "soft error", + // "type", "cache timeout", + // "waited.millis", 1500) + // + // For LogKV (as opposed to LogFields()), the parameters must appear as + // key-value pairs, like + // + // span.LogKV(key1, val1, key2, val2, key3, val3, ...) + // + // The keys must all be strings. The values may be strings, numeric types, + // bools, Go error instances, or arbitrary structs. + // + // (Note to implementors: consider the log.InterleavedKVToFields() helper) + LogKV(alternatingKeyValues ...interface{}) + + // SetBaggageItem sets a key:value pair on this Span and its SpanContext + // that also propagates to descendants of this Span. + // + // SetBaggageItem() enables powerful functionality given a full-stack + // opentracing integration (e.g., arbitrary application data from a mobile + // app can make it, transparently, all the way into the depths of a storage + // system), and with it some powerful costs: use this feature with care. + // + // IMPORTANT NOTE #1: SetBaggageItem() will only propagate baggage items to + // *future* causal descendants of the associated Span. + // + // IMPORTANT NOTE #2: Use this thoughtfully and with care. Every key and + // value is copied into every local *and remote* child of the associated + // Span, and that can add up to a lot of network and cpu overhead. + // + // Returns a reference to this Span for chaining. + SetBaggageItem(restrictedKey, value string) Span + + // Gets the value for a baggage item given its key. Returns the empty string + // if the value isn't found in this Span. + BaggageItem(restrictedKey string) string + + // Provides access to the Tracer that created this Span. + Tracer() Tracer + + // Deprecated: use LogFields or LogKV + LogEvent(event string) + // Deprecated: use LogFields or LogKV + LogEventWithPayload(event string, payload interface{}) + // Deprecated: use LogFields or LogKV + Log(data LogData) +} + +// LogRecord is data associated with a single Span log. Every LogRecord +// instance must specify at least one Field. +type LogRecord struct { + Timestamp time.Time + Fields []log.Field +} + +// FinishOptions allows Span.FinishWithOptions callers to override the finish +// timestamp and provide log data via a bulk interface. +type FinishOptions struct { + // FinishTime overrides the Span's finish time, or implicitly becomes + // time.Now() if FinishTime.IsZero(). + // + // FinishTime must resolve to a timestamp that's >= the Span's StartTime + // (per StartSpanOptions). + FinishTime time.Time + + // LogRecords allows the caller to specify the contents of many LogFields() + // calls with a single slice. May be nil. + // + // None of the LogRecord.Timestamp values may be .IsZero() (i.e., they must + // be set explicitly). Also, they must be >= the Span's start timestamp and + // <= the FinishTime (or time.Now() if FinishTime.IsZero()). Otherwise the + // behavior of FinishWithOptions() is undefined. + // + // If specified, the caller hands off ownership of LogRecords at + // FinishWithOptions() invocation time. + // + // If specified, the (deprecated) BulkLogData must be nil or empty. + LogRecords []LogRecord + + // BulkLogData is DEPRECATED. + BulkLogData []LogData +} + +// LogData is DEPRECATED +type LogData struct { + Timestamp time.Time + Event string + Payload interface{} +} + +// ToLogRecord converts a deprecated LogData to a non-deprecated LogRecord +func (ld *LogData) ToLogRecord() LogRecord { + var literalTimestamp time.Time + if ld.Timestamp.IsZero() { + literalTimestamp = time.Now() + } else { + literalTimestamp = ld.Timestamp + } + rval := LogRecord{ + Timestamp: literalTimestamp, + } + if ld.Payload == nil { + rval.Fields = []log.Field{ + log.String("event", ld.Event), + } + } else { + rval.Fields = []log.Field{ + log.String("event", ld.Event), + log.Object("payload", ld.Payload), + } + } + return rval +} diff --git a/vendor/github.com/opentracing/opentracing-go/tracer.go b/vendor/github.com/opentracing/opentracing-go/tracer.go new file mode 100644 index 000000000..715f0cedf --- /dev/null +++ b/vendor/github.com/opentracing/opentracing-go/tracer.go @@ -0,0 +1,304 @@ +package opentracing + +import "time" + +// Tracer is a simple, thin interface for Span creation and SpanContext +// propagation. +type Tracer interface { + + // Create, start, and return a new Span with the given `operationName` and + // incorporate the given StartSpanOption `opts`. (Note that `opts` borrows + // from the "functional options" pattern, per + // http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) + // + // A Span with no SpanReference options (e.g., opentracing.ChildOf() or + // opentracing.FollowsFrom()) becomes the root of its own trace. + // + // Examples: + // + // var tracer opentracing.Tracer = ... + // + // // The root-span case: + // sp := tracer.StartSpan("GetFeed") + // + // // The vanilla child span case: + // sp := tracer.StartSpan( + // "GetFeed", + // opentracing.ChildOf(parentSpan.Context())) + // + // // All the bells and whistles: + // sp := tracer.StartSpan( + // "GetFeed", + // opentracing.ChildOf(parentSpan.Context()), + // opentracing.Tag{"user_agent", loggedReq.UserAgent}, + // opentracing.StartTime(loggedReq.Timestamp), + // ) + // + StartSpan(operationName string, opts ...StartSpanOption) Span + + // Inject() takes the `sm` SpanContext instance and injects it for + // propagation within `carrier`. The actual type of `carrier` depends on + // the value of `format`. + // + // OpenTracing defines a common set of `format` values (see BuiltinFormat), + // and each has an expected carrier type. + // + // Other packages may declare their own `format` values, much like the keys + // used by `context.Context` (see https://godoc.org/context#WithValue). + // + // Example usage (sans error handling): + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // err := tracer.Inject( + // span.Context(), + // opentracing.HTTPHeaders, + // carrier) + // + // NOTE: All opentracing.Tracer implementations MUST support all + // BuiltinFormats. + // + // Implementations may return opentracing.ErrUnsupportedFormat if `format` + // is not supported by (or not known by) the implementation. + // + // Implementations may return opentracing.ErrInvalidCarrier or any other + // implementation-specific error if the format is supported but injection + // fails anyway. + // + // See Tracer.Extract(). + Inject(sm SpanContext, format interface{}, carrier interface{}) error + + // Extract() returns a SpanContext instance given `format` and `carrier`. + // + // OpenTracing defines a common set of `format` values (see BuiltinFormat), + // and each has an expected carrier type. + // + // Other packages may declare their own `format` values, much like the keys + // used by `context.Context` (see + // https://godoc.org/golang.org/x/net/context#WithValue). + // + // Example usage (with StartSpan): + // + // + // carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) + // clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier) + // + // // ... assuming the ultimate goal here is to resume the trace with a + // // server-side Span: + // var serverSpan opentracing.Span + // if err == nil { + // span = tracer.StartSpan( + // rpcMethodName, ext.RPCServerOption(clientContext)) + // } else { + // span = tracer.StartSpan(rpcMethodName) + // } + // + // + // NOTE: All opentracing.Tracer implementations MUST support all + // BuiltinFormats. + // + // Return values: + // - A successful Extract returns a SpanContext instance and a nil error + // - If there was simply no SpanContext to extract in `carrier`, Extract() + // returns (nil, opentracing.ErrSpanContextNotFound) + // - If `format` is unsupported or unrecognized, Extract() returns (nil, + // opentracing.ErrUnsupportedFormat) + // - If there are more fundamental problems with the `carrier` object, + // Extract() may return opentracing.ErrInvalidCarrier, + // opentracing.ErrSpanContextCorrupted, or implementation-specific + // errors. + // + // See Tracer.Inject(). + Extract(format interface{}, carrier interface{}) (SpanContext, error) +} + +// StartSpanOptions allows Tracer.StartSpan() callers and implementors a +// mechanism to override the start timestamp, specify Span References, and make +// a single Tag or multiple Tags available at Span start time. +// +// StartSpan() callers should look at the StartSpanOption interface and +// implementations available in this package. +// +// Tracer implementations can convert a slice of `StartSpanOption` instances +// into a `StartSpanOptions` struct like so: +// +// func StartSpan(opName string, opts ...opentracing.StartSpanOption) { +// sso := opentracing.StartSpanOptions{} +// for _, o := range opts { +// o.Apply(&sso) +// } +// ... +// } +// +type StartSpanOptions struct { + // Zero or more causal references to other Spans (via their SpanContext). + // If empty, start a "root" Span (i.e., start a new trace). + References []SpanReference + + // StartTime overrides the Span's start time, or implicitly becomes + // time.Now() if StartTime.IsZero(). + StartTime time.Time + + // Tags may have zero or more entries; the restrictions on map values are + // identical to those for Span.SetTag(). May be nil. + // + // If specified, the caller hands off ownership of Tags at + // StartSpan() invocation time. + Tags map[string]interface{} +} + +// StartSpanOption instances (zero or more) may be passed to Tracer.StartSpan. +// +// StartSpanOption borrows from the "functional options" pattern, per +// http://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +type StartSpanOption interface { + Apply(*StartSpanOptions) +} + +// SpanReferenceType is an enum type describing different categories of +// relationships between two Spans. If Span-2 refers to Span-1, the +// SpanReferenceType describes Span-1 from Span-2's perspective. For example, +// ChildOfRef means that Span-1 created Span-2. +// +// NOTE: Span-1 and Span-2 do *not* necessarily depend on each other for +// completion; e.g., Span-2 may be part of a background job enqueued by Span-1, +// or Span-2 may be sitting in a distributed queue behind Span-1. +type SpanReferenceType int + +const ( + // ChildOfRef refers to a parent Span that caused *and* somehow depends + // upon the new child Span. Often (but not always), the parent Span cannot + // finish until the child Span does. + // + // An timing diagram for a ChildOfRef that's blocked on the new Span: + // + // [-Parent Span---------] + // [-Child Span----] + // + // See http://opentracing.io/spec/ + // + // See opentracing.ChildOf() + ChildOfRef SpanReferenceType = iota + + // FollowsFromRef refers to a parent Span that does not depend in any way + // on the result of the new child Span. For instance, one might use + // FollowsFromRefs to describe pipeline stages separated by queues, + // or a fire-and-forget cache insert at the tail end of a web request. + // + // A FollowsFromRef Span is part of the same logical trace as the new Span: + // i.e., the new Span is somehow caused by the work of its FollowsFromRef. + // + // All of the following could be valid timing diagrams for children that + // "FollowFrom" a parent. + // + // [-Parent Span-] [-Child Span-] + // + // + // [-Parent Span--] + // [-Child Span-] + // + // + // [-Parent Span-] + // [-Child Span-] + // + // See http://opentracing.io/spec/ + // + // See opentracing.FollowsFrom() + FollowsFromRef +) + +// SpanReference is a StartSpanOption that pairs a SpanReferenceType and a +// referenced SpanContext. See the SpanReferenceType documentation for +// supported relationships. If SpanReference is created with +// ReferencedContext==nil, it has no effect. Thus it allows for a more concise +// syntax for starting spans: +// +// sc, _ := tracer.Extract(someFormat, someCarrier) +// span := tracer.StartSpan("operation", opentracing.ChildOf(sc)) +// +// The `ChildOf(sc)` option above will not panic if sc == nil, it will just +// not add the parent span reference to the options. +type SpanReference struct { + Type SpanReferenceType + ReferencedContext SpanContext +} + +// Apply satisfies the StartSpanOption interface. +func (r SpanReference) Apply(o *StartSpanOptions) { + if r.ReferencedContext != nil { + o.References = append(o.References, r) + } +} + +// ChildOf returns a StartSpanOption pointing to a dependent parent span. +// If sc == nil, the option has no effect. +// +// See ChildOfRef, SpanReference +func ChildOf(sc SpanContext) SpanReference { + return SpanReference{ + Type: ChildOfRef, + ReferencedContext: sc, + } +} + +// FollowsFrom returns a StartSpanOption pointing to a parent Span that caused +// the child Span but does not directly depend on its result in any way. +// If sc == nil, the option has no effect. +// +// See FollowsFromRef, SpanReference +func FollowsFrom(sc SpanContext) SpanReference { + return SpanReference{ + Type: FollowsFromRef, + ReferencedContext: sc, + } +} + +// StartTime is a StartSpanOption that sets an explicit start timestamp for the +// new Span. +type StartTime time.Time + +// Apply satisfies the StartSpanOption interface. +func (t StartTime) Apply(o *StartSpanOptions) { + o.StartTime = time.Time(t) +} + +// Tags are a generic map from an arbitrary string key to an opaque value type. +// The underlying tracing system is responsible for interpreting and +// serializing the values. +type Tags map[string]interface{} + +// Apply satisfies the StartSpanOption interface. +func (t Tags) Apply(o *StartSpanOptions) { + if o.Tags == nil { + o.Tags = make(map[string]interface{}) + } + for k, v := range t { + o.Tags[k] = v + } +} + +// Tag may be passed as a StartSpanOption to add a tag to new spans, +// or its Set method may be used to apply the tag to an existing Span, +// for example: +// +// tracer.StartSpan("opName", Tag{"Key", value}) +// +// or +// +// Tag{"key", value}.Set(span) +type Tag struct { + Key string + Value interface{} +} + +// Apply satisfies the StartSpanOption interface. +func (t Tag) Apply(o *StartSpanOptions) { + if o.Tags == nil { + o.Tags = make(map[string]interface{}) + } + o.Tags[t.Key] = t.Value +} + +// Set applies the tag to an existing Span. +func (t Tag) Set(s Span) { + s.SetTag(t.Key, t.Value) +} diff --git a/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS b/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS new file mode 100644 index 000000000..2b16e9974 --- /dev/null +++ b/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS @@ -0,0 +1,9 @@ +This is a list of people who have contributed code to go-cache. They, or their +employers, are the copyright holders of the contributed code. Contributed code +is subject to the license restrictions listed in LICENSE (as they were when the +code was contributed.) + +Dustin Sallings +Jason Mooberry +Sergey Shepelev +Alex Edwards diff --git a/vendor/github.com/patrickmn/go-cache/LICENSE b/vendor/github.com/patrickmn/go-cache/LICENSE new file mode 100644 index 000000000..db9903c75 --- /dev/null +++ b/vendor/github.com/patrickmn/go-cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-2017 Patrick Mylund Nielsen and the go-cache contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/patrickmn/go-cache/README.md b/vendor/github.com/patrickmn/go-cache/README.md new file mode 100644 index 000000000..c5789cc66 --- /dev/null +++ b/vendor/github.com/patrickmn/go-cache/README.md @@ -0,0 +1,83 @@ +# go-cache + +go-cache is an in-memory key:value store/cache similar to memcached that is +suitable for applications running on a single machine. Its major advantage is +that, being essentially a thread-safe `map[string]interface{}` with expiration +times, it doesn't need to serialize or transmit its contents over the network. + +Any object can be stored, for a given duration or forever, and the cache can be +safely used by multiple goroutines. + +Although go-cache isn't meant to be used as a persistent datastore, the entire +cache can be saved to and loaded from a file (using `c.Items()` to retrieve the +items map to serialize, and `NewFrom()` to create a cache from a deserialized +one) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats.) + +### Installation + +`go get github.com/patrickmn/go-cache` + +### Usage + +```go +import ( + "fmt" + "github.com/patrickmn/go-cache" + "time" +) + +func main() { + // Create a cache with a default expiration time of 5 minutes, and which + // purges expired items every 10 minutes + c := cache.New(5*time.Minute, 10*time.Minute) + + // Set the value of the key "foo" to "bar", with the default expiration time + c.Set("foo", "bar", cache.DefaultExpiration) + + // Set the value of the key "baz" to 42, with no expiration time + // (the item won't be removed until it is re-set, or removed using + // c.Delete("baz") + c.Set("baz", 42, cache.NoExpiration) + + // Get the string associated with the key "foo" from the cache + foo, found := c.Get("foo") + if found { + fmt.Println(foo) + } + + // Since Go is statically typed, and cache values can be anything, type + // assertion is needed when values are being passed to functions that don't + // take arbitrary types, (i.e. interface{}). The simplest way to do this for + // values which will only be used once--e.g. for passing to another + // function--is: + foo, found := c.Get("foo") + if found { + MyFunction(foo.(string)) + } + + // This gets tedious if the value is used several times in the same function. + // You might do either of the following instead: + if x, found := c.Get("foo"); found { + foo := x.(string) + // ... + } + // or + var foo string + if x, found := c.Get("foo"); found { + foo = x.(string) + } + // ... + // foo can then be passed around freely as a string + + // Want performance? Store pointers! + c.Set("foo", &MyStruct, cache.DefaultExpiration) + if x, found := c.Get("foo"); found { + foo := x.(*MyStruct) + // ... + } +} +``` + +### Reference + +`godoc` or [http://godoc.org/github.com/patrickmn/go-cache](http://godoc.org/github.com/patrickmn/go-cache) diff --git a/vendor/github.com/patrickmn/go-cache/cache.go b/vendor/github.com/patrickmn/go-cache/cache.go new file mode 100644 index 000000000..db88d2f2c --- /dev/null +++ b/vendor/github.com/patrickmn/go-cache/cache.go @@ -0,0 +1,1161 @@ +package cache + +import ( + "encoding/gob" + "fmt" + "io" + "os" + "runtime" + "sync" + "time" +) + +type Item struct { + Object interface{} + Expiration int64 +} + +// Returns true if the item has expired. +func (item Item) Expired() bool { + if item.Expiration == 0 { + return false + } + return time.Now().UnixNano() > item.Expiration +} + +const ( + // For use with functions that take an expiration time. + NoExpiration time.Duration = -1 + // For use with functions that take an expiration time. Equivalent to + // passing in the same expiration duration as was given to New() or + // NewFrom() when the cache was created (e.g. 5 minutes.) + DefaultExpiration time.Duration = 0 +) + +type Cache struct { + *cache + // If this is confusing, see the comment at the bottom of New() +} + +type cache struct { + defaultExpiration time.Duration + items map[string]Item + mu sync.RWMutex + onEvicted func(string, interface{}) + janitor *janitor +} + +// Add an item to the cache, replacing any existing item. If the duration is 0 +// (DefaultExpiration), the cache's default expiration time is used. If it is -1 +// (NoExpiration), the item never expires. +func (c *cache) Set(k string, x interface{}, d time.Duration) { + // "Inlining" of set + var e int64 + if d == DefaultExpiration { + d = c.defaultExpiration + } + if d > 0 { + e = time.Now().Add(d).UnixNano() + } + c.mu.Lock() + c.items[k] = Item{ + Object: x, + Expiration: e, + } + // TODO: Calls to mu.Unlock are currently not deferred because defer + // adds ~200 ns (as of go1.) + c.mu.Unlock() +} + +func (c *cache) set(k string, x interface{}, d time.Duration) { + var e int64 + if d == DefaultExpiration { + d = c.defaultExpiration + } + if d > 0 { + e = time.Now().Add(d).UnixNano() + } + c.items[k] = Item{ + Object: x, + Expiration: e, + } +} + +// Add an item to the cache, replacing any existing item, using the default +// expiration. +func (c *cache) SetDefault(k string, x interface{}) { + c.Set(k, x, DefaultExpiration) +} + +// Add an item to the cache only if an item doesn't already exist for the given +// key, or if the existing item has expired. Returns an error otherwise. +func (c *cache) Add(k string, x interface{}, d time.Duration) error { + c.mu.Lock() + _, found := c.get(k) + if found { + c.mu.Unlock() + return fmt.Errorf("Item %s already exists", k) + } + c.set(k, x, d) + c.mu.Unlock() + return nil +} + +// Set a new value for the cache key only if it already exists, and the existing +// item hasn't expired. Returns an error otherwise. +func (c *cache) Replace(k string, x interface{}, d time.Duration) error { + c.mu.Lock() + _, found := c.get(k) + if !found { + c.mu.Unlock() + return fmt.Errorf("Item %s doesn't exist", k) + } + c.set(k, x, d) + c.mu.Unlock() + return nil +} + +// Get an item from the cache. Returns the item or nil, and a bool indicating +// whether the key was found. +func (c *cache) Get(k string) (interface{}, bool) { + c.mu.RLock() + // "Inlining" of get and Expired + item, found := c.items[k] + if !found { + c.mu.RUnlock() + return nil, false + } + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + c.mu.RUnlock() + return nil, false + } + } + c.mu.RUnlock() + return item.Object, true +} + +// GetWithExpiration returns an item and its expiration time from the cache. +// It returns the item or nil, the expiration time if one is set (if the item +// never expires a zero value for time.Time is returned), and a bool indicating +// whether the key was found. +func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) { + c.mu.RLock() + // "Inlining" of get and Expired + item, found := c.items[k] + if !found { + c.mu.RUnlock() + return nil, time.Time{}, false + } + + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + c.mu.RUnlock() + return nil, time.Time{}, false + } + + // Return the item and the expiration time + c.mu.RUnlock() + return item.Object, time.Unix(0, item.Expiration), true + } + + // If expiration <= 0 (i.e. no expiration time set) then return the item + // and a zeroed time.Time + c.mu.RUnlock() + return item.Object, time.Time{}, true +} + +func (c *cache) get(k string) (interface{}, bool) { + item, found := c.items[k] + if !found { + return nil, false + } + // "Inlining" of Expired + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + return nil, false + } + } + return item.Object, true +} + +// Increment an item of type int, int8, int16, int32, int64, uintptr, uint, +// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the +// item's value is not an integer, if it was not found, or if it is not +// possible to increment it by n. To retrieve the incremented value, use one +// of the specialized methods, e.g. IncrementInt64. +func (c *cache) Increment(k string, n int64) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case int: + v.Object = v.Object.(int) + int(n) + case int8: + v.Object = v.Object.(int8) + int8(n) + case int16: + v.Object = v.Object.(int16) + int16(n) + case int32: + v.Object = v.Object.(int32) + int32(n) + case int64: + v.Object = v.Object.(int64) + n + case uint: + v.Object = v.Object.(uint) + uint(n) + case uintptr: + v.Object = v.Object.(uintptr) + uintptr(n) + case uint8: + v.Object = v.Object.(uint8) + uint8(n) + case uint16: + v.Object = v.Object.(uint16) + uint16(n) + case uint32: + v.Object = v.Object.(uint32) + uint32(n) + case uint64: + v.Object = v.Object.(uint64) + uint64(n) + case float32: + v.Object = v.Object.(float32) + float32(n) + case float64: + v.Object = v.Object.(float64) + float64(n) + default: + c.mu.Unlock() + return fmt.Errorf("The value for %s is not an integer", k) + } + c.items[k] = v + c.mu.Unlock() + return nil +} + +// Increment an item of type float32 or float64 by n. Returns an error if the +// item's value is not floating point, if it was not found, or if it is not +// possible to increment it by n. Pass a negative number to decrement the +// value. To retrieve the incremented value, use one of the specialized methods, +// e.g. IncrementFloat64. +func (c *cache) IncrementFloat(k string, n float64) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case float32: + v.Object = v.Object.(float32) + float32(n) + case float64: + v.Object = v.Object.(float64) + n + default: + c.mu.Unlock() + return fmt.Errorf("The value for %s does not have type float32 or float64", k) + } + c.items[k] = v + c.mu.Unlock() + return nil +} + +// Increment an item of type int by n. Returns an error if the item's value is +// not an int, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementInt(k string, n int) (int, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type int8 by n. Returns an error if the item's value is +// not an int8, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementInt8(k string, n int8) (int8, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int8) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int8", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type int16 by n. Returns an error if the item's value is +// not an int16, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementInt16(k string, n int16) (int16, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int16) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int16", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type int32 by n. Returns an error if the item's value is +// not an int32, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementInt32(k string, n int32) (int32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int32", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type int64 by n. Returns an error if the item's value is +// not an int64, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementInt64(k string, n int64) (int64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int64", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uint by n. Returns an error if the item's value is +// not an uint, or if it was not found. If there is no error, the incremented +// value is returned. +func (c *cache) IncrementUint(k string, n uint) (uint, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uintptr by n. Returns an error if the item's value +// is not an uintptr, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uintptr) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uintptr", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uint8 by n. Returns an error if the item's value +// is not an uint8, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementUint8(k string, n uint8) (uint8, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint8) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint8", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uint16 by n. Returns an error if the item's value +// is not an uint16, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementUint16(k string, n uint16) (uint16, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint16) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint16", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uint32 by n. Returns an error if the item's value +// is not an uint32, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementUint32(k string, n uint32) (uint32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint32", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type uint64 by n. Returns an error if the item's value +// is not an uint64, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementUint64(k string, n uint64) (uint64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint64", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type float32 by n. Returns an error if the item's value +// is not an float32, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementFloat32(k string, n float32) (float32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float32", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Increment an item of type float64 by n. Returns an error if the item's value +// is not an float64, or if it was not found. If there is no error, the +// incremented value is returned. +func (c *cache) IncrementFloat64(k string, n float64) (float64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float64", k) + } + nv := rv + n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type int, int8, int16, int32, int64, uintptr, uint, +// uint8, uint32, or uint64, float32 or float64 by n. Returns an error if the +// item's value is not an integer, if it was not found, or if it is not +// possible to decrement it by n. To retrieve the decremented value, use one +// of the specialized methods, e.g. DecrementInt64. +func (c *cache) Decrement(k string, n int64) error { + // TODO: Implement Increment and Decrement more cleanly. + // (Cannot do Increment(k, n*-1) for uints.) + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item not found") + } + switch v.Object.(type) { + case int: + v.Object = v.Object.(int) - int(n) + case int8: + v.Object = v.Object.(int8) - int8(n) + case int16: + v.Object = v.Object.(int16) - int16(n) + case int32: + v.Object = v.Object.(int32) - int32(n) + case int64: + v.Object = v.Object.(int64) - n + case uint: + v.Object = v.Object.(uint) - uint(n) + case uintptr: + v.Object = v.Object.(uintptr) - uintptr(n) + case uint8: + v.Object = v.Object.(uint8) - uint8(n) + case uint16: + v.Object = v.Object.(uint16) - uint16(n) + case uint32: + v.Object = v.Object.(uint32) - uint32(n) + case uint64: + v.Object = v.Object.(uint64) - uint64(n) + case float32: + v.Object = v.Object.(float32) - float32(n) + case float64: + v.Object = v.Object.(float64) - float64(n) + default: + c.mu.Unlock() + return fmt.Errorf("The value for %s is not an integer", k) + } + c.items[k] = v + c.mu.Unlock() + return nil +} + +// Decrement an item of type float32 or float64 by n. Returns an error if the +// item's value is not floating point, if it was not found, or if it is not +// possible to decrement it by n. Pass a negative number to decrement the +// value. To retrieve the decremented value, use one of the specialized methods, +// e.g. DecrementFloat64. +func (c *cache) DecrementFloat(k string, n float64) error { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return fmt.Errorf("Item %s not found", k) + } + switch v.Object.(type) { + case float32: + v.Object = v.Object.(float32) - float32(n) + case float64: + v.Object = v.Object.(float64) - n + default: + c.mu.Unlock() + return fmt.Errorf("The value for %s does not have type float32 or float64", k) + } + c.items[k] = v + c.mu.Unlock() + return nil +} + +// Decrement an item of type int by n. Returns an error if the item's value is +// not an int, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementInt(k string, n int) (int, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type int8 by n. Returns an error if the item's value is +// not an int8, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementInt8(k string, n int8) (int8, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int8) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int8", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type int16 by n. Returns an error if the item's value is +// not an int16, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementInt16(k string, n int16) (int16, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int16) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int16", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type int32 by n. Returns an error if the item's value is +// not an int32, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementInt32(k string, n int32) (int32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int32", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type int64 by n. Returns an error if the item's value is +// not an int64, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementInt64(k string, n int64) (int64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(int64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an int64", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uint by n. Returns an error if the item's value is +// not an uint, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementUint(k string, n uint) (uint, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uintptr by n. Returns an error if the item's value +// is not an uintptr, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uintptr) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uintptr", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uint8 by n. Returns an error if the item's value is +// not an uint8, or if it was not found. If there is no error, the decremented +// value is returned. +func (c *cache) DecrementUint8(k string, n uint8) (uint8, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint8) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint8", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uint16 by n. Returns an error if the item's value +// is not an uint16, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementUint16(k string, n uint16) (uint16, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint16) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint16", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uint32 by n. Returns an error if the item's value +// is not an uint32, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementUint32(k string, n uint32) (uint32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint32", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type uint64 by n. Returns an error if the item's value +// is not an uint64, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementUint64(k string, n uint64) (uint64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(uint64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an uint64", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type float32 by n. Returns an error if the item's value +// is not an float32, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementFloat32(k string, n float32) (float32, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float32) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float32", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Decrement an item of type float64 by n. Returns an error if the item's value +// is not an float64, or if it was not found. If there is no error, the +// decremented value is returned. +func (c *cache) DecrementFloat64(k string, n float64) (float64, error) { + c.mu.Lock() + v, found := c.items[k] + if !found || v.Expired() { + c.mu.Unlock() + return 0, fmt.Errorf("Item %s not found", k) + } + rv, ok := v.Object.(float64) + if !ok { + c.mu.Unlock() + return 0, fmt.Errorf("The value for %s is not an float64", k) + } + nv := rv - n + v.Object = nv + c.items[k] = v + c.mu.Unlock() + return nv, nil +} + +// Delete an item from the cache. Does nothing if the key is not in the cache. +func (c *cache) Delete(k string) { + c.mu.Lock() + v, evicted := c.delete(k) + c.mu.Unlock() + if evicted { + c.onEvicted(k, v) + } +} + +func (c *cache) delete(k string) (interface{}, bool) { + if c.onEvicted != nil { + if v, found := c.items[k]; found { + delete(c.items, k) + return v.Object, true + } + } + delete(c.items, k) + return nil, false +} + +type keyAndValue struct { + key string + value interface{} +} + +// Delete all expired items from the cache. +func (c *cache) DeleteExpired() { + var evictedItems []keyAndValue + now := time.Now().UnixNano() + c.mu.Lock() + for k, v := range c.items { + // "Inlining" of expired + if v.Expiration > 0 && now > v.Expiration { + ov, evicted := c.delete(k) + if evicted { + evictedItems = append(evictedItems, keyAndValue{k, ov}) + } + } + } + c.mu.Unlock() + for _, v := range evictedItems { + c.onEvicted(v.key, v.value) + } +} + +// Sets an (optional) function that is called with the key and value when an +// item is evicted from the cache. (Including when it is deleted manually, but +// not when it is overwritten.) Set to nil to disable. +func (c *cache) OnEvicted(f func(string, interface{})) { + c.mu.Lock() + c.onEvicted = f + c.mu.Unlock() +} + +// Write the cache's items (using Gob) to an io.Writer. +// +// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the +// documentation for NewFrom().) +func (c *cache) Save(w io.Writer) (err error) { + enc := gob.NewEncoder(w) + defer func() { + if x := recover(); x != nil { + err = fmt.Errorf("Error registering item types with Gob library") + } + }() + c.mu.RLock() + defer c.mu.RUnlock() + for _, v := range c.items { + gob.Register(v.Object) + } + err = enc.Encode(&c.items) + return +} + +// Save the cache's items to the given filename, creating the file if it +// doesn't exist, and overwriting it if it does. +// +// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the +// documentation for NewFrom().) +func (c *cache) SaveFile(fname string) error { + fp, err := os.Create(fname) + if err != nil { + return err + } + err = c.Save(fp) + if err != nil { + fp.Close() + return err + } + return fp.Close() +} + +// Add (Gob-serialized) cache items from an io.Reader, excluding any items with +// keys that already exist (and haven't expired) in the current cache. +// +// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the +// documentation for NewFrom().) +func (c *cache) Load(r io.Reader) error { + dec := gob.NewDecoder(r) + items := map[string]Item{} + err := dec.Decode(&items) + if err == nil { + c.mu.Lock() + defer c.mu.Unlock() + for k, v := range items { + ov, found := c.items[k] + if !found || ov.Expired() { + c.items[k] = v + } + } + } + return err +} + +// Load and add cache items from the given filename, excluding any items with +// keys that already exist in the current cache. +// +// NOTE: This method is deprecated in favor of c.Items() and NewFrom() (see the +// documentation for NewFrom().) +func (c *cache) LoadFile(fname string) error { + fp, err := os.Open(fname) + if err != nil { + return err + } + err = c.Load(fp) + if err != nil { + fp.Close() + return err + } + return fp.Close() +} + +// Copies all unexpired items in the cache into a new map and returns it. +func (c *cache) Items() map[string]Item { + c.mu.RLock() + defer c.mu.RUnlock() + m := make(map[string]Item, len(c.items)) + now := time.Now().UnixNano() + for k, v := range c.items { + // "Inlining" of Expired + if v.Expiration > 0 { + if now > v.Expiration { + continue + } + } + m[k] = v + } + return m +} + +// Returns the number of items in the cache. This may include items that have +// expired, but have not yet been cleaned up. +func (c *cache) ItemCount() int { + c.mu.RLock() + n := len(c.items) + c.mu.RUnlock() + return n +} + +// Delete all items from the cache. +func (c *cache) Flush() { + c.mu.Lock() + c.items = map[string]Item{} + c.mu.Unlock() +} + +type janitor struct { + Interval time.Duration + stop chan bool +} + +func (j *janitor) Run(c *cache) { + ticker := time.NewTicker(j.Interval) + for { + select { + case <-ticker.C: + c.DeleteExpired() + case <-j.stop: + ticker.Stop() + return + } + } +} + +func stopJanitor(c *Cache) { + c.janitor.stop <- true +} + +func runJanitor(c *cache, ci time.Duration) { + j := &janitor{ + Interval: ci, + stop: make(chan bool), + } + c.janitor = j + go j.Run(c) +} + +func newCache(de time.Duration, m map[string]Item) *cache { + if de == 0 { + de = -1 + } + c := &cache{ + defaultExpiration: de, + items: m, + } + return c +} + +func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]Item) *Cache { + c := newCache(de, m) + // This trick ensures that the janitor goroutine (which--granted it + // was enabled--is running DeleteExpired on c forever) does not keep + // the returned C object from being garbage collected. When it is + // garbage collected, the finalizer stops the janitor goroutine, after + // which c can be collected. + C := &Cache{c} + if ci > 0 { + runJanitor(c, ci) + runtime.SetFinalizer(C, stopJanitor) + } + return C +} + +// Return a new cache with a given default expiration duration and cleanup +// interval. If the expiration duration is less than one (or NoExpiration), +// the items in the cache never expire (by default), and must be deleted +// manually. If the cleanup interval is less than one, expired items are not +// deleted from the cache before calling c.DeleteExpired(). +func New(defaultExpiration, cleanupInterval time.Duration) *Cache { + items := make(map[string]Item) + return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) +} + +// Return a new cache with a given default expiration duration and cleanup +// interval. If the expiration duration is less than one (or NoExpiration), +// the items in the cache never expire (by default), and must be deleted +// manually. If the cleanup interval is less than one, expired items are not +// deleted from the cache before calling c.DeleteExpired(). +// +// NewFrom() also accepts an items map which will serve as the underlying map +// for the cache. This is useful for starting from a deserialized cache +// (serialized using e.g. gob.Encode() on c.Items()), or passing in e.g. +// make(map[string]Item, 500) to improve startup performance when the cache +// is expected to reach a certain minimum size. +// +// Only the cache's methods synchronize access to this map, so it is not +// recommended to keep any references to the map around after creating a cache. +// If need be, the map can be accessed at a later point using c.Items() (subject +// to the same caveat.) +// +// Note regarding serialization: When using e.g. gob, make sure to +// gob.Register() the individual types stored in the cache before encoding a +// map retrieved with c.Items(), and to register those same types before +// decoding a blob containing an items map. +func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache { + return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) +} diff --git a/vendor/github.com/patrickmn/go-cache/sharded.go b/vendor/github.com/patrickmn/go-cache/sharded.go new file mode 100644 index 000000000..bcc0538bc --- /dev/null +++ b/vendor/github.com/patrickmn/go-cache/sharded.go @@ -0,0 +1,192 @@ +package cache + +import ( + "crypto/rand" + "math" + "math/big" + insecurerand "math/rand" + "os" + "runtime" + "time" +) + +// This is an experimental and unexported (for now) attempt at making a cache +// with better algorithmic complexity than the standard one, namely by +// preventing write locks of the entire cache when an item is added. As of the +// time of writing, the overhead of selecting buckets results in cache +// operations being about twice as slow as for the standard cache with small +// total cache sizes, and faster for larger ones. +// +// See cache_test.go for a few benchmarks. + +type unexportedShardedCache struct { + *shardedCache +} + +type shardedCache struct { + seed uint32 + m uint32 + cs []*cache + janitor *shardedJanitor +} + +// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead. +func djb33(seed uint32, k string) uint32 { + var ( + l = uint32(len(k)) + d = 5381 + seed + l + i = uint32(0) + ) + // Why is all this 5x faster than a for loop? + if l >= 4 { + for i < l-4 { + d = (d * 33) ^ uint32(k[i]) + d = (d * 33) ^ uint32(k[i+1]) + d = (d * 33) ^ uint32(k[i+2]) + d = (d * 33) ^ uint32(k[i+3]) + i += 4 + } + } + switch l - i { + case 1: + case 2: + d = (d * 33) ^ uint32(k[i]) + case 3: + d = (d * 33) ^ uint32(k[i]) + d = (d * 33) ^ uint32(k[i+1]) + case 4: + d = (d * 33) ^ uint32(k[i]) + d = (d * 33) ^ uint32(k[i+1]) + d = (d * 33) ^ uint32(k[i+2]) + } + return d ^ (d >> 16) +} + +func (sc *shardedCache) bucket(k string) *cache { + return sc.cs[djb33(sc.seed, k)%sc.m] +} + +func (sc *shardedCache) Set(k string, x interface{}, d time.Duration) { + sc.bucket(k).Set(k, x, d) +} + +func (sc *shardedCache) Add(k string, x interface{}, d time.Duration) error { + return sc.bucket(k).Add(k, x, d) +} + +func (sc *shardedCache) Replace(k string, x interface{}, d time.Duration) error { + return sc.bucket(k).Replace(k, x, d) +} + +func (sc *shardedCache) Get(k string) (interface{}, bool) { + return sc.bucket(k).Get(k) +} + +func (sc *shardedCache) Increment(k string, n int64) error { + return sc.bucket(k).Increment(k, n) +} + +func (sc *shardedCache) IncrementFloat(k string, n float64) error { + return sc.bucket(k).IncrementFloat(k, n) +} + +func (sc *shardedCache) Decrement(k string, n int64) error { + return sc.bucket(k).Decrement(k, n) +} + +func (sc *shardedCache) Delete(k string) { + sc.bucket(k).Delete(k) +} + +func (sc *shardedCache) DeleteExpired() { + for _, v := range sc.cs { + v.DeleteExpired() + } +} + +// Returns the items in the cache. This may include items that have expired, +// but have not yet been cleaned up. If this is significant, the Expiration +// fields of the items should be checked. Note that explicit synchronization +// is needed to use a cache and its corresponding Items() return values at +// the same time, as the maps are shared. +func (sc *shardedCache) Items() []map[string]Item { + res := make([]map[string]Item, len(sc.cs)) + for i, v := range sc.cs { + res[i] = v.Items() + } + return res +} + +func (sc *shardedCache) Flush() { + for _, v := range sc.cs { + v.Flush() + } +} + +type shardedJanitor struct { + Interval time.Duration + stop chan bool +} + +func (j *shardedJanitor) Run(sc *shardedCache) { + j.stop = make(chan bool) + tick := time.Tick(j.Interval) + for { + select { + case <-tick: + sc.DeleteExpired() + case <-j.stop: + return + } + } +} + +func stopShardedJanitor(sc *unexportedShardedCache) { + sc.janitor.stop <- true +} + +func runShardedJanitor(sc *shardedCache, ci time.Duration) { + j := &shardedJanitor{ + Interval: ci, + } + sc.janitor = j + go j.Run(sc) +} + +func newShardedCache(n int, de time.Duration) *shardedCache { + max := big.NewInt(0).SetUint64(uint64(math.MaxUint32)) + rnd, err := rand.Int(rand.Reader, max) + var seed uint32 + if err != nil { + os.Stderr.Write([]byte("WARNING: go-cache's newShardedCache failed to read from the system CSPRNG (/dev/urandom or equivalent.) Your system's security may be compromised. Continuing with an insecure seed.\n")) + seed = insecurerand.Uint32() + } else { + seed = uint32(rnd.Uint64()) + } + sc := &shardedCache{ + seed: seed, + m: uint32(n), + cs: make([]*cache, n), + } + for i := 0; i < n; i++ { + c := &cache{ + defaultExpiration: de, + items: map[string]Item{}, + } + sc.cs[i] = c + } + return sc +} + +func unexportedNewSharded(defaultExpiration, cleanupInterval time.Duration, shards int) *unexportedShardedCache { + if defaultExpiration == 0 { + defaultExpiration = -1 + } + sc := newShardedCache(shards, defaultExpiration) + SC := &unexportedShardedCache{sc} + if cleanupInterval > 0 { + runShardedJanitor(sc, cleanupInterval) + runtime.SetFinalizer(SC, stopShardedJanitor) + } + return SC +} diff --git a/vendor/github.com/projectcalico/go-json/LICENSE.txt b/vendor/github.com/projectcalico/go-json/LICENSE.txt deleted file mode 100644 index 6a66aea5e..000000000 --- a/vendor/github.com/projectcalico/go-json/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -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/github.com/projectcalico/go-json/json/decode.go b/vendor/github.com/projectcalico/go-json/json/decode.go deleted file mode 100644 index e0a5a575b..000000000 --- a/vendor/github.com/projectcalico/go-json/json/decode.go +++ /dev/null @@ -1,1322 +0,0 @@ -// Copyright 2010 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. - -// Represents JSON data structure using native Go types: booleans, floats, -// strings, arrays, and maps. - -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "errors" - "fmt" - "reflect" - "runtime" - "strconv" - "strings" - "unicode" - "unicode/utf16" - "unicode/utf8" -) - -// Unmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. -// -// Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, -// with the following additional rules: -// -// To unmarshal JSON into a pointer, Unmarshal first handles the case of -// the JSON being the JSON literal null. In that case, Unmarshal sets -// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into -// the value pointed at by the pointer. If the pointer is nil, Unmarshal -// allocates a new value for it to point to. -// -// To unmarshal JSON into a value implementing the Unmarshaler interface, -// Unmarshal calls that value's UnmarshalJSON method, including -// when the input is a JSON null. -// Otherwise, if the value implements encoding.TextUnmarshaler -// and the input is a JSON quoted string, Unmarshal calls that value's -// UnmarshalText method with the unquoted form of the string. -// -// To unmarshal JSON into a struct, Unmarshal matches incoming object -// keys to the keys used by Marshal (either the struct field name or its tag), -// preferring an exact match but also accepting a case-insensitive match. -// Unmarshal will only set exported fields of the struct. -// -// To unmarshal JSON into an interface value, -// Unmarshal stores one of these in the interface value: -// -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null -// -// To unmarshal a JSON array into a slice, Unmarshal resets the slice length -// to zero and then appends each element to the slice. -// As a special case, to unmarshal an empty JSON array into a slice, -// Unmarshal replaces the slice with a new empty slice. -// -// To unmarshal a JSON array into a Go array, Unmarshal decodes -// JSON array elements into corresponding Go array elements. -// If the Go array is smaller than the JSON array, -// the additional JSON array elements are discarded. -// If the JSON array is smaller than the Go array, -// the additional Go array elements are set to zero values. -// -// To unmarshal a JSON object into a map, Unmarshal first establishes a map to -// use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal -// reuses the existing map, keeping existing entries. Unmarshal then stores -// key-value pairs from the JSON object into the map. The map's key type must -// either be a string, an integer, or implement encoding.TextUnmarshaler. -// -// If a JSON value is not appropriate for a given target type, -// or if a JSON number overflows the target type, Unmarshal -// skips that field and completes the unmarshaling as best it can. -// If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. -// -// The JSON null value unmarshals into an interface, map, pointer, or slice -// by setting that Go value to nil. Because null is often used in JSON to mean -// ``not present,'' unmarshaling a JSON null into any other Go type has no effect -// on the value and produces no error. -// -// When unmarshaling quoted strings, invalid UTF-8 or -// invalid UTF-16 surrogate pairs are not treated as an error. -// Instead, they are replaced by the Unicode replacement -// character U+FFFD. -// -func Unmarshal(data []byte, v interface{}) error { - // Check for well-formedness. - // Avoids filling out half a data structure - // before discovering a JSON syntax error. - var d decodeState - err := checkValid(data, &d.scan) - if err != nil { - return err - } - - d.init(data) - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by types -// that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid encoding of -// a JSON value. UnmarshalJSON must copy the JSON data -// if it wishes to retain the data after returning. -// -// By convention, to approximate the behavior of Unmarshal itself, -// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. -type Unmarshaler interface { - UnmarshalJSON([]byte) error -} - -// An UnmarshalTypeError describes a JSON value that was -// not appropriate for a value of a specific Go type. -type UnmarshalTypeError struct { - Value string // description of JSON value - "bool", "array", "number -5" - Type reflect.Type // type of Go value it could not be assigned to - Offset int64 // error occurred after reading Offset bytes - Struct string // name of the struct type containing the field - Field string // name of the field holding the Go value - Data string // field data -} - -func (e *UnmarshalTypeError) Error() string { - if e.Struct != "" || e.Field != "" { - return "cannot parse " + e.Value + " '" + e.Data + "' into field " + e.Struct + "." + e.Field + " of type " + e.Type.String() - } - return "cannot parse " + e.Value + " '" + e.Data + "' into value of type " + e.Type.String() -} - -// An UnmarshalFieldError describes a JSON object key that -// led to an unexported (and therefore unwritable) struct field. -// (No longer used; kept for compatibility.) -type UnmarshalFieldError struct { - Key string - Type reflect.Type - Field reflect.StructField -} - -func (e *UnmarshalFieldError) Error() string { - return "cannot parse object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String() -} - -// An UnmarshalUnknownFieldsError describes a JSON object that -// has unrecognized keys. -type UnmarshalUnknownFieldsError struct { - Keys []string // JSON keys that could not be mapped to fields -} - -func (e UnmarshalUnknownFieldsError) Error() string { - if len(e.Keys) == 1 { - return fmt.Sprintf("field in document is not recognized or is in the wrong location: %s", - e.Keys[0]) - } else if len(e.Keys) > 5 { - return fmt.Sprintf("fields in document are not recognized or are in the wrong location: %s (+ %d more)", - strings.Join(e.Keys[:4], ", "), len(e.Keys)-4) - } else { - return fmt.Sprintf("fields in document are not recognized or are in the wrong location: %s", - strings.Join(e.Keys, ", ")) - } -} - -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) -type InvalidUnmarshalError struct { - Type reflect.Type -} - -func (e *InvalidUnmarshalError) Error() string { - if e.Type == nil { - return "json: Unmarshal(nil)" - } - - if e.Type.Kind() != reflect.Ptr { - return "json: Unmarshal(non-pointer " + e.Type.String() + ")" - } - return "json: Unmarshal(nil " + e.Type.String() + ")" -} - -func (d *decodeState) unmarshal(v interface{}) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - err = r.(error) - } - }() - - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { - return &InvalidUnmarshalError{reflect.TypeOf(v)} - } - - d.scan.reset() - // We decode rv not rv.Elem because the Unmarshaler interface - // test must be applied at the top level of the value. - d.value(rv) - - // If there are unknown fields save the error. - if len(d.unknownFields) > 0 { - d.saveError(&UnmarshalUnknownFieldsError{Keys: d.unknownFields}) - } - - return d.savedError -} - -// A Number represents a JSON number literal. -type Number string - -// String returns the literal text of the number. -func (n Number) String() string { return string(n) } - -// Float64 returns the number as a float64. -func (n Number) Float64() (float64, error) { - return strconv.ParseFloat(string(n), 64) -} - -// Int64 returns the number as an int64. -func (n Number) Int64() (int64, error) { - return strconv.ParseInt(string(n), 10, 64) -} - -// isValidNumber reports whether s is a valid JSON number literal. -func isValidNumber(s string) bool { - // This function implements the JSON numbers grammar. - // See https://tools.ietf.org/html/rfc7159#section-6 - // and http://json.org/number.gif - - if s == "" { - return false - } - - // Optional - - if s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - - // Digits - switch { - default: - return false - - case s[0] == '0': - s = s[1:] - - case '1' <= s[0] && s[0] <= '9': - s = s[1:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // . followed by 1 or more digits. - if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { - s = s[2:] - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // e or E followed by an optional - or + and - // 1 or more digits. - if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { - s = s[1:] - if s[0] == '+' || s[0] == '-' { - s = s[1:] - if s == "" { - return false - } - } - for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { - s = s[1:] - } - } - - // Make sure we are at the end. - return s == "" -} - -// decodeState represents the state while decoding a JSON value. -type decodeState struct { - data []byte - off int // read offset in data - scan scanner - nextscan scanner // for calls to nextValue - errorContext struct { // provides context for type errors - Struct string - Field string - } - savedError error - useNumber bool - disallowUnknownFields bool - unknownFields []string -} - -// errPhase is used for errors that should not happen unless -// there is a bug in the JSON decoder or something is editing -// the data slice while the decoder executes. -var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?") - -func (d *decodeState) init(data []byte) *decodeState { - d.data = data - d.off = 0 - d.savedError = nil - d.errorContext.Struct = "" - d.errorContext.Field = "" - return d -} - -// error aborts the decoding by panicking with err. -func (d *decodeState) error(err error) { - panic(d.addErrorContext(err)) -} - -// saveError saves the first err it is called with, -// for reporting at the end of the unmarshal. -func (d *decodeState) saveError(err error) { - if d.savedError == nil { - d.savedError = d.addErrorContext(err) - } -} - -// addErrorContext returns a new error enhanced with information from d.errorContext -func (d *decodeState) addErrorContext(err error) error { - if d.errorContext.Struct != "" || d.errorContext.Field != "" { - switch err := err.(type) { - case *UnmarshalTypeError: - err.Struct = d.errorContext.Struct - err.Field = d.errorContext.Field - return err - } - } - return err -} - -// next cuts off and returns the next full JSON value in d.data[d.off:]. -// The next value is known to be an object or array, not a literal. -func (d *decodeState) next() []byte { - c := d.data[d.off] - item, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // Our scanner has seen the opening brace/bracket - // and thinks we're still in the middle of the object. - // invent a closing brace/bracket to get it out. - if c == '{' { - d.scan.step(&d.scan, '}') - } else { - d.scan.step(&d.scan, ']') - } - - return item -} - -// scanWhile processes bytes in d.data[d.off:] until it -// receives a scan code not equal to op. -// It updates d.off and returns the new scan code. -func (d *decodeState) scanWhile(op int) int { - var newOp int - for { - if d.off >= len(d.data) { - newOp = d.scan.eof() - d.off = len(d.data) + 1 // mark processed EOF with len+1 - } else { - c := d.data[d.off] - d.off++ - newOp = d.scan.step(&d.scan, c) - } - if newOp != op { - break - } - } - return newOp -} - -// value decodes a JSON value from d.data[d.off:] into the value. -// it updates d.off to point past the decoded value. -func (d *decodeState) value(v reflect.Value) { - if !v.IsValid() { - _, rest, err := nextValue(d.data[d.off:], &d.nextscan) - if err != nil { - d.error(err) - } - d.off = len(d.data) - len(rest) - - // d.scan thinks we're still at the beginning of the item. - // Feed in an empty string - the shortest, simplest value - - // so that it knows we got to the end of the value. - if d.scan.redo { - // rewind. - d.scan.redo = false - d.scan.step = stateBeginValue - } - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - - n := len(d.scan.parseState) - if n > 0 && d.scan.parseState[n-1] == parseObjectKey { - // d.scan thinks we just read an object key; finish the object - d.scan.step(&d.scan, ':') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '"') - d.scan.step(&d.scan, '}') - } - - return - } - - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(v) - - case scanBeginObject: - d.object(v) - - case scanBeginLiteral: - d.literal(v) - } -} - -type unquotedValue struct{} - -// valueQuoted is like value but decodes a -// quoted string literal or literal null into an interface value. -// If it finds anything other than a quoted string literal or null, -// valueQuoted returns unquotedValue{}. -func (d *decodeState) valueQuoted() interface{} { - switch op := d.scanWhile(scanSkipSpace); op { - default: - d.error(errPhase) - - case scanBeginArray: - d.array(reflect.Value{}) - - case scanBeginObject: - d.object(reflect.Value{}) - - case scanBeginLiteral: - switch v := d.literalInterface().(type) { - case nil, string: - return v - } - } - return unquotedValue{} -} - -// indirect walks down v allocating pointers as needed, -// until it gets to a non-pointer. -// if it encounters an Unmarshaler, indirect stops and returns that. -// if decodingNull is true, indirect stops at the last pointer so it can be set to nil. -func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { - // If v is a named type and is addressable, - // start with its address, so that if the type has pointer methods, - // we find them. - if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { - v = v.Addr() - } - for { - // Load value from interface, but only if the result will be - // usefully addressable. - if v.Kind() == reflect.Interface && !v.IsNil() { - e := v.Elem() - if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { - v = e - continue - } - } - - if v.Kind() != reflect.Ptr { - break - } - - if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { - break - } - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - if v.Type().NumMethod() > 0 { - if u, ok := v.Interface().(Unmarshaler); ok { - return u, nil, reflect.Value{} - } - if !decodingNull { - if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { - return nil, u, reflect.Value{} - } - } - } - v = v.Elem() - } - return nil, nil, v -} - -// array consumes an array from d.data[d.off-1:], decoding into the value v. -// the first byte of the array ('[') has been read already. -func (d *decodeState) array(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - offset := d.off - d.off-- - key := string(d.next()) - d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(offset), Data: key}) - return - } - - v = pv - - // Check type of target. - switch v.Kind() { - case reflect.Interface: - if v.NumMethod() == 0 { - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return - } - // Otherwise it's invalid. - fallthrough - default: - offset := d.off - d.off-- - key := string(d.next()) - d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(offset), Data: key}) - return - case reflect.Array: - case reflect.Slice: - break - } - - i := 0 - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - // Get element of array, growing if necessary. - if v.Kind() == reflect.Slice { - // Grow slice if necessary - if i >= v.Cap() { - newcap := v.Cap() + v.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) - reflect.Copy(newv, v) - v.Set(newv) - } - if i >= v.Len() { - v.SetLen(i + 1) - } - } - - if i < v.Len() { - // Decode into element. - d.value(v.Index(i)) - } else { - // Ran out of fixed array: skip. - d.value(reflect.Value{}) - } - i++ - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - - if i < v.Len() { - if v.Kind() == reflect.Array { - // Array. Zero the rest. - z := reflect.Zero(v.Type().Elem()) - for ; i < v.Len(); i++ { - v.Index(i).Set(z) - } - } else { - v.SetLen(i) - } - } - if i == 0 && v.Kind() == reflect.Slice { - v.Set(reflect.MakeSlice(v.Type(), 0, 0)) - } -} - -var nullLiteral = []byte("null") -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() - -// object consumes an object from d.data[d.off-1:], decoding into the value v. -// the first byte ('{') of the object has been read already. -func (d *decodeState) object(v reflect.Value) { - // Check for unmarshaler. - u, ut, pv := d.indirect(v, false) - if u != nil { - d.off-- - err := u.UnmarshalJSON(d.next()) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - offset := d.off - d.off-- - key := string(d.next()) // skip over { } in input - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(offset), Data: key}) - return - } - v = pv - - // Decoding into nil interface? Switch to non-reflect code. - if v.Kind() == reflect.Interface && v.NumMethod() == 0 { - v.Set(reflect.ValueOf(d.objectInterface())) - return - } - - // Check type of target: - // struct or - // map[T1]T2 where T1 is string, an integer type, - // or an encoding.TextUnmarshaler - switch v.Kind() { - case reflect.Map: - // Map key must either have string kind, have an integer kind, - // or be an encoding.TextUnmarshaler. - t := v.Type() - switch t.Key().Kind() { - case reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - default: - if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { - offset := d.off - d.off-- - key := string(d.next()) // skip over { } in input - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(offset), Data: key}) - return - } - } - if v.IsNil() { - v.Set(reflect.MakeMap(t)) - } - case reflect.Struct: - // ok - default: - offset := d.off - d.off-- - key := string(d.next()) // skip over { } in input - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(offset), Data: key}) - return - } - - var mapElem reflect.Value - - // Key corresponding to ith field. - var usedFields [][]byte - - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquoteBytes(item) - if !ok { - d.error(errPhase) - } - - // Figure out field corresponding to key. - var subv reflect.Value - destring := false // whether the value is wrapped in a string to be decoded first - - if v.Kind() == reflect.Map { - elemType := v.Type().Elem() - if !mapElem.IsValid() { - mapElem = reflect.New(elemType).Elem() - } else { - mapElem.Set(reflect.Zero(elemType)) - } - subv = mapElem - } else { - var f *field - var idx int - fields := cachedTypeFields(v.Type()) - for i := range fields { - ff := &fields[i] - if bytes.Equal(ff.nameBytes, key) { - f = ff - idx = i - break - } - if f == nil && ff.equalFold(ff.nameBytes, key) { - f = ff - idx = i - } - } - if f != nil { - subv = v - destring = f.quoted - for _, i := range f.index { - if subv.Kind() == reflect.Ptr { - if subv.IsNil() { - subv.Set(reflect.New(subv.Type().Elem())) - } - subv = subv.Elem() - } - subv = subv.Field(i) - } - d.errorContext.Field = f.name - d.errorContext.Struct = v.Type().Name() - - if d.disallowUnknownFields { - if usedFields == nil { - usedFields = make([][]byte, len(fields)) - } - if prev := usedFields[idx]; prev != nil { - d.unknownFields = append(d.unknownFields, string(prev)) - } - usedFields[idx] = key - } - } else if d.disallowUnknownFields { - d.unknownFields = append(d.unknownFields, string(key)) - } - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - if destring { - switch qv := d.valueQuoted().(type) { - case nil: - d.literalStore(nullLiteral, subv, false) - case string: - d.literalStore([]byte(qv), subv, true) - default: - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type())) - } - } else { - d.value(subv) - } - - // Write value back to map; - // if using struct, subv points into struct already. - if v.Kind() == reflect.Map { - kt := v.Type().Key() - var kv reflect.Value - switch { - case kt.Kind() == reflect.String: - kv = reflect.ValueOf(key).Convert(kt) - case reflect.PtrTo(kt).Implements(textUnmarshalerType): - kv = reflect.New(v.Type().Key()) - d.literalStore(item, kv, true) - kv = kv.Elem() - default: - switch kt.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - s := string(key) - n, err := strconv.ParseInt(s, 10, 64) - if err != nil || reflect.Zero(kt).OverflowInt(n) { - d.saveError(&UnmarshalTypeError{Value: "number", Type: kt, Offset: int64(start + 1), Data: s}) - return - } - kv = reflect.ValueOf(n).Convert(kt) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - s := string(key) - n, err := strconv.ParseUint(s, 10, 64) - if err != nil || reflect.Zero(kt).OverflowUint(n) { - d.saveError(&UnmarshalTypeError{Value: "number", Type: kt, Offset: int64(start + 1), Data: s}) - return - } - kv = reflect.ValueOf(n).Convert(kt) - default: - panic("json: Unexpected key type") // should never occur - } - } - v.SetMapIndex(kv, subv) - } - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - - d.errorContext.Struct = "" - d.errorContext.Field = "" - } -} - -// literal consumes a literal from d.data[d.off-1:], decoding into the value v. -// The first byte of the literal has been read already -// (that's how the caller knows it's a literal). -func (d *decodeState) literal(v reflect.Value) { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - - d.literalStore(d.data[start:d.off], v, false) -} - -// convertNumber converts the number literal s to a float64 or a Number -// depending on the setting of d.useNumber. -func (d *decodeState) convertNumber(s string) (interface{}, error) { - if d.useNumber { - return Number(s), nil - } - f, err := strconv.ParseFloat(s, 64) - if err != nil { - return nil, &UnmarshalTypeError{Value: "number ", Type: reflect.TypeOf(0.0), Offset: int64(d.off), Data: s} - } - return f, nil -} - -var numberType = reflect.TypeOf(Number("")) - -// literalStore decodes a literal stored in item into v. -// -// fromQuoted indicates whether this literal came from unwrapping a -// string from the ",string" struct tag option. this is used only to -// produce more helpful error messages. -func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) { - // Check for unmarshaler. - if len(item) == 0 { - //Empty string given - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - return - } - isNull := item[0] == 'n' // null - u, ut, pv := d.indirect(v, isNull) - if u != nil { - err := u.UnmarshalJSON(item) - if err != nil { - d.error(err) - } - return - } - if ut != nil { - if item[0] != '"' { - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - var val string - switch item[0] { - case 'n': - val = "null" - case 't', 'f': - val = "bool" - default: - val = "number" - } - d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.off), Data: string(item)}) - } - return - } - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - err := ut.UnmarshalText(s) - if err != nil { - d.error(err) - } - return - } - - v = pv - - switch c := item[0]; c { - case 'n': // null - // The main parser checks that only true and false can reach here, - // but if this was a quoted string input, it could be anything. - if fromQuoted && string(item) != "null" { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - break - } - switch v.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - v.Set(reflect.Zero(v.Type())) - // otherwise, ignore null for primitives/string - } - case 't', 'f': // true, false - value := item[0] == 't' - // The main parser checks that only true and false can reach here, - // but if this was a quoted string input, it could be anything. - if fromQuoted && string(item) != "true" && string(item) != "false" { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - break - } - switch v.Kind() { - default: - if fromQuoted { - d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off), Data: string(item)}) - } - case reflect.Bool: - v.SetBool(value) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(value)) - } else { - d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off), Data: string(item)}) - } - } - - case '"': // string - s, ok := unquoteBytes(item) - if !ok { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off), Data: string(s)}) - case reflect.Slice: - if v.Type().Elem().Kind() != reflect.Uint8 { - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off), Data: string(s)}) - break - } - b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) - n, err := base64.StdEncoding.Decode(b, s) - if err != nil { - d.saveError(err) - break - } - v.SetBytes(b[:n]) - case reflect.String: - v.SetString(string(s)) - case reflect.Interface: - if v.NumMethod() == 0 { - v.Set(reflect.ValueOf(string(s))) - } else { - d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off), Data: string(s)}) - } - } - - default: // number - if c != '-' && (c < '0' || c > '9') { - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(errPhase) - } - } - s := string(item) - switch v.Kind() { - default: - if v.Kind() == reflect.String && v.Type() == numberType { - v.SetString(s) - if !isValidNumber(s) { - d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item)) - } - break - } - if fromQuoted { - d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) - } else { - d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off), Data: string(item)}) - } - case reflect.Interface: - n, err := d.convertNumber(s) - if err != nil { - d.saveError(err) - break - } - if v.NumMethod() != 0 { - d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off), Data: string(item)}) - break - } - v.Set(reflect.ValueOf(n)) - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.ParseInt(s, 10, 64) - if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off), Data: s}) - break - } - v.SetInt(n) - - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.ParseUint(s, 10, 64) - if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off), Data: s}) - break - } - v.SetUint(n) - - case reflect.Float32, reflect.Float64: - n, err := strconv.ParseFloat(s, v.Type().Bits()) - if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off), Data: s}) - break - } - v.SetFloat(n) - } - } -} - -// The xxxInterface routines build up a value to be stored -// in an empty interface. They are not strictly necessary, -// but they avoid the weight of reflection in this common case. - -// valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() interface{} { - switch d.scanWhile(scanSkipSpace) { - default: - d.error(errPhase) - panic("unreachable") - case scanBeginArray: - return d.arrayInterface() - case scanBeginObject: - return d.objectInterface() - case scanBeginLiteral: - return d.literalInterface() - } -} - -// arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v = make([]interface{}, 0) - for { - // Look ahead for ] - can only happen on first iteration. - op := d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - - // Back up so d.value can have the byte we just read. - d.off-- - d.scan.undo(op) - - v = append(v, d.valueInterface()) - - // Next token must be , or ]. - op = d.scanWhile(scanSkipSpace) - if op == scanEndArray { - break - } - if op != scanArrayValue { - d.error(errPhase) - } - } - return v -} - -// objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) - for { - // Read opening " of string key or closing }. - op := d.scanWhile(scanSkipSpace) - if op == scanEndObject { - // closing } - can only happen on first iteration. - break - } - if op != scanBeginLiteral { - d.error(errPhase) - } - - // Read string key. - start := d.off - 1 - op = d.scanWhile(scanContinue) - item := d.data[start : d.off-1] - key, ok := unquote(item) - if !ok { - d.error(errPhase) - } - - // Read : before value. - if op == scanSkipSpace { - op = d.scanWhile(scanSkipSpace) - } - if op != scanObjectKey { - d.error(errPhase) - } - - // Read value. - m[key] = d.valueInterface() - - // Next token must be , or }. - op = d.scanWhile(scanSkipSpace) - if op == scanEndObject { - break - } - if op != scanObjectValue { - d.error(errPhase) - } - } - return m -} - -// literalInterface is like literal but returns an interface value. -func (d *decodeState) literalInterface() interface{} { - // All bytes inside literal return scanContinue op code. - start := d.off - 1 - op := d.scanWhile(scanContinue) - - // Scan read one byte too far; back up. - d.off-- - d.scan.undo(op) - item := d.data[start:d.off] - - switch c := item[0]; c { - case 'n': // null - return nil - - case 't', 'f': // true, false - return c == 't' - - case '"': // string - s, ok := unquote(item) - if !ok { - d.error(errPhase) - } - return s - - default: // number - if c != '-' && (c < '0' || c > '9') { - d.error(errPhase) - } - n, err := d.convertNumber(string(item)) - if err != nil { - d.saveError(err) - } - return n - } -} - -// getu4 decodes \uXXXX from the beginning of s, returning the hex value, -// or it returns -1. -func getu4(s []byte) rune { - if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { - return -1 - } - r, err := strconv.ParseUint(string(s[2:6]), 16, 64) - if err != nil { - return -1 - } - return rune(r) -} - -// unquote converts a quoted JSON string literal s into an actual string t. -// The rules are different than for Go, so cannot use strconv.Unquote. -func unquote(s []byte) (t string, ok bool) { - s, ok = unquoteBytes(s) - t = string(s) - return -} - -func unquoteBytes(s []byte) (t []byte, ok bool) { - if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { - return - } - s = s[1 : len(s)-1] - - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. - r := 0 - for r < len(s) { - c := s[r] - if c == '\\' || c == '"' || c < ' ' { - break - } - if c < utf8.RuneSelf { - r++ - continue - } - rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError && size == 1 { - break - } - r += size - } - if r == len(s) { - return s, true - } - - b := make([]byte, len(s)+2*utf8.UTFMax) - w := copy(b, s[0:r]) - for r < len(s) { - // Out of room? Can only happen if s is full of - // malformed UTF-8 and we're replacing each - // byte with RuneError. - if w >= len(b)-2*utf8.UTFMax { - nb := make([]byte, (len(b)+utf8.UTFMax)*2) - copy(nb, b[0:w]) - b = nb - } - switch c := s[r]; { - case c == '\\': - r++ - if r >= len(s) { - return - } - switch s[r] { - default: - return - case '"', '\\', '/', '\'': - b[w] = s[r] - r++ - w++ - case 'b': - b[w] = '\b' - r++ - w++ - case 'f': - b[w] = '\f' - r++ - w++ - case 'n': - b[w] = '\n' - r++ - w++ - case 'r': - b[w] = '\r' - r++ - w++ - case 't': - b[w] = '\t' - r++ - w++ - case 'u': - r-- - rr := getu4(s[r:]) - if rr < 0 { - return - } - r += 6 - if utf16.IsSurrogate(rr) { - rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { - // A valid pair; consume. - r += 6 - w += utf8.EncodeRune(b[w:], dec) - break - } - // Invalid surrogate; fall back to replacement rune. - rr = unicode.ReplacementChar - } - w += utf8.EncodeRune(b[w:], rr) - } - - // Quote, control characters are invalid. - case c == '"', c < ' ': - return - - // ASCII - case c < utf8.RuneSelf: - b[w] = c - r++ - w++ - - // Coerce to well-formed UTF-8. - default: - rr, size := utf8.DecodeRune(s[r:]) - r += size - w += utf8.EncodeRune(b[w:], rr) - } - } - return b[0:w], true -} diff --git a/vendor/github.com/projectcalico/go-json/json/encode.go b/vendor/github.com/projectcalico/go-json/json/encode.go deleted file mode 100644 index 07fd5c41f..000000000 --- a/vendor/github.com/projectcalico/go-json/json/encode.go +++ /dev/null @@ -1,1306 +0,0 @@ -// Copyright 2010 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 json implements encoding and decoding of JSON as defined in -// RFC 4627. The mapping between JSON and Go values is described -// in the documentation for the Marshal and Unmarshal functions. -// -// See "JSON and Go" for an introduction to this package: -// https://golang.org/doc/articles/json_and_go.html -package json - -import ( - "bytes" - "encoding" - "encoding/base64" - "fmt" - "math" - "reflect" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - "unicode" - "unicode/utf8" -) - -// Marshal returns the JSON encoding of v. -// -// Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface -// and is not a nil pointer, Marshal calls its MarshalJSON method -// to produce JSON. If no MarshalJSON method is present but the -// value implements encoding.TextMarshaler instead, Marshal calls -// its MarshalText method and encodes the result as a JSON string. -// The nil pointer exception is not strictly necessary -// but mimics a similar, necessary exception in the behavior of -// UnmarshalJSON. -// -// Otherwise, Marshal uses the following type-dependent default encodings: -// -// Boolean values encode as JSON booleans. -// -// Floating point, integer, and Number values encode as JSON numbers. -// -// String values encode as JSON strings coerced to valid UTF-8, -// replacing invalid bytes with the Unicode replacement rune. -// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e" -// to keep some browsers from misinterpreting JSON output as HTML. -// Ampersand "&" is also escaped to "\u0026" for the same reason. -// This escaping can be disabled using an Encoder that had SetEscapeHTML(false) -// called on it. -// -// Array and slice values encode as JSON arrays, except that -// []byte encodes as a base64-encoded string, and a nil slice -// encodes as the null JSON value. -// -// Struct values encode as JSON objects. -// Each exported struct field becomes a member of the object, using the -// field name as the object key, unless the field is omitted for one of the -// reasons given below. -// -// The encoding of each struct field can be customized by the format string -// stored under the "json" key in the struct field's tag. -// The format string gives the name of the field, possibly followed by a -// comma-separated list of options. The name may be empty in order to -// specify options without overriding the default field name. -// -// The "omitempty" option specifies that the field should be omitted -// from the encoding if the field has an empty value, defined as -// false, 0, a nil pointer, a nil interface value, and any empty array, -// slice, map, or string. -// -// As a special case, if the field tag is "-", the field is always omitted. -// Note that a field with name "-" can still be generated using the tag "-,". -// -// Examples of struct field tags and their meanings: -// -// // Field appears in JSON as key "myName". -// Field int `json:"myName"` -// -// // Field appears in JSON as key "myName" and -// // the field is omitted from the object if its value is empty, -// // as defined above. -// Field int `json:"myName,omitempty"` -// -// // Field appears in JSON as key "Field" (the default), but -// // the field is skipped if empty. -// // Note the leading comma. -// Field int `json:",omitempty"` -// -// // Field is ignored by this package. -// Field int `json:"-"` -// -// // Field appears in JSON as key "-". -// Field int `json:"-,"` -// -// The "string" option signals that a field is stored as JSON inside a -// JSON-encoded string. It applies only to fields of string, floating point, -// integer, or boolean types. This extra level of encoding is sometimes used -// when communicating with JavaScript programs: -// -// Int64String int64 `json:",string"` -// -// The key name will be used if it's a non-empty string consisting of -// only Unicode letters, digits, and ASCII punctuation except quotation -// marks, backslash, and comma. -// -// Anonymous struct fields are usually marshaled as if their inner exported fields -// were fields in the outer struct, subject to the usual Go visibility rules amended -// as described in the next paragraph. -// An anonymous struct field with a name given in its JSON tag is treated as -// having that name, rather than being anonymous. -// An anonymous struct field of interface type is treated the same as having -// that type as its name, rather than being anonymous. -// -// The Go visibility rules for struct fields are amended for JSON when -// deciding which field to marshal or unmarshal. If there are -// multiple fields at the same level, and that level is the least -// nested (and would therefore be the nesting level selected by the -// usual Go rules), the following extra rules apply: -// -// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered, -// even if there are multiple untagged fields that would otherwise conflict. -// -// 2) If there is exactly one field (tagged or not according to the first rule), that is selected. -// -// 3) Otherwise there are multiple fields, and all are ignored; no error occurs. -// -// Handling of anonymous struct fields is new in Go 1.1. -// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of -// an anonymous struct field in both current and earlier versions, give the field -// a JSON tag of "-". -// -// Map values encode as JSON objects. The map's key type must either be a -// string, an integer type, or implement encoding.TextMarshaler. The map keys -// are sorted and used as JSON object keys by applying the following rules, -// subject to the UTF-8 coercion described for string values above: -// - string keys are used directly -// - encoding.TextMarshalers are marshaled -// - integer keys are converted to strings -// -// Pointer values encode as the value pointed to. -// A nil pointer encodes as the null JSON value. -// -// Interface values encode as the value contained in the interface. -// A nil interface value encodes as the null JSON value. -// -// Channel, complex, and function values cannot be encoded in JSON. -// Attempting to encode such a value causes Marshal to return -// an UnsupportedTypeError. -// -// JSON cannot represent cyclic data structures and Marshal does not -// handle them. Passing cyclic structures to Marshal will result in -// an infinite recursion. -// -func Marshal(v interface{}) ([]byte, error) { - e := &encodeState{} - err := e.marshal(v, encOpts{escapeHTML: true}) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// MarshalIndent is like Marshal but applies Indent to format the output. -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { - b, err := Marshal(v) - if err != nil { - return nil, err - } - var buf bytes.Buffer - err = Indent(&buf, b, prefix, indent) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 -// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 -// so that the JSON will be safe to embed inside HTML